MON Price: $0.019087 (+4.19%)

Contract

0x0A790b53777fDB59D241D8A612B89f27424d15fC

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo27,581.765964990770183779 MON

MON Value

$526.45 (@ $0.02/MON)

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Prospect Batch398542722025-12-04 14:48:1151 days ago1764859691IN
0x0A790b53...7424d15fC
2.6 MON0.51102
Claim Mon Only397062002025-12-03 22:15:5752 days ago1764800157IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397061812025-12-03 22:15:4952 days ago1764800149IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397061622025-12-03 22:15:4252 days ago1764800142IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397061432025-12-03 22:15:3452 days ago1764800134IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397061242025-12-03 22:15:2752 days ago1764800127IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397061052025-12-03 22:15:1952 days ago1764800119IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397060862025-12-03 22:15:1152 days ago1764800111IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397060682025-12-03 22:15:0452 days ago1764800104IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397060492025-12-03 22:14:5652 days ago1764800096IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397060302025-12-03 22:14:4952 days ago1764800089IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397060112025-12-03 22:14:4152 days ago1764800081IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397059922025-12-03 22:14:3452 days ago1764800074IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397059742025-12-03 22:14:2652 days ago1764800066IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397059542025-12-03 22:14:1852 days ago1764800058IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397059362025-12-03 22:14:1152 days ago1764800051IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397059172025-12-03 22:14:0352 days ago1764800043IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397058982025-12-03 22:13:5652 days ago1764800036IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397058792025-12-03 22:13:4852 days ago1764800028IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397058602025-12-03 22:13:4052 days ago1764800020IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397058412025-12-03 22:13:3352 days ago1764800013IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397058242025-12-03 22:13:2652 days ago1764800006IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397058042025-12-03 22:13:1852 days ago1764799998IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397057852025-12-03 22:13:1052 days ago1764799990IN
0x0A790b53...7424d15fC
0 MON0.0153102
Claim Mon Only397057662025-12-03 22:13:0352 days ago1764799983IN
0x0A790b53...7424d15fC
0 MON0.0153102
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
397062002025-12-03 22:15:5752 days ago1764800157
0x0A790b53...7424d15fC
136.72190546 MON
397061812025-12-03 22:15:4952 days ago1764800149
0x0A790b53...7424d15fC
15.68683338 MON
397061622025-12-03 22:15:4252 days ago1764800142
0x0A790b53...7424d15fC
44.48628511 MON
397061432025-12-03 22:15:3452 days ago1764800134
0x0A790b53...7424d15fC
81.63487123 MON
397061242025-12-03 22:15:2752 days ago1764800127
0x0A790b53...7424d15fC
42.90307492 MON
397061052025-12-03 22:15:1952 days ago1764800119
0x0A790b53...7424d15fC
52.45916024 MON
397060862025-12-03 22:15:1152 days ago1764800111
0x0A790b53...7424d15fC
75.1217634 MON
397060682025-12-03 22:15:0452 days ago1764800104
0x0A790b53...7424d15fC
16.30425933 MON
397060492025-12-03 22:14:5652 days ago1764800096
0x0A790b53...7424d15fC
37.45995787 MON
397060302025-12-03 22:14:4952 days ago1764800089
0x0A790b53...7424d15fC
120.70363515 MON
397060112025-12-03 22:14:4152 days ago1764800081
0x0A790b53...7424d15fC
6.26140625 MON
397059922025-12-03 22:14:3452 days ago1764800074
0x0A790b53...7424d15fC
130.46247884 MON
397059742025-12-03 22:14:2652 days ago1764800066
0x0A790b53...7424d15fC
56.40887677 MON
397059542025-12-03 22:14:1852 days ago1764800058
0x0A790b53...7424d15fC
75.80840019 MON
397059362025-12-03 22:14:1152 days ago1764800051
0x0A790b53...7424d15fC
86.3420159 MON
397059172025-12-03 22:14:0352 days ago1764800043
0x0A790b53...7424d15fC
46.33876549 MON
397058982025-12-03 22:13:5652 days ago1764800036
0x0A790b53...7424d15fC
61.73954688 MON
397058792025-12-03 22:13:4852 days ago1764800028
0x0A790b53...7424d15fC
84.28860135 MON
397058602025-12-03 22:13:4052 days ago1764800020
0x0A790b53...7424d15fC
114.5133588 MON
397058412025-12-03 22:13:3352 days ago1764800013
0x0A790b53...7424d15fC
92.96115461 MON
397058242025-12-03 22:13:2652 days ago1764800006
0x0A790b53...7424d15fC
12.05793977 MON
397058042025-12-03 22:13:1852 days ago1764799998
0x0A790b53...7424d15fC
4.34819878 MON
397057852025-12-03 22:13:1052 days ago1764799990
0x0A790b53...7424d15fC
118.1086559 MON
397057662025-12-03 22:13:0352 days ago1764799983
0x0A790b53...7424d15fC
41.20444166 MON
397057472025-12-03 22:12:5552 days ago1764799975
0x0A790b53...7424d15fC
71.12443992 MON
View All Internal Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x1dc5352c...ec6f6d41B
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
CrystalSupply

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 11 : CrystalSupply.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";

