POL Price: $0.683324 (-2.56%)
 

Overview

POL Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo108.559596202244764725 POL

POL Value

$74.18 (@ $0.68/POL)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Mines_Start651427712024-12-06 6:15:392 days ago1733465739IN
0xE922A4Ad...3B1d327E2
0.14595174 POL0.0109804538.84398439
Mines_End651427602024-12-06 6:15:142 days ago1733465714IN
0xE922A4Ad...3B1d327E2
0 POL0.0031302839.30891726
Mines_Start651427392024-12-06 6:14:292 days ago1733465669IN
0xE922A4Ad...3B1d327E2
0.14563793 POL0.0107681238.34187859
Mines_End651421372024-12-06 5:52:412 days ago1733464361IN
0xE922A4Ad...3B1d327E2
0 POL0.0025062931.47310322
Mines_Start651372292024-12-06 2:55:022 days ago1733453702IN
0xE922A4Ad...3B1d327E2
0.14373285 POL0.0099875535.64477056
Mines_End650751102024-12-04 13:54:363 days ago1733320476IN
0xE922A4Ad...3B1d327E2
0 POL0.0071734190.08095179
Mines_Reveal650750812024-12-04 13:53:343 days ago1733320414IN
0xE922A4Ad...3B1d327E2
0.07289492 POL0.0168376282.64595015
Mines_Start650750612024-12-04 13:52:523 days ago1733320372IN
0xE922A4Ad...3B1d327E2
0.08886432 POL0.0258322192.19699566
Mines_End650748792024-12-04 13:46:243 days ago1733319984IN
0xE922A4Ad...3B1d327E2
0 POL0.0066956784.08162553
Mines_Reveal650747382024-12-04 13:41:263 days ago1733319686IN
0xE922A4Ad...3B1d327E2
0.08284541 POL0.020259298.5667369
Mines_Reveal650747212024-12-04 13:40:503 days ago1733319650IN
0xE922A4Ad...3B1d327E2
0.08110045 POL0.0196661195.77480234
Mines_Start650747012024-12-04 13:40:063 days ago1733319606IN
0xE922A4Ad...3B1d327E2
0.08570314 POL0.0244621287.13910505
Mines_Start650744492024-12-04 13:31:113 days ago1733319071IN
0xE922A4Ad...3B1d327E2
0.19445529 POL0.03294652117.49284452
Mines_End650743642024-12-04 13:28:083 days ago1733318888IN
0xE922A4Ad...3B1d327E2
0 POL0.007614495.61865661
Mines_Reveal650743492024-12-04 13:27:363 days ago1733318856IN
0xE922A4Ad...3B1d327E2
0.08773022 POL0.02382082106.73273531
Mines_Start650743052024-12-04 13:26:043 days ago1733318764IN
0xE922A4Ad...3B1d327E2
0.10234368 POL0.03208402114.114265
Mines_Reveal650743052024-12-04 13:26:043 days ago1733318764IN
0xE922A4Ad...3B1d327E2
0.09700305 POL0.02469971121.56926225
Mines_Start650742852024-12-04 13:25:203 days ago1733318720IN
0xE922A4Ad...3B1d327E2
0.1092412 POL0.03518688125.15030713
Mines_Start650742742024-12-04 13:24:583 days ago1733318698IN
0xE922A4Ad...3B1d327E2
0.20774911 POL0.03891093138.76295076
Mines_Start650742652024-12-04 13:24:383 days ago1733318678IN
0xE922A4Ad...3B1d327E2
0.11655573 POL0.03843299136.85355408
Mines_Reveal650742412024-12-04 13:23:483 days ago1733318628IN
0xE922A4Ad...3B1d327E2
0.10214315 POL0.02702714129.79342175
Mines_Start650742172024-12-04 13:22:563 days ago1733318576IN
0xE922A4Ad...3B1d327E2
0.12562109 POL0.04240827151.35812589
Mines_End647466862024-11-26 8:02:0412 days ago1732608124IN
0xE922A4Ad...3B1d327E2
0 POL0.0068780386.37167485
Mines_Reveal647466552024-11-26 8:00:5812 days ago1732608058IN
0xE922A4Ad...3B1d327E2
0.08001523 POL0.0197344996.33301384
Mines_Start647466252024-11-26 7:59:5612 days ago1732607996IN
0xE922A4Ad...3B1d327E2
0.17097373 POL0.0274219397.86662065
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
651427792024-12-06 6:15:552 days ago1733465755
0xE922A4Ad...3B1d327E2
0.1 POL
651427712024-12-06 6:15:392 days ago1733465739
0xE922A4Ad...3B1d327E2
0.00919034 POL
651427602024-12-06 6:15:142 days ago1733465714
0xE922A4Ad...3B1d327E2
0.1 POL
651427392024-12-06 6:14:292 days ago1733465669
0xE922A4Ad...3B1d327E2
0.00912758 POL
651421372024-12-06 5:52:412 days ago1733464361
0xE922A4Ad...3B1d327E2
0.1 POL
651372292024-12-06 2:55:022 days ago1733453702
0xE922A4Ad...3B1d327E2
0.00874657 POL
650751102024-12-04 13:54:363 days ago1733320476
0xE922A4Ad...3B1d327E2
0.01 POL
650750812024-12-04 13:53:343 days ago1733320414
0xE922A4Ad...3B1d327E2
0.01457898 POL
650750612024-12-04 13:52:523 days ago1733320372
0xE922A4Ad...3B1d327E2
0.01577286 POL
650748792024-12-04 13:46:243 days ago1733319984
0xE922A4Ad...3B1d327E2
0.1 POL
650747442024-12-04 13:41:383 days ago1733319698
0xE922A4Ad...3B1d327E2
0.01 POL
650747382024-12-04 13:41:263 days ago1733319686
0xE922A4Ad...3B1d327E2
0.01656908 POL
650747212024-12-04 13:40:503 days ago1733319650
0xE922A4Ad...3B1d327E2
0.01622009 POL
650747012024-12-04 13:40:063 days ago1733319606
0xE922A4Ad...3B1d327E2
0.01514062 POL
650744492024-12-04 13:31:113 days ago1733319071
0xE922A4Ad...3B1d327E2
0.01889105 POL
650743642024-12-04 13:28:083 days ago1733318888
0xE922A4Ad...3B1d327E2
0.01 POL
650743572024-12-04 13:27:543 days ago1733318874
0xE922A4Ad...3B1d327E2
0.1 POL
650743492024-12-04 13:27:363 days ago1733318856
0xE922A4Ad...3B1d327E2
0.01754604 POL
650743052024-12-04 13:26:043 days ago1733318764
0xE922A4Ad...3B1d327E2
0.01846873 POL
650743052024-12-04 13:26:043 days ago1733318764
0xE922A4Ad...3B1d327E2
0.01940061 POL
650742932024-12-04 13:25:383 days ago1733318738
0xE922A4Ad...3B1d327E2
0.01 POL
650742852024-12-04 13:25:203 days ago1733318720
0xE922A4Ad...3B1d327E2
0.01984824 POL
650742742024-12-04 13:24:583 days ago1733318698
0xE922A4Ad...3B1d327E2
0.02154982 POL
650742722024-12-04 13:24:543 days ago1733318694
0xE922A4Ad...3B1d327E2
0.01 POL
650742652024-12-04 13:24:383 days ago1733318678
0xE922A4Ad...3B1d327E2
0.02131114 POL
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Mines

Compiler Version
v0.8.11+commit.d7f03943

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 9 : Mines.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "./Common.sol";

//Bug on reveal event if player loses num mines is incorrect
// End game , event must include num mines and tiles revealed

/**
 * @title Mines game, player have 25 tiles where mines are hidden, players flip tiles until they cashout or reveal a mine in which case they lose
 */
contract Mines is Common {
    using SafeERC20 for IERC20;

    constructor(
        address _bankroll,
        address _vrf,
        address link_eth_feed,
        address _forwarder,
        uint8[24] memory maxReveal
    ) {
        Bankroll = IBankRoll(_bankroll);
        IChainLinkVRF = IVRFCoordinatorV2(_vrf);
        LINK_ETH_FEED = AggregatorV3Interface(link_eth_feed);
        ChainLinkVRF = _vrf;
        _setMaxReveal(maxReveal);
        _trustedForwarder = _forwarder;
    }

    struct MinesGame {
        address tokenAddress;
        uint256 wager;
        uint256 requestID;
        uint64 blockNumber;
        uint64 currentMultiplier;
        uint8 numMines;
        bool[25] revealedTiles;
        bool[25] tilesPicked;
        bool isCashout;
    }

    mapping(address => MinesGame) minesGames;
    mapping(uint256 => address) minesIDs;
    mapping(uint256 => mapping(uint256 => uint256)) minesMultipliers;
    mapping(uint256 => uint256) minesMaxReveal;

    /**
     * @dev event emitted at the start of the game
     * @param playerAddress address of the player that made the bet
     * @param wager wager amount
     * @param tokenAddress address of token the wager was made and payout, 0 address is considered the native coin
     * @param numMines number of mines
     * @param isCashout if player intends to cashout in case of victory
     */
    event Mines_Play_Event(
        address indexed playerAddress,
        uint256 wager,
        address tokenAddress,
        uint8 numMines,
        bool isCashout,
        uint256 VRFFee
    );

    event Mines_Fee_Event(address indexed playerAddress, uint256 VRFFee);

    /**
     * @dev event emitted by the VRF callback with the tile reveal results
     * @param playerAddress address of the player that made the bet
     * @param wager wager amount
     * @param payout payout if player were to end the game
     * @param tokenAddress address of token the wager was made and payout, 0 address is considered the native coin
     * @param numMines number of mines
     * @param minesTiles tiles in which mines were revealed, if any is true the game is over and the player lost
     * @param revealedTiles all tiles that have been revealed, true correspond to a revealed tile
     * @param multiplier current game multiplier if the game player chooses to end the game
     */
    event Mines_Reveal_Event(
        address indexed playerAddress,
        uint256 wager,
        uint256 payout,
        address tokenAddress,
        uint256 numMines,
        bool[25] minesTiles,
        bool[25] revealedTiles,
        uint256 multiplier
    );

    /**
     * @dev event emitted by the VRF callback with the tile reveal results and cashout
     * @param playerAddress address of the player that made the bet
     * @param wager wager amount
     * @param payout total payout transfered to the player
     * @param tokenAddress address of token the wager was made and payout, 0 address is considered the native coin
     * @param numMines number of mines
     * @param minesTiles tiles in which mines were revealed, if any is true the game is over and the player lost
     * @param revealedTiles all tiles that have been revealed, true correspond to a revealed tile
     * @param multiplier current game multiplier
     */
    event Mines_RevealCashout_Event(
        address indexed playerAddress,
        uint256 wager,
        uint256 payout,
        address tokenAddress,
        uint256 numMines,
        bool[25] minesTiles,
        bool[25] revealedTiles,
        uint256 multiplier
    );

    /**
     * @dev event emitted by the VRF callback with the bet results
     * @param playerAddress address of the player that made the bet
     * @param wager wager amount
     * @param payout total payout transfered to the player
     * @param tokenAddress address of token the wager was made and payout, 0 address is considered the native coin
     * @param multiplier final game multiplier
     */
    event Mines_End_Event(
        address indexed playerAddress,
        uint256 wager,
        uint256 payout,
        address tokenAddress,
        uint256 numMines,
        bool[25] revealedTiles,
        uint256 multiplier
    );

    /**
     * @dev event emitted when a refund is done in mines
     * @param player address of the player reciving the refund
     * @param wager amount of wager that was refunded
     * @param tokenAddress address of token the refund was made in
     */
    event Mines_Refund_Event(
        address indexed player,
        uint256 wager,
        address tokenAddress
    );

    error InvalidNumMines();
    error AlreadyInGame();
    error NotInGame();
    error AwaitingVRF(uint256 requestID);
    error InvalidNumberToReveal(uint32 numberPicked, uint256 maxAllowed);
    error TileAlreadyRevealed(uint8 position);
    error WagerAboveLimit(uint256 wager, uint256 maxWager);
    error NoRequestPending();
    error BlockNumberTooLow(uint256 have, uint256 want);
    error OnlyCoordinatorCanFulfill(address have, address want);

    /**
     * @dev function to view the current mines multipliers
     * @param numMines number of mines in the game
     * @param numRevealed tiles revealed
     * @return multiplier multiplier of selected numMines and numRevealed
     */
    function Mines_GetMultipliers(
        uint256 numMines,
        uint256 numRevealed
    ) public view returns (uint256 multiplier) {
        multiplier = minesMultipliers[numMines][numRevealed];
        return multiplier;
    }

    /**
     * @dev function to view the max number of tiles to reveal in mines
     * @return maxReveal array with max number of tiles to reveal for each number of mines
     */
    function Mines_GetMaxReveal()
        external
        view
        returns (uint256[24] memory maxReveal)
    {
        for (uint256 i = 0; i < 24; i++) {
            maxReveal[i] = minesMaxReveal[i + 1];
        }
    }

    /**
     * @dev get current game state of player
     * @param player address of the player that made the bet
     * @return minesState current state of player game
     */
    function Mines_GetState(
        address player
    ) external view returns (MinesGame memory minesState) {
        minesState = minesGames[player];
        return minesState;
    }

    /**
     * @dev function to start mines game, player cannot currently be in a game
     * @param wager wager amount
     * @param tokenAddress address of token the wager was made and payout, 0 address is considered the native coin
     * @param tiles arrays of tiles to initialy reveal, true equals that tile will be revealed
     * @param isCashout if true, game will give payout if player doesn't reveal mines
     * @param numMines number of mines present in game, range from 1-24
     */
    function Mines_Start(
        uint256 wager,
        address tokenAddress,
        uint8 numMines,
        bool[25] calldata tiles,
        bool isCashout
    ) external payable nonReentrant {
        address msgSender = _msgSender();
        if (!(numMines >= 1 && numMines <= 24)) {
            revert InvalidNumMines();
        }

        MinesGame storage game = minesGames[msgSender];
        if (game.requestID != 0) {
            revert AwaitingVRF(game.requestID);
        }
        if (game.numMines != 0) {
            revert AlreadyInGame();
        }

        uint32 numTilesToReveal;
        for (uint8 i = 0; i < tiles.length; i++) {
            if (tiles[i]) {
                numTilesToReveal++;
            }
        }
        uint256 _minesMaxReveal = minesMaxReveal[numMines];
        if (numTilesToReveal == 0 || numTilesToReveal > _minesMaxReveal) {
            revert InvalidNumberToReveal(numTilesToReveal, _minesMaxReveal);
        }

        _kellyWager(wager, tokenAddress, _minesMaxReveal, numMines);
        uint256 fee = _transferWager(tokenAddress, wager, 500000, msgSender);
        uint256 id = _requestRandomWords(numTilesToReveal);
        minesIDs[id] = msgSender;
        game.numMines = numMines;
        game.wager = wager;
        game.tokenAddress = tokenAddress;
        game.isCashout = isCashout;
        game.tilesPicked = tiles;
        game.requestID = id;
        game.blockNumber = uint64(block.number);

        emit Mines_Play_Event(
            msgSender,
            wager,
            tokenAddress,
            numMines,
            isCashout,
            fee
        );
    }

    /**
     * @dev function to reveal tiles in an ongoing game
     * @param tiles array of tiles that the player wishes to reveal, can't choose already revealed tiles, true equals that tile will be revealed
     * @param isCashout if true and player doesn't reveal mines, will cashout
     */
    function Mines_Reveal(
        bool[25] calldata tiles,
        bool isCashout
    ) external payable nonReentrant {
        address msgSender = _msgSender();

        MinesGame storage game = minesGames[msgSender];

        if (game.numMines == 0) {
            revert NotInGame();
        }
        if (game.requestID != 0) {
            revert AwaitingVRF(game.requestID);
        }

        uint32 numTilesRevealed;
        uint32 numTilesToReveal;
        for (uint8 i = 0; i < tiles.length; i++) {
            if (tiles[i]) {
                if (game.revealedTiles[i]) {
                    revert TileAlreadyRevealed(i);
                }
                numTilesToReveal++;
            }
            if (game.revealedTiles[i]) {
                numTilesRevealed++;
            }
        }

        if (
            numTilesToReveal == 0 ||
            numTilesToReveal + numTilesRevealed > minesMaxReveal[game.numMines]
        ) {
            revert InvalidNumberToReveal(
                numTilesToReveal + numTilesRevealed,
                minesMaxReveal[game.numMines]
            );
        }

        uint256 VRFFee = _payVRFFee(500000);

        uint256 id = _requestRandomWords(numTilesToReveal);
        minesIDs[id] = msgSender;
        game.tilesPicked = tiles;
        game.isCashout = isCashout;
        game.requestID = id;
        game.blockNumber = uint64(block.number);

        emit Mines_Fee_Event(msgSender, VRFFee);
    }

    /**
     * @dev function to end player current game and receive payout
     */

    function Mines_End() external nonReentrant {
        address msgSender = _msgSender();

        MinesGame storage game = minesGames[msgSender];
        if (game.numMines == 0) {
            revert NotInGame();
        }
        if (game.requestID != 0) {
            revert AwaitingVRF(game.requestID);
        }
        uint8 numMines = game.numMines;
        bool[25] memory revealedTiles = game.revealedTiles;

        uint256 multiplier = game.currentMultiplier;
        uint256 wager = game.wager;
        uint256 payout = (multiplier * wager) / 10000;
        address tokenAddress = game.tokenAddress;
        _transferToBankroll(tokenAddress, wager);
        delete (minesGames[msgSender]);
        _transferPayout(msgSender, payout, tokenAddress);
        emit Mines_End_Event(
            msgSender,
            wager,
            payout,
            tokenAddress,
            numMines,
            revealedTiles,
            multiplier
        );
    }

    /**
     * @dev Function to get refund for game if VRF request fails
     */
    function Mines_Refund() external nonReentrant {
        address msgSender = _msgSender();
        if (minesGames[msgSender].numMines == 0) {
            revert NotInGame();
        }
        if (minesGames[msgSender].requestID == 0) {
            revert NoRequestPending();
        }
        if (
            minesGames[msgSender].blockNumber + BLOCK_NUMBER_REFUND + 10 >
            block.number
        ) {
            revert BlockNumberTooLow(
                block.number,
                minesGames[msgSender].blockNumber + BLOCK_NUMBER_REFUND + 10
            );
        }

        uint256 wager = minesGames[msgSender].wager;
        address tokenAddress = minesGames[msgSender].tokenAddress;
        delete (minesGames[msgSender]);
        if (tokenAddress == address(0)) {
            (bool success, ) = payable(msgSender).call{value: wager}("");
            if (!success) {
                revert TransferFailed();
            }
        } else {
            IERC20(tokenAddress).safeTransfer(msgSender, wager);
        }
        emit Mines_Refund_Event(msgSender, wager, tokenAddress);
    }

    /**
     * @dev function to set game multipliers
     * @param numMines number of mines to set multipliers for
     */
    function Mines_SetMultipliers(uint256 numMines) external {
        if (numMines == 0 || numMines >= 25) {
            revert();
        }
        if (minesMultipliers[numMines][1] != 0) {
            revert();
        }

        for (uint256 g = 1; g <= 25 - numMines; g++) {
            uint256 multiplier = 1;
            uint256 divisor = 1;
            for (uint256 f = 0; f < g; f++) {
                multiplier *= (25 - numMines - f);
                divisor *= (25 - f);
            }
            minesMultipliers[numMines][g] =
                (9900 * (10 ** 9)) /
                ((multiplier * (10 ** 9)) / divisor);
        }
    }

    /**
     * @dev function called by Chainlink VRF with random numbers
     * @param requestId id provided when the request was made
     * @param randomWords array of random numbers
     */
    function rawFulfillRandomWords(
        uint256 requestId,
        uint256[] memory randomWords
    ) external {
        if (msg.sender != ChainLinkVRF) {
            revert OnlyCoordinatorCanFulfill(msg.sender, ChainLinkVRF);
        }
        fulfillRandomWords(requestId, randomWords);
    }

    function fulfillRandomWords(
        uint256 requestId,
        uint256[] memory randomWords
    ) internal {
        address player = minesIDs[requestId];
        if (player == address(0)) revert();
        delete (minesIDs[requestId]);
        MinesGame storage game = minesGames[player];
        if (block.number > game.blockNumber + BLOCK_NUMBER_REFUND) revert();
        uint8 numMines = game.numMines;
        uint256 numberOfRevealedTiles;
        for (uint256 i = 0; i < game.revealedTiles.length; i++) {
            if (game.revealedTiles[i] == true) {
                numberOfRevealedTiles += 1;
            }
        }

        bool[25] memory mines;
        bool won = true;
        uint256 randomCounter;

        for (uint256 i = 0; i < game.tilesPicked.length; i++) {
            if (
                game.numMines == 0 ||
                25 - numberOfRevealedTiles == game.numMines
            ) {
                if (game.tilesPicked[i]) {
                    game.revealedTiles[i] = true;
                }
                continue;
            }
            if (game.tilesPicked[i]) {
                bool gem = _pickTile(
                    player,
                    i,
                    25 - numberOfRevealedTiles,
                    game.numMines,
                    randomWords[randomCounter]
                );
                if (gem == false) {
                    game.numMines -= 1;
                    mines[i] = true;
                    won = false;
                }
                numberOfRevealedTiles += 1;
                randomCounter += 1;
            }
        }

        if (!won) {
            if (game.isCashout == false) {
                emit Mines_Reveal_Event(
                    player,
                    game.wager,
                    0,
                    game.tokenAddress,
                    numMines,
                    mines,
                    game.revealedTiles,
                    0
                );
            } else {
                emit Mines_RevealCashout_Event(
                    player,
                    game.wager,
                    0,
                    game.tokenAddress,
                    numMines,
                    mines,
                    game.revealedTiles,
                    0
                );
            }
            _transferToBankroll(game.tokenAddress, game.wager);
            delete (minesGames[player]);

            return;
        }

        uint256 multiplier = minesMultipliers[game.numMines][
            numberOfRevealedTiles
        ];

        if (game.isCashout == false) {
            game.currentMultiplier = uint64(multiplier);
            game.requestID = 0;
            emit Mines_Reveal_Event(
                player,
                game.wager,
                (multiplier * game.wager) / 10000,
                game.tokenAddress,
                game.numMines,
                mines,
                game.revealedTiles,
                multiplier
            );
        } else {
            uint256 wager = game.wager;
            address tokenAddress = game.tokenAddress;

            emit Mines_RevealCashout_Event(
                player,
                wager,
                (multiplier * wager) / 10000,
                tokenAddress,
                game.numMines,
                mines,
                game.revealedTiles,
                multiplier
            );
            _transferToBankroll(tokenAddress, game.wager);
            delete (minesGames[player]);
            _transferPayout(player, (multiplier * wager) / 10000, tokenAddress);
        }
    }

    function _pickTile(
        address player,
        uint256 tileNumber,
        uint256 numberTilesLeft,
        uint256 numberOfMinesLeft,
        uint256 rng
    ) internal returns (bool) {
        uint256 winChance = 10000 -
            (numberOfMinesLeft * 10000) /
            numberTilesLeft;

        bool won = false;
        if (rng % 10000 <= winChance) {
            won = true;
        }
        minesGames[player].revealedTiles[tileNumber] = true;
        return won;
    }

    /**
     * @dev function to set game max number of reveals only callable at deploy time
     * @param maxReveal max reveal for each num Mines
     */
    function _setMaxReveal(uint8[24] memory maxReveal) internal {
        for (uint256 i = 0; i < maxReveal.length; i++) {
            minesMaxReveal[i + 1] = maxReveal[i];
        }
    }

    /**
     * @dev calculates the maximum wager allowed based on the bankroll size
     */
    function _kellyWager(
        uint256 wager,
        address tokenAddress,
        uint256 maxReveal,
        uint256 numMines
    ) internal view {
        uint256 balance;
        if (tokenAddress == address(0)) {
            balance = address(Bankroll).balance;
        } else {
            balance = IERC20(tokenAddress).balanceOf(address(Bankroll));
        }
        uint256 maxWager = (balance * (11000 - 10890)) /
            (minesMultipliers[numMines][maxReveal] - 10000);
        if (wager > maxWager) {
            revert WagerAboveLimit(wager, maxWager);
        }
    }
}