contract CrystalSupply is Ownable, ReentrancyGuard, IEntropyConsumer {

    IERC20 public immutable crystalToken;

    // Pyth Entropy V2
    IEntropyV2 public immutable entropy;

    // Halving mechanism constants
    uint256 public constant INITIAL_REWARD = 32 ether;
    uint256 public constant HALVING_INTERVAL = 3_000_000 ether;
    uint256 public constant INITIAL_CIRCULATING = 105_000 ether;
    uint256 public constant MAX_HALVINGS = 6;

    uint256 public constant GRID_SIZE = 25;
    uint256 public constant MIN_STAKE = 0.1 ether;

    uint256 public constant REVENUE_FEE_BP = 1100;
    uint256 public constant VAULT_FEE_BP = 1000;
    uint256 public constant ADMIN_FEE_BP = 100;
    uint256 public constant REFINING_FEE_BP = 1000;
    uint256 public KEEPER_FEE = 0.1 ether;
    uint256 public maxAutoCheckpointRounds = 100;

    address public protocolVault;
    address public adminWallet;

    uint256 public pendingAdminFees;
    uint256 public pendingVaultFees;

    uint256 public currentRoundId;
    uint256 public accumulatedTokenPot;
    uint256 public lastRoundStartTime;
    bool public isRolling;

    struct Round {
        uint256 monTotalOfLosingBlocks;
        uint256 monTotalOnWinningBlock;
        uint256 totalRoundStake;
        uint8 winningBlock;
        bool finalized;
        bool motherlodeHit;
        bool isWinnerTakeAll;
        uint256 winningTicket;
        uint256 monPerShare;
        uint256 tokenPerShare;
        uint256 winnerTakeAllTokenAmount;
        uint256 finalizedAt;
        uint256[25] blockTotals;
        uint256[25] minerCounts;
    }

    mapping(uint256 => Round) public rounds;
    mapping(uint256 => mapping(address => mapping(uint8 => uint256))) public userBlockDeposits;
    mapping(uint256 => mapping(address => mapping(uint8 => uint256))) public userCumulativeStart;
    mapping(uint256 => mapping(address => uint256)) public userKeeperFee;

    event Prospect(address indexed user, uint256 roundId, uint8 blockIdx, uint256 amount);
    event RoundEnded(uint256 roundId, uint8 winBlock, bool winnerTakeAll, bool motherlode);
    event CheckpointedByKeeper(address indexed keeper, address indexed user, uint256 roundId, uint256 tokens, uint256 mon, uint256 keeperFee);
    event Claimed(address indexed user, uint256 monAmt, uint256 tokenAmt);
    event RoundStarted(uint256 indexed roundId, uint256 timestamp);
    event KeeperFeeUpdated(uint256 oldFee, uint256 newFee);
    event MaxAutoCheckpointRoundsUpdated(uint256 oldLimit, uint256 newLimit);
    event ProtocolVaultUpdated(address indexed oldVault, address indexed newVault);
    event AdminWalletUpdated(address indexed oldWallet, address indexed newWallet);
    event AdminFeesClaimed(address indexed admin, uint256 amount);
    event VaultFeesClaimed(address indexed vault, uint256 amount);
    event RandomnessRequested(uint256 indexed roundId, uint64 sequenceNumber);
    event RandomnessReRequested(uint256 indexed roundId, uint64 oldSequence, uint64 newSequence, uint256 blocksSinceRequest);

    // Pyth Entropy tracking
    uint64 public currentSequenceNumber;
    mapping(uint64 => uint256) public sequenceToRound;
    uint256 public vrfRequestedAt;
    uint256 public constant VRF_TIMEOUT_BLOCKS = 500;

    uint256 public constant MIN_ROUND_DURATION = 60;
    uint256 public roundDuration = 60;
    uint256 public intermissionDuration = 12; // Configurable intermission for winner reveal
    uint256 public minimumParticipants = 1;
    mapping(uint256 => uint256) public roundParticipantCount;
    mapping(uint256 => mapping(address => bool)) public hasParticipated;
    mapping(uint256 => mapping(uint8 => mapping(address => bool))) public userHasProspectedBlock;
    mapping(uint256 => address[]) public roundParticipants; // Track all participants per round

    uint256 public totalUnclaimedTokens;
    uint256 public minerRewardsFactor;
    uint256 public totalRefinedTokens;
    uint256 public totalSupplyDistributed;  // Tracks TAL distributed for halving calculations
    mapping(address => uint256) public userUnclaimedBalance;
    mapping(address => uint256) public userRewardsFactor;
    mapping(address => uint256) public userRefinedCrystal;
    mapping(uint256 => mapping(address => bool)) public userCreditedRound;
    mapping(address => uint256) public lastCheckpointedRound;
    mapping(address => uint256) public userPendingMon;

    struct CheckpointRequest {
        address user;
        uint256 roundId;
    }
    CheckpointRequest[] public checkpointQueue;
    mapping(address => mapping(uint256 => bool)) public inCheckpointQueue;

    constructor(
        address _entropy,
        address _token,
        address _protocolVault,
        address _adminWallet
    ) Ownable(msg.sender) {
        entropy = IEntropyV2(_entropy);
        crystalToken = IERC20(_token);
        protocolVault = _protocolVault;
        adminWallet = _adminWallet;
        lastRoundStartTime = block.timestamp;
    }
    function getEntropy() internal view override returns (address) {
        return address(entropy);
    }

    function entropyCallback(
        uint64 sequenceNumber,
        address,
        bytes32 randomNumber
    ) internal override {
        uint256 roundId = sequenceToRound[sequenceNumber];
        require(isRolling && roundId == currentRoundId && sequenceNumber == currentSequenceNumber, "Invalid or stale sequence");

        uint256 randomValue = uint256(randomNumber);
        uint256 rwBlock = randomValue;
        uint256 rwMode = uint256(keccak256(abi.encodePacked(randomValue, uint256(1))));
        uint256 rwTicket = uint256(keccak256(abi.encodePacked(randomValue, uint256(2))));

        _finalizeRound(rwBlock, rwMode, rwTicket);

        currentSequenceNumber = 0;
    }

    function setRoundConfig(uint256 _duration, uint256 _minParticipants, uint256 _intermission) external onlyOwner {
        require(_duration >= MIN_ROUND_DURATION, "Too short");
        roundDuration = _duration;
        minimumParticipants = _minParticipants;
        intermissionDuration = _intermission;
    }

    function blockTotalStake(uint256 rId, uint8 blockIdx) external view returns (uint256) {
        return rounds[rId].blockTotals[blockIdx];
    }

    function getUserBalances(address user) external view returns (
        uint256 unclaimed,
        uint256 refined,
        uint256 total
    ) {
        unclaimed = userUnclaimedBalance[user];
        refined = userRefinedCrystal[user];
        if (minerRewardsFactor > userRewardsFactor[user]) {
            uint256 delta = minerRewardsFactor - userRewardsFactor[user];
            uint256 accruedRefined = (delta * unclaimed) / 1e18;
            refined += accruedRefined;
        }
        if (unclaimed > 0 || refined > 0) {
            uint256 fee = (unclaimed * REFINING_FEE_BP) / 10000;
            uint256 netUnclaimed = unclaimed - fee;
            total = netUnclaimed + refined;
        } else {
            total = 0;
        }
        return (unclaimed, refined, total);
    }

    function getEntropyFee() public view returns (uint128) {
        return entropy.getFeeV2();
    }

    function getEntropyFeeWithGas(uint32 callbackGasLimit) public view returns (uint128) {
        return entropy.getFeeV2(callbackGasLimit);
    }

    /**
     * @notice Calculate current reward per round based on halving schedule
     * @return Current reward amount
     */
    function getCurrentReward() public view returns (uint256) {
        uint256 totalCirculating = INITIAL_CIRCULATING + totalSupplyDistributed;
        uint256 halvings = totalCirculating / HALVING_INTERVAL;

        // Cap at MAX_HALVINGS to prevent underflow
        if (halvings > MAX_HALVINGS) {
            halvings = MAX_HALVINGS;
        }

        // Use bitshift for efficient division by 2^halvings
        return INITIAL_REWARD >> halvings;
    }

    /**
     * @notice Calculate motherlode increment (20% of current reward)
     * @return Motherlode increment amount
     */
    function getMotherlodeInc() public view returns (uint256) {
        return getCurrentReward() / 5;  // 20% of current reward
    }

    function prospect(uint8 blockIdx) external payable nonReentrant {
        require(block.timestamp >= lastRoundStartTime, "Round not started yet (intermission)");
        uint256 rId = isRolling ? currentRoundId + 1 : currentRoundId;
        bool isFirstProspect = (userKeeperFee[rId][msg.sender] == 0);

        if (isFirstProspect) {
            require(msg.value >= MIN_STAKE + KEEPER_FEE, "Send stake + keeper fee");
        } else {
            require(msg.value >= MIN_STAKE, "Min stake");
        }

        _prospect(msg.sender, blockIdx, msg.value, rId);
    }

    function prospectBatch(uint32 blockMask) external payable nonReentrant {
        require(block.timestamp >= lastRoundStartTime, "Round not started yet (intermission)");
        require(msg.value >= MIN_STAKE + KEEPER_FEE, "Send total stake + keeper fee");
        require(blockMask > 0, "No blocks selected");

        uint256 rId = isRolling ? currentRoundId + 1 : currentRoundId;

        uint256 selectedCount = 0;
        uint32 tempMask = blockMask;
        for (uint8 i = 0; i < GRID_SIZE; i++) {
            if (tempMask & 1 == 1) {
                selectedCount++;
            }
            tempMask >>= 1;
        }
        require(selectedCount > 0, "No blocks selected");

        uint256 totalStake = msg.value - KEEPER_FEE;
        uint256 stakePerBlock = totalStake / selectedCount;
        uint256 remainder = totalStake % selectedCount;
        require(stakePerBlock >= MIN_STAKE, "Min per block");

        tempMask = blockMask;
        bool isFirst = true;
        for (uint8 i = 0; i < GRID_SIZE; i++) {
            if (tempMask & 1 == 1) {
                if (isFirst) {
                    _prospect(msg.sender, i, stakePerBlock + KEEPER_FEE + remainder, rId);
                    isFirst = false;
                } else {
                    _prospect(msg.sender, i, stakePerBlock, rId);
                }
            }
            tempMask >>= 1;
        }
    }

    function _prospect(address user, uint8 blockIdx, uint256 amount, uint256 rId) internal {
        require(blockIdx < GRID_SIZE, "Invalid Block");

        uint256 stakeAmount = amount;
        bool isFirstProspect = (userKeeperFee[rId][user] == 0);

        if (isFirstProspect) {
            stakeAmount = amount - KEEPER_FEE;
            userKeeperFee[rId][user] = KEEPER_FEE;
        }

        _prospectInternal(user, blockIdx, stakeAmount, rId);
    }

    function _prospectInternal(address user, uint8 blockIdx, uint256 netAmount, uint256 rId) internal {
        uint256 currentTotal = rounds[rId].blockTotals[blockIdx];

        if (userBlockDeposits[rId][user][blockIdx] == 0) {
            userCumulativeStart[rId][user][blockIdx] = currentTotal;
        }

        userBlockDeposits[rId][user][blockIdx] += netAmount;
        rounds[rId].blockTotals[blockIdx] += netAmount;
        rounds[rId].totalRoundStake += netAmount;

        if (!userHasProspectedBlock[rId][blockIdx][user]) {
            userHasProspectedBlock[rId][blockIdx][user] = true;
            rounds[rId].minerCounts[blockIdx]++;
        }

        if (!hasParticipated[rId][user]) {
            hasParticipated[rId][user] = true;
            roundParticipantCount[rId]++;
            roundParticipants[rId].push(user); // Track participant address
            if (!inCheckpointQueue[user][rId]) {
                inCheckpointQueue[user][rId] = true;
                checkpointQueue.push(CheckpointRequest({user: user, roundId: rId}));
            }
        }

        emit Prospect(user, rId, blockIdx, netAmount);
    }

    function _updateUserRewards(address user) internal {
        if (minerRewardsFactor > userRewardsFactor[user]) {
            uint256 delta = minerRewardsFactor - userRewardsFactor[user];
            uint256 unclaimed = userUnclaimedBalance[user];
            uint256 refined = (delta * unclaimed) / 1e18;
            userRefinedCrystal[user] += refined;
        }
        userRewardsFactor[user] = minerRewardsFactor;
    }

    function _creditUserReward(address user, uint256 amount) internal {
        _updateUserRewards(user);
        userUnclaimedBalance[user] += amount;
        totalUnclaimedTokens += amount;
    }

    function triggerRound() external payable {
        require(block.timestamp >= lastRoundStartTime + roundDuration, "Too early");
        require(!isRolling, "Already rolling");
        require(roundParticipantCount[currentRoundId] >= minimumParticipants, "Not enough participants");

        // Get fee for Pyth Entropy V2 - keeper must pay this
        uint128 fee = entropy.getFeeV2();
        require(msg.value >= fee, "Insufficient entropy fee");

        isRolling = true;

        // Request randomness from Pyth Entropy V2 (no provider/userRandom needed)
        uint64 sequenceNumber = entropy.requestV2{value: fee}();

        currentSequenceNumber = sequenceNumber;
        sequenceToRound[sequenceNumber] = currentRoundId;
        vrfRequestedAt = block.number;

        // Refund excess MON to caller
        if (msg.value > fee) {
            (bool success, ) = msg.sender.call{value: msg.value - fee}("");
            require(success, "Refund failed");
        }

        emit RandomnessRequested(currentRoundId, sequenceNumber);
    }

    function reRequestVRF(uint32 callbackGasLimit) external payable {
        require(isRolling, "Not rolling");
        require(block.number >= vrfRequestedAt + VRF_TIMEOUT_BLOCKS, "VRF timeout not reached");

        // Use custom gas limit for callback (getFeeV2 with gas param)
        uint128 fee = entropy.getFeeV2(callbackGasLimit);
        require(msg.value >= fee, "Insufficient entropy fee");

        uint64 oldSequence = currentSequenceNumber;
        uint256 blocksSinceRequest = block.number - vrfRequestedAt;

        // Request new randomness with custom callback gas limit
        uint64 sequenceNumber = entropy.requestV2{value: fee}(callbackGasLimit);

        currentSequenceNumber = sequenceNumber;
        sequenceToRound[sequenceNumber] = currentRoundId;
        vrfRequestedAt = block.number;

        // Refund excess MON to caller
        if (msg.value > fee) {
            (bool success, ) = msg.sender.call{value: msg.value - fee}("");
            require(success, "Refund failed");
        }

        emit RandomnessReRequested(currentRoundId, oldSequence, sequenceNumber, blocksSinceRequest);
    }

    function _finalizeRound(uint256 rwBlock, uint256 rwMode, uint256 rwTicket) internal {
        Round storage r = rounds[currentRoundId];

        r.winningBlock = uint8(rwBlock % GRID_SIZE);
        r.motherlodeHit = ((rwBlock >> 32) % 625 == 0);
        uint256 winningBlockTotal = r.blockTotals[r.winningBlock];
        r.monTotalOnWinningBlock = winningBlockTotal;
        uint256 roundTotal = r.totalRoundStake;
        uint256 losingMon = roundTotal - winningBlockTotal;
        r.monTotalOfLosingBlocks = losingMon;

        if (winningBlockTotal == 0) {
            uint256 adminFee = (roundTotal * ADMIN_FEE_BP) / 10000;
            pendingAdminFees += adminFee;
            pendingVaultFees += roundTotal - adminFee;
            r.monPerShare = 0;
            r.tokenPerShare = 0;
        } else {
            uint256 adminFee = (losingMon * ADMIN_FEE_BP) / 10000;
            uint256 vaultFee = (losingMon * VAULT_FEE_BP) / 10000;
            uint256 totalFees = adminFee + vaultFee;

            pendingAdminFees += adminFee;
            pendingVaultFees += vaultFee;

            uint256 losingMonAfterFees = losingMon - totalFees;
            r.monPerShare = (losingMonAfterFees * 1e18) / winningBlockTotal;

            // Use dynamic reward based on halving schedule
            uint256 baseReward = getCurrentReward();
            uint256 motherlodeInc = getMotherlodeInc();

            // Accumulate motherlode pot if not hit
            if (!r.motherlodeHit) {
                accumulatedTokenPot += motherlodeInc;
            }

            // Calculate payout
            uint256 payoutToken;
            if (r.motherlodeHit) {
                payoutToken = accumulatedTokenPot + baseReward;
                accumulatedTokenPot = 0;
            } else {
                payoutToken = baseReward;
            }

            // Track total distributed for halving calculations
            totalSupplyDistributed += payoutToken;

            if (rwMode % 2 == 0) {
                r.isWinnerTakeAll = true;
                r.winnerTakeAllTokenAmount = payoutToken;
                r.winningTicket = rwTicket % winningBlockTotal;
            } else {
                r.isWinnerTakeAll = false;
                r.tokenPerShare = (payoutToken * 1e18) / winningBlockTotal;
            }
        }

        r.finalized = true;
        r.finalizedAt = block.timestamp;
        emit RoundEnded(currentRoundId, r.winningBlock, r.isWinnerTakeAll, r.motherlodeHit);

        currentRoundId++;
        lastRoundStartTime = block.timestamp + intermissionDuration;
        isRolling = false;
        emit RoundStarted(currentRoundId, lastRoundStartTime);
    }

    function checkpointUserAuto(address user) external nonReentrant {
        uint256 startRound = lastCheckpointedRound[user] + 1;
        uint256 endRound = currentRoundId;

        if (endRound - startRound > maxAutoCheckpointRounds) {
            endRound = startRound + maxAutoCheckpointRounds;
        }

        uint256 totalKeeperFee = 0;

        for (uint256 rId = startRound; rId < endRound; rId++) {
            Round storage r = rounds[rId];
            if (!r.finalized || userCreditedRound[rId][user]) {
                continue;
            }
            uint256 userStake = userBlockDeposits[rId][user][r.winningBlock];
            if (userStake > 0) {
                uint256 roundTokens = 0;
                if (r.isWinnerTakeAll) {
                    uint256 userStart = userCumulativeStart[rId][user][r.winningBlock];
                    if (r.winningTicket >= userStart && r.winningTicket < userStart + userStake) {
                        roundTokens = r.winnerTakeAllTokenAmount;
                    }
                } else {
                    roundTokens = (userStake * r.tokenPerShare) / 1e18;
                }
                if (roundTokens > 0) {
                    _creditUserReward(user, roundTokens);
                }

                // Calculate and track MON inline to save stack space
                uint256 roundMon = userStake + (userStake * r.monPerShare) / 1e18;
                userPendingMon[user] += roundMon;

                userCreditedRound[rId][user] = true;
                uint256 keeperFee = userKeeperFee[rId][user];
                if (keeperFee > 0) {
                    userKeeperFee[rId][user] = 0;
                    totalKeeperFee += keeperFee;
                    emit CheckpointedByKeeper(msg.sender, user, rId, roundTokens, roundMon, keeperFee);
                } else {
                    emit CheckpointedByKeeper(msg.sender, user, rId, roundTokens, roundMon, 0);
                }
            }
        }
        if (endRound > 0) {
            lastCheckpointedRound[user] = endRound - 1;
        }
        if (totalKeeperFee > 0) {
            payable(msg.sender).transfer(totalKeeperFee);
        }
    }

    function checkpointUser(address user, uint256[] calldata rIds) external nonReentrant {
        uint256 totalKeeperFee = 0;
        for (uint i = 0; i < rIds.length; i++) {
            uint256 rId = rIds[i];
            require(rId < currentRoundId, "Active round");
            Round storage r = rounds[rId];
            require(r.finalized, "Round not finalized");
            if (userCreditedRound[rId][user]) {
                continue;
            }
            uint256 userStake = userBlockDeposits[rId][user][r.winningBlock];
            if (userStake > 0) {
                uint256 roundTokens = 0;
                if (r.isWinnerTakeAll) {
                    uint256 userStart = userCumulativeStart[rId][user][r.winningBlock];
                    if (r.winningTicket >= userStart && r.winningTicket < userStart + userStake) {
                        roundTokens = r.winnerTakeAllTokenAmount;
                    }
                } else {
                    roundTokens = (userStake * r.tokenPerShare) / 1e18;
                }
                if (roundTokens > 0) {
                    _creditUserReward(user, roundTokens);
                }

                // Calculate and track MON inline to save stack space
                uint256 roundMon = userStake + (userStake * r.monPerShare) / 1e18;
                userPendingMon[user] += roundMon;

                userCreditedRound[rId][user] = true;
                uint256 keeperFee = userKeeperFee[rId][user];
                if (keeperFee > 0) {
                    userKeeperFee[rId][user] = 0;
                    totalKeeperFee += keeperFee;
                    emit CheckpointedByKeeper(msg.sender, user, rId, roundTokens, roundMon, keeperFee);
                } else {
                    emit CheckpointedByKeeper(msg.sender, user, rId, roundTokens, roundMon, 0);
                }
            }
        }
        if (totalKeeperFee > 0) {
            payable(msg.sender).transfer(totalKeeperFee);
        }
    }

    function checkpointFromQueue(uint256[] calldata queueIndices) external nonReentrant {
        uint256 totalKeeperFee = 0;
        uint256 queueLen = checkpointQueue.length;

        for (uint i = 0; i < queueIndices.length; i++) {
            uint256 idx = queueIndices[i];
            require(idx < queueLen, "Invalid index");

            if (i > 0) {
                require(queueIndices[i] < queueIndices[i - 1], "Indices must be sorted descending");
            }

            CheckpointRequest memory req = checkpointQueue[idx];
            address user = req.user;
            uint256 rId = req.roundId;

            if (rId >= currentRoundId) continue;
            Round storage r = rounds[rId];
            if (!r.finalized) continue;

            if (userCreditedRound[rId][user]) {
                _removeFromQueue(idx);
                queueLen--;
                continue;
            }

            uint256 userStake = userBlockDeposits[rId][user][r.winningBlock];
            if (userStake > 0) {
                uint256 roundTokens = 0;
                if (r.isWinnerTakeAll) {
                    uint256 userStart = userCumulativeStart[rId][user][r.winningBlock];
                    if (r.winningTicket >= userStart && r.winningTicket < userStart + userStake) {
                        roundTokens = r.winnerTakeAllTokenAmount;
                    }
                } else {
                    roundTokens = (userStake * r.tokenPerShare) / 1e18;
                }
                if (roundTokens > 0) {
                    _creditUserReward(user, roundTokens);
                }

                uint256 roundMon = userStake + (userStake * r.monPerShare) / 1e18;
                userPendingMon[user] += roundMon;

                userCreditedRound[rId][user] = true;
                uint256 keeperFee = userKeeperFee[rId][user];
                if (keeperFee > 0) {
                    userKeeperFee[rId][user] = 0;
                    totalKeeperFee += keeperFee;
                    emit CheckpointedByKeeper(msg.sender, user, rId, roundTokens, roundMon, keeperFee);
                } else {
                    emit CheckpointedByKeeper(msg.sender, user, rId, roundTokens, roundMon, 0);
                }
            }

            _removeFromQueue(idx);
            queueLen--;
        }

        if (totalKeeperFee > 0) {
            payable(msg.sender).transfer(totalKeeperFee);
        }
    }

    function _removeFromQueue(uint256 idx) internal {
        uint256 lastIdx = checkpointQueue.length - 1;
        CheckpointRequest memory removed = checkpointQueue[idx];
        inCheckpointQueue[removed.user][removed.roundId] = false;
        if (idx != lastIdx) {
            checkpointQueue[idx] = checkpointQueue[lastIdx];
        }
        checkpointQueue.pop();
    }


    function claimMonOnly() external nonReentrant {
        uint256 pendingMon = userPendingMon[msg.sender];
        require(pendingMon > 0, "Nothing to claim");

        userPendingMon[msg.sender] = 0;

        (bool s, ) = payable(msg.sender).call{value: pendingMon}("");
        require(s, "Transfer MON failed");

        emit Claimed(msg.sender, pendingMon, 0);
    }

    function claim() external nonReentrant {
        uint256 pendingMon = userPendingMon[msg.sender];

        _updateUserRewards(msg.sender);
        uint256 totalTokens = userUnclaimedBalance[msg.sender];
        uint256 refined = userRefinedCrystal[msg.sender];

        require(pendingMon > 0 || totalTokens > 0 || refined > 0, "Nothing to claim");

        uint256 tokensToTransfer = 0;
        if (totalTokens > 0 || refined > 0) {
            uint256 fee = (totalTokens * REFINING_FEE_BP) / 10000;
            uint256 netTokens = totalTokens - fee;
            if (fee > 0 && totalUnclaimedTokens > totalTokens) {
                uint256 remainingUnclaimed = totalUnclaimedTokens - totalTokens;
                minerRewardsFactor += (fee * 1e18) / remainingUnclaimed;
                totalRefinedTokens += fee;
            }
            userUnclaimedBalance[msg.sender] = 0;
            userRefinedCrystal[msg.sender] = 0;
            totalUnclaimedTokens -= totalTokens;
            tokensToTransfer = netTokens + refined;
        }

        if (pendingMon > 0) {
            userPendingMon[msg.sender] = 0;
            (bool s, ) = payable(msg.sender).call{value: pendingMon}("");
            require(s, "Transfer MON failed");
        }

        if (tokensToTransfer > 0) {
            require(crystalToken.transfer(msg.sender, tokensToTransfer), "Transfer TOK failed");
        }

        emit Claimed(msg.sender, pendingMon, tokensToTransfer);
    }

    function setKeeperFee(uint256 _newFee) external onlyOwner {
        uint256 oldFee = KEEPER_FEE;
        KEEPER_FEE = _newFee;
        emit KeeperFeeUpdated(oldFee, _newFee);
    }

    function setMaxAutoCheckpointRounds(uint256 _newLimit) external onlyOwner {
        require(_newLimit > 0, "Limit must be > 0");
        uint256 oldLimit = maxAutoCheckpointRounds;
        maxAutoCheckpointRounds = _newLimit;
        emit MaxAutoCheckpointRoundsUpdated(oldLimit, _newLimit);
    }

    function setProtocolVault(address _newVault) external onlyOwner {
        require(_newVault != address(0), "Invalid address");
        address oldVault = protocolVault;
        if (pendingVaultFees > 0) {
            uint256 amount = pendingVaultFees;
            pendingVaultFees = 0;
            (bool success, ) = oldVault.call{value: amount}("");
            require(success, "Fee transfer failed");
            emit VaultFeesClaimed(oldVault, amount);
        }
        protocolVault = _newVault;
        emit ProtocolVaultUpdated(oldVault, _newVault);
    }

    function setAdminWallet(address _newWallet) external onlyOwner {
        require(_newWallet != address(0), "Invalid address");
        address oldWallet = adminWallet;
        if (pendingAdminFees > 0) {
            uint256 amount = pendingAdminFees;
            pendingAdminFees = 0;
            (bool success, ) = oldWallet.call{value: amount}("");
            require(success, "Fee transfer failed");
            emit AdminFeesClaimed(oldWallet, amount);
        }
        adminWallet = _newWallet;
        emit AdminWalletUpdated(oldWallet, _newWallet);
    }

    function claimAdminFees() external nonReentrant {
        require(msg.sender == adminWallet, "Not admin");
        uint256 amount = pendingAdminFees;
        require(amount > 0, "No fees to claim");
        pendingAdminFees = 0;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        emit AdminFeesClaimed(msg.sender, amount);
    }

    function claimVaultFees() external nonReentrant {
        require(msg.sender == protocolVault, "Not vault");
        uint256 amount = pendingVaultFees;
        require(amount > 0, "No fees to claim");
        pendingVaultFees = 0;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        emit VaultFeesClaimed(msg.sender, amount);
    }

    function emergencyUnstick() external onlyOwner {
        isRolling = false;
        currentSequenceNumber = 0;
    }

    function getCurrentRoundGrid() external view returns (uint256[25] memory) {
        return rounds[currentRoundId].blockTotals;
    }

    function getCurrentRoundMinerCounts() external view returns (uint256[25] memory) {
        return rounds[currentRoundId].minerCounts;
    }

    function getUserBlockDepositsForRound(uint256 roundId, address user) external view returns (uint256[25] memory) {
        uint256[25] memory deposits;
        for (uint8 i = 0; i < GRID_SIZE; i++) {
            deposits[i] = userBlockDeposits[roundId][user][i];
        }
        return deposits;
    }

    function getCheckpointQueueLength() external view returns (uint256) {
        return checkpointQueue.length;
    }

    function getCheckpointQueue(uint256 offset, uint256 limit) external view returns (
        CheckpointRequest[] memory requests,
        uint256 total
    ) {
        total = checkpointQueue.length;
        if (offset >= total) {
            return (new CheckpointRequest[](0), total);
        }
        uint256 end = offset + limit;
        if (end > total) {
            end = total;
        }
        uint256 count = end - offset;
        requests = new CheckpointRequest[](count);
        for (uint256 i = 0; i < count; i++) {
            requests[i] = checkpointQueue[offset + i];
        }
        return (requests, total);
    }

    function getRoundParticipants(uint256 roundId, uint256 offset, uint256 limit) external view returns (
        address[] memory participants,
        uint256 total
    ) {
        total = roundParticipants[roundId].length;
        if (offset >= total) {
            return (new address[](0), total);
        }
        uint256 end = offset + limit;
        if (end > total) {
            end = total;
        }
        uint256 count = end - offset;
        participants = new address[](count);
        for (uint256 i = 0; i < count; i++) {
            participants[i] = roundParticipants[roundId][offset + i];
        }
        return (participants, total);
    }

    struct WinnerInfo {
        address user;
        uint256 stake;
        uint256 monWon;
        uint256 tokenWon;
    }

    struct RoundWinnersResult {
        WinnerInfo[] winners;
        uint256 totalWinners;
        uint8 winningBlock;
        bool motherlodeHit;
        bool isWinnerTakeAll;
        uint256 finalizedAt;
    }

    function _countWinners(uint256 roundId, uint8 winBlock) internal view returns (uint256 count) {
        uint256 len = roundParticipants[roundId].length;
        for (uint256 i = 0; i < len; i++) {
            if (userBlockDeposits[roundId][roundParticipants[roundId][i]][winBlock] > 0) {
                count++;
            }
        }
    }

    function _calculateWinnerReward(
        uint256 roundId,
        address user,
        uint8 winBlock,
        uint256 stake
    ) internal view returns (uint256 monWon, uint256 tokenWon) {
        Round storage r = rounds[roundId];
        monWon = stake + (stake * r.monPerShare) / 1e18;

        if (r.isWinnerTakeAll) {
            uint256 userStart = userCumulativeStart[roundId][user][winBlock];
            if (r.winningTicket >= userStart && r.winningTicket < userStart + stake) {
                tokenWon = r.winnerTakeAllTokenAmount;
            }
        } else {
            tokenWon = (stake * r.tokenPerShare) / 1e18;
        }
    }

    function getRoundWinners(uint256 roundId, uint256 offset, uint256 limit) external view returns (RoundWinnersResult memory result) {
        Round storage r = rounds[roundId];
        require(r.finalized, "Round not finalized");

        result.winningBlock = r.winningBlock;
        result.motherlodeHit = r.motherlodeHit;
        result.isWinnerTakeAll = r.isWinnerTakeAll;
        result.finalizedAt = r.finalizedAt;

        result.totalWinners = _countWinners(roundId, r.winningBlock);

        if (offset >= result.totalWinners) {
            result.winners = new WinnerInfo[](0);
            return result;
        }

        uint256 end = offset + limit;
        if (end > result.totalWinners) {
            end = result.totalWinners;
        }
        uint256 count = end - offset;
        result.winners = new WinnerInfo[](count);

        uint256 found = 0;
        uint256 collected = 0;
        uint256 len = roundParticipants[roundId].length;

        for (uint256 i = 0; i < len && collected < count; i++) {
            address user = roundParticipants[roundId][i];
            uint256 stake = userBlockDeposits[roundId][user][r.winningBlock];
            if (stake > 0) {
                if (found >= offset) {
                    (uint256 monWon, uint256 tokenWon) = _calculateWinnerReward(roundId, user, r.winningBlock, stake);
                    result.winners[collected] = WinnerInfo(user, stake, monWon, tokenWon);
                    collected++;
                }
                found++;
            }
        }
    }

    struct RoundInfo {
        uint256 totalStake;
        uint8 winningBlock;
        bool finalized;
        bool motherlodeHit;
        bool isWinnerTakeAll;
        uint256 monPerShare;
        uint256 tokenPerShare;
        uint256 winnerTakeAllTokenAmount;
        uint256 finalizedAt;
        uint256 participantCount;
    }

    function getRoundData(uint256 roundId) external view returns (RoundInfo memory info) {
        Round storage r = rounds[roundId];
        info.totalStake = r.totalRoundStake;
        info.winningBlock = r.winningBlock;
        info.finalized = r.finalized;
        info.motherlodeHit = r.motherlodeHit;
        info.isWinnerTakeAll = r.isWinnerTakeAll;
        info.monPerShare = r.monPerShare;
        info.tokenPerShare = r.tokenPerShare;
        info.winnerTakeAllTokenAmount = r.winnerTakeAllTokenAmount;
        info.finalizedAt = r.finalizedAt;
        info.participantCount = roundParticipants[roundId].length;
    }

    receive() external payable {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

File 6 of 11 : EntropyEvents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./EntropyStructs.sol";

// Deprecated -- these events are still emitted, but the lack of indexing
// makes them hard to use.
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 CallbackFailed(
        address indexed provider,
        address indexed requestor,
        uint64 indexed sequenceNumber,
        bytes32 userRandomNumber,
        bytes32 providerRevelation,
        bytes32 randomNumber,
        bytes errorCode
    );

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

    event ProviderDefaultGasLimitUpdated(
        address indexed provider,
        uint32 oldDefaultGasLimit,
        uint32 newDefaultGasLimit
    );

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

    event ProviderFeeManagerUpdated(
        address provider,
        address oldFeeManager,
        address newFeeManager
    );
    event ProviderMaxNumHashesAdvanced(
        address provider,
        uint32 oldMaxNumHashes,
        uint32 newMaxNumHashes
    );

    event Withdrawal(
        address provider,
        address recipient,
        uint128 withdrawnAmount
    );
}

File 7 of 11 : EntropyEventsV2.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./EntropyStructs.sol";

/**
 * @title EntropyEventsV2
 * @notice Interface defining events for the Entropy V2 system, which handles random number generation
 * and provider management on Ethereum.
 * @dev This interface is used to emit events that track the lifecycle of random number requests,
 * provider registrations, and system configurations.
 */
interface EntropyEventsV2 {
    /**
     * @notice Emitted when a new provider registers with the Entropy system
     * @param provider The address of the registered provider
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event Registered(address indexed provider, bytes extraArgs);

    /**
     * @notice Emitted when a user requests a random number from a provider
     * @param provider The address of the provider handling the request
     * @param caller The address of the user requesting the random number
     * @param sequenceNumber A unique identifier for this request
     * @param userContribution The user's contribution to the random number
     * @param gasLimit The gas limit for the callback.
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event Requested(
        address indexed provider,
        address indexed caller,
        uint64 indexed sequenceNumber,
        bytes32 userContribution,
        uint32 gasLimit,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider reveals the generated random number
     * @param provider The address of the provider that generated the random number
     * @param caller The address of the user who requested the random number (and who receives a callback)
     * @param sequenceNumber The unique identifier of the request
     * @param randomNumber The generated random number
     * @param userContribution The user's contribution to the random number
     * @param providerContribution The provider's contribution to the random number
     * @param callbackFailed Whether the callback to the caller failed
     * @param callbackReturnValue Return value from the callback. If the callback failed, this field contains
     * the error code and any additional returned data. Note that "" often indicates an out-of-gas error.
     * If the callback returns more than 256 bytes, only the first 256 bytes of the callback return value are included.
     * @param callbackGasUsed How much gas the callback used.
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event Revealed(
        address indexed provider,
        address indexed caller,
        uint64 indexed sequenceNumber,
        bytes32 randomNumber,
        bytes32 userContribution,
        bytes32 providerContribution,
        bool callbackFailed,
        bytes callbackReturnValue,
        uint32 callbackGasUsed,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider updates their fee
     * @param provider The address of the provider updating their fee
     * @param oldFee The previous fee amount
     * @param newFee The new fee amount
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event ProviderFeeUpdated(
        address indexed provider,
        uint128 oldFee,
        uint128 newFee,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider updates their default gas limit
     * @param provider The address of the provider updating their gas limit
     * @param oldDefaultGasLimit The previous default gas limit
     * @param newDefaultGasLimit The new default gas limit
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event ProviderDefaultGasLimitUpdated(
        address indexed provider,
        uint32 oldDefaultGasLimit,
        uint32 newDefaultGasLimit,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider updates their URI
     * @param provider The address of the provider updating their URI
     * @param oldUri The previous URI
     * @param newUri The new URI
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event ProviderUriUpdated(
        address indexed provider,
        bytes oldUri,
        bytes newUri,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider updates their fee manager address
     * @param provider The address of the provider updating their fee manager
     * @param oldFeeManager The previous fee manager address
     * @param newFeeManager The new fee manager address
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event ProviderFeeManagerUpdated(
        address indexed provider,
        address oldFeeManager,
        address newFeeManager,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider updates their maximum number of hashes that can be advanced
     * @param provider The address of the provider updating their max hashes
     * @param oldMaxNumHashes The previous maximum number of hashes
     * @param newMaxNumHashes The new maximum number of hashes
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event ProviderMaxNumHashesAdvanced(
        address indexed provider,
        uint32 oldMaxNumHashes,
        uint32 newMaxNumHashes,
        bytes extraArgs
    );

    /**
     * @notice Emitted when a provider withdraws their accumulated fees
     * @param provider The address of the provider withdrawing fees
     * @param recipient The address receiving the withdrawn fees
     * @param withdrawnAmount The amount of fees withdrawn
     * @param extraArgs A field for extra data for forward compatibility.
     */
    event Withdrawal(
        address indexed provider,
        address indexed recipient,
        uint128 withdrawnAmount,
        bytes extraArgs
    );
}

File 8 of 11 : EntropyStructs.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

// This contract holds old versions of the Entropy structs that are no longer used for contract storage.
// However, they are still used in EntropyEvents to maintain the public interface of prior versions of
// the Entropy contract.
//
// See EntropyStructsV2 for the struct definitions currently in use.
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 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;
        // An address that is authorized to set / withdraw fees on behalf of this provider.
        address feeManager;
        // Maximum number of hashes to record in a request. This should be set according to the maximum gas limit
        // the provider supports for callbacks.
        uint32 maxNumHashes;
    }

    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;
        // True if this is a request that expects a callback.
        bool isRequestWithCallback;
    }
}