File 2 of 9 : Common.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

//import "hardhat/console.sol";

interface IBankRoll {
    function getIsGame(address game) external view returns (bool);

    function getIsValidWager(
        address game,
        address tokenAddress
    ) external view returns (bool);

    function transferPayout(
        address player,
        uint256 payout,
        address token
    ) external;

    function getOwner() external view returns (address);

    function isPlayerSuspended(
        address player
    ) external view returns (bool, uint256);
}

interface IVRFCoordinatorV2 is VRFCoordinatorV2Interface {
    function getFeeConfig()
        external
        view
        returns (
            uint32,
            uint32,
            uint32,
            uint32,
            uint32,
            uint24,
            uint24,
            uint24,
            uint24
        );
}

contract Common is ReentrancyGuard {
    using SafeERC20 for IERC20;

    uint256 public VRFFees;
    address public ChainLinkVRF;
    address public _trustedForwarder;

    uint64 constant BLOCK_NUMBER_REFUND = 1000;

    AggregatorV3Interface public LINK_ETH_FEED;
    IVRFCoordinatorV2 public IChainLinkVRF;
    IBankRoll public Bankroll;

    error NotApprovedBankroll();
    error InvalidValue(uint256 required, uint256 sent);
    error TransferFailed();
    error RefundFailed();
    error NotOwner(address want, address have);
    error ZeroWager();
    error PlayerSuspended(uint256 suspensionTime);

    /**
     * @dev function to transfer the player wager to bankroll, and charge for VRF fee
     * , reverts if bankroll doesn't approve game or token
     * @param tokenAddress address of the token the wager is made on
     * @param wager total amount wagered
     */

    function _transferWager(
        address tokenAddress,
        uint256 wager,
        uint256 gasAmount,
        address msgSender
    ) internal returns (uint256 VRFfee) {
        if (!Bankroll.getIsValidWager(address(this), tokenAddress)) {
            revert NotApprovedBankroll();
        }
        if (wager == 0) {
            revert ZeroWager();
        }
        (bool suspended, uint256 suspendedTime) = Bankroll.isPlayerSuspended(
            msgSender
        );
        if (suspended) {
            revert PlayerSuspended(suspendedTime);
        }
        VRFfee = getVRFFee(gasAmount);

        if (tokenAddress == address(0)) {
            if (msg.value < wager + VRFfee) {
                revert InvalidValue(wager + VRFfee, msg.value);
            }
            _refundExcessValue(msg.value - (VRFfee + wager));
        } else {
            if (msg.value < VRFfee) {
                revert InvalidValue(VRFfee, msg.value);
            }

            IERC20(tokenAddress).safeTransferFrom(
                msgSender,
                address(this),
                wager
            );

            _refundExcessValue(msg.value - VRFfee);
        }
        VRFFees += VRFfee;
    }

    /**
     * @dev function to transfer the wager held by the game contract to the bankroll
     * @param tokenAddress address of the token to transfer
     * @param amount token amount to transfer
     */
    function _transferToBankroll(
        address tokenAddress,
        uint256 amount
    ) internal {
        if (tokenAddress == address(0)) {
            (bool success, ) = payable(address(Bankroll)).call{value: amount}(
                ""
            );
            if (!success) {
                revert RefundFailed();
            }
        } else {
            IERC20(tokenAddress).safeTransfer(address(Bankroll), amount);
        }
    }

    /**
     * @dev calculates in form of native token the fee charged by chainlink VRF
     * @return fee amount of fee user has to pay
     */
    function getVRFFee(uint256 gasAmount) public view returns (uint256 fee) {
        (, int256 answer, , , ) = LINK_ETH_FEED.latestRoundData();
        (uint32 fulfillmentFlatFeeLinkPPMTier1, , , , , , , , ) = IChainLinkVRF
            .getFeeConfig();

        fee =
            tx.gasprice *
            (gasAmount) +
            ((1e12 *
                uint256(fulfillmentFlatFeeLinkPPMTier1) *
                uint256(answer)) / 1e18);
    }

    /**
     * @dev returns to user the excess fee sent to pay for the VRF
     * @param refund amount to send back to user
     */
    function _refundExcessValue(uint256 refund) internal {
        if (refund == 0) {
            return;
        }
        (bool success, ) = payable(msg.sender).call{value: refund}("");
        if (!success) {
            revert RefundFailed();
        }
    }

    /**
     * @dev function to charge user for VRF
     */
    function _payVRFFee(uint256 gasAmount) internal returns (uint256 VRFfee) {
        VRFfee = getVRFFee(gasAmount);
        if (msg.value < VRFfee) {
            revert InvalidValue(VRFfee, msg.value);
        }
        _refundExcessValue(msg.value - VRFfee);
        VRFFees += VRFfee;
    }

    /**
     * @dev function to transfer VRF fees acumulated in the contract to the Bankroll
     * Can only be called by owner
     */
    function transferFees(address to) external nonReentrant {
        if (msg.sender != Bankroll.getOwner()) {
            revert NotOwner(Bankroll.getOwner(), msg.sender);
        }
        uint256 fee = VRFFees;
        VRFFees = 0;
        (bool success, ) = payable(address(to)).call{value: fee}("");
        if (!success) {
            revert TransferFailed();
        }
    }

    /**
     * @dev function to transfer wager to game contract, without charging for VRF
     * @param tokenAddress tokenAddress the wager is made on
     * @param wager wager amount
     */
    function _transferWagerPvPNoVRF(
        address tokenAddress,
        uint256 wager
    ) internal {
        if (!Bankroll.getIsValidWager(address(this), tokenAddress)) {
            revert NotApprovedBankroll();
        }
        if (tokenAddress == address(0)) {
            if (!(msg.value == wager)) {
                revert InvalidValue(wager, msg.value);
            }
        } else {
            IERC20(tokenAddress).safeTransferFrom(
                msg.sender,
                address(this),
                wager
            );
        }
    }

    /**
     * @dev function to transfer wager to game contract, including charge for VRF
     * @param tokenAddress tokenAddress the wager is made on
     * @param wager wager amount
     */
    function _transferWagerPvP(
        address tokenAddress,
        uint256 wager,
        uint256 gasAmount
    ) internal {
        if (!Bankroll.getIsValidWager(address(this), tokenAddress)) {
            revert NotApprovedBankroll();
        }

        uint256 VRFfee = getVRFFee(gasAmount);
        if (tokenAddress == address(0)) {
            if (msg.value < wager + VRFfee) {
                revert InvalidValue(wager, msg.value);
            }

            _refundExcessValue(msg.value - (VRFfee + wager));
        } else {
            if (msg.value < VRFfee) {
                revert InvalidValue(VRFfee, msg.value);
            }

            IERC20(tokenAddress).safeTransferFrom(
                msg.sender,
                address(this),
                wager
            );
            _refundExcessValue(msg.value - VRFfee);
        }
        VRFFees += VRFfee;
    }

    /**
     * @dev transfers payout from the game contract to the players
     * @param player address of the player to transfer the payout to
     * @param payout amount of payout to transfer
     * @param tokenAddress address of the token that payout will be transfered
     */
    function _transferPayoutPvP(
        address player,
        uint256 payout,
        address tokenAddress
    ) internal {
        if (tokenAddress == address(0)) {
            (bool success, ) = payable(player).call{value: payout}("");
            if (!success) {
                revert TransferFailed();
            }
        } else {
            IERC20(tokenAddress).safeTransfer(player, payout);
        }
    }

    /**
     * @dev transfers house edge from game contract to bankroll
     * @param amount amount to transfer
     * @param tokenAddress address of token to transfer
     */
    function _transferHouseEdgePvP(
        uint256 amount,
        address tokenAddress
    ) internal {
        if (tokenAddress == address(0)) {
            (bool success, ) = payable(address(Bankroll)).call{value: amount}(
                ""
            );
            if (!success) {
                revert TransferFailed();
            }
        } else {
            IERC20(tokenAddress).safeTransfer(address(Bankroll), amount);
        }
    }

    /**
     * @dev function to request bankroll to give payout to player
     * @param player address of the player
     * @param payout amount of payout to give
     * @param tokenAddress address of the token in which to give the payout
     */
    function _transferPayout(
        address player,
        uint256 payout,
        address tokenAddress
    ) internal {
        Bankroll.transferPayout(player, payout, tokenAddress);
    }

    /**
     * @dev function to send the request for randomness to chainlink
     * @param numWords number of random numbers required
     */
    function _requestRandomWords(
        uint32 numWords
    ) internal returns (uint256 s_requestId) {
        s_requestId = VRFCoordinatorV2Interface(ChainLinkVRF)
            .requestRandomWords(
                0xd729dc84e21ae57ffb6be0053bf2b0668aa2aaf300a2a7b2ddf7dc0bb6e875a8,
                576,
                3,
                2500000,
                numWords
            );
    }

    function isTrustedForwarder(address forwarder) public view returns (bool) {
        return forwarder == _trustedForwarder;
    }

    function _msgSender() internal view returns (address ret) {
        if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
            // At this point we know that the sender is a trusted forwarder,
            // so we trust that the last bytes of msg.data are the verified sender address.
            // extract sender address from the end of msg.data
            assembly {
                ret := shr(96, calldataload(sub(calldatasize(), 20)))
            }
        } else {
            ret = msg.sender;
        }
    }
}

File 3 of 9 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

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

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

File 4 of 9 : VRFCoordinatorV2Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface VRFCoordinatorV2Interface {
  /**
   * @notice Get configuration relevant for making requests
   * @return minimumRequestConfirmations global min for request confirmations
   * @return maxGasLimit global max for request gas limit
   * @return s_provingKeyHashes list of registered key hashes
   */
  function getRequestConfig()
    external
    view
    returns (
      uint16,
      uint32,
      bytes32[] memory
    );

  /**
   * @notice Request a set of random words.
   * @param keyHash - Corresponds to a particular oracle job which uses
   * that key for generating the VRF proof. Different keyHash's have different gas price
   * ceilings, so you can select a specific one to bound your maximum per request cost.
   * @param subId  - The ID of the VRF subscription. Must be funded
   * with the minimum subscription balance required for the selected keyHash.
   * @param minimumRequestConfirmations - How many blocks you'd like the
   * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
   * for why you may want to request more. The acceptable range is
   * [minimumRequestBlockConfirmations, 200].
   * @param callbackGasLimit - How much gas you'd like to receive in your
   * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
   * may be slightly less than this amount because of gas used calling the function
   * (argument decoding etc.), so you may need to request slightly more than you expect
   * to have inside fulfillRandomWords. The acceptable range is
   * [0, maxGasLimit]
   * @param numWords - The number of uint256 random values you'd like to receive
   * in your fulfillRandomWords callback. Note these numbers are expanded in a
   * secure way by the VRFCoordinator from a single random value supplied by the oracle.
   * @return requestId - A unique identifier of the request. Can be used to match
   * a request to a response in fulfillRandomWords.
   */
  function requestRandomWords(
    bytes32 keyHash,
    uint64 subId,
    uint16 minimumRequestConfirmations,
    uint32 callbackGasLimit,
    uint32 numWords
  ) external returns (uint256 requestId);

  /**
   * @notice Create a VRF subscription.
   * @return subId - A unique subscription id.
   * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
   * @dev Note to fund the subscription, use transferAndCall. For example
   * @dev  LINKTOKEN.transferAndCall(
   * @dev    address(COORDINATOR),
   * @dev    amount,
   * @dev    abi.encode(subId));
   */
  function createSubscription() external returns (uint64 subId);

  /**
   * @notice Get a VRF subscription.
   * @param subId - ID of the subscription
   * @return balance - LINK balance of the subscription in juels.
   * @return reqCount - number of requests for this subscription, determines fee tier.
   * @return owner - owner of the subscription.
   * @return consumers - list of consumer address which are able to use this subscription.
   */
  function getSubscription(uint64 subId)
    external
    view
    returns (
      uint96 balance,
      uint64 reqCount,
      address owner,
      address[] memory consumers
    );

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @param newOwner - proposed new owner of the subscription
   */
  function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @dev will revert if original owner of subId has
   * not requested that msg.sender become the new owner.
   */
  function acceptSubscriptionOwnerTransfer(uint64 subId) external;

  /**
   * @notice Add a consumer to a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - New consumer which can use the subscription
   */
  function addConsumer(uint64 subId, address consumer) external;

  /**
   * @notice Remove a consumer from a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - Consumer to remove from the subscription
   */
  function removeConsumer(uint64 subId, address consumer) external;

  /**
   * @notice Cancel a subscription
   * @param subId - ID of the subscription
   * @param to - Where to send the remaining LINK to
   */
  function cancelSubscription(uint64 subId, address to) external;

  /*
   * @notice Check to see if there exists a request commitment consumers
   * for all consumers and keyhashes for a given sub.
   * @param subId - ID of the subscription
   * @return true if there exists at least one unfulfilled request for the subscription, false
   * otherwise.
   */
  function pendingRequestExists(uint64 subId) external view returns (bool);
}