File 9 of 11 : EntropyStructsV2.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

contract EntropyStructsV2 {
    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 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;
        // An address that is authorized to set / withdraw fees on behalf of this provider.
        address feeManager;
        // Maximum number of hashes to record in a request. This should be set according to the maximum gas limit
        // the provider supports for callbacks.
        uint32 maxNumHashes;
        // Default gas limit to use for callbacks.
        uint32 defaultGasLimit;
    }

    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;
        // Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag.
        uint8 callbackStatus;
        // The gasLimit in units of 10k gas. (i.e., 2 = 20k gas). We're using units of 10k in order to fit this
        // field into the remaining 2 bytes of this storage slot. The dynamic range here is 10k - 655M, which should
        // cover all real-world use cases.
        uint16 gasLimit10k;
    }
}

// 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: Apache 2
pragma solidity ^0.8.0;

import "./EntropyEvents.sol";
import "./EntropyEventsV2.sol";
import "./EntropyStructsV2.sol";

interface IEntropyV2 is EntropyEventsV2 {
    /// @notice Request a random number using the default provider with default gas limit
    /// @return assignedSequenceNumber A unique identifier for this request
    /// @dev 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 returned sequence number and
    /// the generated random number.
    ///
    /// `entropyCallback` will be run with the provider's configured default gas limit.
    ///
    /// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2()`) as msg.value.
    /// Note that the fee can change over time. Callers of this method should explicitly compute `getFeeV2()`
    /// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
    ///
    /// Note that this method uses an in-contract PRNG to generate the user's contribution to the random number.
    /// This approach modifies the security guarantees such that a dishonest validator and provider can
    /// collude to manipulate the result (as opposed to a malicious user and provider). That is, the user
    /// now trusts the validator honestly draw a random number. If you wish to avoid this trust assumption,
    /// call a variant of `requestV2` that accepts a `userRandomNumber` parameter.
    function requestV2()
        external
        payable
        returns (uint64 assignedSequenceNumber);

    /// @notice Request a random number using the default provider with specified gas limit
    /// @param gasLimit The gas limit for the callback function.
    /// @return assignedSequenceNumber A unique identifier for this request
    /// @dev 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 returned sequence number and
    /// the generated random number.
    ///
    /// `entropyCallback` will be run with the `gasLimit` provided to this function.
    /// The `gasLimit` will be rounded up to a multiple of 10k (e.g., 19000 -> 20000), and furthermore is lower bounded
    /// by the provider's configured default limit.
    ///
    /// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2(gasLimit)`) as msg.value.
    /// Note that the fee can change over time. Callers of this method should explicitly compute `getFeeV2(gasLimit)`
    /// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
    ///
    /// Note that this method uses an in-contract PRNG to generate the user's contribution to the random number.
    /// This approach modifies the security guarantees such that a dishonest validator and provider can
    /// collude to manipulate the result (as opposed to a malicious user and provider). That is, the user
    /// now trusts the validator honestly draw a random number. If you wish to avoid this trust assumption,
    /// call a variant of `requestV2` that accepts a `userRandomNumber` parameter.
    function requestV2(
        uint32 gasLimit
    ) external payable returns (uint64 assignedSequenceNumber);

    /// @notice Request a random number from a specific provider with specified gas limit
    /// @param provider The address of the provider to request from
    /// @param gasLimit The gas limit for the callback function
    /// @return assignedSequenceNumber A unique identifier for this request
    /// @dev 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 returned sequence number and
    /// the generated random number.
    ///
    /// `entropyCallback` will be run with the `gasLimit` provided to this function.
    /// The `gasLimit` will be rounded up to a multiple of 10k (e.g., 19000 -> 20000), and furthermore is lower bounded
    /// by the provider's configured default limit.
    ///
    /// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2(provider, gasLimit)`) as msg.value.
    /// Note that provider fees can change over time. Callers of this method should explicitly compute `getFeeV2(provider, gasLimit)`
    /// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
    ///
    /// Note that this method uses an in-contract PRNG to generate the user's contribution to the random number.
    /// This approach modifies the security guarantees such that a dishonest validator and provider can
    /// collude to manipulate the result (as opposed to a malicious user and provider). That is, the user
    /// now trusts the validator honestly draw a random number. If you wish to avoid this trust assumption,
    /// call a variant of `requestV2` that accepts a `userRandomNumber` parameter.
    function requestV2(
        address provider,
        uint32 gasLimit
    ) external payable returns (uint64 assignedSequenceNumber);

    /// @notice Request a random number from a specific provider with a user-provided random number and gas limit
    /// @param provider The address of the provider to request from
    /// @param userRandomNumber A random number provided by the user for additional entropy
    /// @param gasLimit The gas limit for the callback function. Pass 0 to get a sane default value -- see note below.
    /// @return assignedSequenceNumber A unique identifier for this request
    /// @dev 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 returned sequence number and
    /// the generated random number.
    ///
    /// `entropyCallback` will be run with the `gasLimit` provided to this function.
    /// The `gasLimit` will be rounded up to a multiple of 10k (e.g., 19000 -> 20000), and furthermore is lower bounded
    /// by the provider's configured default limit.
    ///
    /// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2(provider, gasLimit)`) as msg.value.
    /// Note that provider fees can change over time. Callers of this method should explicitly compute `getFeeV2(provider, gasLimit)`
    /// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
    function requestV2(
        address provider,
        bytes32 userRandomNumber,
        uint32 gasLimit
    ) external payable returns (uint64 assignedSequenceNumber);

    /// @notice Get information about a specific entropy provider
    /// @param provider The address of the provider to query
    /// @return info The provider information including configuration, fees, and operational status
    /// @dev This method returns detailed information about a provider's configuration and capabilities.
    /// The returned ProviderInfo struct contains information such as the provider's fee structure and gas limits.
    function getProviderInfoV2(
        address provider
    ) external view returns (EntropyStructsV2.ProviderInfo memory info);

    /// @notice Get the address of the default entropy provider
    /// @return provider The address of the default provider
    /// @dev This method returns the address of the provider that will be used when no specific provider is specified
    /// in the requestV2 calls. The default provider can be used to get the base fee and gas limit information.
    function getDefaultProvider() external view returns (address provider);

    /// @notice Get information about a specific request
    /// @param provider The address of the provider that handled the request
    /// @param sequenceNumber The unique identifier of the request
    /// @return req The request information including status, random number, and other metadata
    /// @dev This method allows querying the state of a previously made request. The returned Request struct
    /// contains information about whether the request was fulfilled, the generated random number (if available),
    /// and other metadata about the request.
    function getRequestV2(
        address provider,
        uint64 sequenceNumber
    ) external view returns (EntropyStructsV2.Request memory req);

    /// @notice Get the fee charged by the default provider for the default gas limit
    /// @return feeAmount The fee amount in wei
    /// @dev This method returns the base fee required to make a request using the default provider with
    /// the default gas limit. This fee should be passed as msg.value when calling requestV2().
    /// The fee can change over time, so this method should be called before each request.
    function getFeeV2() external view returns (uint128 feeAmount);

    /// @notice Get the fee charged by the default provider for a specific gas limit
    /// @param gasLimit The gas limit for the callback function
    /// @return feeAmount The fee amount in wei
    /// @dev This method returns the fee required to make a request using the default provider with
    /// the specified gas limit. This fee should be passed as msg.value when calling requestV2(gasLimit).
    /// The fee can change over time, so this method should be called before each request.
    function getFeeV2(
        uint32 gasLimit
    ) external view returns (uint128 feeAmount);

    /// @notice Get the fee charged by a specific provider for a request with a given gas limit
    /// @param provider The address of the provider to query
    /// @param gasLimit The gas limit for the callback function
    /// @return feeAmount The fee amount in wei
    /// @dev This method returns the fee required to make a request using the specified provider with
    /// the given gas limit. This fee should be passed as msg.value when calling requestV2(provider, gasLimit)
    /// or requestV2(provider, userRandomNumber, gasLimit). The fee can change over time, so this method
    /// should be called before each request.
    function getFeeV2(
        address provider,
        uint32 gasLimit
    ) external view returns (uint128 feeAmount);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "viaIR": false,
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_entropy","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_protocolVault","type":"address"},{"internalType":"address","name":"_adminWallet","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AdminFeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldWallet","type":"address"},{"indexed":true,"internalType":"address","name":"newWallet","type":"address"}],"name":"AdminWalletUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mon","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"keeperFee","type":"uint256"}],"name":"CheckpointedByKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"monAmt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmt","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"KeeperFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"MaxAutoCheckpointRoundsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"blockIdx","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Prospect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldVault","type":"address"},{"indexed":true,"internalType":"address","name":"newVault","type":"address"}],"name":"ProtocolVaultUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"oldSequence","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newSequence","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"blocksSinceRequest","type":"uint256"}],"name":"RandomnessReRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"RandomnessRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"winBlock","type":"uint8"},{"indexed":false,"internalType":"bool","name":"winnerTakeAll","type":"bool"},{"indexed":false,"internalType":"bool","name":"motherlode","type":"bool"}],"name":"RoundEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RoundStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VaultFeesClaimed","type":"event"},{"inputs":[],"name":"ADMIN_FEE_BP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GRID_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HALVING_INTERVAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIAL_CIRCULATING","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIAL_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_HALVINGS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ROUND_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_STAKE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REFINING_FEE_BP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REVENUE_FEE_BP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_FEE_BP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VRF_TIMEOUT_BLOCKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":[],"name":"accumulatedTokenPot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adminWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rId","type":"uint256"},{"internalType":"uint8","name":"blockIdx","type":"uint8"}],"name":"blockTotalStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"queueIndices","type":"uint256[]"}],"name":"checkpointFromQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"checkpointQueue","outputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256[]","name":"rIds","type":"uint256[]"}],"name":"checkpointUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"checkpointUserAuto","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimAdminFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimMonOnly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimVaultFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"crystalToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRoundId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentSequenceNumber","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyUnstick","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entropy","outputs":[{"internalType":"contract IEntropyV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getCheckpointQueue","outputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"internalType":"struct CrystalSupply.CheckpointRequest[]","name":"requests","type":"tuple[]"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCheckpointQueueLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentRoundGrid","outputs":[{"internalType":"uint256[25]","name":"","type":"uint256[25]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentRoundMinerCounts","outputs":[{"internalType":"uint256[25]","name":"","type":"uint256[25]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEntropyFee","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"callbackGasLimit","type":"uint32"}],"name":"getEntropyFeeWithGas","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMotherlodeInc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getRoundData","outputs":[{"components":[{"internalType":"uint256","name":"totalStake","type":"uint256"},{"internalType":"uint8","name":"winningBlock","type":"uint8"},{"internalType":"bool","name":"finalized","type":"bool"},{"internalType":"bool","name":"motherlodeHit","type":"bool"},{"internalType":"bool","name":"isWinnerTakeAll","type":"bool"},{"internalType":"uint256","name":"monPerShare","type":"uint256"},{"internalType":"uint256","name":"tokenPerShare","type":"uint256"},{"internalType":"uint256","name":"winnerTakeAllTokenAmount","type":"uint256"},{"internalType":"uint256","name":"finalizedAt","type":"uint256"},{"internalType":"uint256","name":"participantCount","type":"uint256"}],"internalType":"struct CrystalSupply.RoundInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getRoundParticipants","outputs":[{"internalType":"address[]","name":"participants","type":"address[]"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getRoundWinners","outputs":[{"components":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"uint256","name":"monWon","type":"uint256"},{"internalType":"uint256","name":"tokenWon","type":"uint256"}],"internalType":"struct CrystalSupply.WinnerInfo[]","name":"winners","type":"tuple[]"},{"internalType":"uint256","name":"totalWinners","type":"uint256"},{"internalType":"uint8","name":"winningBlock","type":"uint8"},{"internalType":"bool","name":"motherlodeHit","type":"bool"},{"internalType":"bool","name":"isWinnerTakeAll","type":"bool"},{"internalType":"uint256","name":"finalizedAt","type":"uint256"}],"internalType":"struct CrystalSupply.RoundWinnersResult","name":"result","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserBalances","outputs":[{"internalType":"uint256","name":"unclaimed","type":"uint256"},{"internalType":"uint256","name":"refined","type":"uint256"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getUserBlockDepositsForRound","outputs":[{"internalType":"uint256[25]","name":"","type":"uint256[25]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"hasParticipated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"inCheckpointQueue","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"intermissionDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRolling","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastCheckpointedRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRoundStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAutoCheckpointRounds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minerRewardsFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumParticipants","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdminFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingVaultFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"blockIdx","type":"uint8"}],"name":"prospect","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"blockMask","type":"uint32"}],"name":"prospectBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"protocolVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"callbackGasLimit","type":"uint32"}],"name":"reRequestVRF","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"roundDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"roundParticipantCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"roundParticipants","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rounds","outputs":[{"internalType":"uint256","name":"monTotalOfLosingBlocks","type":"uint256"},{"internalType":"uint256","name":"monTotalOnWinningBlock","type":"uint256"},{"internalType":"uint256","name":"totalRoundStake","type":"uint256"},{"internalType":"uint8","name":"winningBlock","type":"uint8"},{"internalType":"bool","name":"finalized","type":"bool"},{"internalType":"bool","name":"motherlodeHit","type":"bool"},{"internalType":"bool","name":"isWinnerTakeAll","type":"bool"},{"internalType":"uint256","name":"winningTicket","type":"uint256"},{"internalType":"uint256","name":"monPerShare","type":"uint256"},{"internalType":"uint256","name":"tokenPerShare","type":"uint256"},{"internalType":"uint256","name":"winnerTakeAllTokenAmount","type":"uint256"},{"internalType":"uint256","name":"finalizedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"sequenceToRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newWallet","type":"address"}],"name":"setAdminWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"setKeeperFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newLimit","type":"uint256"}],"name":"setMaxAutoCheckpointRounds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newVault","type":"address"}],"name":"setProtocolVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"uint256","name":"_minParticipants","type":"uint256"},{"internalType":"uint256","name":"_intermission","type":"uint256"}],"name":"setRoundConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalRefinedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupplyDistributed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUnclaimedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triggerRound","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint8","name":"","type":"uint8"}],"name":"userBlockDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userCreditedRound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint8","name":"","type":"uint8"}],"name":"userCumulativeStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"userHasProspectedBlock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userKeeperFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userPendingMon","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRefinedCrystal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardsFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userUnclaimedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vrfRequestedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