File 5 of 9 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 6 of 9 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

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

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

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

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 8 of 9 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 9 of 9 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_bankroll","type":"address"},{"internalType":"address","name":"_vrf","type":"address"},{"internalType":"address","name":"link_eth_feed","type":"address"},{"internalType":"address","name":"_forwarder","type":"address"},{"internalType":"uint8[24]","name":"maxReveal","type":"uint8[24]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInGame","type":"error"},{"inputs":[{"internalType":"uint256","name":"requestID","type":"uint256"}],"name":"AwaitingVRF","type":"error"},{"inputs":[{"internalType":"uint256","name":"have","type":"uint256"},{"internalType":"uint256","name":"want","type":"uint256"}],"name":"BlockNumberTooLow","type":"error"},{"inputs":[],"name":"InvalidNumMines","type":"error"},{"inputs":[{"internalType":"uint32","name":"numberPicked","type":"uint32"},{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"name":"InvalidNumberToReveal","type":"error"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"sent","type":"uint256"}],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"NoRequestPending","type":"error"},{"inputs":[],"name":"NotApprovedBankroll","type":"error"},{"inputs":[],"name":"NotInGame","type":"error"},{"inputs":[{"internalType":"address","name":"want","type":"address"},{"internalType":"address","name":"have","type":"address"}],"name":"NotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyCoordinatorCanFulfill","type":"error"},{"inputs":[{"internalType":"uint256","name":"suspensionTime","type":"uint256"}],"name":"PlayerSuspended","type":"error"},{"inputs":[],"name":"RefundFailed","type":"error"},{"inputs":[{"internalType":"uint8","name":"position","type":"uint8"}],"name":"TileAlreadyRevealed","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"wager","type":"uint256"},{"internalType":"uint256","name":"maxWager","type":"uint256"}],"name":"WagerAboveLimit","type":"error"},{"inputs":[],"name":"ZeroWager","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"playerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"wager","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"numMines","type":"uint256"},{"indexed":false,"internalType":"bool[25]","name":"revealedTiles","type":"bool[25]"},{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"Mines_End_Event","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"playerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"VRFFee","type":"uint256"}],"name":"Mines_Fee_Event","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"playerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"wager","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint8","name":"numMines","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isCashout","type":"bool"},{"indexed":false,"internalType":"uint256","name":"VRFFee","type":"uint256"}],"name":"Mines_Play_Event","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"wager","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"Mines_Refund_Event","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"playerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"wager","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"numMines","type":"uint256"},{"indexed":false,"internalType":"bool[25]","name":"minesTiles","type":"bool[25]"},{"indexed":false,"internalType":"bool[25]","name":"revealedTiles","type":"bool[25]"},{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"Mines_RevealCashout_Event","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"playerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"wager","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"numMines","type":"uint256"},{"indexed":false,"internalType":"bool[25]","name":"minesTiles","type":"bool[25]"},{"indexed":false,"internalType":"bool[25]","name":"revealedTiles","type":"bool[25]"},{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"Mines_Reveal_Event","type":"event"},{"inputs":[],"name":"Bankroll","outputs":[{"internalType":"contract IBankRoll","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ChainLinkVRF","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IChainLinkVRF","outputs":[{"internalType":"contract IVRFCoordinatorV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINK_ETH_FEED","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Mines_End","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"Mines_GetMaxReveal","outputs":[{"internalType":"uint256[24]","name":"maxReveal","type":"uint256[24]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numMines","type":"uint256"},{"internalType":"uint256","name":"numRevealed","type":"uint256"}],"name":"Mines_GetMultipliers","outputs":[{"internalType":"uint256","name":"multiplier","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"Mines_GetState","outputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"wager","type":"uint256"},{"internalType":"uint256","name":"requestID","type":"uint256"},{"internalType":"uint64","name":"blockNumber","type":"uint64"},{"internalType":"uint64","name":"currentMultiplier","type":"uint64"},{"internalType":"uint8","name":"numMines","type":"uint8"},{"internalType":"bool[25]","name":"revealedTiles","type":"bool[25]"},{"internalType":"bool[25]","name":"tilesPicked","type":"bool[25]"},{"internalType":"bool","name":"isCashout","type":"bool"}],"internalType":"struct Mines.MinesGame","name":"minesState","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Mines_Refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool[25]","name":"tiles","type":"bool[25]"},{"internalType":"bool","name":"isCashout","type":"bool"}],"name":"Mines_Reveal","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numMines","type":"uint256"}],"name":"Mines_SetMultipliers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"wager","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint8","name":"numMines","type":"uint8"},{"internalType":"bool[25]","name":"tiles","type":"bool[25]"},{"internalType":"bool","name":"isCashout","type":"bool"}],"name":"Mines_Start","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"VRFFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_trustedForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasAmount","type":"uint256"}],"name":"getVRFFee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256[]","name":"randomWords","type":"uint256[]"}],"name":"rawFulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604051620033f5380380620033f5833981016040819052620000349162000164565b6001600055600680546001600160a01b038088166001600160a01b03199283161790925560058054878416908316811790915560048054938716938316939093179092556002805490911690911790556200008f81620000ba565b50600380546001600160a01b0319166001600160a01b039290921691909117905550620002a7915050565b60005b60188110156200011b57818160188110620000dc57620000dc62000242565b602002015160ff16600a6000620000f58460016200026e565b815260208101919091526040016000205580620001128162000289565b915050620000bd565b5050565b80516001600160a01b03811681146200013757600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b805160ff811681146200013757600080fd5b60008060008060006103808087890312156200017f57600080fd5b6200018a876200011f565b955060206200019b8189016200011f565b9550620001ab604089016200011f565b9450620001bb606089016200011f565b935088609f890112620001cd57600080fd5b60405161030081016001600160401b0381118282101715620001f357620001f36200013c565b60405291880191808a8411156200020957600080fd5b60808a015b848110156200023057620002228162000152565b82529083019083016200020e565b50508093505050509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000821982111562000284576200028462000258565b500190565b6000600019821415620002a057620002a062000258565b5060010190565b61313e80620002b76000396000f3fe6080604052600436106101295760003560e01c80638bd16114116100a5578063ad17836111610074578063c6318aad11610059578063c6318aad1461033e578063d0bcfc0c14610360578063eb5d80751461038057600080fd5b8063ad17836114610309578063b5f2da971461032957600080fd5b80638bd16114146102935780638d7c16d2146102b3578063953995ea146102c9578063a6d18c51146102e957600080fd5b80634dd189d5116100fc57806356c022bb116100e157806356c022bb14610214578063572b6c051461023457806363dc4ef51461027357600080fd5b80634dd189d5146101ae57806350ffd786146101e657600080fd5b80630f53b8261461012e5780631fe543e31461014557806320ade00314610165578063320a51351461019b575b600080fd5b34801561013a57600080fd5b50610143610393565b005b34801561015157600080fd5b5061014361016036600461279c565b610679565b34801561017157600080fd5b5061018561018036600461287b565b6106e0565b60405161019291906128c4565b60405180910390f35b6101436101a9366004612997565b610827565b3480156101ba57600080fd5b506005546101ce906001600160a01b031681565b6040516001600160a01b039091168152602001610192565b3480156101f257600080fd5b506102066102013660046129d1565b610b22565b604051908152602001610192565b34801561022057600080fd5b506003546101ce906001600160a01b031681565b34801561024057600080fd5b5061026361024f36600461287b565b6003546001600160a01b0391821691161490565b6040519015158152602001610192565b34801561027f57600080fd5b5061014361028e36600461287b565b610b43565b34801561029f57600080fd5b506102066102ae3660046129f3565b610d20565b3480156102bf57600080fd5b5061020660015481565b3480156102d557600080fd5b506002546101ce906001600160a01b031681565b3480156102f557600080fd5b506101436103043660046129f3565b610e81565b34801561031557600080fd5b506004546101ce906001600160a01b031681565b34801561033557600080fd5b50610143610f84565b34801561034a57600080fd5b50610353611194565b6040516101929190612a0c565b34801561036c57600080fd5b506006546101ce906001600160a01b031681565b61014361038e366004612a3e565b6111f3565b61039b6114f2565b60006103a561154c565b6001600160a01b038116600090815260076020526040902060030154909150600160801b900460ff166103eb5760405163218286cb60e11b815260040160405180910390fd5b6001600160a01b03811660009081526007602052604090206002015461043d576040517f90a7340b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600760205260409020600301544390610472906103e89067ffffffffffffffff16612ac3565b61047d90600a612ac3565b67ffffffffffffffff161115610517576001600160a01b03811660009081526007602052604090206003015443906104c2906103e89067ffffffffffffffff16612ac3565b6104cd90600a612ac3565b6040517fb4ae675a000000000000000000000000000000000000000000000000000000008152600481019290925267ffffffffffffffff1660248201526044015b60405180910390fd5b6001600160a01b03808216600090815260076020526040812060018101805482546001600160a01b031981168455918490556002830184905560038301805470ffffffffffffffffffffffffffffffffff19169055600483018490559316916000600583015550600601805460ff191690556001600160a01b038116610611576000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146105e4576040519150601f19603f3d011682016040523d82523d6000602084013e6105e9565b606091505b505090508061060b576040516312171d8360e31b815260040160405180910390fd5b50610625565b6106256001600160a01b0382168484611580565b604080518381526001600160a01b0383811660208301528516917f47d03d5b779b98ffcf8d141c72233e07e1eefe64744ce9d5288aea98eaf6bc9c910160405180910390a25050506106776001600055565b565b6002546001600160a01b031633146106d2576002546040517f1cf993f40000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b03909116602482015260440161050e565b6106dc828261162e565b5050565b6106e861262b565b6001600160a01b03828116600090815260076020908152604080832081516101208101835281549095168552600181015492850192909252600282015484820152600382015467ffffffffffffffff8082166060870152680100000000000000008204166080860152600160801b900460ff1660a08501528051610320810191829052919260c0850192916004850191601991908390855b825461010083900a900460ff1615158152602060019283018181049485019490930390920291018084116107805750505092845250506040805161032081019182905260209093019291506005840190601990826000855b825461010083900a900460ff1615158152602060019283018181049485019490930390920291018084116107d8575050509284525050506006919091015460ff16151560209091015292915050565b61082f6114f2565b600061083961154c565b6001600160a01b0381166000908152600760205260409020600381015491925090600160801b900460ff166108815760405163218286cb60e11b815260040160405180910390fd5b6002810154156108ac578060020154604051632206dcb760e11b815260040161050e91815260200190565b60008060005b60198160ff1610156109b657868160ff16601981106108d3576108d3612aef565b6020020160208101906108e69190612b05565b1561096357836004018160ff166019811061090357610903612aef565b602081049091015460ff601f9092166101000a90041615610955576040517f63926f5100000000000000000000000000000000000000000000000000000000815260ff8216600482015260240161050e565b8161095f81612b22565b9250505b836004018160ff166019811061097b5761097b612aef565b602081049091015460ff601f9092166101000a900416156109a457826109a081612b22565b9350505b806109ae81612b46565b9150506108b2565b5063ffffffff811615806109f557506003830154600160801b900460ff166000908152600a60205260409020546109ed8383612b66565b63ffffffff16115b15610a4a57610a048282612b66565b6003840154600160801b900460ff166000908152600a60205260409081902054905163377e537f60e11b815263ffffffff9092166004830152602482015260440161050e565b6000610a586207a120611c8c565b90506000610a6583611cf2565b600081815260086020526040902080546001600160a01b0319166001600160a01b0389161790559050610a9d600586018960196126a3565b5060068501805460ff19168815151790556002850181905560038501805467ffffffffffffffff19164367ffffffffffffffff161790556040518281526001600160a01b038716907f676c918f37fe2b3bf042543e3fdaf058ce52fd6fde49a5303cf971d7037f84ec9060200160405180910390a25050505050506106dc6001600055565b60008281526009602090815260408083208484529091529020545b92915050565b610b4b6114f2565b600660009054906101000a90046001600160a01b03166001600160a01b031663893d20e86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190612b85565b6001600160a01b0316336001600160a01b031614610c9557600660009054906101000a90046001600160a01b03166001600160a01b031663893d20e86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c519190612b85565b6040517f23295f0e0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015233602482015260440161050e565b600180546000918290556040519091906001600160a01b0384169083908381818185875af1925050503d8060008114610cea576040519150601f19603f3d011682016040523d82523d6000602084013e610cef565b606091505b5050905080610d11576040516312171d8360e31b815260040160405180910390fd5b5050610d1d6001600055565b50565b60048054604080517ffeaf968c000000000000000000000000000000000000000000000000000000008152905160009384936001600160a01b03169263feaf968c928183019260a0928290030181865afa158015610d82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da69190612bc1565b5050509150506000600560009054906101000a90046001600160a01b03166001600160a01b0316635fbbc0d26040518163ffffffff1660e01b815260040161012060405180830381865afa158015610e02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e269190612c38565b50505050505050509050670de0b6b3a7640000828263ffffffff1664e8d4a51000610e519190612ce0565b610e5b9190612ce0565b610e659190612d15565b610e6f853a612ce0565b610e799190612d29565b949350505050565b801580610e8f575060198110155b15610e9957600080fd5b60008181526009602090815260408083206001845290915290205415610ebe57600080fd5b60015b610ecc826019612d41565b81116106dc5760018060005b83811015610f2b5780610eec866019612d41565b610ef69190612d41565b610f009084612ce0565b9250610f0d816019612d41565b610f179083612ce0565b915080610f2381612d58565b915050610ed8565b5080610f3b83633b9aca00612ce0565b610f459190612d15565b610f559065090105fbb800612d15565b600085815260096020908152604080832087845290915290205550819050610f7c81612d58565b915050610ec1565b610f8c6114f2565b6000610f9661154c565b6001600160a01b0381166000908152600760205260409020600381015491925090600160801b900460ff16610fde5760405163218286cb60e11b815260040160405180910390fd5b600281015415611009578060020154604051632206dcb760e11b815260040161050e91815260200190565b600381015460408051610320810191829052600160801b90920460ff169160009160048501906019908285855b825461010083900a900460ff16151581526020600192830181810494850194909303909202910180841161103657505050506003860154600187015493945068010000000000000000900467ffffffffffffffff169291506000905061271061109f8385612ce0565b6110a99190612d15565b86549091506001600160a01b03166110c18184611dbe565b6001600160a01b038816600090815260076020526040812080546001600160a01b0319168155600181018290556002810182905560038101805470ffffffffffffffffffffffffffffffffff1916905560048101829055906000600583015550600601805460ff19169055611137888383611e5a565b876001600160a01b03167f1fe1dd196606ad2650d7969421fce1457a68ab5ef856f561b02c43d85e2b23058484848a8a8a60405161117a96959493929190612d73565b60405180910390a250505050505050506106776001600055565b61119c612733565b60005b60188110156111ef57600a60006111b7836001612d29565b8152602001908152602001600020548282601881106111d8576111d8612aef565b6020020152806111e781612d58565b91505061119f565b5090565b6111fb6114f2565b600061120561154c565b905060018460ff161015801561121f575060188460ff1611155b611255576040517f822cb1a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600760205260409020600281015415611298578060020154604051632206dcb760e11b815260040161050e91815260200190565b6003810154600160801b900460ff16156112de576040517fc56fb25300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b60198160ff16101561133b57858160ff166019811061130357611303612aef565b6020020160208101906113169190612b05565b15611329578161132581612b22565b9250505b8061133381612b46565b9150506112e2565b5060ff86166000908152600a602052604090205463ffffffff821615806113675750808263ffffffff16115b156113945760405163377e537f60e11b815263ffffffff831660048201526024810182905260440161050e565b6113a38989838a60ff16611ee6565b60006113b4898b6207a1208861201d565b905060006113c184611cf2565b600081815260086020526040902080546001600160a01b03199081166001600160a01b038a8116919091179092556003880180547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff16600160801b60ff8f1602179055600188018e9055875416908c1617865560068601805460ff19168915151790559050611455600586018960196126a3565b506002850181905560038501805467ffffffffffffffff19164367ffffffffffffffff16179055604080518c81526001600160a01b038c8116602083015260ff8c16828401528915156060830152608082018590529151918816917f06b735e3b542c1e7f37c869cdc85e44e5653bb9d7df7669703552379dc5d89b59160a0908290030190a25050505050506114eb6001600055565b5050505050565b600260005414156115455760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161050e565b6002600055565b60006014361080159061156957506003546001600160a01b031633145b1561157b575060131936013560601c90565b503390565b6040516001600160a01b0383166024820152604481018290526116299084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526122c2565b505050565b6000828152600860205260409020546001600160a01b03168061165057600080fd5b600083815260086020908152604080832080546001600160a01b03191690556001600160a01b038416835260079091529020600381015461169e906103e89067ffffffffffffffff16612ac3565b67ffffffffffffffff164311156116b457600080fd5b6003810154600160801b900460ff166000805b6019811015611727578360040181601981106116e5576116e5612aef565b602091828204019190069054906101000a900460ff16151560011515141561171557611712600183612d29565b91505b8061171f81612d58565b9150506116c7565b50611730612752565b60016000805b60198110156118e9576003870154600160801b900460ff16158061177157506003870154600160801b900460ff1661176f866019612d41565b145b156117e25786600501816019811061178b5761178b612aef565b602081049091015460ff601f9092166101000a900416156117dd5760018760040182601981106117bd576117bd612aef565b602091828204019190066101000a81548160ff0219169083151502179055505b6118d7565b8660050181601981106117f7576117f7612aef565b602081049091015460ff601f9092166101000a900416156118d75760006118588983611824896019612d41565b8b60030160109054906101000a900460ff1660ff168e888151811061184b5761184b612aef565b60200260200101516123a7565b9050806118bb5760018860030160108282829054906101000a900460ff166118809190612db9565b92506101000a81548160ff021916908360ff16021790555060018583601981106118ac576118ac612aef565b91151560209092020152600093505b6118c6600187612d29565b95506118d3600184612d29565b9250505b806118e181612d58565b915050611736565b5081611a4957600686015460ff1661195c57600186015486546040516001600160a01b03808b16937fa2c15df8b18610ebd81258461bf85a580874824addb5419f1a723080a1cbb9df9361194f9391926000929116908b908a9060048f01908590612fc0565b60405180910390a26119b9565b600186015486546040516001600160a01b03808b16937feac20db8d33062f61bf7dd8f8c6fb0ce14563dec2fa4726683f5bb14c01241b4936119b09391926000929116908b908a9060048f01908590612fc0565b60405180910390a25b855460018701546119d3916001600160a01b031690611dbe565b6001600160a01b038716600090815260076020526040812080546001600160a01b0319168155600181018290556002810182905560038101805470ffffffffffffffffffffffffffffffffff1916905560048101829055906000600583015550600601805460ff19169055505050505050505050565b600386015460ff600160801b909104811660009081526009602090815260408083208884529091529020546006880154909116611b61576003870180547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8416021790556000600288015560018701546001600160a01b038916907fa2c15df8b18610ebd81258461bf85a580874824addb5419f1a723080a1cbb9df90612710611b078286612ce0565b611b119190612d15565b8a60000160009054906101000a90046001600160a01b03168b60030160109054906101000a900460ff16898d60040188604051611b549796959493929190612fc0565b60405180910390a2611c80565b600187015487546001600160a01b03908116908a167feac20db8d33062f61bf7dd8f8c6fb0ce14563dec2fa4726683f5bb14c01241b483612710611ba58288612ce0565b611baf9190612d15565b848d60030160109054906101000a900460ff168b8f6004018a604051611bdb9796959493929190612fc0565b60405180910390a2611bf1818a60010154611dbe565b6001600160a01b038a16600090815260076020526040812080546001600160a01b0319168155600181018290556002810182905560038101805470ffffffffffffffffffffffffffffffffff1916905560048101829055906000600583015550600601805460ff19169055611c7d8a612710611c6d8587612ce0565b611c779190612d15565b83611e5a565b50505b50505050505050505050565b6000611c9782610d20565b905080341015611cc35760405163de9b74a160e01b81526004810182905234602482015260440161050e565b611cd5611cd08234612d41565b612443565b8060016000828254611ce79190612d29565b909155509092915050565b6002546040517f5d3b1d300000000000000000000000000000000000000000000000000000000081527fd729dc84e21ae57ffb6be0053bf2b0668aa2aaf300a2a7b2ddf7dc0bb6e875a86004820152610240602482015260036044820152622625a0606482015263ffffffff831660848201526000916001600160a01b031690635d3b1d309060a4016020604051808303816000875af1158015611d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3d9190613015565b6001600160a01b038216611e40576006546040516000916001600160a01b03169083908381818185875af1925050503d8060008114611e19576040519150601f19603f3d011682016040523d82523d6000602084013e611e1e565b606091505b505090508061162957604051633c31275160e21b815260040160405180910390fd5b6006546106dc906001600160a01b03848116911683611580565b6006546040517f6c025ec20000000000000000000000000000000000000000000000000000000081526001600160a01b03858116600483015260248201859052838116604483015290911690636c025ec290606401600060405180830381600087803b158015611ec957600080fd5b505af1158015611edd573d6000803e3d6000fd5b50505050505050565b60006001600160a01b038416611f0957506006546001600160a01b031631611f93565b6006546040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152908516906370a0823190602401602060405180830381865afa158015611f6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f909190613015565b90505b6000828152600960209081526040808320868452909152812054611fba9061271090612d41565b611fc583606e612ce0565b611fcf9190612d15565b905080861115612015576040517fd53662aa000000000000000000000000000000000000000000000000000000008152600481018790526024810182905260440161050e565b505050505050565b6006546040517fbfd7f9cc0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152600092169063bfd7f9cc90604401602060405180830381865afa158015612087573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ab919061302e565b6120e1576040517f51c3b94f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83612118576040517f07375c8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040517f0a5748a80000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526000928392911690630a5748a8906024016040805180830381865afa15801561217e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121a2919061304b565b9150915081156121e1576040517f6c2775f50000000000000000000000000000000000000000000000000000000081526004810182905260240161050e565b6121ea85610d20565b92506001600160a01b038716612254576122048387612d29565b341015612238576122158387612d29565b60405163de9b74a160e01b8152600481019190915234602482015260440161050e565b61224f6122458785612d29565b611cd09034612d41565b6122a0565b8234101561227e5760405163de9b74a160e01b81526004810184905234602482015260440161050e565b6122936001600160a01b0388168530896124b4565b6122a0611cd08434612d41565b82600160008282546122b29190612d29565b9091555092979650505050505050565b6000612317826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661250b9092919063ffffffff16565b8051909150156116295780806020019051810190612335919061302e565b6116295760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161050e565b600080846123b785612710612ce0565b6123c19190612d15565b6123cd90612710612d41565b90506000816123de61271086613079565b116123e7575060015b6001600160a01b0388166000908152600760205260409020600190600401886019811061241657612416612aef565b602091828204019190066101000a81548160ff021916908315150217905550809250505095945050505050565b8061244b5750565b604051600090339083908381818185875af1925050503d806000811461248d576040519150601f19603f3d011682016040523d82523d6000602084013e612492565b606091505b50509050806106dc57604051633c31275160e21b815260040160405180910390fd5b6040516001600160a01b03808516602483015283166044820152606481018290526125059085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016115c5565b50505050565b6060610e79848460008585600080866001600160a01b0316858760405161253291906130b9565b60006040518083038185875af1925050503d806000811461256f576040519150601f19603f3d011682016040523d82523d6000602084013e612574565b606091505b509150915061258587838387612590565b979650505050505050565b606083156125fc5782516125f5576001600160a01b0385163b6125f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161050e565b5081610e79565b610e7983838151156126115781518083602001fd5b8060405162461bcd60e51b815260040161050e91906130d5565b60405180610120016040528060006001600160a01b031681526020016000815260200160008152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600060ff168152602001612689612752565b8152602001612696612752565b8152600060209091015290565b6001830191839082156127275791602002820160005b838211156126f8578335151583826101000a81548160ff02191690831515021790555092602001926001016020816000010492830192600103026126b9565b80156127255782816101000a81549060ff02191690556001016020816000010492830192600103026126f8565b505b506111ef929150612771565b6040518061030001604052806018906020820280368337509192915050565b6040518061032001604052806019906020820280368337509192915050565b5b808211156111ef5760008155600101612772565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156127af57600080fd5b8235915060208084013567ffffffffffffffff808211156127cf57600080fd5b818601915086601f8301126127e357600080fd5b8135818111156127f5576127f5612786565b8060051b604051601f19603f8301168101818110858211171561281a5761281a612786565b60405291825284820192508381018501918983111561283857600080fd5b938501935b828510156128565784358452938501939285019261283d565b8096505050505050509250929050565b6001600160a01b0381168114610d1d57600080fd5b60006020828403121561288d57600080fd5b813561289881612866565b9392505050565b8060005b601981101561250557815115158452602093840193909101906001016128a3565b6000610720820190506001600160a01b03835116825260208301516020830152604083015160408301526060830151612909606084018267ffffffffffffffff169052565b506080830151612925608084018267ffffffffffffffff169052565b5060a083015161293a60a084018260ff169052565b5060c083015161294d60c084018261289f565b5060e08301516129616103e084018261289f565b5061010092909201511515610700919091015290565b806103208101831015610b3d57600080fd5b8015158114610d1d57600080fd5b60008061034083850312156129ab57600080fd5b6129b58484612977565b91506103208301356129c681612989565b809150509250929050565b600080604083850312156129e457600080fd5b50508035926020909101359150565b600060208284031215612a0557600080fd5b5035919050565b6103008101818360005b6018811015612a35578151835260209283019290910190600101612a16565b50505092915050565b60008060008060006103a08688031215612a5757600080fd5b853594506020860135612a6981612866565b9350604086013560ff81168114612a7f57600080fd5b9250612a8e8760608801612977565b9150610380860135612a9f81612989565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff808316818516808303821115612ae657612ae6612aad565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612b1757600080fd5b813561289881612989565b600063ffffffff80831681811415612b3c57612b3c612aad565b6001019392505050565b600060ff821660ff811415612b5d57612b5d612aad565b60010192915050565b600063ffffffff808316818516808303821115612ae657612ae6612aad565b600060208284031215612b9757600080fd5b815161289881612866565b805169ffffffffffffffffffff81168114612bbc57600080fd5b919050565b600080600080600060a08688031215612bd957600080fd5b612be286612ba2565b9450602086015193506040860151925060608601519150612c0560808701612ba2565b90509295509295909350565b805163ffffffff81168114612bbc57600080fd5b805162ffffff81168114612bbc57600080fd5b60008060008060008060008060006101208a8c031215612c5757600080fd5b612c608a612c11565b9850612c6e60208b01612c11565b9750612c7c60408b01612c11565b9650612c8a60608b01612c11565b9550612c9860808b01612c11565b9450612ca660a08b01612c25565b9350612cb460c08b01612c25565b9250612cc260e08b01612c25565b9150612cd16101008b01612c25565b90509295985092959850929598565b6000816000190483118215151615612cfa57612cfa612aad565b500290565b634e487b7160e01b600052601260045260246000fd5b600082612d2457612d24612cff565b500490565b60008219821115612d3c57612d3c612aad565b500190565b600082821015612d5357612d53612aad565b500390565b6000600019821415612d6c57612d6c612aad565b5060010190565b868152602081018690526001600160a01b038516604082015260ff841660608201526103c08101612da7608083018561289f565b826103a0830152979650505050505050565b600060ff821660ff841680821015612dd357612dd3612aad565b90039392505050565b805460ff80821615158452612dfa60208501828460081c1615159052565b612e0d60408501828460101c1615159052565b612e2060608501828460181c1615159052565b612e3360808501828460201c1615159052565b612e4660a08501828460281c1615159052565b612e5960c08501828460301c1615159052565b612e6c60e08501828460381c1615159052565b612e806101008501828460401c1615159052565b612e946101208501828460481c1615159052565b612ea86101408501828460501c1615159052565b612ebc6101608501828460581c1615159052565b612ed06101808501828460601c1615159052565b612ee46101a08501828460681c1615159052565b612ef86101c08501828460701c1615159052565b612f0c6101e08501828460781c1615159052565b612f206102008501828460801c1615159052565b612f346102208501828460881c1615159052565b612f486102408501828460901c1615159052565b612f5c6102608501828460981c1615159052565b612f706102808501828460a01c1615159052565b612f846102a08501828460a81c1615159052565b612f986102c08501828460b01c1615159052565b612fac6102e08501828460b81c1615159052565b6125056103008501828460c01c1615159052565b878152602081018790526001600160a01b038616604082015260ff851660608201526106e08101612ff4608083018661289f565b6130026103a0830185612ddc565b826106c083015298975050505050505050565b60006020828403121561302757600080fd5b5051919050565b60006020828403121561304057600080fd5b815161289881612989565b6000806040838503121561305e57600080fd5b825161306981612989565b6020939093015192949293505050565b60008261308857613088612cff565b500690565b60005b838110156130a8578181015183820152602001613090565b838111156125055750506000910152565b600082516130cb81846020870161308d565b9190910192915050565b60208152600082518060208401526130f481604085016020870161308d565b601f01601f1916919091016040019291505056fea26469706673582212203ce81215919bea7d6fa99fd9d77bcf19217c6d48ab97ac418b9fcfc3425cc00564736f6c634300080b003300000000000000000000000051e99a0d09eeca8d7efec3062ac024b6d0989959000000000000000000000000ae975071be8f8ee67addbc1a82488f1c248580670000000000000000000000005787befdc0ecd210dfa948264631cd53e68f7802000000000000000000000000f73ab2d782bf6ba97ac4405d2cd4f1135da8dbd9000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x6080604052600436106101295760003560e01c80638bd16114116100a5578063ad17836111610074578063c6318aad11610059578063c6318aad1461033e578063d0bcfc0c14610360578063eb5d80751461038057600080fd5b8063ad17836114610309578063b5f2da971461032957600080fd5b80638bd16114146102935780638d7c16d2146102b3578063953995ea146102c9578063a6d18c51146102e957600080fd5b80634dd189d5116100fc57806356c022bb116100e157806356c022bb14610214578063572b6c051461023457806363dc4ef51461027357600080fd5b80634dd189d5146101ae57806350ffd786146101e657600080fd5b80630f53b8261461012e5780631fe543e31461014557806320ade00314610165578063320a51351461019b575b600080fd5b34801561013a57600080fd5b50610143610393565b005b34801561015157600080fd5b5061014361016036600461279c565b610679565b34801561017157600080fd5b5061018561018036600461287b565b6106e0565b60405161019291906128c4565b60405180910390f35b6101436101a9366004612997565b610827565b3480156101ba57600080fd5b506005546101ce906001600160a01b031681565b6040516001600160a01b039091168152602001610192565b3480156101f257600080fd5b506102066102013660046129d1565b610b22565b604051908152602001610192565b34801561022057600080fd5b506003546101ce906001600160a01b031681565b34801561024057600080fd5b5061026361024f36600461287b565b6003546001600160a01b0391821691161490565b6040519015158152602001610192565b34801561027f57600080fd5b5061014361028e36600461287b565b610b43565b34801561029f57600080fd5b506102066102ae3660046129f3565b610d20565b3480156102bf57600080fd5b5061020660015481565b3480156102d557600080fd5b506002546101ce906001600160a01b031681565b3480156102f557600080fd5b506101436103043660046129f3565b610e81565b34801561031557600080fd5b506004546101ce906001600160a01b031681565b34801561033557600080fd5b50610143610f84565b34801561034a57600080fd5b50610353611194565b6040516101929190612a0c565b34801561036c57600080fd5b506006546101ce906001600160a01b031681565b61014361038e366004612a3e565b6111f3565b61039b6114f2565b60006103a561154c565b6001600160a01b038116600090815260076020526040902060030154909150600160801b900460ff166103eb5760405163218286cb60e11b815260040160405180910390fd5b6001600160a01b03811660009081526007602052604090206002015461043d576040517f90a7340b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600760205260409020600301544390610472906103e89067ffffffffffffffff16612ac3565b61047d90600a612ac3565b67ffffffffffffffff161115610517576001600160a01b03811660009081526007602052604090206003015443906104c2906103e89067ffffffffffffffff16612ac3565b6104cd90600a612ac3565b6040517fb4ae675a000000000000000000000000000000000000000000000000000000008152600481019290925267ffffffffffffffff1660248201526044015b60405180910390fd5b6001600160a01b03808216600090815260076020526040812060018101805482546001600160a01b031981168455918490556002830184905560038301805470ffffffffffffffffffffffffffffffffff19169055600483018490559316916000600583015550600601805460ff191690556001600160a01b038116610611576000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146105e4576040519150601f19603f3d011682016040523d82523d6000602084013e6105e9565b606091505b505090508061060b576040516312171d8360e31b815260040160405180910390fd5b50610625565b6106256001600160a01b0382168484611580565b604080518381526001600160a01b0383811660208301528516917f47d03d5b779b98ffcf8d141c72233e07e1eefe64744ce9d5288aea98eaf6bc9c910160405180910390a25050506106776001600055565b565b6002546001600160a01b031633146106d2576002546040517f1cf993f40000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b03909116602482015260440161050e565b6106dc828261162e565b5050565b6106e861262b565b6001600160a01b03828116600090815260076020908152604080832081516101208101835281549095168552600181015492850192909252600282015484820152600382015467ffffffffffffffff8082166060870152680100000000000000008204166080860152600160801b900460ff1660a08501528051610320810191829052919260c0850192916004850191601991908390855b825461010083900a900460ff1615158152602060019283018181049485019490930390920291018084116107805750505092845250506040805161032081019182905260209093019291506005840190601990826000855b825461010083900a900460ff1615158152602060019283018181049485019490930390920291018084116107d8575050509284525050506006919091015460ff16151560209091015292915050565b61082f6114f2565b600061083961154c565b6001600160a01b0381166000908152600760205260409020600381015491925090600160801b900460ff166108815760405163218286cb60e11b815260040160405180910390fd5b6002810154156108ac578060020154604051632206dcb760e11b815260040161050e91815260200190565b60008060005b60198160ff1610156109b657868160ff16601981106108d3576108d3612aef565b6020020160208101906108e69190612b05565b1561096357836004018160ff166019811061090357610903612aef565b602081049091015460ff601f9092166101000a90041615610955576040517f63926f5100000000000000000000000000000000000000000000000000000000815260ff8216600482015260240161050e565b8161095f81612b22565b9250505b836004018160ff166019811061097b5761097b612aef565b602081049091015460ff601f9092166101000a900416156109a457826109a081612b22565b9350505b806109ae81612b46565b9150506108b2565b5063ffffffff811615806109f557506003830154600160801b900460ff166000908152600a60205260409020546109ed8383612b66565b63ffffffff16115b15610a4a57610a048282612b66565b6003840154600160801b900460ff166000908152600a60205260409081902054905163377e537f60e11b815263ffffffff9092166004830152602482015260440161050e565b6000610a586207a120611c8c565b90506000610a6583611cf2565b600081815260086020526040902080546001600160a01b0319166001600160a01b0389161790559050610a9d600586018960196126a3565b5060068501805460ff19168815151790556002850181905560038501805467ffffffffffffffff19164367ffffffffffffffff161790556040518281526001600160a01b038716907f676c918f37fe2b3bf042543e3fdaf058ce52fd6fde49a5303cf971d7037f84ec9060200160405180910390a25050505050506106dc6001600055565b60008281526009602090815260408083208484529091529020545b92915050565b610b4b6114f2565b600660009054906101000a90046001600160a01b03166001600160a01b031663893d20e86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bc29190612b85565b6001600160a01b0316336001600160a01b031614610c9557600660009054906101000a90046001600160a01b03166001600160a01b031663893d20e86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c519190612b85565b6040517f23295f0e0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015233602482015260440161050e565b600180546000918290556040519091906001600160a01b0384169083908381818185875af1925050503d8060008114610cea576040519150601f19603f3d011682016040523d82523d6000602084013e610cef565b606091505b5050905080610d11576040516312171d8360e31b815260040160405180910390fd5b5050610d1d6001600055565b50565b60048054604080517ffeaf968c000000000000000000000000000000000000000000000000000000008152905160009384936001600160a01b03169263feaf968c928183019260a0928290030181865afa158015610d82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da69190612bc1565b5050509150506000600560009054906101000a90046001600160a01b03166001600160a01b0316635fbbc0d26040518163ffffffff1660e01b815260040161012060405180830381865afa158015610e02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e269190612c38565b50505050505050509050670de0b6b3a7640000828263ffffffff1664e8d4a51000610e519190612ce0565b610e5b9190612ce0565b610e659190612d15565b610e6f853a612ce0565b610e799190612d29565b949350505050565b801580610e8f575060198110155b15610e9957600080fd5b60008181526009602090815260408083206001845290915290205415610ebe57600080fd5b60015b610ecc826019612d41565b81116106dc5760018060005b83811015610f2b5780610eec866019612d41565b610ef69190612d41565b610f009084612ce0565b9250610f0d816019612d41565b610f179083612ce0565b915080610f2381612d58565b915050610ed8565b5080610f3b83633b9aca00612ce0565b610f459190612d15565b610f559065090105fbb800612d15565b600085815260096020908152604080832087845290915290205550819050610f7c81612d58565b915050610ec1565b610f8c6114f2565b6000610f9661154c565b6001600160a01b0381166000908152600760205260409020600381015491925090600160801b900460ff16610fde5760405163218286cb60e11b815260040160405180910390fd5b600281015415611009578060020154604051632206dcb760e11b815260040161050e91815260200190565b600381015460408051610320810191829052600160801b90920460ff169160009160048501906019908285855b825461010083900a900460ff16151581526020600192830181810494850194909303909202910180841161103657505050506003860154600187015493945068010000000000000000900467ffffffffffffffff169291506000905061271061109f8385612ce0565b6110a99190612d15565b86549091506001600160a01b03166110c18184611dbe565b6001600160a01b038816600090815260076020526040812080546001600160a01b0319168155600181018290556002810182905560038101805470ffffffffffffffffffffffffffffffffff1916905560048101829055906000600583015550600601805460ff19169055611137888383611e5a565b876001600160a01b03167f1fe1dd196606ad2650d7969421fce1457a68ab5ef856f561b02c43d85e2b23058484848a8a8a60405161117a96959493929190612d73565b60405180910390a250505050505050506106776001600055565b61119c612733565b60005b60188110156111ef57600a60006111b7836001612d29565b8152602001908152602001600020548282601881106111d8576111d8612aef565b6020020152806111e781612d58565b91505061119f565b5090565b6111fb6114f2565b600061120561154c565b905060018460ff161015801561121f575060188460ff1611155b611255576040517f822cb1a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600760205260409020600281015415611298578060020154604051632206dcb760e11b815260040161050e91815260200190565b6003810154600160801b900460ff16156112de576040517fc56fb25300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b60198160ff16101561133b57858160ff166019811061130357611303612aef565b6020020160208101906113169190612b05565b15611329578161132581612b22565b9250505b8061133381612b46565b9150506112e2565b5060ff86166000908152600a602052604090205463ffffffff821615806113675750808263ffffffff16115b156113945760405163377e537f60e11b815263ffffffff831660048201526024810182905260440161050e565b6113a38989838a60ff16611ee6565b60006113b4898b6207a1208861201d565b905060006113c184611cf2565b600081815260086020526040902080546001600160a01b03199081166001600160a01b038a8116919091179092556003880180547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff16600160801b60ff8f1602179055600188018e9055875416908c1617865560068601805460ff19168915151790559050611455600586018960196126a3565b506002850181905560038501805467ffffffffffffffff19164367ffffffffffffffff16179055604080518c81526001600160a01b038c8116602083015260ff8c16828401528915156060830152608082018590529151918816917f06b735e3b542c1e7f37c869cdc85e44e5653bb9d7df7669703552379dc5d89b59160a0908290030190a25050505050506114eb6001600055565b5050505050565b600260005414156115455760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161050e565b6002600055565b60006014361080159061156957506003546001600160a01b031633145b1561157b575060131936013560601c90565b503390565b6040516001600160a01b0383166024820152604481018290526116299084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526122c2565b505050565b6000828152600860205260409020546001600160a01b03168061165057600080fd5b600083815260086020908152604080832080546001600160a01b03191690556001600160a01b038416835260079091529020600381015461169e906103e89067ffffffffffffffff16612ac3565b67ffffffffffffffff164311156116b457600080fd5b6003810154600160801b900460ff166000805b6019811015611727578360040181601981106116e5576116e5612aef565b602091828204019190069054906101000a900460ff16151560011515141561171557611712600183612d29565b91505b8061171f81612d58565b9150506116c7565b50611730612752565b60016000805b60198110156118e9576003870154600160801b900460ff16158061177157506003870154600160801b900460ff1661176f866019612d41565b145b156117e25786600501816019811061178b5761178b612aef565b602081049091015460ff601f9092166101000a900416156117dd5760018760040182601981106117bd576117bd612aef565b602091828204019190066101000a81548160ff0219169083151502179055505b6118d7565b8660050181601981106117f7576117f7612aef565b602081049091015460ff601f9092166101000a900416156118d75760006118588983611824896019612d41565b8b60030160109054906101000a900460ff1660ff168e888151811061184b5761184b612aef565b60200260200101516123a7565b9050806118bb5760018860030160108282829054906101000a900460ff166118809190612db9565b92506101000a81548160ff021916908360ff16021790555060018583601981106118ac576118ac612aef565b91151560209092020152600093505b6118c6600187612d29565b95506118d3600184612d29565b9250505b806118e181612d58565b915050611736565b5081611a4957600686015460ff1661195c57600186015486546040516001600160a01b03808b16937fa2c15df8b18610ebd81258461bf85a580874824addb5419f1a723080a1cbb9df9361194f9391926000929116908b908a9060048f01908590612fc0565b60405180910390a26119b9565b600186015486546040516001600160a01b03808b16937feac20db8d33062f61bf7dd8f8c6fb0ce14563dec2fa4726683f5bb14c01241b4936119b09391926000929116908b908a9060048f01908590612fc0565b60405180910390a25b855460018701546119d3916001600160a01b031690611dbe565b6001600160a01b038716600090815260076020526040812080546001600160a01b0319168155600181018290556002810182905560038101805470ffffffffffffffffffffffffffffffffff1916905560048101829055906000600583015550600601805460ff19169055505050505050505050565b600386015460ff600160801b909104811660009081526009602090815260408083208884529091529020546006880154909116611b61576003870180547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000067ffffffffffffffff8416021790556000600288015560018701546001600160a01b038916907fa2c15df8b18610ebd81258461bf85a580874824addb5419f1a723080a1cbb9df90612710611b078286612ce0565b611b119190612d15565b8a60000160009054906101000a90046001600160a01b03168b60030160109054906101000a900460ff16898d60040188604051611b549796959493929190612fc0565b60405180910390a2611c80565b600187015487546001600160a01b03908116908a167feac20db8d33062f61bf7dd8f8c6fb0ce14563dec2fa4726683f5bb14c01241b483612710611ba58288612ce0565b611baf9190612d15565b848d60030160109054906101000a900460ff168b8f6004018a604051611bdb9796959493929190612fc0565b60405180910390a2611bf1818a60010154611dbe565b6001600160a01b038a16600090815260076020526040812080546001600160a01b0319168155600181018290556002810182905560038101805470ffffffffffffffffffffffffffffffffff1916905560048101829055906000600583015550600601805460ff19169055611c7d8a612710611c6d8587612ce0565b611c779190612d15565b83611e5a565b50505b50505050505050505050565b6000611c9782610d20565b905080341015611cc35760405163de9b74a160e01b81526004810182905234602482015260440161050e565b611cd5611cd08234612d41565b612443565b8060016000828254611ce79190612d29565b909155509092915050565b6002546040517f5d3b1d300000000000000000000000000000000000000000000000000000000081527fd729dc84e21ae57ffb6be0053bf2b0668aa2aaf300a2a7b2ddf7dc0bb6e875a86004820152610240602482015260036044820152622625a0606482015263ffffffff831660848201526000916001600160a01b031690635d3b1d309060a4016020604051808303816000875af1158015611d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3d9190613015565b6001600160a01b038216611e40576006546040516000916001600160a01b03169083908381818185875af1925050503d8060008114611e19576040519150601f19603f3d011682016040523d82523d6000602084013e611e1e565b606091505b505090508061162957604051633c31275160e21b815260040160405180910390fd5b6006546106dc906001600160a01b03848116911683611580565b6006546040517f6c025ec20000000000000000000000000000000000000000000000000000000081526001600160a01b03858116600483015260248201859052838116604483015290911690636c025ec290606401600060405180830381600087803b158015611ec957600080fd5b505af1158015611edd573d6000803e3d6000fd5b50505050505050565b60006001600160a01b038416611f0957506006546001600160a01b031631611f93565b6006546040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152908516906370a0823190602401602060405180830381865afa158015611f6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f909190613015565b90505b6000828152600960209081526040808320868452909152812054611fba9061271090612d41565b611fc583606e612ce0565b611fcf9190612d15565b905080861115612015576040517fd53662aa000000000000000000000000000000000000000000000000000000008152600481018790526024810182905260440161050e565b505050505050565b6006546040517fbfd7f9cc0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152600092169063bfd7f9cc90604401602060405180830381865afa158015612087573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ab919061302e565b6120e1576040517f51c3b94f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83612118576040517f07375c8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040517f0a5748a80000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301526000928392911690630a5748a8906024016040805180830381865afa15801561217e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121a2919061304b565b9150915081156121e1576040517f6c2775f50000000000000000000000000000000000000000000000000000000081526004810182905260240161050e565b6121ea85610d20565b92506001600160a01b038716612254576122048387612d29565b341015612238576122158387612d29565b60405163de9b74a160e01b8152600481019190915234602482015260440161050e565b61224f6122458785612d29565b611cd09034612d41565b6122a0565b8234101561227e5760405163de9b74a160e01b81526004810184905234602482015260440161050e565b6122936001600160a01b0388168530896124b4565b6122a0611cd08434612d41565b82600160008282546122b29190612d29565b9091555092979650505050505050565b6000612317826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661250b9092919063ffffffff16565b8051909150156116295780806020019051810190612335919061302e565b6116295760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161050e565b600080846123b785612710612ce0565b6123c19190612d15565b6123cd90612710612d41565b90506000816123de61271086613079565b116123e7575060015b6001600160a01b0388166000908152600760205260409020600190600401886019811061241657612416612aef565b602091828204019190066101000a81548160ff021916908315150217905550809250505095945050505050565b8061244b5750565b604051600090339083908381818185875af1925050503d806000811461248d576040519150601f19603f3d011682016040523d82523d6000602084013e612492565b606091505b50509050806106dc57604051633c31275160e21b815260040160405180910390fd5b6040516001600160a01b03808516602483015283166044820152606481018290526125059085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016115c5565b50505050565b6060610e79848460008585600080866001600160a01b0316858760405161253291906130b9565b60006040518083038185875af1925050503d806000811461256f576040519150601f19603f3d011682016040523d82523d6000602084013e612574565b606091505b509150915061258587838387612590565b979650505050505050565b606083156125fc5782516125f5576001600160a01b0385163b6125f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161050e565b5081610e79565b610e7983838151156126115781518083602001fd5b8060405162461bcd60e51b815260040161050e91906130d5565b60405180610120016040528060006001600160a01b031681526020016000815260200160008152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600060ff168152602001612689612752565b8152602001612696612752565b8152600060209091015290565b6001830191839082156127275791602002820160005b838211156126f8578335151583826101000a81548160ff02191690831515021790555092602001926001016020816000010492830192600103026126b9565b80156127255782816101000a81549060ff02191690556001016020816000010492830192600103026126f8565b505b506111ef929150612771565b6040518061030001604052806018906020820280368337509192915050565b6040518061032001604052806019906020820280368337509192915050565b5b808211156111ef5760008155600101612772565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156127af57600080fd5b8235915060208084013567ffffffffffffffff808211156127cf57600080fd5b818601915086601f8301126127e357600080fd5b8135818111156127f5576127f5612786565b8060051b604051601f19603f8301168101818110858211171561281a5761281a612786565b60405291825284820192508381018501918983111561283857600080fd5b938501935b828510156128565784358452938501939285019261283d565b8096505050505050509250929050565b6001600160a01b0381168114610d1d57600080fd5b60006020828403121561288d57600080fd5b813561289881612866565b9392505050565b8060005b601981101561250557815115158452602093840193909101906001016128a3565b6000610720820190506001600160a01b03835116825260208301516020830152604083015160408301526060830151612909606084018267ffffffffffffffff169052565b506080830151612925608084018267ffffffffffffffff169052565b5060a083015161293a60a084018260ff169052565b5060c083015161294d60c084018261289f565b5060e08301516129616103e084018261289f565b5061010092909201511515610700919091015290565b806103208101831015610b3d57600080fd5b8015158114610d1d57600080fd5b60008061034083850312156129ab57600080fd5b6129b58484612977565b91506103208301356129c681612989565b809150509250929050565b600080604083850312156129e457600080fd5b50508035926020909101359150565b600060208284031215612a0557600080fd5b5035919050565b6103008101818360005b6018811015612a35578151835260209283019290910190600101612a16565b50505092915050565b60008060008060006103a08688031215612a5757600080fd5b853594506020860135612a6981612866565b9350604086013560ff81168114612a7f57600080fd5b9250612a8e8760608801612977565b9150610380860135612a9f81612989565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b600067ffffffffffffffff808316818516808303821115612ae657612ae6612aad565b01949350505050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612b1757600080fd5b813561289881612989565b600063ffffffff80831681811415612b3c57612b3c612aad565b6001019392505050565b600060ff821660ff811415612b5d57612b5d612aad565b60010192915050565b600063ffffffff808316818516808303821115612ae657612ae6612aad565b600060208284031215612b9757600080fd5b815161289881612866565b805169ffffffffffffffffffff81168114612bbc57600080fd5b919050565b600080600080600060a08688031215612bd957600080fd5b612be286612ba2565b9450602086015193506040860151925060608601519150612c0560808701612ba2565b90509295509295909350565b805163ffffffff81168114612bbc57600080fd5b805162ffffff81168114612bbc57600080fd5b60008060008060008060008060006101208a8c031215612c5757600080fd5b612c608a612c11565b9850612c6e60208b01612c11565b9750612c7c60408b01612c11565b9650612c8a60608b01612c11565b9550612c9860808b01612c11565b9450612ca660a08b01612c25565b9350612cb460c08b01612c25565b9250612cc260e08b01612c25565b9150612cd16101008b01612c25565b90509295985092959850929598565b6000816000190483118215151615612cfa57612cfa612aad565b500290565b634e487b7160e01b600052601260045260246000fd5b600082612d2457612d24612cff565b500490565b60008219821115612d3c57612d3c612aad565b500190565b600082821015612d5357612d53612aad565b500390565b6000600019821415612d6c57612d6c612aad565b5060010190565b868152602081018690526001600160a01b038516604082015260ff841660608201526103c08101612da7608083018561289f565b826103a0830152979650505050505050565b600060ff821660ff841680821015612dd357612dd3612aad565b90039392505050565b805460ff80821615158452612dfa60208501828460081c1615159052565b612e0d60408501828460101c1615159052565b612e2060608501828460181c1615159052565b612e3360808501828460201c1615159052565b612e4660a08501828460281c1615159052565b612e5960c08501828460301c1615159052565b612e6c60e08501828460381c1615159052565b612e806101008501828460401c1615159052565b612e946101208501828460481c1615159052565b612ea86101408501828460501c1615159052565b612ebc6101608501828460581c1615159052565b612ed06101808501828460601c1615159052565b612ee46101a08501828460681c1615159052565b612ef86101c08501828460701c1615159052565b612f0c6101e08501828460781c1615159052565b612f206102008501828460801c1615159052565b612f346102208501828460881c1615159052565b612f486102408501828460901c1615159052565b612f5c6102608501828460981c1615159052565b612f706102808501828460a01c1615159052565b612f846102a08501828460a81c1615159052565b612f986102c08501828460b01c1615159052565b612fac6102e08501828460b81c1615159052565b6125056103008501828460c01c1615159052565b878152602081018790526001600160a01b038616604082015260ff851660608201526106e08101612ff4608083018661289f565b6130026103a0830185612ddc565b826106c083015298975050505050505050565b60006020828403121561302757600080fd5b5051919050565b60006020828403121561304057600080fd5b815161289881612989565b6000806040838503121561305e57600080fd5b825161306981612989565b6020939093015192949293505050565b60008261308857613088612cff565b500690565b60005b838110156130a8578181015183820152602001613090565b838111156125055750506000910152565b600082516130cb81846020870161308d565b9190910192915050565b60208152600082518060208401526130f481604085016020870161308d565b601f01601f1916919091016040019291505056fea26469706673582212203ce81215919bea7d6fa99fd9d77bcf19217c6d48ab97ac418b9fcfc3425cc00564736f6c634300080b0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000051e99a0d09eeca8d7efec3062ac024b6d0989959000000000000000000000000ae975071be8f8ee67addbc1a82488f1c248580670000000000000000000000005787befdc0ecd210dfa948264631cd53e68f7802000000000000000000000000f73ab2d782bf6ba97ac4405d2cd4f1135da8dbd9000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : _bankroll (address): 0x51e99A0D09EeCa8d7EFEc3062AC024B6d0989959
Arg [1] : _vrf (address): 0xAE975071Be8F8eE67addBC1A82488F1C24858067
Arg [2] : link_eth_feed (address): 0x5787BefDc0ECd210Dfa948264631CD53E68F7802
Arg [3] : _forwarder (address): 0xF73ab2d782bf6BA97ac4405D2CD4F1135da8dbd9
Arg [4] : maxReveal (uint8[24]): 24,21,17,14,12,10,9,8,7,6,5,5,4,4,3,3,3,2,2,2,2,1,1,1

-----Encoded View---------------
28 Constructor Arguments found :
Arg [0] : 00000000000000000000000051e99a0d09eeca8d7efec3062ac024b6d0989959
Arg [1] : 000000000000000000000000ae975071be8f8ee67addbc1a82488f1c24858067
Arg [2] : 0000000000000000000000005787befdc0ecd210dfa948264631cd53e68f7802
Arg [3] : 000000000000000000000000f73ab2d782bf6ba97ac4405d2cd4f1135da8dbd9
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000018
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000015
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000011
Arg [7] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [9] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000009
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [19] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [21] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [25] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [26] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [27] : 0000000000000000000000000000000000000000000000000000000000000001


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.