0x60c060405267016345785d8a00006002556064600355603c601355600c601455600160155534801561003057600080fd5b5060405161562838038061562883398101604081905261004f9161012f565b338061007557604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61007e816100c3565b50600180556001600160a01b0393841660a052918316608052600480549184166001600160a01b03199283161790556005805492909316911617905542600a55610183565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b038116811461012a57600080fd5b919050565b6000806000806080858703121561014557600080fd5b61014e85610113565b935061015c60208601610113565b925061016a60408601610113565b915061017860608601610113565b905092959194509250565b60805160a0516154486101e0600039600081816106fe015281816111e401528181611d77015281816124e601528181612a6a01528181612b6a015281816135030152613614015260008181610eb60152611c7601526154486000f3fe6080604052600436106104ae5760003560e01c80638aec85421161026b578063c9f73df61161014f578063f0f2f6ee116100c1578063fbb098ee11610085578063fbb098ee14610fbd578063fc44e4c714610fd3578063fc89aeaa1461100e578063fcfd837f1461102e578063fd5c0cc614611043578063fe31a6131461106357600080fd5b8063f0f2f6ee14610f16578063f2fde38b14610f29578063f7cb789a14610f49578063fa0648cb14610f5f578063faddbdb214610f7f57600080fd5b8063d20e434111610113578063d20e434114610798578063da1f57c814610e78578063dbab925314610e8e578063dbf4025b14610ea4578063de26eadf14610ed8578063ebca8add14610ef857600080fd5b8063c9f73df614610dd7578063c9fbcfb014610dec578063cb1c2b5c14610e01578063cb56532014610e1d578063cb6b532e14610e3757600080fd5b8063a345bf43116101e8578063ae1eac91116101ac578063ae1eac9114610cf2578063af85bdbc14610d12578063b116944414610d28578063b370488014610d60578063c4c7902814610d98578063c566482c14610dab57600080fd5b8063a345bf4314610c32578063a6edbfa814610c47578063aafbc82114610c67578063ab5669ad14610ca2578063abd3f61214610cb757600080fd5b806398dfca7f1161022f57806398dfca7f14610b7b5780639c785d0614610b9b5780639cbe5efd14610bb1578063a1b44ff814610bc7578063a34398a214610be757600080fd5b80638aec854214610a205780638c65c81f14610a355780638da5cb5b14610b175780638f32b2fe14610b3557806390cc399c14610b7357600080fd5b806352a5f1f81161039257806368aa6ffc1161030f578063737b014d116102d3578063737b014d1461096857806376c154a5146109885780637838c031146109b5578063783fe32f146109ca5780637fc4eda8146109dd5780638396c2b0146109f257600080fd5b806368aa6ffc146108b55780636920c820146108e25780636a84246214610910578063715018a61461092657806371e51e9e1461093b57600080fd5b80636157714c116103565780636157714c1461080d578063616138971461082f57806363a4390b1461085c57806364febe9614610889578063687ddf3b1461089f57600080fd5b806352a5f1f8146107785780635342c8bc1461079857806353fd66fd146107ae5780635a9249e6146107ce5780635fd9491d146107ee57600080fd5b806336b19cd71161042b5780633d1b760b116103ef5780633d1b760b146106c057806343a70146146106d657806347ce07cc146106ec57806349208981146107205780634c5970601461074d5780634e71d92d1461076357600080fd5b806336b19cd71461060957806336bcf7d61461064157806336f1e6c01461065e57806339c5dec7146106735780633be830351461069357600080fd5b80632007641911610472578063200764191461057a5780632d971e6314610591578063317a9877146105be57806335082933146105d35780633528ec30146105f357600080fd5b8063053b37e6146104ba5780630720da52146104e35780630bab460b1461051057806312a2f1f414610525578063170ad53c1461056457600080fd5b366104b557005b600080fd5b3480156104c657600080fd5b506104d060035481565b6040519081526020015b60405180910390f35b3480156104ef57600080fd5b506105036104fe366004614ccf565b611079565b6040516104da9190614ce8565b34801561051c57600080fd5b506104d0611168565b34801561053157600080fd5b50610545610540366004614ccf565b611183565b604080516001600160a01b0390931683526020830191909152016104da565b34801561057057600080fd5b506104d0601a5481565b34801561058657600080fd5b5061058f6111bb565b005b34801561059d57600080fd5b506105a66111e0565b6040516001600160801b0390911681526020016104da565b3480156105ca57600080fd5b5061058f611264565b3480156105df57600080fd5b5061058f6105ee366004614d9b565b6113a7565b3480156105ff57600080fd5b506104d06101f481565b34801561061557600080fd5b50600554610629906001600160a01b031681565b6040516001600160a01b0390911681526020016104da565b34801561064d57600080fd5b506104d06801bc16d674ec80000081565b34801561066a57600080fd5b506104d0600681565b34801561067f57600080fd5b5061058f61068e366004614d9b565b611546565b34801561069f57600080fd5b506106b36106ae366004614db6565b6116e5565b6040516104da9190614de2565b3480156106cc57600080fd5b506104d060025481565b3480156106e257600080fd5b506104d060095481565b3480156106f857600080fd5b506106297f000000000000000000000000000000000000000000000000000000000000000081565b34801561072c57600080fd5b506104d061073b366004614d9b565b60226020526000908152604090205481565b34801561075957600080fd5b506104d060155481565b34801561076f57600080fd5b5061058f611a21565b34801561078457600080fd5b5061058f610793366004614ebf565b611d75565b3480156107a457600080fd5b506104d06103e881565b3480156107ba57600080fd5b5061058f6107c9366004614f41565b611e61565b3480156107da57600080fd5b50600454610629906001600160a01b031681565b3480156107fa57600080fd5b506104d06a027b46536c66c8e300000081565b34801561081957600080fd5b50610822612300565b6040516104da9190614f82565b34801561083b57600080fd5b506104d061084a366004614fb4565b60116020526000908152604090205481565b34801561086857600080fd5b506104d0610877366004614ccf565b60166020526000908152604090205481565b34801561089557600080fd5b506104d060145481565b3480156108ab57600080fd5b506104d0601d5481565b3480156108c157600080fd5b506104d06108d0366004614d9b565b60236020526000908152604090205481565b3480156108ee57600080fd5b506109026108fd366004614fd1565b612351565b6040516104da929190614ff3565b34801561091c57600080fd5b506104d060075481565b34801561093257600080fd5b5061058f6124b6565b34801561094757600080fd5b506104d0610956366004614d9b565b601f6020526000908152604090205481565b34801561097457600080fd5b506105a6610983366004615055565b6124c8565b34801561099457600080fd5b506104d06109a3366004614d9b565b601e6020526000908152604090205481565b3480156109c157600080fd5b506104d0606481565b61058f6109d8366004615055565b61255f565b3480156109e957600080fd5b506104d0601981565b3480156109fe57600080fd5b50610a12610a0d366004614db6565b6127f5565b6040516104da92919061507b565b348015610a2c57600080fd5b506104d061291e565b348015610a4157600080fd5b50610aba610a50366004614ccf565b600c6020526000908152604090208054600182015460028301546003840154600485015460058601546006870154600788015460089098015496979596949560ff80861696610100870482169662010000810483169663010000009091049092169491939192908c565b604080519c8d5260208d019b909b52998b019890985260ff90961660608a0152931515608089015291151560a0880152151560c087015260e0860152610100850152610120840152610140830152610160820152610180016104da565b348015610b2357600080fd5b506000546001600160a01b0316610629565b348015610b4157600080fd5b506104d0610b503660046150cf565b600e60209081526000938452604080852082529284528284209052825290205481565b61058f612973565b348015610b8757600080fd5b5061058f610b96366004614ccf565b612d0c565b348015610ba757600080fd5b506104d060125481565b348015610bbd57600080fd5b506104d060085481565b348015610bd357600080fd5b5061058f610be2366004614ccf565b612d5a565b348015610bf357600080fd5b50610c22610c0236600461510b565b601760209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016104da565b348015610c3e57600080fd5b5061058f612de4565b348015610c5357600080fd5b5061058f610c62366004615137565b612f35565b348015610c7357600080fd5b50610c22610c82366004615189565b602560209081526000928352604080842090915290825290205460ff1681565b348015610cae57600080fd5b506104d0603c81565b348015610cc357600080fd5b50610cd7610cd2366004614d9b565b6132d9565b604080519384526020840192909252908201526060016104da565b348015610cfe57600080fd5b50610822610d0d36600461510b565b6133c6565b348015610d1e57600080fd5b506104d0601b5481565b348015610d3457600080fd5b506104d0610d4336600461510b565b600f60209081526000928352604080842090915290825290205481565b348015610d6c57600080fd5b50601054610d80906001600160401b031681565b6040516001600160401b0390911681526020016104da565b61058f610da6366004615055565b613446565b348015610db757600080fd5b506104d0610dc6366004614d9b565b602080526000908152604090205481565b348015610de357600080fd5b506108226137c5565b348015610df857600080fd5b5061058f613815565b348015610e0d57600080fd5b506104d067016345785d8a000081565b348015610e2957600080fd5b50600b54610c229060ff1681565b348015610e4357600080fd5b50610c22610e523660046151b3565b601860209081526000938452604080852082529284528284209052825290205460ff1681565b348015610e8457600080fd5b506104d060065481565b348015610e9a57600080fd5b506104d0601c5481565b348015610eb057600080fd5b506106297f000000000000000000000000000000000000000000000000000000000000000081565b348015610ee457600080fd5b50610629610ef3366004614fd1565b613966565b348015610f0457600080fd5b506104d069163c0fb846284fa0000081565b61058f610f243660046151e6565b61399e565b348015610f3557600080fd5b5061058f610f44366004614d9b565b613ad3565b348015610f5557600080fd5b506104d060135481565b348015610f6b57600080fd5b506104d0610f7a366004615201565b613b0e565b348015610f8b57600080fd5b506104d0610f9a3660046150cf565b600d60209081526000938452604080852082529284528284209052825290205481565b348015610fc957600080fd5b506104d061044c81565b348015610fdf57600080fd5b50610c22610fee36600461510b565b602160209081526000928352604080842090915290825290205460ff1681565b34801561101a57600080fd5b5061058f611029366004614d9b565b613b3c565b34801561103a57600080fd5b506024546104d0565b34801561104f57600080fd5b5061058f61105e366004614db6565b613ebd565b34801561106f57600080fd5b506104d0600a5481565b6110d860405180610140016040528060008152602001600060ff16815260200160001515815260200160001515815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000828152600c6020908152604080832060028101548552600381015460ff80821687860152610100808304821615158886015262010000830482161515606089015263010000009092041615156080870152600582015460a0870152600682015460c0870152600782015460e08701526008909101549085015293825260199052919091205461012082015290565b6000600561117461291e565b61117e9190615250565b905090565b6024818154811061119357600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b6111c3613f10565b600b805460ff191690556010805467ffffffffffffffff19169055565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638204b67a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611240573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117e9190615264565b61126c613f3d565b33600090815260236020526040902054806112c15760405162461bcd60e51b815260206004820152601060248201526f4e6f7468696e6720746f20636c61696d60801b60448201526064015b60405180910390fd5b336000818152602360205260408082208290555190919083908381818185875af1925050503d8060008114611312576040519150601f19603f3d011682016040523d82523d6000602084013e611317565b606091505b505090508061135e5760405162461bcd60e51b8152602060048201526013602482015272151c985b9cd9995c881353d38819985a5b1959606a1b60448201526064016112b8565b604080518381526000602082015233917f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a91015b60405180910390a250506113a560018055565b565b6113af613f10565b6001600160a01b0381166113f75760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016112b8565b6005546006546001600160a01b0390911690156114f557600680546000918290556040519091906001600160a01b0384169083908381818185875af1925050503d8060008114611463576040519150601f19603f3d011682016040523d82523d6000602084013e611468565b606091505b50509050806114af5760405162461bcd60e51b8152602060048201526013602482015272119959481d1c985b9cd9995c8819985a5b1959606a1b60448201526064016112b8565b826001600160a01b03167f6dda1c050330bbf19404606d86eb8277c7a84ee0b310a5311f255750f72e71ea836040516114ea91815260200190565b60405180910390a250505b600580546001600160a01b0319166001600160a01b0384811691821790925560405190918316907fb03e0c2c1a6f3df4b388eee743d2ab30b064eb703533a6808a42b292cff9a97990600090a35050565b61154e613f10565b6001600160a01b0381166115965760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016112b8565b6004546007546001600160a01b03909116901561169457600780546000918290556040519091906001600160a01b0384169083908381818185875af1925050503d8060008114611602576040519150601f19603f3d011682016040523d82523d6000602084013e611607565b606091505b505090508061164e5760405162461bcd60e51b8152602060048201526013602482015272119959481d1c985b9cd9995c8819985a5b1959606a1b60448201526064016112b8565b826001600160a01b03167fe1b962d6a17421db47dc42910fbf9205832ca07f240e024fbc819c44540d88128360405161168991815260200190565b60405180910390a250505b600480546001600160a01b0319166001600160a01b0384811691821790925560405190918316907f6d0ea9c8b710f5c47c6869e683ae5d091320fac973498fc2b9ff84140dc9838390600090a35050565b6040805160c081018252606080825260006020830181905292820183905281018290526080810182905260a08101919091526000848152600c602052604090206003810154610100900460ff166117745760405162461bcd60e51b8152602060048201526013602482015272149bdd5b99081b9bdd08199a5b985b1a5e9959606a1b60448201526064016112b8565b600381015460ff8082166040850181905262010000830482161515606086015263010000009092041615156080840152600882015460a08401526117b9908690613f67565b60208301819052841061182c576040805160008082526020820190925290611823565b611810604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b8152602001906001900390816117dc5790505b50825250611a1a565b600061183884866152a3565b9050826020015181111561184d575060208201515b600061185986836152b6565b9050806001600160401b038111156118735761187361528d565b6040519080825280602002602001820160405280156118d857816020015b6118c5604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b8152602001906001900390816118915790505b5084526000878152601960205260408120548190815b81811080156118fc57508483105b15611a125760008b8152601960205260408120805483908110611921576119216152c9565b60009182526020808320909101548e8352600d825260408084206001600160a01b0390921680855291835280842060038d015460ff16855290925291205490915080156119fd578b86106119ef5760008061198f8f858d60030160009054906101000a900460ff1686614004565b915091506040518060800160405280856001600160a01b03168152602001848152602001838152602001828152508c6000015188815181106119d3576119d36152c9565b602002602001018190525086806119e9906152df565b97505050505b856119f9816152df565b9650505b50508080611a0a906152df565b9150506118ee565b505050505050505b9392505050565b611a29613f3d565b3360008181526023602052604090205490611a43906140ea565b336000908152601e60209081526040808320549180529091205482151580611a6b5750600082115b80611a765750600081115b611ab55760405162461bcd60e51b815260206004820152601060248201526f4e6f7468696e6720746f20636c61696d60801b60448201526064016112b8565b600080831180611ac55750600082115b15611baf576000612710611adb6103e8866152f8565b611ae59190615250565b90506000611af382866152b6565b9050600082118015611b06575084601a54115b15611b6b57600085601a54611b1b91906152b6565b905080611b3084670de0b6b3a76400006152f8565b611b3a9190615250565b601b6000828254611b4b91906152a3565b9250508190555082601c6000828254611b6491906152a3565b9091555050505b336000908152601e602090815260408083208390559080528120819055601a8054879290611b9a9084906152b6565b90915550611baa905084826152a3565b925050505b8315611c5457336000818152602360205260408082208290555190919086908381818185875af1925050503d8060008114611c06576040519150601f19603f3d011682016040523d82523d6000602084013e611c0b565b606091505b5050905080611c525760405162461bcd60e51b8152602060048201526013602482015272151c985b9cd9995c881353d38819985a5b1959606a1b60448201526064016112b8565b505b8015611d2d5760405163a9059cbb60e01b8152336004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015611cc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ceb919061530f565b611d2d5760405162461bcd60e51b8152602060048201526013602482015272151c985b9cd9995c881513d2c819985a5b1959606a1b60448201526064016112b8565b604080518581526020810183905233917f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a910160405180910390a2505050506113a560018055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116611dec5760405162461bcd60e51b815260206004820152601760248201527f456e74726f70792061646472657373206e6f742073657400000000000000000060448201526064016112b8565b336001600160a01b03821614611e505760405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b60648201526084016112b8565b611e5b8484846141bf565b50505050565b611e69613f3d565b602454600090815b838110156122bb576000858583818110611e8d57611e8d6152c9565b905060200201359050828110611ed55760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b60448201526064016112b8565b8115611f6e578585611ee86001856152b6565b818110611ef757611ef76152c9565b90506020020135868684818110611f1057611f106152c9565b9050602002013510611f6e5760405162461bcd60e51b815260206004820152602160248201527f496e6469636573206d75737420626520736f727465642064657363656e64696e6044820152606760f81b60648201526084016112b8565b600060248281548110611f8357611f836152c9565b60009182526020918290206040805180820190915260029092020180546001600160a01b031680835260019091015492820183905260085491935091908110611fcf57505050506122b3565b6000818152600c602052604090206003810154610100900460ff16611ff85750505050506122b3565b60008281526021602090815260408083206001600160a01b038716845290915290205460ff16156120435761202c856142da565b8661203681615331565b97505050505050506122b3565b6000828152600d602090815260408083206001600160a01b03871684528252808320600385015460ff16845290915290205480156122965760038201546000906301000000900460ff16156120f7576000848152600e602090815260408083206001600160a01b03891684528252808320600387015460ff168452909152902054600484015481118015906120e457506120dd83826152a3565b8460040154105b156120f157836007015491505b5061211d565b670de0b6b3a764000083600601548361211091906152f8565b61211a9190615250565b90505b801561212d5761212d8582614409565b6000670de0b6b3a764000084600501548461214891906152f8565b6121529190615250565b61215c90846152a3565b6001600160a01b0387166000908152602360205260408120805492935083929091906121899084906152a3565b909155505060008581526021602090815260408083206001600160a01b038a16808552908352818420805460ff19166001179055888452600f8352818420908452909152902054801561224e576000868152600f602090815260408083206001600160a01b038b168452909152812055612203818d6152a3565b604080518881526020810186905290810184905260608101839052909c506001600160a01b0388169033906000805160206153f38339815191529060800160405180910390a3612292565b6040805187815260208101859052908101839052600060608201526001600160a01b0388169033906000805160206153f38339815191529060800160405180910390a35b5050505b61229f866142da565b876122a981615331565b9850505050505050505b600101611e71565b5081156122f157604051339083156108fc029084906000818181858888f193505050501580156122ef573d6000803e3d6000fd5b505b50506122fc60018055565b5050565b612308614cb0565b6008546000908152600c6020526040908190208151610320810190925260220160198282826020028201915b815481526020019060010190808311612334575050505050905090565b6024546060908084106123a357604080516000808252602082019092529061239b565b60408051808201909152600080825260208201528152602001906001900390816123745790505b5091506124af565b60006123af84866152a3565b9050818111156123bc5750805b60006123c886836152b6565b9050806001600160401b038111156123e2576123e261528d565b60405190808252806020026020018201604052801561242757816020015b60408051808201909152600080825260208201528152602001906001900390816124005790505b50935060005b818110156124ab57602461244182896152a3565b81548110612451576124516152c9565b60009182526020918290206040805180820190915260029092020180546001600160a01b0316825260010154918101919091528551869083908110612498576124986152c9565b602090810291909101015260010161242d565b5050505b9250929050565b6124be613f10565b6113a5600061445c565b60405163ca1642e160e01b815263ffffffff821660048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ca1642e190602401602060405180830381865afa158015612535573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125599190615264565b92915050565b612567613f3d565b600a544210156125895760405162461bcd60e51b81526004016112b890615348565b60025461259e9067016345785d8a00006152a3565b3410156125ed5760405162461bcd60e51b815260206004820152601d60248201527f53656e6420746f74616c207374616b65202b206b65657065722066656500000060448201526064016112b8565b60008163ffffffff16116126385760405162461bcd60e51b8152602060048201526012602482015271139bc8189b1bd8dadcc81cd95b1958dd195960721b60448201526064016112b8565b600b5460009060ff1661264d5760085461265b565b60085461265b9060016152a3565b9050600082815b60198160ff1610156126a757600180831690036126875782612683816152df565b9350505b60018263ffffffff16901c9150808061269f9061538c565b915050612662565b50600082116126ed5760405162461bcd60e51b8152602060048201526012602482015271139bc8189b1bd8dadcc81cd95b1958dd195960721b60448201526064016112b8565b6000600254346126fd91906152b6565b9050600061270b8483615250565b9050600061271985846153ab565b905067016345785d8a00008210156127635760405162461bcd60e51b815260206004820152600d60248201526c4d696e2070657220626c6f636b60981b60448201526064016112b8565b869350600160005b60198160ff1610156127e157600180871690036127c15781156127b5576127ac3382856002548861279c91906152a3565b6127a691906152a3565b8b6144ac565b600091506127c1565b6127c13382868b6144ac565b60018663ffffffff16901c955080806127d99061538c565b91505061276b565b50505050505050506127f260018055565b50565b600083815260196020526040902054606090808410612824576040805160008152602081019091529150612916565b600061283084866152a3565b90508181111561283d5750805b600061284986836152b6565b9050806001600160401b038111156128635761286361528d565b60405190808252806020026020018201604052801561288c578160200160208202803683370190505b50935060005b818110156129125760008881526019602052604090206128b282896152a3565b815481106128c2576128c26152c9565b9060005260206000200160009054906101000a90046001600160a01b03168582815181106128f2576128f26152c9565b6001600160a01b0390921660209283029190910190910152600101612892565b5050505b935093915050565b600080601d5469163c0fb846284fa0000061293991906152a3565b905060006129526a027b46536c66c8e300000083615250565b90506006811115612961575060065b6801bc16d674ec800000901c92915050565b601354600a5461298391906152a3565b4210156129be5760405162461bcd60e51b8152602060048201526009602482015268546f6f206561726c7960b81b60448201526064016112b8565b600b5460ff1615612a035760405162461bcd60e51b815260206004820152600f60248201526e416c726561647920726f6c6c696e6760881b60448201526064016112b8565b6015546008546000908152601660205260409020541015612a665760405162461bcd60e51b815260206004820152601760248201527f4e6f7420656e6f756768207061727469636970616e747300000000000000000060448201526064016112b8565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638204b67a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ac6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aea9190615264565b9050806001600160801b0316341015612b405760405162461bcd60e51b8152602060048201526018602482015277496e73756666696369656e7420656e74726f70792066656560401b60448201526064016112b8565b600b805460ff1916600117905560408051637b43155d60e01b815290516000916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691637b43155d916001600160801b038616916004808301926020929190829003018185885af1158015612bc1573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612be691906153bf565b6010805467ffffffffffffffff19166001600160401b038316908117909155600854600091825260116020526040909120554360125590506001600160801b038216341115612cc957600033612c456001600160801b038516346152b6565b604051600081818185875af1925050503d8060008114612c81576040519150601f19603f3d011682016040523d82523d6000602084013e612c86565b606091505b5050905080612cc75760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b60448201526064016112b8565b505b6008546040516001600160401b03831681527fb9560bd3e25a95a3fcca6c0f21c6e8d9d07165bb54ae84dd553e21bbd94ce2629060200160405180910390a25050565b612d14613f10565b600280549082905560408051828152602081018490527fe22b4203e51a04604258271faf98bf06a6f25e13004a7bce95940d2a2adbae8c91015b60405180910390a15050565b612d62613f10565b60008111612da65760405162461bcd60e51b815260206004820152601160248201527004c696d6974206d757374206265203e203607c1b60448201526064016112b8565b600380549082905560408051828152602081018490527fd0497f09900611069b215093d3f3a5087f9719137e3a3fc9f716995a4be91f3b9101612d4e565b612dec613f3d565b6005546001600160a01b03163314612e325760405162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b60448201526064016112b8565b60065480612e755760405162461bcd60e51b815260206004820152601060248201526f4e6f206665657320746f20636c61696d60801b60448201526064016112b8565b60006006819055604051339083908381818185875af1925050503d8060008114612ebb576040519150601f19603f3d011682016040523d82523d6000602084013e612ec0565b606091505b5050905080612f035760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016112b8565b60405182815233907f6dda1c050330bbf19404606d86eb8277c7a84ee0b310a5311f255750f72e71ea90602001611392565b612f3d613f3d565b6000805b82811015613294576000848483818110612f5d57612f5d6152c9565b9050602002013590506008548110612fa65760405162461bcd60e51b815260206004820152600c60248201526b1058dd1a5d99481c9bdd5b9960a21b60448201526064016112b8565b6000818152600c602052604090206003810154610100900460ff166130035760405162461bcd60e51b8152602060048201526013602482015272149bdd5b99081b9bdd08199a5b985b1a5e9959606a1b60448201526064016112b8565b60008281526021602090815260408083206001600160a01b038b16845290915290205460ff161561303557505061328c565b6000828152600d602090815260408083206001600160a01b038b1684528252808320600385015460ff16845290915290205480156132885760038201546000906301000000900460ff16156130e9576000848152600e602090815260408083206001600160a01b038d1684528252808320600387015460ff168452909152902054600484015481118015906130d657506130cf83826152a3565b8460040154105b156130e357836007015491505b5061310f565b670de0b6b3a764000083600601548361310291906152f8565b61310c9190615250565b90505b801561311f5761311f8982614409565b6000670de0b6b3a764000084600501548461313a91906152f8565b6131449190615250565b61314e90846152a3565b6001600160a01b038b1660009081526023602052604081208054929350839290919061317b9084906152a3565b909155505060008581526021602090815260408083206001600160a01b038e16808552908352818420805460ff19166001179055888452600f83528184209084529091529020548015613240576000868152600f602090815260408083206001600160a01b038f1684529091528120556131f581896152a3565b6040805188815260208101869052908101849052606081018390529098506001600160a01b038c169033906000805160206153f38339815191529060800160405180910390a3613284565b6040805187815260208101859052908101839052600060608201526001600160a01b038c169033906000805160206153f38339815191529060800160405180910390a35b5050505b5050505b600101612f41565b5080156132ca57604051339082156108fc029083906000818181858888f193505050501580156132c8573d6000803e3d6000fd5b505b506132d460018055565b505050565b6001600160a01b0381166000908152601e602090815260408083205482805281842054601f90935290832054601b549193911115613369576001600160a01b0384166000908152601f6020526040812054601b5461333791906152b6565b90506000670de0b6b3a764000061334e86846152f8565b6133589190615250565b905061336481856152a3565b935050505b60008311806133785750600082115b156133bb57600061271061338e6103e8866152f8565b6133989190615250565b905060006133a682866152b6565b90506133b284826152a3565b925050506133bf565b5060005b9193909250565b6133ce614cb0565b6133d6614cb0565b60005b60198160ff16101561343e576000858152600d602090815260408083206001600160a01b0388168452825280832060ff851680855292529091205490839060198110613427576134276152c9565b6020020152806134368161538c565b9150506133d9565b509392505050565b600b5460ff166134865760405162461bcd60e51b815260206004820152600b60248201526a4e6f7420726f6c6c696e6760a81b60448201526064016112b8565b6101f460125461349691906152a3565b4310156134e55760405162461bcd60e51b815260206004820152601760248201527f5652462074696d656f7574206e6f74207265616368656400000000000000000060448201526064016112b8565b60405163ca1642e160e01b815263ffffffff821660048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ca1642e190602401602060405180830381865afa158015613552573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135769190615264565b9050806001600160801b03163410156135cc5760405162461bcd60e51b8152602060048201526018602482015277496e73756666696369656e7420656e74726f70792066656560401b60448201526064016112b8565b6010546012546001600160401b03909116906000906135eb90436152b6565b604051630bed189f60e01b815263ffffffff861660048201529091506000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630bed189f906001600160801b0387169060240160206040518083038185885af1158015613667573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061368c91906153bf565b6010805467ffffffffffffffff19166001600160401b038316908117909155600854600091825260116020526040909120554360125590506001600160801b03841634111561376f576000336136eb6001600160801b038716346152b6565b604051600081818185875af1925050503d8060008114613727576040519150601f19603f3d011682016040523d82523d6000602084013e61372c565b606091505b505090508061376d5760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b60448201526064016112b8565b505b600854604080516001600160401b038087168252841660208201529081018490527f46df967790930427bf9d61b0cbc196e7e28b27d130faa338db5de10f90c327d9906060015b60405180910390a25050505050565b6137cd614cb0565b6008546000908152600c602052604090819020815161032081019092526009016019828282602002820191815481526020019060010190808311612334575050505050905090565b61381d613f3d565b6004546001600160a01b031633146138635760405162461bcd60e51b8152602060048201526009602482015268139bdd081d985d5b1d60ba1b60448201526064016112b8565b600754806138a65760405162461bcd60e51b815260206004820152601060248201526f4e6f206665657320746f20636c61696d60801b60448201526064016112b8565b60006007819055604051339083908381818185875af1925050503d80600081146138ec576040519150601f19603f3d011682016040523d82523d6000602084013e6138f1565b606091505b50509050806139345760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016112b8565b60405182815233907fe1b962d6a17421db47dc42910fbf9205832ca07f240e024fbc819c44540d881290602001611392565b6019602052816000526040600020818154811061398257600080fd5b6000918252602090912001546001600160a01b03169150829050565b6139a6613f3d565b600a544210156139c85760405162461bcd60e51b81526004016112b890615348565b600b5460009060ff166139dd576008546139eb565b6008546139eb9060016152a3565b6000818152600f60209081526040808320338452909152902054909150158015613a7857600254613a249067016345785d8a00006152a3565b341015613a735760405162461bcd60e51b815260206004820152601760248201527f53656e64207374616b65202b206b65657065722066656500000000000000000060448201526064016112b8565b613abc565b67016345785d8a0000341015613abc5760405162461bcd60e51b81526020600482015260096024820152684d696e207374616b6560b81b60448201526064016112b8565b613ac8338434856144ac565b50506127f260018055565b613adb613f10565b6001600160a01b038116613b0557604051631e4fbdf760e01b8152600060048201526024016112b8565b6127f28161445c565b6000828152600c6020526040812060090160ff831660198110613b3357613b336152c9565b01549392505050565b613b44613f3d565b6001600160a01b038116600090815260226020526040812054613b689060016152a3565b60085460035491925090613b7c83836152b6565b1115613b9257600354613b8f90836152a3565b90505b6000825b82811015613e50576000818152600c602052604090206003810154610100900460ff161580613be7575060008281526021602090815260408083206001600160a01b038a16845290915290205460ff165b15613bf25750613e48565b6000828152600d602090815260408083206001600160a01b038a1684528252808320600385015460ff1684529091529020548015613e455760038201546000906301000000900460ff1615613ca6576000848152600e602090815260408083206001600160a01b038c1684528252808320600387015460ff16845290915290205460048401548111801590613c935750613c8c83826152a3565b8460040154105b15613ca057836007015491505b50613ccc565b670de0b6b3a7640000836006015483613cbf91906152f8565b613cc99190615250565b90505b8015613cdc57613cdc8882614409565b6000670de0b6b3a7640000846005015484613cf791906152f8565b613d019190615250565b613d0b90846152a3565b6001600160a01b038a16600090815260236020526040812080549293508392909190613d389084906152a3565b909155505060008581526021602090815260408083206001600160a01b038d16808552908352818420805460ff19166001179055888452600f83528184209084529091529020548015613dfd576000868152600f602090815260408083206001600160a01b038e168452909152812055613db281886152a3565b6040805188815260208101869052908101849052606081018390529097506001600160a01b038b169033906000805160206153f38339815191529060800160405180910390a3613e41565b6040805187815260208101859052908101839052600060608201526001600160a01b038b169033906000805160206153f38339815191529060800160405180910390a35b5050505b50505b600101613b96565b508115613e7c57613e626001836152b6565b6001600160a01b0385166000908152602260205260409020555b8015613eb157604051339082156108fc029083906000818181858888f19350505050158015613eaf573d6000803e3d6000fd5b505b5050506127f260018055565b613ec5613f10565b603c831015613f025760405162461bcd60e51b8152602060048201526009602482015268151bdbc81cda1bdc9d60ba1b60448201526064016112b8565b601392909255601555601455565b6000546001600160a01b031633146113a55760405163118cdaa760e01b81523360048201526024016112b8565b600260015403613f6057604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b600082815260196020526040812054815b81811015613ffc576000858152600d6020908152604080832060199092528220805483919085908110613fad57613fad6152c9565b60009182526020808320909101546001600160a01b03168352828101939093526040918201812060ff891682529092529020541115613ff45782613ff0816152df565b9350505b600101613f78565b505092915050565b6000848152600c602052604081206005810154829190670de0b6b3a76400009061402e90866152f8565b6140389190615250565b61404290856152a3565b60038201549093506301000000900460ff16156140ba576000878152600e602090815260408083206001600160a01b038a168452825280832060ff89168452909152902054600482015481118015906140a757506140a085826152a3565b8260040154105b156140b457816007015492505b506140e0565b670de0b6b3a76400008160060154856140d391906152f8565b6140dd9190615250565b91505b5094509492505050565b6001600160a01b0381166000908152601f6020526040902054601b5411156141a0576001600160a01b0381166000908152601f6020526040812054601b5461413291906152b6565b6001600160a01b0383166000908152601e6020526040812054919250670de0b6b3a764000061416183856152f8565b61416b9190615250565b6001600160a01b03851660009081526020805260408120805492935083929091906141979084906152a3565b90915550505050505b601b546001600160a01b039091166000908152601f6020526040902055565b6001600160401b038316600090815260116020526040902054600b5460ff1680156141eb575060085481145b801561420457506010546001600160401b038581169116145b6142505760405162461bcd60e51b815260206004820152601960248201527f496e76616c6964206f72207374616c652073657175656e63650000000000000060448201526064016112b8565b60405182908190600090614274908390600190602001918252602082015260400190565b60408051601f198184030181528282528051602091820120818401879052600284840152825180850384018152606090940190925282519201919091209091506142bf838383614565565b50506010805467ffffffffffffffff19169055505050505050565b6024546000906142ec906001906152b6565b9050600060248381548110614303576143036152c9565b60009182526020808320604080518082018252600290940290910180546001600160a01b03168085526001909101548484019081529085526025835281852090518552909152909120805460ff1916905590508282146143ca5760248281548110614370576143706152c9565b906000526020600020906002020160248481548110614391576143916152c9565b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b039092169190911781556001918201549101555b60248054806143db576143db6153dc565b60008281526020812060026000199093019283020180546001600160a01b0319168155600101559055505050565b614412826140ea565b6001600160a01b0382166000908152601e60205260408120805483929061443a9084906152a3565b9250508190555080601a600082825461445391906152a3565b90915550505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60198360ff16106144ef5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420426c6f636b60981b60448201526064016112b8565b6000818152600f602090815260408083206001600160a01b038816845290915290205482901580156145515760025461452890856152b6565b6002546000858152600f602090815260408083206001600160a01b038c16845290915290205591505b61455d86868486614912565b505050505050565b6008546000908152600c602052604090206145816019856153ab565b60038201805460ff191660ff929092169190911790556145a7610271602086901c6153ab565b60038201805462ff000019811692156201000002928317909155600091600984019160ff908116911617601981106145e1576145e16152c9565b015460018301819055600283015490915060006145fe83836152b6565b8085559050600083900361467457600061271061461c6064856152f8565b6146269190615250565b9050806006600082825461463a91906152a3565b9091555061464a905081846152b6565b6007600082825461465b91906152a3565b9091555050600060058601819055600686015550614826565b60006127106146846064846152f8565b61468e9190615250565b905060006127106146a16103e8856152f8565b6146ab9190615250565b905060006146b982846152a3565b905082600660008282546146cd91906152a3565b9250508190555081600760008282546146e691906152a3565b90915550600090506146f882866152b6565b90508661470d82670de0b6b3a76400006152f8565b6147179190615250565b6005890155600061472661291e565b90506000614732611168565b60038b015490915062010000900460ff1661475f57806009600082825461475991906152a3565b90915550505b60038a015460009062010000900460ff161561478f578260095461478391906152a3565b60006009559050614792565b50815b80601d60008282546147a491906152a3565b909155506147b5905060028e6153ab565b6000036147ec5760038b01805463ff0000001916630100000017905560078b018190556147e28a8d6153ab565b60048c015561481e565b60038b01805463ff000000191690558961480e82670de0b6b3a76400006152f8565b6148189190615250565b60068c01555b505050505050505b60038401805461010061ff00198216179182905542600880880191909155546040805191825260ff92831660208301526301000000840483161515908201526201000090920416151560608201527f73b6989256872bd8853751760a7960291ec2e04e1d8191bd624381f27fca9f8e9060800160405180910390a1600880549060006148b1836152df565b90915550506014546148c390426152a3565b600a819055600b805460ff19169055600854604051918252907f278844837bcf8364a705384bf3a2812901f54155bae86dea81dd52aa5b9ec0e39060200160405180910390a250505050505050565b6000818152600c6020526040812060090160ff851660198110614937576149376152c9565b01546000838152600d602090815260408083206001600160a01b038a168452825280832060ff89168452909152812054919250036149a0576000828152600e602090815260408083206001600160a01b0389168452825280832060ff8816845290915290208190555b6000828152600d602090815260408083206001600160a01b0389168452825280832060ff88168452909152812080548592906149dd9084906152a3565b90915550506000828152600c60205260409020839060090160ff861660198110614a0957614a096152c9565b016000828254614a1991906152a3565b90915550506000828152600c602052604081206002018054859290614a3f9084906152a3565b9091555050600082815260186020908152604080832060ff80891685529083528184206001600160a01b038a1685529092529091205416614ae857600082815260186020908152604080832060ff88168085529083528184206001600160a01b038a1685528352818420805460ff19166001179055858452600c9092529091206022019060198110614ad357614ad36152c9565b018054906000614ae2836152df565b91905055505b60008281526017602090815260408083206001600160a01b038916845290915290205460ff16614c655760008281526017602090815260408083206001600160a01b03891684528252808320805460ff1916600117905584835260169091528120805491614b55836152df565b9091555050600082815260196020908152604080832080546001810182559084528284200180546001600160a01b0319166001600160a01b038a1690811790915583526025825280832085845290915290205460ff16614c65576001600160a01b03858116600081815260256020908152604080832087845282528083208054600160ff1990911681179091558151808301909252938152908101868152602480549485018155909252517f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec4600290930292830180546001600160a01b031916919094161790925590517f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec5909101555b6040805183815260ff861660208201529081018490526001600160a01b038616907f60c16227b368ec1b75014bc56837d084cbaf0b6989beb0cced9d343d9b5a9467906060016137b6565b6040518061032001604052806019906020820280368337509192915050565b600060208284031215614ce157600080fd5b5035919050565b81518152602080830151610140830191614d069084018260ff169052565b506040830151614d1a604084018215159052565b506060830151614d2e606084018215159052565b506080830151614d42608084018215159052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010083015161010083015261012083015161012083015292915050565b80356001600160a01b0381168114614d9657600080fd5b919050565b600060208284031215614dad57600080fd5b611a1a82614d7f565b600080600060608486031215614dcb57600080fd5b505081359360208301359350604090920135919050565b6020808252825160c083830152805160e08401819052600092919091019082906101008501905b80831015614e5557835160018060a01b03815116835260208101516020840152604081015160408401526060810151606084015250608082019150602084019350600183019250614e09565b506020860151604086015260408601519250614e76606086018460ff169052565b606086015180151560808701529250608086015180151560a0870152925060a086015160c086015280935050505092915050565b6001600160401b03811681146127f257600080fd5b600080600060608486031215614ed457600080fd5b8335614edf81614eaa565b9250614eed60208501614d7f565b9150604084013590509250925092565b60008083601f840112614f0f57600080fd5b5081356001600160401b03811115614f2657600080fd5b6020830191508360208260051b85010111156124af57600080fd5b60008060208385031215614f5457600080fd5b82356001600160401b03811115614f6a57600080fd5b614f7685828601614efd565b90969095509350505050565b6103208101818360005b6019811015614fab578151835260209283019290910190600101614f8c565b50505092915050565b600060208284031215614fc657600080fd5b8135611a1a81614eaa565b60008060408385031215614fe457600080fd5b50508035926020909101359150565b6040808252835190820181905260009060208501906060840190835b8181101561504257835180516001600160a01b03168452602090810151818501529093019260409092019160010161500f565b5050602093909301939093525092915050565b60006020828403121561506757600080fd5b813563ffffffff81168114611a1a57600080fd5b6040808252835190820181905260009060208501906060840190835b818110156150425783516001600160a01b0316835260209384019390920191600101615097565b803560ff81168114614d9657600080fd5b6000806000606084860312156150e457600080fd5b833592506150f460208501614d7f565b9150615102604085016150be565b90509250925092565b6000806040838503121561511e57600080fd5b8235915061512e60208401614d7f565b90509250929050565b60008060006040848603121561514c57600080fd5b61515584614d7f565b925060208401356001600160401b0381111561517057600080fd5b61517c86828701614efd565b9497909650939450505050565b6000806040838503121561519c57600080fd5b6151a583614d7f565b946020939093013593505050565b6000806000606084860312156151c857600080fd5b833592506151d8602085016150be565b915061510260408501614d7f565b6000602082840312156151f857600080fd5b611a1a826150be565b6000806040838503121561521457600080fd5b8235915061512e602084016150be565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008261525f5761525f615224565b500490565b60006020828403121561527657600080fd5b81516001600160801b0381168114611a1a57600080fd5b634e487b7160e01b600052604160045260246000fd5b808201808211156125595761255961523a565b818103818111156125595761255961523a565b634e487b7160e01b600052603260045260246000fd5b6000600182016152f1576152f161523a565b5060010190565b80820281158282048414176125595761255961523a565b60006020828403121561532157600080fd5b81518015158114611a1a57600080fd5b6000816153405761534061523a565b506000190190565b60208082526024908201527f526f756e64206e6f742073746172746564207965742028696e7465726d697373604082015263696f6e2960e01b606082015260800190565b600060ff821660ff81036153a2576153a261523a565b60010192915050565b6000826153ba576153ba615224565b500690565b6000602082840312156153d157600080fd5b8151611a1a81614eaa565b634e487b7160e01b600052603160045260246000fdfe07f32f6f4c1b7fe2a67cebec7f6546a6324a14e661023fa65d8dd4e6605183a2a2646970667358221220c2de5eb2b0d62f6b5989008c805ac4c86e8c6a89d59cb6056ce3b64d3eeca1b264736f6c634300081c0033000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f13400000000000000000000000026aae4c1ccb0c5263203af7eb5a88eb7fcd196ae00000000000000000000000099662e7cc1970de202e97d5b2b70215d5370b1f400000000000000000000000099662e7cc1970de202e97d5b2b70215d5370b1f4

Deployed Bytecode

0x6080604052600436106104ae5760003560e01c80638aec85421161026b578063c9f73df61161014f578063f0f2f6ee116100c1578063fbb098ee11610085578063fbb098ee14610fbd578063fc44e4c714610fd3578063fc89aeaa1461100e578063fcfd837f1461102e578063fd5c0cc614611043578063fe31a6131461106357600080fd5b8063f0f2f6ee14610f16578063f2fde38b14610f29578063f7cb789a14610f49578063fa0648cb14610f5f578063faddbdb214610f7f57600080fd5b8063d20e434111610113578063d20e434114610798578063da1f57c814610e78578063dbab925314610e8e578063dbf4025b14610ea4578063de26eadf14610ed8578063ebca8add14610ef857600080fd5b8063c9f73df614610dd7578063c9fbcfb014610dec578063cb1c2b5c14610e01578063cb56532014610e1d578063cb6b532e14610e3757600080fd5b8063a345bf43116101e8578063ae1eac91116101ac578063ae1eac9114610cf2578063af85bdbc14610d12578063b116944414610d28578063b370488014610d60578063c4c7902814610d98578063c566482c14610dab57600080fd5b8063a345bf4314610c32578063a6edbfa814610c47578063aafbc82114610c67578063ab5669ad14610ca2578063abd3f61214610cb757600080fd5b806398dfca7f1161022f57806398dfca7f14610b7b5780639c785d0614610b9b5780639cbe5efd14610bb1578063a1b44ff814610bc7578063a34398a214610be757600080fd5b80638aec854214610a205780638c65c81f14610a355780638da5cb5b14610b175780638f32b2fe14610b3557806390cc399c14610b7357600080fd5b806352a5f1f81161039257806368aa6ffc1161030f578063737b014d116102d3578063737b014d1461096857806376c154a5146109885780637838c031146109b5578063783fe32f146109ca5780637fc4eda8146109dd5780638396c2b0146109f257600080fd5b806368aa6ffc146108b55780636920c820146108e25780636a84246214610910578063715018a61461092657806371e51e9e1461093b57600080fd5b80636157714c116103565780636157714c1461080d578063616138971461082f57806363a4390b1461085c57806364febe9614610889578063687ddf3b1461089f57600080fd5b806352a5f1f8146107785780635342c8bc1461079857806353fd66fd146107ae5780635a9249e6146107ce5780635fd9491d146107ee57600080fd5b806336b19cd71161042b5780633d1b760b116103ef5780633d1b760b146106c057806343a70146146106d657806347ce07cc146106ec57806349208981146107205780634c5970601461074d5780634e71d92d1461076357600080fd5b806336b19cd71461060957806336bcf7d61461064157806336f1e6c01461065e57806339c5dec7146106735780633be830351461069357600080fd5b80632007641911610472578063200764191461057a5780632d971e6314610591578063317a9877146105be57806335082933146105d35780633528ec30146105f357600080fd5b8063053b37e6146104ba5780630720da52146104e35780630bab460b1461051057806312a2f1f414610525578063170ad53c1461056457600080fd5b366104b557005b600080fd5b3480156104c657600080fd5b506104d060035481565b6040519081526020015b60405180910390f35b3480156104ef57600080fd5b506105036104fe366004614ccf565b611079565b6040516104da9190614ce8565b34801561051c57600080fd5b506104d0611168565b34801561053157600080fd5b50610545610540366004614ccf565b611183565b604080516001600160a01b0390931683526020830191909152016104da565b34801561057057600080fd5b506104d0601a5481565b34801561058657600080fd5b5061058f6111bb565b005b34801561059d57600080fd5b506105a66111e0565b6040516001600160801b0390911681526020016104da565b3480156105ca57600080fd5b5061058f611264565b3480156105df57600080fd5b5061058f6105ee366004614d9b565b6113a7565b3480156105ff57600080fd5b506104d06101f481565b34801561061557600080fd5b50600554610629906001600160a01b031681565b6040516001600160a01b0390911681526020016104da565b34801561064d57600080fd5b506104d06801bc16d674ec80000081565b34801561066a57600080fd5b506104d0600681565b34801561067f57600080fd5b5061058f61068e366004614d9b565b611546565b34801561069f57600080fd5b506106b36106ae366004614db6565b6116e5565b6040516104da9190614de2565b3480156106cc57600080fd5b506104d060025481565b3480156106e257600080fd5b506104d060095481565b3480156106f857600080fd5b506106297f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f13481565b34801561072c57600080fd5b506104d061073b366004614d9b565b60226020526000908152604090205481565b34801561075957600080fd5b506104d060155481565b34801561076f57600080fd5b5061058f611a21565b34801561078457600080fd5b5061058f610793366004614ebf565b611d75565b3480156107a457600080fd5b506104d06103e881565b3480156107ba57600080fd5b5061058f6107c9366004614f41565b611e61565b3480156107da57600080fd5b50600454610629906001600160a01b031681565b3480156107fa57600080fd5b506104d06a027b46536c66c8e300000081565b34801561081957600080fd5b50610822612300565b6040516104da9190614f82565b34801561083b57600080fd5b506104d061084a366004614fb4565b60116020526000908152604090205481565b34801561086857600080fd5b506104d0610877366004614ccf565b60166020526000908152604090205481565b34801561089557600080fd5b506104d060145481565b3480156108ab57600080fd5b506104d0601d5481565b3480156108c157600080fd5b506104d06108d0366004614d9b565b60236020526000908152604090205481565b3480156108ee57600080fd5b506109026108fd366004614fd1565b612351565b6040516104da929190614ff3565b34801561091c57600080fd5b506104d060075481565b34801561093257600080fd5b5061058f6124b6565b34801561094757600080fd5b506104d0610956366004614d9b565b601f6020526000908152604090205481565b34801561097457600080fd5b506105a6610983366004615055565b6124c8565b34801561099457600080fd5b506104d06109a3366004614d9b565b601e6020526000908152604090205481565b3480156109c157600080fd5b506104d0606481565b61058f6109d8366004615055565b61255f565b3480156109e957600080fd5b506104d0601981565b3480156109fe57600080fd5b50610a12610a0d366004614db6565b6127f5565b6040516104da92919061507b565b348015610a2c57600080fd5b506104d061291e565b348015610a4157600080fd5b50610aba610a50366004614ccf565b600c6020526000908152604090208054600182015460028301546003840154600485015460058601546006870154600788015460089098015496979596949560ff80861696610100870482169662010000810483169663010000009091049092169491939192908c565b604080519c8d5260208d019b909b52998b019890985260ff90961660608a0152931515608089015291151560a0880152151560c087015260e0860152610100850152610120840152610140830152610160820152610180016104da565b348015610b2357600080fd5b506000546001600160a01b0316610629565b348015610b4157600080fd5b506104d0610b503660046150cf565b600e60209081526000938452604080852082529284528284209052825290205481565b61058f612973565b348015610b8757600080fd5b5061058f610b96366004614ccf565b612d0c565b348015610ba757600080fd5b506104d060125481565b348015610bbd57600080fd5b506104d060085481565b348015610bd357600080fd5b5061058f610be2366004614ccf565b612d5a565b348015610bf357600080fd5b50610c22610c0236600461510b565b601760209081526000928352604080842090915290825290205460ff1681565b60405190151581526020016104da565b348015610c3e57600080fd5b5061058f612de4565b348015610c5357600080fd5b5061058f610c62366004615137565b612f35565b348015610c7357600080fd5b50610c22610c82366004615189565b602560209081526000928352604080842090915290825290205460ff1681565b348015610cae57600080fd5b506104d0603c81565b348015610cc357600080fd5b50610cd7610cd2366004614d9b565b6132d9565b604080519384526020840192909252908201526060016104da565b348015610cfe57600080fd5b50610822610d0d36600461510b565b6133c6565b348015610d1e57600080fd5b506104d0601b5481565b348015610d3457600080fd5b506104d0610d4336600461510b565b600f60209081526000928352604080842090915290825290205481565b348015610d6c57600080fd5b50601054610d80906001600160401b031681565b6040516001600160401b0390911681526020016104da565b61058f610da6366004615055565b613446565b348015610db757600080fd5b506104d0610dc6366004614d9b565b602080526000908152604090205481565b348015610de357600080fd5b506108226137c5565b348015610df857600080fd5b5061058f613815565b348015610e0d57600080fd5b506104d067016345785d8a000081565b348015610e2957600080fd5b50600b54610c229060ff1681565b348015610e4357600080fd5b50610c22610e523660046151b3565b601860209081526000938452604080852082529284528284209052825290205460ff1681565b348015610e8457600080fd5b506104d060065481565b348015610e9a57600080fd5b506104d0601c5481565b348015610eb057600080fd5b506106297f00000000000000000000000026aae4c1ccb0c5263203af7eb5a88eb7fcd196ae81565b348015610ee457600080fd5b50610629610ef3366004614fd1565b613966565b348015610f0457600080fd5b506104d069163c0fb846284fa0000081565b61058f610f243660046151e6565b61399e565b348015610f3557600080fd5b5061058f610f44366004614d9b565b613ad3565b348015610f5557600080fd5b506104d060135481565b348015610f6b57600080fd5b506104d0610f7a366004615201565b613b0e565b348015610f8b57600080fd5b506104d0610f9a3660046150cf565b600d60209081526000938452604080852082529284528284209052825290205481565b348015610fc957600080fd5b506104d061044c81565b348015610fdf57600080fd5b50610c22610fee36600461510b565b602160209081526000928352604080842090915290825290205460ff1681565b34801561101a57600080fd5b5061058f611029366004614d9b565b613b3c565b34801561103a57600080fd5b506024546104d0565b34801561104f57600080fd5b5061058f61105e366004614db6565b613ebd565b34801561106f57600080fd5b506104d0600a5481565b6110d860405180610140016040528060008152602001600060ff16815260200160001515815260200160001515815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000828152600c6020908152604080832060028101548552600381015460ff80821687860152610100808304821615158886015262010000830482161515606089015263010000009092041615156080870152600582015460a0870152600682015460c0870152600782015460e08701526008909101549085015293825260199052919091205461012082015290565b6000600561117461291e565b61117e9190615250565b905090565b6024818154811061119357600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b6111c3613f10565b600b805460ff191690556010805467ffffffffffffffff19169055565b60007f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b0316638204b67a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611240573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117e9190615264565b61126c613f3d565b33600090815260236020526040902054806112c15760405162461bcd60e51b815260206004820152601060248201526f4e6f7468696e6720746f20636c61696d60801b60448201526064015b60405180910390fd5b336000818152602360205260408082208290555190919083908381818185875af1925050503d8060008114611312576040519150601f19603f3d011682016040523d82523d6000602084013e611317565b606091505b505090508061135e5760405162461bcd60e51b8152602060048201526013602482015272151c985b9cd9995c881353d38819985a5b1959606a1b60448201526064016112b8565b604080518381526000602082015233917f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a91015b60405180910390a250506113a560018055565b565b6113af613f10565b6001600160a01b0381166113f75760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016112b8565b6005546006546001600160a01b0390911690156114f557600680546000918290556040519091906001600160a01b0384169083908381818185875af1925050503d8060008114611463576040519150601f19603f3d011682016040523d82523d6000602084013e611468565b606091505b50509050806114af5760405162461bcd60e51b8152602060048201526013602482015272119959481d1c985b9cd9995c8819985a5b1959606a1b60448201526064016112b8565b826001600160a01b03167f6dda1c050330bbf19404606d86eb8277c7a84ee0b310a5311f255750f72e71ea836040516114ea91815260200190565b60405180910390a250505b600580546001600160a01b0319166001600160a01b0384811691821790925560405190918316907fb03e0c2c1a6f3df4b388eee743d2ab30b064eb703533a6808a42b292cff9a97990600090a35050565b61154e613f10565b6001600160a01b0381166115965760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016112b8565b6004546007546001600160a01b03909116901561169457600780546000918290556040519091906001600160a01b0384169083908381818185875af1925050503d8060008114611602576040519150601f19603f3d011682016040523d82523d6000602084013e611607565b606091505b505090508061164e5760405162461bcd60e51b8152602060048201526013602482015272119959481d1c985b9cd9995c8819985a5b1959606a1b60448201526064016112b8565b826001600160a01b03167fe1b962d6a17421db47dc42910fbf9205832ca07f240e024fbc819c44540d88128360405161168991815260200190565b60405180910390a250505b600480546001600160a01b0319166001600160a01b0384811691821790925560405190918316907f6d0ea9c8b710f5c47c6869e683ae5d091320fac973498fc2b9ff84140dc9838390600090a35050565b6040805160c081018252606080825260006020830181905292820183905281018290526080810182905260a08101919091526000848152600c602052604090206003810154610100900460ff166117745760405162461bcd60e51b8152602060048201526013602482015272149bdd5b99081b9bdd08199a5b985b1a5e9959606a1b60448201526064016112b8565b600381015460ff8082166040850181905262010000830482161515606086015263010000009092041615156080840152600882015460a08401526117b9908690613f67565b60208301819052841061182c576040805160008082526020820190925290611823565b611810604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b8152602001906001900390816117dc5790505b50825250611a1a565b600061183884866152a3565b9050826020015181111561184d575060208201515b600061185986836152b6565b9050806001600160401b038111156118735761187361528d565b6040519080825280602002602001820160405280156118d857816020015b6118c5604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b8152602001906001900390816118915790505b5084526000878152601960205260408120548190815b81811080156118fc57508483105b15611a125760008b8152601960205260408120805483908110611921576119216152c9565b60009182526020808320909101548e8352600d825260408084206001600160a01b0390921680855291835280842060038d015460ff16855290925291205490915080156119fd578b86106119ef5760008061198f8f858d60030160009054906101000a900460ff1686614004565b915091506040518060800160405280856001600160a01b03168152602001848152602001838152602001828152508c6000015188815181106119d3576119d36152c9565b602002602001018190525086806119e9906152df565b97505050505b856119f9816152df565b9650505b50508080611a0a906152df565b9150506118ee565b505050505050505b9392505050565b611a29613f3d565b3360008181526023602052604090205490611a43906140ea565b336000908152601e60209081526040808320549180529091205482151580611a6b5750600082115b80611a765750600081115b611ab55760405162461bcd60e51b815260206004820152601060248201526f4e6f7468696e6720746f20636c61696d60801b60448201526064016112b8565b600080831180611ac55750600082115b15611baf576000612710611adb6103e8866152f8565b611ae59190615250565b90506000611af382866152b6565b9050600082118015611b06575084601a54115b15611b6b57600085601a54611b1b91906152b6565b905080611b3084670de0b6b3a76400006152f8565b611b3a9190615250565b601b6000828254611b4b91906152a3565b9250508190555082601c6000828254611b6491906152a3565b9091555050505b336000908152601e602090815260408083208390559080528120819055601a8054879290611b9a9084906152b6565b90915550611baa905084826152a3565b925050505b8315611c5457336000818152602360205260408082208290555190919086908381818185875af1925050503d8060008114611c06576040519150601f19603f3d011682016040523d82523d6000602084013e611c0b565b606091505b5050905080611c525760405162461bcd60e51b8152602060048201526013602482015272151c985b9cd9995c881353d38819985a5b1959606a1b60448201526064016112b8565b505b8015611d2d5760405163a9059cbb60e01b8152336004820152602481018290527f00000000000000000000000026aae4c1ccb0c5263203af7eb5a88eb7fcd196ae6001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015611cc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ceb919061530f565b611d2d5760405162461bcd60e51b8152602060048201526013602482015272151c985b9cd9995c881513d2c819985a5b1959606a1b60448201526064016112b8565b604080518581526020810183905233917f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a910160405180910390a2505050506113a560018055565b7f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b038116611dec5760405162461bcd60e51b815260206004820152601760248201527f456e74726f70792061646472657373206e6f742073657400000000000000000060448201526064016112b8565b336001600160a01b03821614611e505760405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b60648201526084016112b8565b611e5b8484846141bf565b50505050565b611e69613f3d565b602454600090815b838110156122bb576000858583818110611e8d57611e8d6152c9565b905060200201359050828110611ed55760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b60448201526064016112b8565b8115611f6e578585611ee86001856152b6565b818110611ef757611ef76152c9565b90506020020135868684818110611f1057611f106152c9565b9050602002013510611f6e5760405162461bcd60e51b815260206004820152602160248201527f496e6469636573206d75737420626520736f727465642064657363656e64696e6044820152606760f81b60648201526084016112b8565b600060248281548110611f8357611f836152c9565b60009182526020918290206040805180820190915260029092020180546001600160a01b031680835260019091015492820183905260085491935091908110611fcf57505050506122b3565b6000818152600c602052604090206003810154610100900460ff16611ff85750505050506122b3565b60008281526021602090815260408083206001600160a01b038716845290915290205460ff16156120435761202c856142da565b8661203681615331565b97505050505050506122b3565b6000828152600d602090815260408083206001600160a01b03871684528252808320600385015460ff16845290915290205480156122965760038201546000906301000000900460ff16156120f7576000848152600e602090815260408083206001600160a01b03891684528252808320600387015460ff168452909152902054600484015481118015906120e457506120dd83826152a3565b8460040154105b156120f157836007015491505b5061211d565b670de0b6b3a764000083600601548361211091906152f8565b61211a9190615250565b90505b801561212d5761212d8582614409565b6000670de0b6b3a764000084600501548461214891906152f8565b6121529190615250565b61215c90846152a3565b6001600160a01b0387166000908152602360205260408120805492935083929091906121899084906152a3565b909155505060008581526021602090815260408083206001600160a01b038a16808552908352818420805460ff19166001179055888452600f8352818420908452909152902054801561224e576000868152600f602090815260408083206001600160a01b038b168452909152812055612203818d6152a3565b604080518881526020810186905290810184905260608101839052909c506001600160a01b0388169033906000805160206153f38339815191529060800160405180910390a3612292565b6040805187815260208101859052908101839052600060608201526001600160a01b0388169033906000805160206153f38339815191529060800160405180910390a35b5050505b61229f866142da565b876122a981615331565b9850505050505050505b600101611e71565b5081156122f157604051339083156108fc029084906000818181858888f193505050501580156122ef573d6000803e3d6000fd5b505b50506122fc60018055565b5050565b612308614cb0565b6008546000908152600c6020526040908190208151610320810190925260220160198282826020028201915b815481526020019060010190808311612334575050505050905090565b6024546060908084106123a357604080516000808252602082019092529061239b565b60408051808201909152600080825260208201528152602001906001900390816123745790505b5091506124af565b60006123af84866152a3565b9050818111156123bc5750805b60006123c886836152b6565b9050806001600160401b038111156123e2576123e261528d565b60405190808252806020026020018201604052801561242757816020015b60408051808201909152600080825260208201528152602001906001900390816124005790505b50935060005b818110156124ab57602461244182896152a3565b81548110612451576124516152c9565b60009182526020918290206040805180820190915260029092020180546001600160a01b0316825260010154918101919091528551869083908110612498576124986152c9565b602090810291909101015260010161242d565b5050505b9250929050565b6124be613f10565b6113a5600061445c565b60405163ca1642e160e01b815263ffffffff821660048201526000907f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b03169063ca1642e190602401602060405180830381865afa158015612535573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125599190615264565b92915050565b612567613f3d565b600a544210156125895760405162461bcd60e51b81526004016112b890615348565b60025461259e9067016345785d8a00006152a3565b3410156125ed5760405162461bcd60e51b815260206004820152601d60248201527f53656e6420746f74616c207374616b65202b206b65657065722066656500000060448201526064016112b8565b60008163ffffffff16116126385760405162461bcd60e51b8152602060048201526012602482015271139bc8189b1bd8dadcc81cd95b1958dd195960721b60448201526064016112b8565b600b5460009060ff1661264d5760085461265b565b60085461265b9060016152a3565b9050600082815b60198160ff1610156126a757600180831690036126875782612683816152df565b9350505b60018263ffffffff16901c9150808061269f9061538c565b915050612662565b50600082116126ed5760405162461bcd60e51b8152602060048201526012602482015271139bc8189b1bd8dadcc81cd95b1958dd195960721b60448201526064016112b8565b6000600254346126fd91906152b6565b9050600061270b8483615250565b9050600061271985846153ab565b905067016345785d8a00008210156127635760405162461bcd60e51b815260206004820152600d60248201526c4d696e2070657220626c6f636b60981b60448201526064016112b8565b869350600160005b60198160ff1610156127e157600180871690036127c15781156127b5576127ac3382856002548861279c91906152a3565b6127a691906152a3565b8b6144ac565b600091506127c1565b6127c13382868b6144ac565b60018663ffffffff16901c955080806127d99061538c565b91505061276b565b50505050505050506127f260018055565b50565b600083815260196020526040902054606090808410612824576040805160008152602081019091529150612916565b600061283084866152a3565b90508181111561283d5750805b600061284986836152b6565b9050806001600160401b038111156128635761286361528d565b60405190808252806020026020018201604052801561288c578160200160208202803683370190505b50935060005b818110156129125760008881526019602052604090206128b282896152a3565b815481106128c2576128c26152c9565b9060005260206000200160009054906101000a90046001600160a01b03168582815181106128f2576128f26152c9565b6001600160a01b0390921660209283029190910190910152600101612892565b5050505b935093915050565b600080601d5469163c0fb846284fa0000061293991906152a3565b905060006129526a027b46536c66c8e300000083615250565b90506006811115612961575060065b6801bc16d674ec800000901c92915050565b601354600a5461298391906152a3565b4210156129be5760405162461bcd60e51b8152602060048201526009602482015268546f6f206561726c7960b81b60448201526064016112b8565b600b5460ff1615612a035760405162461bcd60e51b815260206004820152600f60248201526e416c726561647920726f6c6c696e6760881b60448201526064016112b8565b6015546008546000908152601660205260409020541015612a665760405162461bcd60e51b815260206004820152601760248201527f4e6f7420656e6f756768207061727469636970616e747300000000000000000060448201526064016112b8565b60007f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b0316638204b67a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ac6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aea9190615264565b9050806001600160801b0316341015612b405760405162461bcd60e51b8152602060048201526018602482015277496e73756666696369656e7420656e74726f70792066656560401b60448201526064016112b8565b600b805460ff1916600117905560408051637b43155d60e01b815290516000916001600160a01b037f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1341691637b43155d916001600160801b038616916004808301926020929190829003018185885af1158015612bc1573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612be691906153bf565b6010805467ffffffffffffffff19166001600160401b038316908117909155600854600091825260116020526040909120554360125590506001600160801b038216341115612cc957600033612c456001600160801b038516346152b6565b604051600081818185875af1925050503d8060008114612c81576040519150601f19603f3d011682016040523d82523d6000602084013e612c86565b606091505b5050905080612cc75760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b60448201526064016112b8565b505b6008546040516001600160401b03831681527fb9560bd3e25a95a3fcca6c0f21c6e8d9d07165bb54ae84dd553e21bbd94ce2629060200160405180910390a25050565b612d14613f10565b600280549082905560408051828152602081018490527fe22b4203e51a04604258271faf98bf06a6f25e13004a7bce95940d2a2adbae8c91015b60405180910390a15050565b612d62613f10565b60008111612da65760405162461bcd60e51b815260206004820152601160248201527004c696d6974206d757374206265203e203607c1b60448201526064016112b8565b600380549082905560408051828152602081018490527fd0497f09900611069b215093d3f3a5087f9719137e3a3fc9f716995a4be91f3b9101612d4e565b612dec613f3d565b6005546001600160a01b03163314612e325760405162461bcd60e51b81526020600482015260096024820152682737ba1030b236b4b760b91b60448201526064016112b8565b60065480612e755760405162461bcd60e51b815260206004820152601060248201526f4e6f206665657320746f20636c61696d60801b60448201526064016112b8565b60006006819055604051339083908381818185875af1925050503d8060008114612ebb576040519150601f19603f3d011682016040523d82523d6000602084013e612ec0565b606091505b5050905080612f035760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016112b8565b60405182815233907f6dda1c050330bbf19404606d86eb8277c7a84ee0b310a5311f255750f72e71ea90602001611392565b612f3d613f3d565b6000805b82811015613294576000848483818110612f5d57612f5d6152c9565b9050602002013590506008548110612fa65760405162461bcd60e51b815260206004820152600c60248201526b1058dd1a5d99481c9bdd5b9960a21b60448201526064016112b8565b6000818152600c602052604090206003810154610100900460ff166130035760405162461bcd60e51b8152602060048201526013602482015272149bdd5b99081b9bdd08199a5b985b1a5e9959606a1b60448201526064016112b8565b60008281526021602090815260408083206001600160a01b038b16845290915290205460ff161561303557505061328c565b6000828152600d602090815260408083206001600160a01b038b1684528252808320600385015460ff16845290915290205480156132885760038201546000906301000000900460ff16156130e9576000848152600e602090815260408083206001600160a01b038d1684528252808320600387015460ff168452909152902054600484015481118015906130d657506130cf83826152a3565b8460040154105b156130e357836007015491505b5061310f565b670de0b6b3a764000083600601548361310291906152f8565b61310c9190615250565b90505b801561311f5761311f8982614409565b6000670de0b6b3a764000084600501548461313a91906152f8565b6131449190615250565b61314e90846152a3565b6001600160a01b038b1660009081526023602052604081208054929350839290919061317b9084906152a3565b909155505060008581526021602090815260408083206001600160a01b038e16808552908352818420805460ff19166001179055888452600f83528184209084529091529020548015613240576000868152600f602090815260408083206001600160a01b038f1684529091528120556131f581896152a3565b6040805188815260208101869052908101849052606081018390529098506001600160a01b038c169033906000805160206153f38339815191529060800160405180910390a3613284565b6040805187815260208101859052908101839052600060608201526001600160a01b038c169033906000805160206153f38339815191529060800160405180910390a35b5050505b5050505b600101612f41565b5080156132ca57604051339082156108fc029083906000818181858888f193505050501580156132c8573d6000803e3d6000fd5b505b506132d460018055565b505050565b6001600160a01b0381166000908152601e602090815260408083205482805281842054601f90935290832054601b549193911115613369576001600160a01b0384166000908152601f6020526040812054601b5461333791906152b6565b90506000670de0b6b3a764000061334e86846152f8565b6133589190615250565b905061336481856152a3565b935050505b60008311806133785750600082115b156133bb57600061271061338e6103e8866152f8565b6133989190615250565b905060006133a682866152b6565b90506133b284826152a3565b925050506133bf565b5060005b9193909250565b6133ce614cb0565b6133d6614cb0565b60005b60198160ff16101561343e576000858152600d602090815260408083206001600160a01b0388168452825280832060ff851680855292529091205490839060198110613427576134276152c9565b6020020152806134368161538c565b9150506133d9565b509392505050565b600b5460ff166134865760405162461bcd60e51b815260206004820152600b60248201526a4e6f7420726f6c6c696e6760a81b60448201526064016112b8565b6101f460125461349691906152a3565b4310156134e55760405162461bcd60e51b815260206004820152601760248201527f5652462074696d656f7574206e6f74207265616368656400000000000000000060448201526064016112b8565b60405163ca1642e160e01b815263ffffffff821660048201526000907f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b03169063ca1642e190602401602060405180830381865afa158015613552573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135769190615264565b9050806001600160801b03163410156135cc5760405162461bcd60e51b8152602060048201526018602482015277496e73756666696369656e7420656e74726f70792066656560401b60448201526064016112b8565b6010546012546001600160401b03909116906000906135eb90436152b6565b604051630bed189f60e01b815263ffffffff861660048201529091506000906001600160a01b037f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1341690630bed189f906001600160801b0387169060240160206040518083038185885af1158015613667573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061368c91906153bf565b6010805467ffffffffffffffff19166001600160401b038316908117909155600854600091825260116020526040909120554360125590506001600160801b03841634111561376f576000336136eb6001600160801b038716346152b6565b604051600081818185875af1925050503d8060008114613727576040519150601f19603f3d011682016040523d82523d6000602084013e61372c565b606091505b505090508061376d5760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b60448201526064016112b8565b505b600854604080516001600160401b038087168252841660208201529081018490527f46df967790930427bf9d61b0cbc196e7e28b27d130faa338db5de10f90c327d9906060015b60405180910390a25050505050565b6137cd614cb0565b6008546000908152600c602052604090819020815161032081019092526009016019828282602002820191815481526020019060010190808311612334575050505050905090565b61381d613f3d565b6004546001600160a01b031633146138635760405162461bcd60e51b8152602060048201526009602482015268139bdd081d985d5b1d60ba1b60448201526064016112b8565b600754806138a65760405162461bcd60e51b815260206004820152601060248201526f4e6f206665657320746f20636c61696d60801b60448201526064016112b8565b60006007819055604051339083908381818185875af1925050503d80600081146138ec576040519150601f19603f3d011682016040523d82523d6000602084013e6138f1565b606091505b50509050806139345760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016112b8565b60405182815233907fe1b962d6a17421db47dc42910fbf9205832ca07f240e024fbc819c44540d881290602001611392565b6019602052816000526040600020818154811061398257600080fd5b6000918252602090912001546001600160a01b03169150829050565b6139a6613f3d565b600a544210156139c85760405162461bcd60e51b81526004016112b890615348565b600b5460009060ff166139dd576008546139eb565b6008546139eb9060016152a3565b6000818152600f60209081526040808320338452909152902054909150158015613a7857600254613a249067016345785d8a00006152a3565b341015613a735760405162461bcd60e51b815260206004820152601760248201527f53656e64207374616b65202b206b65657065722066656500000000000000000060448201526064016112b8565b613abc565b67016345785d8a0000341015613abc5760405162461bcd60e51b81526020600482015260096024820152684d696e207374616b6560b81b60448201526064016112b8565b613ac8338434856144ac565b50506127f260018055565b613adb613f10565b6001600160a01b038116613b0557604051631e4fbdf760e01b8152600060048201526024016112b8565b6127f28161445c565b6000828152600c6020526040812060090160ff831660198110613b3357613b336152c9565b01549392505050565b613b44613f3d565b6001600160a01b038116600090815260226020526040812054613b689060016152a3565b60085460035491925090613b7c83836152b6565b1115613b9257600354613b8f90836152a3565b90505b6000825b82811015613e50576000818152600c602052604090206003810154610100900460ff161580613be7575060008281526021602090815260408083206001600160a01b038a16845290915290205460ff165b15613bf25750613e48565b6000828152600d602090815260408083206001600160a01b038a1684528252808320600385015460ff1684529091529020548015613e455760038201546000906301000000900460ff1615613ca6576000848152600e602090815260408083206001600160a01b038c1684528252808320600387015460ff16845290915290205460048401548111801590613c935750613c8c83826152a3565b8460040154105b15613ca057836007015491505b50613ccc565b670de0b6b3a7640000836006015483613cbf91906152f8565b613cc99190615250565b90505b8015613cdc57613cdc8882614409565b6000670de0b6b3a7640000846005015484613cf791906152f8565b613d019190615250565b613d0b90846152a3565b6001600160a01b038a16600090815260236020526040812080549293508392909190613d389084906152a3565b909155505060008581526021602090815260408083206001600160a01b038d16808552908352818420805460ff19166001179055888452600f83528184209084529091529020548015613dfd576000868152600f602090815260408083206001600160a01b038e168452909152812055613db281886152a3565b6040805188815260208101869052908101849052606081018390529097506001600160a01b038b169033906000805160206153f38339815191529060800160405180910390a3613e41565b6040805187815260208101859052908101839052600060608201526001600160a01b038b169033906000805160206153f38339815191529060800160405180910390a35b5050505b50505b600101613b96565b508115613e7c57613e626001836152b6565b6001600160a01b0385166000908152602260205260409020555b8015613eb157604051339082156108fc029083906000818181858888f19350505050158015613eaf573d6000803e3d6000fd5b505b5050506127f260018055565b613ec5613f10565b603c831015613f025760405162461bcd60e51b8152602060048201526009602482015268151bdbc81cda1bdc9d60ba1b60448201526064016112b8565b601392909255601555601455565b6000546001600160a01b031633146113a55760405163118cdaa760e01b81523360048201526024016112b8565b600260015403613f6057604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b600082815260196020526040812054815b81811015613ffc576000858152600d6020908152604080832060199092528220805483919085908110613fad57613fad6152c9565b60009182526020808320909101546001600160a01b03168352828101939093526040918201812060ff891682529092529020541115613ff45782613ff0816152df565b9350505b600101613f78565b505092915050565b6000848152600c602052604081206005810154829190670de0b6b3a76400009061402e90866152f8565b6140389190615250565b61404290856152a3565b60038201549093506301000000900460ff16156140ba576000878152600e602090815260408083206001600160a01b038a168452825280832060ff89168452909152902054600482015481118015906140a757506140a085826152a3565b8260040154105b156140b457816007015492505b506140e0565b670de0b6b3a76400008160060154856140d391906152f8565b6140dd9190615250565b91505b5094509492505050565b6001600160a01b0381166000908152601f6020526040902054601b5411156141a0576001600160a01b0381166000908152601f6020526040812054601b5461413291906152b6565b6001600160a01b0383166000908152601e6020526040812054919250670de0b6b3a764000061416183856152f8565b61416b9190615250565b6001600160a01b03851660009081526020805260408120805492935083929091906141979084906152a3565b90915550505050505b601b546001600160a01b039091166000908152601f6020526040902055565b6001600160401b038316600090815260116020526040902054600b5460ff1680156141eb575060085481145b801561420457506010546001600160401b038581169116145b6142505760405162461bcd60e51b815260206004820152601960248201527f496e76616c6964206f72207374616c652073657175656e63650000000000000060448201526064016112b8565b60405182908190600090614274908390600190602001918252602082015260400190565b60408051601f198184030181528282528051602091820120818401879052600284840152825180850384018152606090940190925282519201919091209091506142bf838383614565565b50506010805467ffffffffffffffff19169055505050505050565b6024546000906142ec906001906152b6565b9050600060248381548110614303576143036152c9565b60009182526020808320604080518082018252600290940290910180546001600160a01b03168085526001909101548484019081529085526025835281852090518552909152909120805460ff1916905590508282146143ca5760248281548110614370576143706152c9565b906000526020600020906002020160248481548110614391576143916152c9565b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b039092169190911781556001918201549101555b60248054806143db576143db6153dc565b60008281526020812060026000199093019283020180546001600160a01b0319168155600101559055505050565b614412826140ea565b6001600160a01b0382166000908152601e60205260408120805483929061443a9084906152a3565b9250508190555080601a600082825461445391906152a3565b90915550505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60198360ff16106144ef5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420426c6f636b60981b60448201526064016112b8565b6000818152600f602090815260408083206001600160a01b038816845290915290205482901580156145515760025461452890856152b6565b6002546000858152600f602090815260408083206001600160a01b038c16845290915290205591505b61455d86868486614912565b505050505050565b6008546000908152600c602052604090206145816019856153ab565b60038201805460ff191660ff929092169190911790556145a7610271602086901c6153ab565b60038201805462ff000019811692156201000002928317909155600091600984019160ff908116911617601981106145e1576145e16152c9565b015460018301819055600283015490915060006145fe83836152b6565b8085559050600083900361467457600061271061461c6064856152f8565b6146269190615250565b9050806006600082825461463a91906152a3565b9091555061464a905081846152b6565b6007600082825461465b91906152a3565b9091555050600060058601819055600686015550614826565b60006127106146846064846152f8565b61468e9190615250565b905060006127106146a16103e8856152f8565b6146ab9190615250565b905060006146b982846152a3565b905082600660008282546146cd91906152a3565b9250508190555081600760008282546146e691906152a3565b90915550600090506146f882866152b6565b90508661470d82670de0b6b3a76400006152f8565b6147179190615250565b6005890155600061472661291e565b90506000614732611168565b60038b015490915062010000900460ff1661475f57806009600082825461475991906152a3565b90915550505b60038a015460009062010000900460ff161561478f578260095461478391906152a3565b60006009559050614792565b50815b80601d60008282546147a491906152a3565b909155506147b5905060028e6153ab565b6000036147ec5760038b01805463ff0000001916630100000017905560078b018190556147e28a8d6153ab565b60048c015561481e565b60038b01805463ff000000191690558961480e82670de0b6b3a76400006152f8565b6148189190615250565b60068c01555b505050505050505b60038401805461010061ff00198216179182905542600880880191909155546040805191825260ff92831660208301526301000000840483161515908201526201000090920416151560608201527f73b6989256872bd8853751760a7960291ec2e04e1d8191bd624381f27fca9f8e9060800160405180910390a1600880549060006148b1836152df565b90915550506014546148c390426152a3565b600a819055600b805460ff19169055600854604051918252907f278844837bcf8364a705384bf3a2812901f54155bae86dea81dd52aa5b9ec0e39060200160405180910390a250505050505050565b6000818152600c6020526040812060090160ff851660198110614937576149376152c9565b01546000838152600d602090815260408083206001600160a01b038a168452825280832060ff89168452909152812054919250036149a0576000828152600e602090815260408083206001600160a01b0389168452825280832060ff8816845290915290208190555b6000828152600d602090815260408083206001600160a01b0389168452825280832060ff88168452909152812080548592906149dd9084906152a3565b90915550506000828152600c60205260409020839060090160ff861660198110614a0957614a096152c9565b016000828254614a1991906152a3565b90915550506000828152600c602052604081206002018054859290614a3f9084906152a3565b9091555050600082815260186020908152604080832060ff80891685529083528184206001600160a01b038a1685529092529091205416614ae857600082815260186020908152604080832060ff88168085529083528184206001600160a01b038a1685528352818420805460ff19166001179055858452600c9092529091206022019060198110614ad357614ad36152c9565b018054906000614ae2836152df565b91905055505b60008281526017602090815260408083206001600160a01b038916845290915290205460ff16614c655760008281526017602090815260408083206001600160a01b03891684528252808320805460ff1916600117905584835260169091528120805491614b55836152df565b9091555050600082815260196020908152604080832080546001810182559084528284200180546001600160a01b0319166001600160a01b038a1690811790915583526025825280832085845290915290205460ff16614c65576001600160a01b03858116600081815260256020908152604080832087845282528083208054600160ff1990911681179091558151808301909252938152908101868152602480549485018155909252517f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec4600290930292830180546001600160a01b031916919094161790925590517f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec5909101555b6040805183815260ff861660208201529081018490526001600160a01b038616907f60c16227b368ec1b75014bc56837d084cbaf0b6989beb0cced9d343d9b5a9467906060016137b6565b6040518061032001604052806019906020820280368337509192915050565b600060208284031215614ce157600080fd5b5035919050565b81518152602080830151610140830191614d069084018260ff169052565b506040830151614d1a604084018215159052565b506060830151614d2e606084018215159052565b506080830151614d42608084018215159052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010083015161010083015261012083015161012083015292915050565b80356001600160a01b0381168114614d9657600080fd5b919050565b600060208284031215614dad57600080fd5b611a1a82614d7f565b600080600060608486031215614dcb57600080fd5b505081359360208301359350604090920135919050565b6020808252825160c083830152805160e08401819052600092919091019082906101008501905b80831015614e5557835160018060a01b03815116835260208101516020840152604081015160408401526060810151606084015250608082019150602084019350600183019250614e09565b506020860151604086015260408601519250614e76606086018460ff169052565b606086015180151560808701529250608086015180151560a0870152925060a086015160c086015280935050505092915050565b6001600160401b03811681146127f257600080fd5b600080600060608486031215614ed457600080fd5b8335614edf81614eaa565b9250614eed60208501614d7f565b9150604084013590509250925092565b60008083601f840112614f0f57600080fd5b5081356001600160401b03811115614f2657600080fd5b6020830191508360208260051b85010111156124af57600080fd5b60008060208385031215614f5457600080fd5b82356001600160401b03811115614f6a57600080fd5b614f7685828601614efd565b90969095509350505050565b6103208101818360005b6019811015614fab578151835260209283019290910190600101614f8c565b50505092915050565b600060208284031215614fc657600080fd5b8135611a1a81614eaa565b60008060408385031215614fe457600080fd5b50508035926020909101359150565b6040808252835190820181905260009060208501906060840190835b8181101561504257835180516001600160a01b03168452602090810151818501529093019260409092019160010161500f565b5050602093909301939093525092915050565b60006020828403121561506757600080fd5b813563ffffffff81168114611a1a57600080fd5b6040808252835190820181905260009060208501906060840190835b818110156150425783516001600160a01b0316835260209384019390920191600101615097565b803560ff81168114614d9657600080fd5b6000806000606084860312156150e457600080fd5b833592506150f460208501614d7f565b9150615102604085016150be565b90509250925092565b6000806040838503121561511e57600080fd5b8235915061512e60208401614d7f565b90509250929050565b60008060006040848603121561514c57600080fd5b61515584614d7f565b925060208401356001600160401b0381111561517057600080fd5b61517c86828701614efd565b9497909650939450505050565b6000806040838503121561519c57600080fd5b6151a583614d7f565b946020939093013593505050565b6000806000606084860312156151c857600080fd5b833592506151d8602085016150be565b915061510260408501614d7f565b6000602082840312156151f857600080fd5b611a1a826150be565b6000806040838503121561521457600080fd5b8235915061512e602084016150be565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008261525f5761525f615224565b500490565b60006020828403121561527657600080fd5b81516001600160801b0381168114611a1a57600080fd5b634e487b7160e01b600052604160045260246000fd5b808201808211156125595761255961523a565b818103818111156125595761255961523a565b634e487b7160e01b600052603260045260246000fd5b6000600182016152f1576152f161523a565b5060010190565b80820281158282048414176125595761255961523a565b60006020828403121561532157600080fd5b81518015158114611a1a57600080fd5b6000816153405761534061523a565b506000190190565b60208082526024908201527f526f756e64206e6f742073746172746564207965742028696e7465726d697373604082015263696f6e2960e01b606082015260800190565b600060ff821660ff81036153a2576153a261523a565b60010192915050565b6000826153ba576153ba615224565b500690565b6000602082840312156153d157600080fd5b8151611a1a81614eaa565b634e487b7160e01b600052603160045260246000fdfe07f32f6f4c1b7fe2a67cebec7f6546a6324a14e661023fa65d8dd4e6605183a2a2646970667358221220c2de5eb2b0d62f6b5989008c805ac4c86e8c6a89d59cb6056ce3b64d3eeca1b264736f6c634300081c0033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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.