ETH Price: $3,155.15 (-4.34%)
Gas: 3 Gwei

Contract

0x0A9f25Bac1E81D8558a1D738Bbb275299333742B
 

Overview

ETH Balance

0.8635 ETH

Eth Value

$2,724.47 (@ $3,155.15/ETH)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Collect Fees188500332023-12-23 17:35:23194 days ago1703352923IN
0x0A9f25Ba...99333742B
0 ETH0.0008701924.85710902
Leave186473972023-11-25 8:03:47222 days ago1700899427IN
0x0A9f25Ba...99333742B
0 ETH0.0016231827.01704889
Leave186178232023-11-21 4:40:47226 days ago1700541647IN
0x0A9f25Ba...99333742B
0 ETH0.0017583929.26759736
Leave186161022023-11-20 22:53:59226 days ago1700520839IN
0x0A9f25Ba...99333742B
0 ETH0.0019961933.22562873
Leave185882032023-11-17 1:04:35230 days ago1700183075IN
0x0A9f25Ba...99333742B
0 ETH0.0012090521.87153844
Leave185875962023-11-16 23:02:23230 days ago1700175743IN
0x0A9f25Ba...99333742B
0 ETH0.0018324730.50065466
Leave185661872023-11-13 23:13:47233 days ago1699917227IN
0x0A9f25Ba...99333742B
0 ETH0.0018179230.25844541
Leave185627672023-11-13 11:44:59234 days ago1699875899IN
0x0A9f25Ba...99333742B
0 ETH0.002229337.1056329
Leave185616222023-11-13 7:54:11234 days ago1699862051IN
0x0A9f25Ba...99333742B
0 ETH0.0015839626.36419388
Leave185614562023-11-13 7:20:47234 days ago1699860047IN
0x0A9f25Ba...99333742B
0 ETH0.0019196131.95098925
Leave185614532023-11-13 7:20:11234 days ago1699860011IN
0x0A9f25Ba...99333742B
0 ETH0.0017304631.27649609
Leave185614512023-11-13 7:19:47234 days ago1699859987IN
0x0A9f25Ba...99333742B
0 ETH0.00168930.52711718
Leave185614222023-11-13 7:13:59234 days ago1699859639IN
0x0A9f25Ba...99333742B
0 ETH0.0019386235.0691468
Leave185605172023-11-13 4:11:11234 days ago1699848671IN
0x0A9f25Ba...99333742B
0 ETH0.0017687429.43975733
Leave185605142023-11-13 4:10:35234 days ago1699848635IN
0x0A9f25Ba...99333742B
0 ETH0.0017851132.29223174
Leave185601282023-11-13 2:53:11234 days ago1699843991IN
0x0A9f25Ba...99333742B
0 ETH0.0024062740.0511382
Leave185601262023-11-13 2:52:47234 days ago1699843967IN
0x0A9f25Ba...99333742B
0 ETH0.0024846841.35624046
Leave185593682023-11-13 0:20:23234 days ago1699834823IN
0x0A9f25Ba...99333742B
0 ETH0.0014168125.62415439
Leave185593672023-11-13 0:20:11234 days ago1699834811IN
0x0A9f25Ba...99333742B
0 ETH0.0013561224.52654592
Leave185593612023-11-13 0:18:59234 days ago1699834739IN
0x0A9f25Ba...99333742B
0 ETH0.0016337827.18804196
Claim185588772023-11-12 22:41:23234 days ago1699828883IN
0x0A9f25Ba...99333742B
0 ETH0.0014748723.54337527
Play185588582023-11-12 22:37:35234 days ago1699828655IN
0x0A9f25Ba...99333742B
0.01 ETH0.001413325.97089094
Play185588562023-11-12 22:37:11234 days ago1699828631IN
0x0A9f25Ba...99333742B
0.01 ETH0.0015365827.76824723
Play185588422023-11-12 22:34:23234 days ago1699828463IN
0x0A9f25Ba...99333742B
0.01 ETH0.0014733327.14476786
Join185588282023-11-12 22:31:35234 days ago1699828295IN
0x0A9f25Ba...99333742B
0.01 ETH0.0022582322.86523965
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To Value
188500332023-12-23 17:35:23194 days ago1703352923
0x0A9f25Ba...99333742B
0.026 ETH
186473972023-11-25 8:03:47222 days ago1700899427
0x0A9f25Ba...99333742B
0.01 ETH
186178232023-11-21 4:40:47226 days ago1700541647
0x0A9f25Ba...99333742B
0.01 ETH
186161022023-11-20 22:53:59226 days ago1700520839
0x0A9f25Ba...99333742B
0.01 ETH
185882032023-11-17 1:04:35230 days ago1700183075
0x0A9f25Ba...99333742B
0.01 ETH
185875962023-11-16 23:02:23230 days ago1700175743
0x0A9f25Ba...99333742B
0.01 ETH
185661872023-11-13 23:13:47233 days ago1699917227
0x0A9f25Ba...99333742B
0.01 ETH
185627672023-11-13 11:44:59234 days ago1699875899
0x0A9f25Ba...99333742B
0.01 ETH
185616222023-11-13 7:54:11234 days ago1699862051
0x0A9f25Ba...99333742B
0.01 ETH
185614562023-11-13 7:20:47234 days ago1699860047
0x0A9f25Ba...99333742B
0.01 ETH
185614532023-11-13 7:20:11234 days ago1699860011
0x0A9f25Ba...99333742B
0.05 ETH
185614512023-11-13 7:19:47234 days ago1699859987
0x0A9f25Ba...99333742B
0.03 ETH
185614222023-11-13 7:13:59234 days ago1699859639
0x0A9f25Ba...99333742B
0.01 ETH
185605172023-11-13 4:11:11234 days ago1699848671
0x0A9f25Ba...99333742B
0.01 ETH
185605142023-11-13 4:10:35234 days ago1699848635
0x0A9f25Ba...99333742B
0.01 ETH
185601282023-11-13 2:53:11234 days ago1699843991
0x0A9f25Ba...99333742B
0.01 ETH
185601262023-11-13 2:52:47234 days ago1699843967
0x0A9f25Ba...99333742B
0.01 ETH
185593682023-11-13 0:20:23234 days ago1699834823
0x0A9f25Ba...99333742B
0.05 ETH
185593672023-11-13 0:20:11234 days ago1699834811
0x0A9f25Ba...99333742B
0.03 ETH
185593612023-11-13 0:18:59234 days ago1699834739
0x0A9f25Ba...99333742B
0.03 ETH
185588772023-11-12 22:41:23234 days ago1699828883
0x0A9f25Ba...99333742B
0.057 ETH
185587672023-11-12 22:19:23234 days ago1699827563
0x0A9f25Ba...99333742B
0.0665 ETH
185578122023-11-12 19:07:23235 days ago1699816043
0x0A9f25Ba...99333742B
0.01 ETH
185528952023-11-12 2:36:11235 days ago1699756571
0x0A9f25Ba...99333742B
0.057 ETH
185527592023-11-12 2:08:59235 days ago1699754939
0x0A9f25Ba...99333742B
0.038 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Arbitrator

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 20000 runs

Other Settings:
paris EvmVersion
File 1 of 15 : Arbitrator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Owned} from "solmate/auth/Owned.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol";
import {EIP712} from "openzeppelin/utils/cryptography/EIP712.sol";

import {IArbitrator} from "../interfaces/IArbitrator.sol";

contract Arbitrator is Owned, IArbitrator, EIP712 {
    using ECDSA for bytes32;

    /// Minimum random value
    uint256 private constant RAND_MIN = 1;
    /// Maximum random value
    uint256 private constant RAND_MAX = 25;
    /// Ante percentage e.g 1000 / 10000 = .1 = 10%
    uint128 private constant ANTE = 1000;
    /// Denominator for percentage values e.g 1000 / 10000 = .1 = 10%
    uint128 private constant DENOMINATOR = 10000;
    /// Maximum allowed fee percentage
    uint128 private constant MAX_FEE = 1000;
    /// Number of blocks before a given game can begin
    uint64 private constant START_DELAY = 99 seconds;
    /// TODO ? Time between turns ?
    uint64 private constant DEFAULT_CADENCE = 99 seconds;
    /// Minimum number of players in a given game
    uint8 private constant MIN_SEATS = 2;
    /// Maximum number of players in a given game
    uint8 private constant MAX_SEATS = 100;
    /// Randomness function signature
    bytes32 private constant RANDOMNESS_TYPEHASH =
        keccak256("Randomness(uint256 randomness,uint64 counter,uint256 id)");

    /// Fee receiver address
    address public feeController;
    /// Current fee percentage
    uint128 public fee = 500;
    /// Latest created game (array index)
    uint256 public currentId;
    /// Randomness supplier/signer address
    address public rngSource;

    /// Bitmasked of allowed game modes
    uint256 private modeAllowlist;
    /// Current game balances
    mapping(address => uint256) private balances;
    /// Arrays of participants based on tontine ID
    mapping(uint256 => address[]) private participants;
    /// Assets allowed for game use
    mapping(address => bool) private assetAllowlist;
    /// Tontines
    mapping(uint256 => Tontine) private tontines;
    /// Activity tracking
    mapping(bytes32 => bool) private activityLog;

    constructor(
        address _admin,
        address _feeController,
        address _rngSource
    ) Owned(_admin) EIP712("Tontine", "1") {
        feeController = _feeController;
        rngSource = _rngSource;
    }

    /// @notice Create a new Tontine
    /// @param _asset Game currency to be used
    /// @param _bet Starting bet amount
    /// @param _seats Number of players
    /// @param _betMode Lobby betting mode
    /// @param _rngMode Lobby RNG mode
    function create(
        address _asset,
        uint128 _bet,
        uint8 _seats,
        BetMode _betMode,
        RNGMode _rngMode
    ) external payable {
        if (tx.origin != msg.sender) revert NotEOA();
        if (_seats < MIN_SEATS || _seats > MAX_SEATS) revert InvalidSeatCount();
        if (_bet == 0) revert InvalidBet();
        if (!getBetModeAllowed(_betMode) || !getRNGModeAllowed(_rngMode))
            revert InvalidMode();
        if (!assetAllowlist[_asset]) revert InvalidAsset();

        Tontine storage tontine = tontines[currentId];
        tontine.asset = _asset;
        tontine.seats = _seats;
        tontine.betMode = _betMode;
        tontine.rngMode = _rngMode;
        tontine.bet = _bet;
        tontine.balance = _bet;
        tontine.participantState = 1 << 127;

        participants[currentId].push(msg.sender);
        unchecked {
            balances[_asset] += _bet;
            currentId++;
        }

        activityLog[
            _getParticipantIdentifier(msg.sender, currentId - 1)
        ] = true;

        if (_asset == address(0)) {
            if (msg.value != _bet) revert InvalidBet();
        } else {
            if (msg.value > 0) revert InvalidBet();
            ERC20(_asset).transferFrom(msg.sender, address(this), _bet);
        }

        emit Created(currentId - 1, _bet, _asset);
        emit Joined(currentId - 1, msg.sender);
    }

    /// @notice Join an existing Tontine
    /// @param _id Tontine ID
    function join(uint256 _id) external payable {
        Tontine storage tontine = tontines[_id];
        address asset = tontine.asset;
        uint128 bet = tontine.bet;

        if (tx.origin != msg.sender) revert NotEOA();
        if (tontine.lastBetTime != 0) revert AlreadyStarted();
        // If the ID doesn't exist it will revert as "Ended"
        if (tontine.participantState == 0) revert Ended();

        bytes32 participantId = _getParticipantIdentifier(msg.sender, _id);
        if (activityLog[participantId]) revert AlreadyJoined();

        uint8 participantLength = uint8(participants[_id].length);

        unchecked {
            tontine.participantState |= uint128(1 << (127 - participantLength));
            tontine.balance += bet;
            balances[asset] += bet;
        }

        participants[_id].push(msg.sender);
        activityLog[participantId] = true;

        // Start round if full
        if (++participantLength == tontine.seats) {
            // Schedule start time
            tontine.lastBetTime = uint64(block.timestamp) + START_DELAY;
            // Scramble order to keep odds uniform
            tontine.lastIndex = _scramble(tontine.seats);

            // Deduct fee on start such that participants are not charged fees when leaving
            uint128 feeDelta = tontine.balance -
                _getBetAfterFee(tontine.balance);
            unchecked {
                tontine.balance -= feeDelta;
                balances[asset] -= feeDelta;
            }

            emit Started(_id);
        }

        if (asset == address(0)) {
            if (msg.value != bet) revert InvalidBet();
        } else {
            if (msg.value != 0) revert InvalidBet();
            ERC20(asset).transferFrom(msg.sender, address(this), bet);
        }

        emit Joined(currentId, msg.sender);
    }

    /// @notice Leave a tontine lobby (that hasn't started yet)
    /// @param _id Tontine ID
    /// @param _index Participant array ID
    function leave(uint256 _id, uint8 _index) external {
        Tontine storage tontine = tontines[_id];
        address[] storage participantArray = participants[_id];

        if (tx.origin != msg.sender) revert NotEOA();
        if (tontine.lastBetTime != 0) revert AlreadyStarted();
        if (participantArray[_index] != msg.sender) revert NotJoined();

        uint128 bet = tontine.bet;
        address asset = tontine.asset;
        unchecked {
            balances[tontine.asset] -= bet;
            tontine.balance -= bet;
        }

        // Clear state
        if (participantArray.length <= 1) {
            delete participants[_id];
            delete tontines[_id];
            emit Claimed(_id, address(0), address(0), 0);
        } else {
            participantArray[_index] = participantArray[
                participantArray.length - 1
            ];
            participantArray.pop();
            tontine.participantState ^= uint128(
                1 << (127 - participantArray.length)
            );
        }

        delete activityLog[_getParticipantIdentifier(msg.sender, _id)];

        // Return funds
        if (asset == address(0)) {
            payable(msg.sender).transfer(bet);
        } else {
            ERC20(asset).transfer(msg.sender, bet);
        }

        emit Left(_id, msg.sender);
    }

    /// @notice Leave an already started game - funds will NOT be returned
    /// @param _id Tontine ID
    /// @param _index Participant array ID
    function fold(uint256 _id, uint8 _index) external {
        Tontine storage tontine = tontines[_id];
        uint64 lastBetTime = tontine.lastBetTime;

        if (tx.origin != msg.sender) revert NotEOA();
        if (lastBetTime == 0 || lastBetTime > block.timestamp)
            revert NotStarted();
        if (participants[_id][_index] != msg.sender) revert NotJoined();

        (
            uint128 participantState,
            bool ended,
            uint256 currentIndex
        ) = _updateParticipantState(
                tontine.participantState,
                tontine.lastIndex,
                lastBetTime,
                tontine.seats
            );

        if (currentIndex != _index) revert NotTurn();
        if (ended || _isLastAlive(participantState, _index)) revert Ended();
        if (!_isAlive(participantState, _index)) revert PlayerNotAlive();

        tontine.participantState = _killPlayer(participantState, _index);
        tontine.lastIndex = _index;
        tontine.lastBetTime = uint64(block.timestamp);
        ++tontine.counter;

        emit Folded(_id, msg.sender);
    }

    /// Play your turn
    /// @param _id Tontine ID
    /// @param _index Participant array ID
    /// @param _bet Bet amount in  wei
    /// @param _rng Randomness data
    /// @param _sig Signed randomness data
    function play(
        uint256 _id,
        uint8 _index,
        uint128 _bet,
        Randomness calldata _rng,
        bytes calldata _sig
    ) external payable {
        Tontine storage tontine = tontines[_id];
        uint64 lastBetTime = tontine.lastBetTime;

        if (tx.origin != msg.sender) revert NotEOA();
        if (lastBetTime == 0 || lastBetTime > block.timestamp)
            revert NotStarted();
        if (participants[_id][_index] != msg.sender) revert NotJoined();

        if (tontine.counter > 0 && tontine.rngMode == RNGMode.RANDOM) {
            uint32 counter = tontine.counter;
            if (_rng.id != _id) revert InvalidID();
            if (_rng.counter != counter) revert InvalidCounter();
            _validateRandomness(_rng, _sig);

            if (
                _shouldKillLastPlayer(
                    _id,
                    counter,
                    tontine.rngMode,
                    _rng.randomness
                )
            ) {
                tontine.participantState = _killPlayer(
                    tontine.participantState,
                    tontine.lastIndex
                );
            }
        }

        (
            uint128 participantState,
            bool ended,
            uint256 currentIndex
        ) = _updateParticipantState(
                tontine.participantState,
                tontine.lastIndex,
                lastBetTime,
                tontine.seats
            );

        if (currentIndex != _index) revert NotTurn();
        if (ended || _isLastAlive(participantState, _index)) revert Ended();

        // Handling for alternate bet modes
        if (_bet >= tontine.bet && tontine.betMode == BetMode.VARIABLE) {
            tontine.bet = _bet;
        } else if (tontine.betMode == BetMode.ANTE) {
            tontine.bet = (tontine.bet * (DENOMINATOR + ANTE)) / DENOMINATOR;
        }

        uint128 betAfterFee = _getBetAfterFee(tontine.bet);
        tontine.balance += betAfterFee;
        balances[tontine.asset] += betAfterFee;
        tontine.lastBetTime = uint64(block.timestamp);
        tontine.lastIndex = _index;
        tontine.participantState = participantState;
        ++tontine.counter;

        if (tontine.asset == address(0)) {
            if (msg.value != tontine.bet) revert InvalidBet();
        } else {
            if (msg.value > 0) revert InvalidBet();
            ERC20(tontine.asset).transferFrom(
                msg.sender,
                address(this),
                tontine.bet
            );
        }

        emit Played(_id, msg.sender, tontine.bet);
    }

    /// @notice Claim winnings when you're the last person standing
    /// @param _id Tontine ID
    /// @param _index Participant array ID
    /// @param _rng Randomness data
    /// @param _sig Signed randomness data
    function claim(
        uint256 _id,
        uint8 _index,
        Randomness calldata _rng,
        bytes calldata _sig
    ) external {
        Tontine storage tontine = tontines[_id];
        uint64 lastBetTime = tontine.lastBetTime;

        if (tx.origin != msg.sender) revert NotEOA();
        if (tontine.participantState == 0) revert AlreadyClaimed();
        if (lastBetTime == 0 || lastBetTime > block.timestamp)
            revert NotStarted();
        if (participants[_id][_index] != msg.sender) revert NotJoined();

        if (tontine.counter > 0 && tontine.rngMode == RNGMode.RANDOM) {
            uint32 counter = tontine.counter;
            if (_rng.id != _id) revert InvalidID();
            if (_rng.counter != counter) revert InvalidCounter();
            _validateRandomness(_rng, _sig);

            if (
                _shouldKillLastPlayer(
                    _id,
                    counter,
                    tontine.rngMode,
                    _rng.randomness
                )
            ) {
                tontine.participantState = _killPlayer(
                    tontine.participantState,
                    tontine.lastIndex
                );
            }
        }

        (uint128 participantState, bool ended, ) = _updateParticipantState(
            tontine.participantState,
            tontine.lastIndex,
            lastBetTime,
            tontine.seats
        );

        if (!_isAlive(participantState, _index)) revert PlayerNotAlive();
        if (!ended && !_isLastAlive(participantState, _index)) revert Running();

        uint128 amount = tontine.balance;

        tontine.balance = 0;
        unchecked {
            balances[tontine.asset] -= amount;
        }
        address asset = tontine.asset;

        delete tontines[_id];
        delete participants[_id];

        // Return funds
        if (asset == address(0)) {
            payable(msg.sender).transfer(amount);
        } else {
            ERC20(asset).transfer(msg.sender, amount);
        }

        emit Claimed(_id, msg.sender, asset, amount);
    }

    /// @notice Collect and send held fees to controller
    /// @dev Use zero address for native currency
    /// @param _asset Asset to be collected
    function collectFees(address _asset) external {
        if (!assetAllowlist[_asset]) revert InvalidAsset();

        if (_asset == address(0)) {
            payable(feeController).transfer(
                address(this).balance - balances[_asset]
            );
        } else {
            ERC20(_asset).transfer(
                feeController,
                ERC20(_asset).balanceOf(address(this)) - balances[_asset]
            );
        }
    }

    /// @notice Sets a new controller address
    /// @param _feeController New controller address
    function setFeeController(address _feeController) external {
        if (msg.sender != feeController) revert InvalidCaller();
        feeController = _feeController;
    }

    /// @notice Sets a new fee
    /// @dev Fee is determined by division e.g 500 / 10000 = .05 = 5%
    /// @param _fee New fee percentage
    function setFee(uint128 _fee) external {
        if (msg.sender != feeController) revert InvalidCaller();
        if (_fee > MAX_FEE) revert MaxFeeExceeded();
        fee = _fee;
    }

    /// @notice White/blacklists a given asset
    /// @dev Use  zero address for native currency
    /// @param _asset Asset to be allowed/disallowed
    /// @param _allowed Is this asset allowed?
    function setAssetAllowlist(
        address _asset,
        bool _allowed
    ) external onlyOwner {
        assetAllowlist[_asset] = _allowed;
    }

    /// @notice Sets a new bitmask of allowed game modes
    /// @param _allowlist New mode allow list value
    function setModeAllowlist(uint256 _allowlist) external onlyOwner {
        modeAllowlist = _allowlist;
    }

    /// @notice Sets the signer that is in charge of supplying randomness
    /// @param _rngSource New signer address
    function setRNGSource(address _rngSource) external onlyOwner {
        rngSource = _rngSource;
    }

    /// @notice Helper to more easily determine if a bet mode is allowed as opposed to shifting bitmask
    /// @param _mode Betmode enum value
    /// @return Is the supplied mode allowed?
    function getBetModeAllowed(
        BetMode _mode
    ) public view override returns (bool) {
        return ((modeAllowlist >> uint8(_mode)) & 1) == 1;
    }

    /// @notice Helper to more easily determine if a bet mode is allowed as opposed to shifting bitmask
    /// @param _mode RNG enum value
    /// @return Is the supplied mode allowed?
    function getRNGModeAllowed(
        RNGMode _mode
    ) public view override returns (bool) {
        return ((modeAllowlist >> (uint8(_mode) + 128)) & 1) == 1;
    }

    /// TODO: Is this needed? Appears unused.  Document if needed
    function getRandomness(
        uint256 _id,
        uint64 _counter
    ) public pure override returns (uint256) {
        return (uint256(keccak256(abi.encodePacked(_id, _counter))) % RAND_MAX);
    }

    /// Participant state for a given lobby
    /// @param _id Tontine ID
    /// @param _rng Randomness data
    /// @return Participant state
    /// @return Last player index
    /// @return Seats in tontine
    function getParticipantState(
        uint256 _id,
        Randomness calldata _rng
    ) public view override returns (uint128, bool, uint256) {
        Tontine memory tontine = tontines[_id];
        uint64 lastBetTime = tontine.lastBetTime;

        if (lastBetTime == 0) {
            return (tontine.participantState, false, 0);
        }

        if (tontine.counter > 0 && tontine.rngMode == RNGMode.RANDOM) {
            if (
                _shouldKillLastPlayer(
                    _id,
                    tontine.counter,
                    tontine.rngMode,
                    _rng.randomness
                )
            ) {
                tontine.participantState = _killPlayer(
                    tontine.participantState,
                    tontine.lastIndex
                );
            }
        }

        return
            _updateParticipantState(
                tontine.participantState,
                tontine.lastIndex,
                lastBetTime,
                tontine.seats
            );
    }

    /// TODO: Duplicate function, not exactly necessary.  Change internal visibility on original (ln 600)
    function getParticipantIdentifier(
        address _participant,
        uint256 _id
    ) external pure override returns (bytes32) {
        return _getParticipantIdentifier(_participant, _id);
    }

    /// Gets amount after fees
    /// @dev Use WEI
    /// @param _amount Amount in
    /// @return Amount out
    function getAmountAfterFee(
        uint256 _amount
    ) external view override returns (uint256) {
        return (_amount * (DENOMINATOR - fee)) / DENOMINATOR;
    }

    /// Gets a tontine
    /// @param _id Tontine ID
    function getTontine(
        uint256 _id
    ) external view override returns (Tontine memory) {
        return (tontines[_id]);
    }

    /// Checks if a given address/participant is still active
    /// @param _participant Participant address
    /// @param _id Tontine ID
    /// @return Is participant active?
    function isActive(
        address _participant,
        uint256 _id
    ) external view override returns (bool) {
        return activityLog[_getParticipantIdentifier(_participant, _id)];
    }

    /// Gets balance of supplied token address (contract accounting)
    /// @dev Amount returned in WEI
    /// @dev Use zero address for base currency
    /// @param _token Token address
    function getBalance(
        address _token
    ) external view override returns (uint256) {
        return balances[_token];
    }

    /// Gets address for supplied IDs
    /// @param _id Tontine ID
    /// @param _index Participant array ID
    /// @return Participant address
    function getParticipant(
        uint256 _id,
        uint256 _index
    ) external view override returns (address) {
        return participants[_id][_index];
    }

    /// Gets all participants in a given lobby
    /// @param _id Tontine ID
    /// @return Array of participants
    function getParticipants(
        uint256 _id
    ) external view override returns (address[] memory) {
        return participants[_id];
    }

    /// Checks if an asset is allowed for game
    /// @dev Use zero address for base currency
    /// @param _asset Asset/token address
    /// @return Is asset allowed?
    function getAssetAllowed(
        address _asset
    ) external view override returns (bool) {
        return assetAllowlist[_asset];
    }

    /// Hashes randomness data
    /// @param _rng Randomness data
    /// @return Hashed randomness data
    function randomnessHash(
        Randomness memory _rng
    ) public view returns (bytes32) {
        return
            _hashTypedDataV4(
                keccak256(
                    abi.encode(
                        RANDOMNESS_TYPEHASH,
                        _rng.randomness,
                        _rng.counter,
                        _rng.id
                    )
                )
            );
    }

    /// Validates signed randomness data
    /// @param _rng Randomness data
    /// @param _sig Signed randomness data
    function _validateRandomness(
        Randomness memory _rng,
        bytes memory _sig
    ) internal view {
        bytes32 hash = randomnessHash(_rng);
        address signer = ECDSA.recover(hash, _sig);
        if (signer != rngSource) revert InvalidSigner();
    }

    /// Shuffle index
    /// @param _range Number of seats/players
    /// @return Shuffled index within bounds
    function _scramble(uint8 _range) internal view returns (uint8) {
        return
            uint8(
                uint256(
                    keccak256(
                        abi.encodePacked(block.prevrandao, block.timestamp)
                    )
                )
            ) % _range;
    }

    /// TODO: Same as ln 432.  Remove?
    function _getBetAfterFee(uint128 _amount) internal view returns (uint128) {
        return (_amount * (DENOMINATOR - fee)) / DENOMINATOR;
    }

    /// Checks if player is alive
    /// @param _participantState Participant state bitmask
    /// @param _index Participant array ID
    /// @return Is player alive?
    function _isAlive(
        uint128 _participantState,
        uint8 _index
    ) internal pure returns (bool) {
        uint128 liveMask = uint128(1 << (127 - _index));

        return (liveMask & _participantState) != 0;
    }

    /// Checks if player is last alive
    /// @param _participantState Participant state bitmask
    /// @param _index Participant array ID
    function _isLastAlive(
        uint128 _participantState,
        uint8 _index
    ) internal pure returns (bool) {
        return (_killPlayer(_participantState, _index) == 0);
    }

    /// Kills a designated player
    /// @param _participantState Participant state bitmask
    /// @param _index Participant array ID
    /// @return Updated state with designated player killed
    function _killPlayer(
        uint128 _participantState,
        uint8 _index
    ) internal pure returns (uint128) {
        uint128 deadMask = ~uint128(1 << (127 - _index));

        return deadMask & _participantState;
    }

    /// Updates tontine participants state
    /// @param _participantState Participant state bitmask
    /// @param _lastIndex Last player index on participant state
    /// @param _lastBetTime Last time a bet was received, or when game started
    /// @param _seats Number of players/seats in lobby
    /// @return Participant state
    /// @return Is last alive?
    /// @return Next player index
    function _updateParticipantState(
        uint128 _participantState,
        uint64 _lastIndex,
        uint64 _lastBetTime,
        uint8 _seats
    ) internal view returns (uint128, bool, uint256) {
        if (_seats == 0) return (0, false, 0);
        if (_lastBetTime > block.timestamp)
            return (_participantState, false, (_lastIndex + 1) % _seats);

        uint256 iterations = (block.timestamp - _lastBetTime) / DEFAULT_CADENCE;

        for (uint256 i = 1; i <= iterations; i++) {
            uint8 index = uint8((_lastIndex + i) % _seats);

            if (_isAlive(_participantState, index)) {
                if (_isLastAlive(_participantState, index)) {
                    return (_participantState, true, index);
                } else {
                    _participantState = _killPlayer(_participantState, index);
                }
            } else {
                unchecked {
                    ++iterations;
                }
            }
        }

        uint256 newIndex = (_lastIndex + iterations + 1) % _seats;

        return (
            _participantState,
            _isLastAlive(_participantState, uint8(newIndex)),
            newIndex
        );
    }

    /// Check if last player should be killed
    /// @param _id Participant array ID
    /// @param _counter RNG counter
    /// @param _rngMode RNG enum value
    /// @param randomness Randomness int
    /// @return Should last player be killed?
    function _shouldKillLastPlayer(
        uint256 _id,
        uint64 _counter,
        RNGMode _rngMode,
        uint256 randomness
    ) internal pure returns (bool) {
        if (_rngMode == RNGMode.RANDOM) {
            uint256 seed = uint256(keccak256(abi.encodePacked(randomness)));

            uint256 range = (uint256(
                keccak256(abi.encodePacked(_id, _counter))
            ) % RAND_MAX);

            return (seed % 100) <= range;
        }

        return false;
    }

    /// Get hashed identifier
    /// @param _participant Participant/player address
    /// @param _id Participant array ID
    /// @return Participant identifier
    function _getParticipantIdentifier(
        address _participant,
        uint256 _id
    ) internal pure returns (bytes32) {
        return keccak256(abi.encode(_participant, _id));
    }

    /// @dev Prevent direct sending of funds
    receive() external payable {
        revert();
    }

    /// @dev default
    fallback() external {
        revert();
    }
}

File 2 of 15 : Owned.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

File 3 of 15 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 4 of 15 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 5 of 15 : EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _name.toStringWithFallback(_nameFallback),
            _version.toStringWithFallback(_versionFallback),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }
}

File 6 of 15 : IArbitrator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IArbitratorErrors} from "./IArbitratorErrors.sol";
import {IArbitratorEvents} from "./IArbitratorEvents.sol";
import {IArbitratorData} from "./IArbitratorData.sol";

interface IArbitrator is IArbitratorErrors, IArbitratorEvents, IArbitratorData {
    /*////////////////////////////////////////////////////////////// 
                                  Core                          
    //////////////////////////////////////////////////////////////*/

    function create(
        address _asset,
        uint128 _bet,
        uint8 _seats,
        BetMode _betMode,
        RNGMode _rngMode
    ) external payable;

    function join(uint256 _id) external payable;

    function leave(uint256 _id, uint8 _index) external;

    function fold(uint256 _id, uint8 _index) external;

    function play(
        uint256 _id,
        uint8 _index,
        uint128 _bet,
        Randomness calldata _rng,
        bytes calldata _sig
    ) external payable;

    function claim(
        uint256 _id,
        uint8 _index,
        Randomness calldata _rng,
        bytes calldata _sig
    ) external;

    function collectFees(address _asset) external;

    /*//////////////////////////////////////////////////////////////
                                  Views
    //////////////////////////////////////////////////////////////*/

    function rngSource() external view returns (address);

    function fee() external view returns (uint128);

    function currentId() external view returns (uint256);

    function getBalance(address _token) external view returns (uint256);

    function getParticipant(
        uint256 _id,
        uint256 _index
    ) external view returns (address);

    function getParticipants(
        uint256 _id
    ) external view returns (address[] memory);

    function getAssetAllowed(address _asset) external view returns (bool);

    function getBetModeAllowed(BetMode _mode) external view returns (bool);

    function getRNGModeAllowed(RNGMode _mode) external view returns (bool);

    function getRandomness(
        uint256 _id,
        uint64 _counter
    ) external view returns (uint256);

    function getTontine(uint256 _id) external view returns (Tontine memory);

    function getAmountAfterFee(uint256 _amount) external view returns (uint256);

    function isActive(
        address _participant,
        uint256 _id
    ) external view returns (bool);

    function getParticipantState(
        uint256 _id,
        Randomness calldata _rng
    ) external view returns (uint128, bool, uint256);

    function getParticipantIdentifier(
        address _participant,
        uint256 _id
    ) external pure returns (bytes32);
}

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

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

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

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

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

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

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

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

File 8 of 15 : ShortStrings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

File 9 of 15 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

File 10 of 15 : IArbitratorErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IArbitratorErrors {
    /*////////////////////////////////////////////////////////////// 
                                 Errors                              
    //////////////////////////////////////////////////////////////*/

    /// @notice caller not EOA.
    error NotEOA();

    /// @notice invalid asset.
    error InvalidAsset();

    /// @notice invalid caller.
    error InvalidCaller();

    /// @notice provided fee greater than maximum allowed.
    error MaxFeeExceeded();

    /// @notice number of seats are invalid.
    error InvalidSeatCount();

    /// @notice invalid bet size.
    error InvalidBet();

    /// @notice invalid mode.
    error InvalidMode();

    /// @notice game already started.
    error AlreadyStarted();

    /// @notice game not started.
    error NotStarted();

    /// @notice game already joined.
    error AlreadyJoined();

    /// @notice game not joined.
    error NotJoined();

    /// @notice not player's turn.
    error NotTurn();

    /// @notice game ended.
    error Ended();

    /// @notice game running.
    error Running();

    /// @notice player no longer participating.
    error PlayerNotAlive();

    /// @notice already claimed.
    error AlreadyClaimed();

    /// @notice invalid signer.
    error InvalidSigner();

    /// @notice invalid signer.
    error InvalidID();

    /// @notice invalid signer.
    error InvalidCounter();
}

File 11 of 15 : IArbitratorEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IArbitratorEvents {
    /*////////////////////////////////////////////////////////////// 
                                 Events                              
    //////////////////////////////////////////////////////////////*/

    event Created(
        uint256 indexed id,
        uint256 indexed bet,
        address indexed asset
    );

    event Joined(uint256 indexed id, address indexed participant);

    event Left(uint256 indexed id, address indexed participant);

    event Played(uint256 indexed id, address indexed participant, uint128 bet);

    event Folded(uint256 indexed id, address indexed participant);

    event Claimed(
        uint256 indexed id,
        address indexed participant,
        address indexed asset,
        uint128 amount
    );

    event Started(uint256 indexed id);
}

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

interface IArbitratorData {
    /*//////////////////////////////////////////////////////////////
                             Data Structures
    //////////////////////////////////////////////////////////////*/

    enum BetMode {
        // Fixed bet size
        CLASSIC,
        // Bet size is strictly increasing and determined by the player
        VARIABLE,
        // Bet size is increasing linearly
        ANTE
    }

    enum RNGMode {
        // No odds of death
        ZERO,
        // Odds of death randomly selected between RAND_MIN and RAND_MAX
        RANDOM
    }

    struct Randomness {
        uint256 randomness;
        uint64 counter;
        uint256 id;
    }

    struct Tontine {
        // Asset used for Tontine
        address asset;
        // Available seats
        uint8 seats;
        // Bet Mode
        BetMode betMode;
        // RNG Mode
        RNGMode rngMode;
        // Balance
        uint128 balance;
        // Current bet amount
        uint128 bet;
        // State of all participants
        uint128 participantState;
        // Last time a bet was received, or game started
        uint64 lastBetTime;
        // Counter for rng
        uint32 counter;
        // Last player index on participant state
        uint8 lastIndex;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

File 15 of 15 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 20000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_feeController","type":"address"},{"internalType":"address","name":"_rngSource","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"AlreadyJoined","type":"error"},{"inputs":[],"name":"AlreadyStarted","type":"error"},{"inputs":[],"name":"Ended","type":"error"},{"inputs":[],"name":"InvalidAsset","type":"error"},{"inputs":[],"name":"InvalidBet","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidCounter","type":"error"},{"inputs":[],"name":"InvalidID","type":"error"},{"inputs":[],"name":"InvalidMode","type":"error"},{"inputs":[],"name":"InvalidSeatCount","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"MaxFeeExceeded","type":"error"},{"inputs":[],"name":"NotEOA","type":"error"},{"inputs":[],"name":"NotJoined","type":"error"},{"inputs":[],"name":"NotStarted","type":"error"},{"inputs":[],"name":"NotTurn","type":"error"},{"inputs":[],"name":"PlayerNotAlive","type":"error"},{"inputs":[],"name":"Running","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"participant","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"bet","type":"uint256"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"Created","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"participant","type":"address"}],"name":"Folded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"participant","type":"address"}],"name":"Joined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"participant","type":"address"}],"name":"Left","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"participant","type":"address"},{"indexed":false,"internalType":"uint128","name":"bet","type":"uint128"}],"name":"Played","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Started","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint8","name":"_index","type":"uint8"},{"components":[{"internalType":"uint256","name":"randomness","type":"uint256"},{"internalType":"uint64","name":"counter","type":"uint64"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct IArbitratorData.Randomness","name":"_rng","type":"tuple"},{"internalType":"bytes","name":"_sig","type":"bytes"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"collectFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint128","name":"_bet","type":"uint128"},{"internalType":"uint8","name":"_seats","type":"uint8"},{"internalType":"enum IArbitratorData.BetMode","name":"_betMode","type":"uint8"},{"internalType":"enum IArbitratorData.RNGMode","name":"_rngMode","type":"uint8"}],"name":"create","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"currentId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint8","name":"_index","type":"uint8"}],"name":"fold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getAmountAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"getAssetAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IArbitratorData.BetMode","name":"_mode","type":"uint8"}],"name":"getBetModeAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getParticipant","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_participant","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getParticipantIdentifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"components":[{"internalType":"uint256","name":"randomness","type":"uint256"},{"internalType":"uint64","name":"counter","type":"uint64"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct IArbitratorData.Randomness","name":"_rng","type":"tuple"}],"name":"getParticipantState","outputs":[{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getParticipants","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IArbitratorData.RNGMode","name":"_mode","type":"uint8"}],"name":"getRNGModeAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint64","name":"_counter","type":"uint64"}],"name":"getRandomness","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getTontine","outputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint8","name":"seats","type":"uint8"},{"internalType":"enum IArbitratorData.BetMode","name":"betMode","type":"uint8"},{"internalType":"enum IArbitratorData.RNGMode","name":"rngMode","type":"uint8"},{"internalType":"uint128","name":"balance","type":"uint128"},{"internalType":"uint128","name":"bet","type":"uint128"},{"internalType":"uint128","name":"participantState","type":"uint128"},{"internalType":"uint64","name":"lastBetTime","type":"uint64"},{"internalType":"uint32","name":"counter","type":"uint32"},{"internalType":"uint8","name":"lastIndex","type":"uint8"}],"internalType":"struct IArbitratorData.Tontine","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_participant","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"join","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint8","name":"_index","type":"uint8"}],"name":"leave","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint8","name":"_index","type":"uint8"},{"internalType":"uint128","name":"_bet","type":"uint128"},{"components":[{"internalType":"uint256","name":"randomness","type":"uint256"},{"internalType":"uint64","name":"counter","type":"uint64"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct IArbitratorData.Randomness","name":"_rng","type":"tuple"},{"internalType":"bytes","name":"_sig","type":"bytes"}],"name":"play","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"randomness","type":"uint256"},{"internalType":"uint64","name":"counter","type":"uint64"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct IArbitratorData.Randomness","name":"_rng","type":"tuple"}],"name":"randomnessHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rngSource","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setAssetAllowlist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_fee","type":"uint128"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeController","type":"address"}],"name":"setFeeController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_allowlist","type":"uint256"}],"name":"setModeAllowlist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rngSource","type":"address"}],"name":"setRNGSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

610160604052600480546001600160801b0319166101f41790553480156200002657600080fd5b506040516200554738038062005547833981016040819052620000499162000258565b6040805180820182526007815266546f6e74696e6560c81b6020808301919091528251808401845260018152603160f81b91810191909152600080546001600160a01b0319166001600160a01b0388169081178255935192939192879291907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350620000db826001620001b6565b61012052620000ec816002620001b6565b61014052815160208084019190912060e052815190820120610100524660a0526200017a60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c052600380546001600160a01b039384166001600160a01b031991821617909155600680549290931691161790555062000488565b6000602083511015620001d657620001ce83620001ef565b9050620001e9565b81620001e3848262000347565b5060ff90505b92915050565b600080829050601f8151111562000226578260405163305a27a960e01b81526004016200021d919062000413565b60405180910390fd5b8051620002338262000463565b179392505050565b80516001600160a01b03811681146200025357600080fd5b919050565b6000806000606084860312156200026e57600080fd5b62000279846200023b565b925062000289602085016200023b565b915062000299604085016200023b565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620002cd57607f821691505b602082108103620002ee57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200034257600081815260208120601f850160051c810160208610156200031d5750805b601f850160051c820191505b818110156200033e5782815560010162000329565b5050505b505050565b81516001600160401b03811115620003635762000363620002a2565b6200037b81620003748454620002b8565b84620002f4565b602080601f831160018114620003b357600084156200039a5750858301515b600019600386901b1c1916600185901b1785556200033e565b600085815260208120601f198616915b82811015620003e457888601518255948401946001909101908401620003c3565b5085821015620004035787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208083528351808285015260005b81811015620004425785810183015185820160400152820162000424565b506000604082860101526040601f19601f8301168501019250505092915050565b80516020808301519190811015620002ee5760001960209190910360031b1b16919050565b60805160a05160c05160e051610100516101205161014051615064620004e3600039600061225201526000612227015260006141ca015260006141a2015260006140fd015260006141270152600061415101526150646000f3fe6080604052600436106101e75760003560e01c806384b0196e11610102578063ddca3f4311610095578063f2fde38b11610064578063f2fde38b14610687578063f6927e7d146106a7578063f8b2cb4f146106c7578063feff17441461070a576101f1565b8063ddca3f43146105f4578063e00dd1611461063e578063e3d5c08c14610654578063ed64346214610674576101f1565b8063a480ca79116100d1578063a480ca7914610507578063c1e3bd3e14610527578063d266e83b14610554578063dc6f867c146105c7576101f1565b806384b0196e1461047f5780638da5cb5b146104a75780639738f87b146104d4578063a06412ac146104e7576101f1565b80633687f24a1161017a57806358db40d81161014957806358db40d8146103f2578063681b950d146104125780636999b3771461043257806379fa35071461045f576101f1565b80633687f24a146103655780633ed4c678146103855780634266580a146103a5578063450bde78146103d2576101f1565b80631aed59f0116101b65780631aed59f0146102775780631f6dc875146102c15780631fc6a5db1461031757806334fe228714610345576101f1565b806301fe1fdc14610202578063049878f3146102245780630ca522081461023757806314c9216a14610257576101f1565b366101f157600080fd5b3480156101fd57600080fd5b600080fd5b34801561020e57600080fd5b5061022261021d366004614590565b610758565b005b6102226102323660046145bc565b610cdd565b34801561024357600080fd5b506102226102523660046145bc565b611313565b34801561026357600080fd5b50610222610272366004614590565b61139e565b34801561028357600080fd5b506102976102923660046145d5565b611763565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156102cd57600080fd5b506103076102dc36600461461b565b73ffffffffffffffffffffffffffffffffffffffff166000908152600a602052604090205460ff1690565b60405190151581526020016102b8565b34801561032357600080fd5b506103376103323660046145bc565b6117ae565b6040519081526020016102b8565b34801561035157600080fd5b50610307610360366004614645565b6117fa565b34801561037157600080fd5b50610222610380366004614680565b611823565b34801561039157600080fd5b506102226103a036600461461b565b611905565b3480156103b157600080fd5b506006546102979073ffffffffffffffffffffffffffffffffffffffff1681565b3480156103de57600080fd5b506103376103ed3660046146b3565b61199d565b3480156103fe57600080fd5b5061022261040d36600461461b565b611a0c565b34801561041e57600080fd5b5061022261042d366004614730565b611ad4565b34801561043e57600080fd5b506003546102979073ffffffffffffffffffffffffffffffffffffffff1681565b34801561046b57600080fd5b5061030761047a3660046147af565b6121fa565b34801561048b57600080fd5b50610494612219565b6040516102b8979695949392919061482e565b3480156104b357600080fd5b506000546102979073ffffffffffffffffffffffffffffffffffffffff1681565b6102226104e23660046148ed565b6122be565b3480156104f357600080fd5b50610337610502366004614952565b6128df565b34801561051357600080fd5b5061022261052236600461461b565b612927565b34801561053357600080fd5b506105476105423660046145bc565b612b56565b6040516102b8919061497c565b34801561056057600080fd5b5061030761056f366004614952565b6040805173ffffffffffffffffffffffffffffffffffffffff9390931660208085019190915283820192909252805180840382018152606090930181528251928201929092206000908152600c909152205460ff1690565b3480156105d357600080fd5b506105e76105e23660046145bc565b612bcf565b6040516102b89190614a29565b34801561060057600080fd5b5060045461061d906fffffffffffffffffffffffffffffffff1681565b6040516fffffffffffffffffffffffffffffffff90911681526020016102b8565b34801561064a57600080fd5b5061033760055481565b34801561066057600080fd5b5061033761066f366004614b34565b612dbf565b610222610682366004614bbc565b612e42565b34801561069357600080fd5b506102226106a236600461461b565b6137aa565b3480156106b357600080fd5b506102226106c2366004614c4a565b61389b565b3480156106d357600080fd5b506103376106e236600461461b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526008602052604090205490565b34801561071657600080fd5b5061072a610725366004614c81565b613972565b604080516fffffffffffffffffffffffffffffffff90941684529115156020840152908201526060016102b8565b6000828152600b6020908152604080832060099092529091203233146107aa576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002820154700100000000000000000000000000000000900467ffffffffffffffff1615610804576040517f1fbde44500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff16818460ff168154811061083057610830614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1614610889576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018281018054845473ffffffffffffffffffffffffffffffffffffffff16600081815260086020526040902080547001000000000000000000000000000000009093046fffffffffffffffffffffffffffffffff9081169384900390915583547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081169082168490039091161790925583549092106109d957600086815260096020526040812061093a91614548565b6000868152600b6020908152604080832080547fffffffffffffffffff00000000000000000000000000000000000000000000001681556001810184905560020180547fffffff000000000000000000000000000000000000000000000000000000000016905551828152829189917f4fc9df21c274d2396432490df6fcda723ce987bc1b640015d34b26ca0ed98063910160405180910390a4610b41565b825483906109e990600190614d03565b815481106109f9576109f9614ca5565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16838660ff1681548110610a3957610a39614ca5565b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082805480610a9157610a91614d16565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190558254610af690607f614d03565b6002850180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008116600190931b6fffffffffffffffffffffffffffffffff91821618169190911790555b6040805133602080830191909152818301899052825180830384018152606090920183528151918101919091206000908152600c9091522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905573ffffffffffffffffffffffffffffffffffffffff8116610bfe5760405133906fffffffffffffffffffffffffffffffff841680156108fc02916000818181858888f19350505050158015610bf8573d6000803e3d6000fd5b50610ca8565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526fffffffffffffffffffffffffffffffff8316602482015273ffffffffffffffffffffffffffffffffffffffff82169063a9059cbb906044016020604051808303816000875af1158015610c82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca69190614d45565b505b604051339087907ff9b63d6ab6b86370bd291baf2443198e10294bc1a369e21ff12b34b904cb0fff90600090a3505050505050565b6000818152600b602052604090208054600182015473ffffffffffffffffffffffffffffffffffffffff9091169070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16333214610d6a576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002830154700100000000000000000000000000000000900467ffffffffffffffff1615610dc4576040517f1fbde44500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028301546fffffffffffffffffffffffffffffffff16600003610e14576040517f477383f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805133602080830191909152818301879052825180830384018152606090920183528151918101919091206000818152600c9092529190205460ff1615610e88576040517e3b268200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260096020908152604080832080546002890180546fffffffffffffffffffffffffffffffff600160ff607f869003811682901b8316838516177fffffffffffffffffffffffffffffffff00000000000000000000000000000000948516179094558c810180548084168d01841694169390931790925573ffffffffffffffffffffffffffffffffffffffff8b168852600887528588208054918b169190910190558354808201855593875285872090930180547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055868652600c9094529190932080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909317909255855474010000000000000000000000000000000000000000900416610fc082614d62565b91508160ff160361118957610fd6606342614d81565b60028601805467ffffffffffffffff92909216700100000000000000000000000000000000027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff909216919091179055845461104d9060ff7401000000000000000000000000000000000000000090910416613bde565b60028601805460ff929092167c0100000000000000000000000000000000000000000000000000000000027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905560018501546000906110c5906fffffffffffffffffffffffffffffffff16613c20565b60018701546110e691906fffffffffffffffffffffffffffffffff16614da9565b6001870180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff91821684900382161790915573ffffffffffffffffffffffffffffffffffffffff87166000908152600860205260408082208054938516909303909255905191925088917e6e0c97de781a7389d44ba8fd35d1467cabb17ed04d038d166d34ab819213f39190a2505b73ffffffffffffffffffffffffffffffffffffffff84166111f457826fffffffffffffffffffffffffffffffff1634146111ef576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112dc565b341561122c576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526fffffffffffffffffffffffffffffffff8416604482015273ffffffffffffffffffffffffffffffffffffffff8516906323b872dd906064016020604051808303816000875af11580156112b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112da9190614d45565b505b6005546040513391907fc0081eeafa0d002abcd7d58d84b6e688d1526b9c3d915a282212ba08c49a17a790600090a3505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600755565b6000828152600b602052604090206002810154700100000000000000000000000000000000900467ffffffffffffffff16333214611408576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811615806114285750428167ffffffffffffffff16115b1561145f576040517f6f312cbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152600960205260409020805433919060ff861690811061148557611485614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16146114de576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600282015482546000918291829161154b916fffffffffffffffffffffffffffffffff82169160ff7c010000000000000000000000000000000000000000000000000000000090910481169188917401000000000000000000000000000000000000000090910416613c5a565b9250925092508560ff16811461158d576040517fc6e1dd1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818061159e575061159e8387613dbd565b156115d5576040517f477383f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115df8387613de3565b611615576040517ff6fa148200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61161f8387613e1c565b6002860180546fffffffffffffffffffffffffffffffff929092167fffffff00ffffffffffffffffffffffff00000000000000000000000000000000909216919091177c010000000000000000000000000000000000000000000000000000000060ff891602177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004267ffffffffffffffff1602178082556018906116f8907801000000000000000000000000000000000000000000000000900463ffffffff16614dd2565b91906101000a81548163ffffffff021916908363ffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16877f537f4d0c896772cdfaaac608ab3c4705fe48cc44b3a2e63451b4a7eb9808ba1b60405160405180910390a350505050505050565b600082815260096020526040812080548390811061178357611783614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690505b92915050565b600454600090612710906117d4906fffffffffffffffffffffffffffffffff1682614da9565b6117f0906fffffffffffffffffffffffffffffffff1684614df5565b6117a89190614e3b565b600081600281111561180e5761180e6149d6565b60ff16600754901c6001166001149050919050565b60035473ffffffffffffffffffffffffffffffffffffffff163314611874576040517f48f5c3ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e86fffffffffffffffffffffffffffffffff821611156118c2576040517ff4df6ae500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b60035473ffffffffffffffffffffffffffffffffffffffff163314611956576040517f48f5c3ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000601983836040516020016119e292919091825260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016602082015260280190565b6040516020818303038152906040528051906020012060001c611a059190614e4f565b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611a8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401611390565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000858152600b602052604090206002810154700100000000000000000000000000000000900467ffffffffffffffff16333214611b3e576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201546fffffffffffffffffffffffffffffffff16600003611b8e576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81161580611bae5750428167ffffffffffffffff16115b15611be5576040517f6f312cbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000878152600960205260409020805433919060ff8916908110611c0b57611c0b614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1614611c64576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201547801000000000000000000000000000000000000000000000000900463ffffffff1615801590611cc8575060018254760100000000000000000000000000000000000000000000900460ff166001811115611cc657611cc66149d6565b145b15611e9b5760028201547801000000000000000000000000000000000000000000000000900463ffffffff1660408601358814611d31576040517ff5d2101e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8116611d486040880160208901614e63565b67ffffffffffffffff1614611d89576040517f63e0897a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dd7611d9b36889003880188614b34565b86868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e3f92505050565b8254611e0b90899063ffffffff841690760100000000000000000000000000000000000000000000900460ff168935613eb5565b15611e99576002830154611e55906fffffffffffffffffffffffffffffffff8116907c0100000000000000000000000000000000000000000000000000000000900460ff16613e1c565b6002840180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff929092169190911790555b505b600282015482546000918291611f05916fffffffffffffffffffffffffffffffff81169160ff7c0100000000000000000000000000000000000000000000000000000000909204821691879174010000000000000000000000000000000000000000900416613c5a565b5091509150611f148289613de3565b611f4a576040517ff6fa148200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015611f5f5750611f5d8289613dbd565b155b15611f96576040517fa63065ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180850180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008116909155855473ffffffffffffffffffffffffffffffffffffffff908116600090815260086020908152604080832080546fffffffffffffffffffffffffffffffff90961695869003905589548f8452600b835281842080547fffffffffffffffffff0000000000000000000000000000000000000000000000168155968701849055600290960180547fffffff00000000000000000000000000000000000000000000000000000000001690556009909152812092939091169161208391614548565b73ffffffffffffffffffffffffffffffffffffffff81166120e25760405133906fffffffffffffffffffffffffffffffff841680156108fc02916000818181858888f193505050501580156120dc573d6000803e3d6000fd5b5061218c565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526fffffffffffffffffffffffffffffffff8316602482015273ffffffffffffffffffffffffffffffffffffffff82169063a9059cbb906044016020604051808303816000875af1158015612166573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218a9190614d45565b505b6040516fffffffffffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff82169033908d907f4fc9df21c274d2396432490df6fcda723ce987bc1b640015d34b26ca0ed980639060200160405180910390a45050505050505050505050565b600081600181111561220e5761220e6149d6565b61180e906080614e7e565b60006060808280808361224d7f00000000000000000000000000000000000000000000000000000000000000006001613f8d565b6122787f00000000000000000000000000000000000000000000000000000000000000006002613f8d565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b3233146122f7576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600260ff8416108061230c5750606460ff8416115b15612343576040517f59e59f3f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836fffffffffffffffffffffffffffffffff1660000361238f576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612398826117fa565b15806123aa57506123a8816121fa565b155b156123e1576040517fa0042b1700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166000908152600a602052604090205460ff16612440576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546000908152600b60205260409020805460ff851674010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090911673ffffffffffffffffffffffffffffffffffffffff88161717808255839082907fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000008360028111156124fd576124fd6149d6565b02179055508054829082907fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff16760100000000000000000000000000000000000000000000836001811115612554576125546149d6565b02179055506fffffffffffffffffffffffffffffffff851670010000000000000000000000000000000081027fffffffffffffffffffffffffffffffff0000000000000000000000000000000090811682176001808501919091556002840180549092166f8000000000000000000000000000000017909155600580546000908152600960209081526040808320805480870182559084528284200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000163390811790915573ffffffffffffffffffffffffffffffffffffffff8d16845260089092528220805490950190945581548301918290559192600c92916126ab9190612661908690614d03565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905573ffffffffffffffffffffffffffffffffffffffff861661275357846fffffffffffffffffffffffffffffffff16341461274e576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61283b565b341561278b576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526fffffffffffffffffffffffffffffffff8616604482015273ffffffffffffffffffffffffffffffffffffffff8716906323b872dd906064016020604051808303816000875af1158015612815573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128399190614d45565b505b8573ffffffffffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff1660016005546128749190614d03565b6040517f7bc3e781453a761be4ae9daaacb192000aff04f1c45c3bf9ca9e118b4c9e2e9c90600090a460055433906128ae90600190614d03565b6040517fc0081eeafa0d002abcd7d58d84b6e688d1526b9c3d915a282212ba08c49a17a790600090a3505050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff841660208083019190915281830184905282518083038401815260609092019092528051910120600090611a05565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a602052604090205460ff16612986576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116612a085760035473ffffffffffffffffffffffffffffffffffffffff8281166000908152600860205260409020549116906108fc906129dc9047614d03565b6040518115909202916000818181858888f19350505050158015612a04573d6000803e3d6000fd5b5050565b60035473ffffffffffffffffffffffffffffffffffffffff828116600081815260086020526040908190205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919363a9059cbb93169184906370a0823190602401602060405180830381865afa158015612a91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab59190614e97565b612abf9190614d03565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303816000875af1158015612b2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a049190614d45565b50565b600081815260096020908152604091829020805483518184028101840190945280845260609392830182828015612bc357602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612b98575b50505050509050919050565b612c256040805161014081018252600080825260208201819052909182019081526020016000815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b6000828152600b6020908152604091829020825161014081018452815473ffffffffffffffffffffffffffffffffffffffff8116825260ff74010000000000000000000000000000000000000000820481169483019490945290939192918401917501000000000000000000000000000000000000000000909104166002811115612cb257612cb26149d6565b6002811115612cc357612cc36149d6565b81528154602090910190760100000000000000000000000000000000000000000000900460ff166001811115612cfb57612cfb6149d6565b6001811115612d0c57612d0c6149d6565b815260018201546fffffffffffffffffffffffffffffffff808216602084015270010000000000000000000000000000000091829004811660408401526002909301549283166060830152820467ffffffffffffffff1660808201527801000000000000000000000000000000000000000000000000820463ffffffff1660a08201527c010000000000000000000000000000000000000000000000000000000090910460ff1660c09091015292915050565b60006117a87fd133bbc7f95acfb85dec01a77edf5a7c3665782f8a05b72294c6bdf7fb8b115c836000015184602001518560400151604051602001612e279493929190938452602084019290925267ffffffffffffffff166040830152606082015260800190565b60405160208183030381529060405280519060200120614038565b6000868152600b602052604090206002810154700100000000000000000000000000000000900467ffffffffffffffff16333214612eac576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81161580612ecc5750428167ffffffffffffffff16115b15612f03576040517f6f312cbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000888152600960205260409020805433919060ff8a16908110612f2957612f29614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1614612f82576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201547801000000000000000000000000000000000000000000000000900463ffffffff1615801590612fe6575060018254760100000000000000000000000000000000000000000000900460ff166001811115612fe457612fe46149d6565b145b1561317d5760028201547801000000000000000000000000000000000000000000000000900463ffffffff166040860135891461304f576040517ff5d2101e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff81166130666040880160208901614e63565b67ffffffffffffffff16146130a7576040517f63e0897a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6130b9611d9b36889003880188614b34565b82546130ed908a9063ffffffff841690760100000000000000000000000000000000000000000000900460ff168935613eb5565b1561317b576002830154613137906fffffffffffffffffffffffffffffffff8116907c0100000000000000000000000000000000000000000000000000000000900460ff16613e1c565b6002840180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff929092169190911790555b505b60028201548254600091829182916131ea916fffffffffffffffffffffffffffffffff82169160ff7c010000000000000000000000000000000000000000000000000000000090910481169188917401000000000000000000000000000000000000000090910416613c5a565b9250925092508960ff16811461322c576040517fc6e1dd1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818061323d575061323d838b613dbd565b15613274576040517f477383f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018501546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000009091048116908a16108015906132e05750600185547501000000000000000000000000000000000000000000900460ff1660028111156132de576132de6149d6565b145b1561331c576001850180546fffffffffffffffffffffffffffffffff808c167001000000000000000000000000000000000291161790556133d6565b600285547501000000000000000000000000000000000000000000900460ff16600281111561334d5761334d6149d6565b036133d6576127106133616103e882614eb0565b6001870154613396919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16614ed9565b6133a09190614f05565b6001860180546fffffffffffffffffffffffffffffffff9283167001000000000000000000000000000000000292169190911790555b600185015460009061340d9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16613c20565b60018701805491925082916000906134389084906fffffffffffffffffffffffffffffffff16614eb0565b82546101009290920a6fffffffffffffffffffffffffffffffff818102199093169183160217909155875473ffffffffffffffffffffffffffffffffffffffff166000908152600860205260408120805492851693509161349a908490614f34565b90915550506002860180547fffffff00ffffffff0000000000000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004267ffffffffffffffff16027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff16177c010000000000000000000000000000000000000000000000000000000060ff8e1602177fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff861617808255601890613593907801000000000000000000000000000000000000000000000000900463ffffffff16614dd2565b825463ffffffff9182166101009390930a9283029190920219909116179055855473ffffffffffffffffffffffffffffffffffffffff1661363657600186015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff163414613631576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61373a565b341561366e576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855460018701546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201527001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16604482015273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303816000875af1158015613714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137389190614d45565b505b60018601546040517001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16815233908d907f34e592c5d9452abe7b4df38f36795ed86fbcded8a266d5bdeeaaf5375a8cf0d39060200160405180910390a3505050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461382b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401611390565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b60005473ffffffffffffffffffffffffffffffffffffffff16331461391c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401611390565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152600a6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b6000828152600b60209081526040808320815161014081018352815473ffffffffffffffffffffffffffffffffffffffff8116825260ff74010000000000000000000000000000000000000000820481169583019590955285948594859492908401917501000000000000000000000000000000000000000000909104166002811115613a0157613a016149d6565b6002811115613a1257613a126149d6565b81528154602090910190760100000000000000000000000000000000000000000000900460ff166001811115613a4a57613a4a6149d6565b6001811115613a5b57613a5b6149d6565b815260018201546fffffffffffffffffffffffffffffffff808216602084015270010000000000000000000000000000000091829004811660408401526002909301549283166060830152820467ffffffffffffffff90811660808301527801000000000000000000000000000000000000000000000000830463ffffffff1660a08301527c010000000000000000000000000000000000000000000000000000000090920460ff1660c09091015260e08201519192508116600003613b2e575060c00151925060009150819050613bd7565b600082610100015163ffffffff16118015613b5e5750600182606001516001811115613b5c57613b5c6149d6565b145b15613bb257613b828783610100015163ffffffff1684606001518960000135613eb5565b15613bb257613b9a8260c00151836101200151613e1c565b6fffffffffffffffffffffffffffffffff1660c08301525b613bce8260c0015183610120015160ff16838560200151613c5a565b94509450945050505b9250925092565b6000814442604051602001613bfd929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c6117a89190614f47565b60045460009061271090613c46906fffffffffffffffffffffffffffffffff1682614da9565b613c509084614ed9565b6117a89190614f05565b60008060008360ff16600003613c7857506000915081905080613db3565b428567ffffffffffffffff161115613cbc5786600060ff8616613c9c896001614d81565b613ca69190614f69565b919450925067ffffffffffffffff169050613db3565b60006063613cd467ffffffffffffffff881642614d03565b613cde9190614e3b565b905060015b818111613d6d57600060ff8716613d048367ffffffffffffffff8c16614f34565b613d0e9190614e4f565b9050613d1a8a82613de3565b15613d5357613d298a82613dbd565b15613d42578995506001945060ff169250613db3915050565b613d4c8a82613e1c565b9950613d5a565b8260010192505b5080613d6581614f90565b915050613ce3565b50600060ff8616613d888367ffffffffffffffff8b16614f34565b613d93906001614f34565b613d9d9190614e4f565b905088613daa8a83613dbd565b90955093509150505b9450945094915050565b6000613dc98383613e1c565b6fffffffffffffffffffffffffffffffff16159392505050565b600080613df183607f614fc8565b60ff166001901b90508381166fffffffffffffffffffffffffffffffff166000141591505092915050565b600080613e2a83607f614fc8565b600160ff919091161b19841691505092915050565b6000613e4a83612dbf565b90506000613e588284614080565b60065490915073ffffffffffffffffffffffffffffffffffffffff808316911614613eaf576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b60006001836001811115613ecb57613ecb6149d6565b03613f8157600082604051602001613ee591815260200190565b6040516020818303038152906040528051906020012060001c9050600060198787604051602001613f4592919091825260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016602082015260280190565b6040516020818303038152906040528051906020012060001c613f689190614e4f565b905080613f76606484614e4f565b111592505050613f85565b5060005b949350505050565b606060ff8314613fa757613fa0836140a4565b90506117a8565b818054613fb390614fe1565b80601f0160208091040260200160405190810160405280929190818152602001828054613fdf90614fe1565b801561402c5780601f106140015761010080835404028352916020019161402c565b820191906000526020600020905b81548152906001019060200180831161400f57829003601f168201915b505050505090506117a8565b60006117a86140456140e3565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b600080600061408f8585614220565b9150915061409c81614265565b509392505050565b606060006140b183614418565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614801561414957507f000000000000000000000000000000000000000000000000000000000000000046145b1561417357507f000000000000000000000000000000000000000000000000000000000000000090565b61421b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b60008082516041036142565760208301516040840151606085015160001a61424a87828585614459565b9450945050505061425e565b506000905060025b9250929050565b6000816004811115614279576142796149d6565b036142815750565b6001816004811115614295576142956149d6565b036142fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611390565b6002816004811115614310576143106149d6565b03614377576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611390565b600381600481111561438b5761438b6149d6565b03612b53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611390565b600060ff8216601f8111156117a8576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614490575060009050600361453f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156144e4573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166145385760006001925092505061453f565b9150600090505b94509492505050565b5080546000825590600052602060002090810190612b5391905b808211156145765760008155600101614562565b5090565b803560ff8116811461458b57600080fd5b919050565b600080604083850312156145a357600080fd5b823591506145b36020840161457a565b90509250929050565b6000602082840312156145ce57600080fd5b5035919050565b600080604083850312156145e857600080fd5b50508035926020909101359150565b803573ffffffffffffffffffffffffffffffffffffffff8116811461458b57600080fd5b60006020828403121561462d57600080fd5b611a05826145f7565b80356003811061458b57600080fd5b60006020828403121561465757600080fd5b611a0582614636565b80356fffffffffffffffffffffffffffffffff8116811461458b57600080fd5b60006020828403121561469257600080fd5b611a0582614660565b803567ffffffffffffffff8116811461458b57600080fd5b600080604083850312156146c657600080fd5b823591506145b36020840161469b565b6000606082840312156146e857600080fd5b50919050565b60008083601f84011261470057600080fd5b50813567ffffffffffffffff81111561471857600080fd5b60208301915083602082850101111561425e57600080fd5b600080600080600060c0868803121561474857600080fd5b853594506147586020870161457a565b935061476787604088016146d6565b925060a086013567ffffffffffffffff81111561478357600080fd5b61478f888289016146ee565b969995985093965092949392505050565b80356002811061458b57600080fd5b6000602082840312156147c157600080fd5b611a05826147a0565b6000815180845260005b818110156147f0576020818501810151868301820152016147d4565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e08184015261486a60e084018a6147ca565b838103604085015261487c818a6147ca565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825283870192509083019060005b818110156148db578351835292840192918401916001016148bf565b50909c9b505050505050505050505050565b600080600080600060a0868803121561490557600080fd5b61490e866145f7565b945061491c60208701614660565b935061492a6040870161457a565b925061493860608701614636565b9150614946608087016147a0565b90509295509295909350565b6000806040838503121561496557600080fd5b61496e836145f7565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b818110156149ca57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101614998565b50909695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110614a1557614a156149d6565b9052565b60028110614a1557614a156149d6565b815173ffffffffffffffffffffffffffffffffffffffff16815261014081016020830151614a5c602084018260ff169052565b506040830151614a6f6040840182614a05565b506060830151614a826060840182614a19565b506080830151614aa660808401826fffffffffffffffffffffffffffffffff169052565b5060a0830151614aca60a08401826fffffffffffffffffffffffffffffffff169052565b5060c0830151614aee60c08401826fffffffffffffffffffffffffffffffff169052565b5060e0830151614b0a60e084018267ffffffffffffffff169052565b506101008381015163ffffffff16908301526101208084015160ff8116828501525b505092915050565b600060608284031215614b4657600080fd5b6040516060810181811067ffffffffffffffff82111715614b90577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358152614ba36020840161469b565b6020820152604083013560408201528091505092915050565b60008060008060008060e08789031215614bd557600080fd5b86359550614be56020880161457a565b9450614bf360408801614660565b9350614c0288606089016146d6565b925060c087013567ffffffffffffffff811115614c1e57600080fd5b614c2a89828a016146ee565b979a9699509497509295939492505050565b8015158114612b5357600080fd5b60008060408385031215614c5d57600080fd5b614c66836145f7565b91506020830135614c7681614c3c565b809150509250929050565b60008060808385031215614c9457600080fd5b823591506145b384602085016146d6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156117a8576117a8614cd4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600060208284031215614d5757600080fd5b8151611a0581614c3c565b600060ff821660ff8103614d7857614d78614cd4565b60010192915050565b67ffffffffffffffff818116838216019080821115614da257614da2614cd4565b5092915050565b6fffffffffffffffffffffffffffffffff828116828216039080821115614da257614da2614cd4565b600063ffffffff808316818103614deb57614deb614cd4565b6001019392505050565b80820281158282048414176117a8576117a8614cd4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614e4a57614e4a614e0c565b500490565b600082614e5e57614e5e614e0c565b500690565b600060208284031215614e7557600080fd5b611a058261469b565b60ff81811683821601908111156117a8576117a8614cd4565b600060208284031215614ea957600080fd5b5051919050565b6fffffffffffffffffffffffffffffffff818116838216019080821115614da257614da2614cd4565b6fffffffffffffffffffffffffffffffff818116838216028082169190828114614b2c57614b2c614cd4565b60006fffffffffffffffffffffffffffffffff80841680614f2857614f28614e0c565b92169190910492915050565b808201808211156117a8576117a8614cd4565b600060ff831680614f5a57614f5a614e0c565b8060ff84160691505092915050565b600067ffffffffffffffff80841680614f8457614f84614e0c565b92169190910692915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614fc157614fc1614cd4565b5060010190565b60ff82811682821603908111156117a8576117a8614cd4565b600181811c90821680614ff557607f821691505b6020821081036146e8577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fdfea26469706673582212201ede09760f66a31d17f5e2a9522175e60e0ca768cea14188a402a917d3a7a6c564736f6c63430008150033000000000000000000000000a92bfe11459e75c994dc612d4f64b7f2935a732d000000000000000000000000a92bfe11459e75c994dc612d4f64b7f2935a732d0000000000000000000000006b572731fd736a4e78f7307e27d8a2cba622ca02

Deployed Bytecode

0x6080604052600436106101e75760003560e01c806384b0196e11610102578063ddca3f4311610095578063f2fde38b11610064578063f2fde38b14610687578063f6927e7d146106a7578063f8b2cb4f146106c7578063feff17441461070a576101f1565b8063ddca3f43146105f4578063e00dd1611461063e578063e3d5c08c14610654578063ed64346214610674576101f1565b8063a480ca79116100d1578063a480ca7914610507578063c1e3bd3e14610527578063d266e83b14610554578063dc6f867c146105c7576101f1565b806384b0196e1461047f5780638da5cb5b146104a75780639738f87b146104d4578063a06412ac146104e7576101f1565b80633687f24a1161017a57806358db40d81161014957806358db40d8146103f2578063681b950d146104125780636999b3771461043257806379fa35071461045f576101f1565b80633687f24a146103655780633ed4c678146103855780634266580a146103a5578063450bde78146103d2576101f1565b80631aed59f0116101b65780631aed59f0146102775780631f6dc875146102c15780631fc6a5db1461031757806334fe228714610345576101f1565b806301fe1fdc14610202578063049878f3146102245780630ca522081461023757806314c9216a14610257576101f1565b366101f157600080fd5b3480156101fd57600080fd5b600080fd5b34801561020e57600080fd5b5061022261021d366004614590565b610758565b005b6102226102323660046145bc565b610cdd565b34801561024357600080fd5b506102226102523660046145bc565b611313565b34801561026357600080fd5b50610222610272366004614590565b61139e565b34801561028357600080fd5b506102976102923660046145d5565b611763565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156102cd57600080fd5b506103076102dc36600461461b565b73ffffffffffffffffffffffffffffffffffffffff166000908152600a602052604090205460ff1690565b60405190151581526020016102b8565b34801561032357600080fd5b506103376103323660046145bc565b6117ae565b6040519081526020016102b8565b34801561035157600080fd5b50610307610360366004614645565b6117fa565b34801561037157600080fd5b50610222610380366004614680565b611823565b34801561039157600080fd5b506102226103a036600461461b565b611905565b3480156103b157600080fd5b506006546102979073ffffffffffffffffffffffffffffffffffffffff1681565b3480156103de57600080fd5b506103376103ed3660046146b3565b61199d565b3480156103fe57600080fd5b5061022261040d36600461461b565b611a0c565b34801561041e57600080fd5b5061022261042d366004614730565b611ad4565b34801561043e57600080fd5b506003546102979073ffffffffffffffffffffffffffffffffffffffff1681565b34801561046b57600080fd5b5061030761047a3660046147af565b6121fa565b34801561048b57600080fd5b50610494612219565b6040516102b8979695949392919061482e565b3480156104b357600080fd5b506000546102979073ffffffffffffffffffffffffffffffffffffffff1681565b6102226104e23660046148ed565b6122be565b3480156104f357600080fd5b50610337610502366004614952565b6128df565b34801561051357600080fd5b5061022261052236600461461b565b612927565b34801561053357600080fd5b506105476105423660046145bc565b612b56565b6040516102b8919061497c565b34801561056057600080fd5b5061030761056f366004614952565b6040805173ffffffffffffffffffffffffffffffffffffffff9390931660208085019190915283820192909252805180840382018152606090930181528251928201929092206000908152600c909152205460ff1690565b3480156105d357600080fd5b506105e76105e23660046145bc565b612bcf565b6040516102b89190614a29565b34801561060057600080fd5b5060045461061d906fffffffffffffffffffffffffffffffff1681565b6040516fffffffffffffffffffffffffffffffff90911681526020016102b8565b34801561064a57600080fd5b5061033760055481565b34801561066057600080fd5b5061033761066f366004614b34565b612dbf565b610222610682366004614bbc565b612e42565b34801561069357600080fd5b506102226106a236600461461b565b6137aa565b3480156106b357600080fd5b506102226106c2366004614c4a565b61389b565b3480156106d357600080fd5b506103376106e236600461461b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526008602052604090205490565b34801561071657600080fd5b5061072a610725366004614c81565b613972565b604080516fffffffffffffffffffffffffffffffff90941684529115156020840152908201526060016102b8565b6000828152600b6020908152604080832060099092529091203233146107aa576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002820154700100000000000000000000000000000000900467ffffffffffffffff1615610804576040517f1fbde44500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff16818460ff168154811061083057610830614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1614610889576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018281018054845473ffffffffffffffffffffffffffffffffffffffff16600081815260086020526040902080547001000000000000000000000000000000009093046fffffffffffffffffffffffffffffffff9081169384900390915583547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081169082168490039091161790925583549092106109d957600086815260096020526040812061093a91614548565b6000868152600b6020908152604080832080547fffffffffffffffffff00000000000000000000000000000000000000000000001681556001810184905560020180547fffffff000000000000000000000000000000000000000000000000000000000016905551828152829189917f4fc9df21c274d2396432490df6fcda723ce987bc1b640015d34b26ca0ed98063910160405180910390a4610b41565b825483906109e990600190614d03565b815481106109f9576109f9614ca5565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16838660ff1681548110610a3957610a39614ca5565b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082805480610a9157610a91614d16565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190558254610af690607f614d03565b6002850180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008116600190931b6fffffffffffffffffffffffffffffffff91821618169190911790555b6040805133602080830191909152818301899052825180830384018152606090920183528151918101919091206000908152600c9091522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905573ffffffffffffffffffffffffffffffffffffffff8116610bfe5760405133906fffffffffffffffffffffffffffffffff841680156108fc02916000818181858888f19350505050158015610bf8573d6000803e3d6000fd5b50610ca8565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526fffffffffffffffffffffffffffffffff8316602482015273ffffffffffffffffffffffffffffffffffffffff82169063a9059cbb906044016020604051808303816000875af1158015610c82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca69190614d45565b505b604051339087907ff9b63d6ab6b86370bd291baf2443198e10294bc1a369e21ff12b34b904cb0fff90600090a3505050505050565b6000818152600b602052604090208054600182015473ffffffffffffffffffffffffffffffffffffffff9091169070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16333214610d6a576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002830154700100000000000000000000000000000000900467ffffffffffffffff1615610dc4576040517f1fbde44500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028301546fffffffffffffffffffffffffffffffff16600003610e14576040517f477383f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805133602080830191909152818301879052825180830384018152606090920183528151918101919091206000818152600c9092529190205460ff1615610e88576040517e3b268200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260096020908152604080832080546002890180546fffffffffffffffffffffffffffffffff600160ff607f869003811682901b8316838516177fffffffffffffffffffffffffffffffff00000000000000000000000000000000948516179094558c810180548084168d01841694169390931790925573ffffffffffffffffffffffffffffffffffffffff8b168852600887528588208054918b169190910190558354808201855593875285872090930180547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055868652600c9094529190932080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909317909255855474010000000000000000000000000000000000000000900416610fc082614d62565b91508160ff160361118957610fd6606342614d81565b60028601805467ffffffffffffffff92909216700100000000000000000000000000000000027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff909216919091179055845461104d9060ff7401000000000000000000000000000000000000000090910416613bde565b60028601805460ff929092167c0100000000000000000000000000000000000000000000000000000000027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905560018501546000906110c5906fffffffffffffffffffffffffffffffff16613c20565b60018701546110e691906fffffffffffffffffffffffffffffffff16614da9565b6001870180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166fffffffffffffffffffffffffffffffff91821684900382161790915573ffffffffffffffffffffffffffffffffffffffff87166000908152600860205260408082208054938516909303909255905191925088917e6e0c97de781a7389d44ba8fd35d1467cabb17ed04d038d166d34ab819213f39190a2505b73ffffffffffffffffffffffffffffffffffffffff84166111f457826fffffffffffffffffffffffffffffffff1634146111ef576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112dc565b341561122c576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526fffffffffffffffffffffffffffffffff8416604482015273ffffffffffffffffffffffffffffffffffffffff8516906323b872dd906064016020604051808303816000875af11580156112b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112da9190614d45565b505b6005546040513391907fc0081eeafa0d002abcd7d58d84b6e688d1526b9c3d915a282212ba08c49a17a790600090a3505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611399576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600755565b6000828152600b602052604090206002810154700100000000000000000000000000000000900467ffffffffffffffff16333214611408576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811615806114285750428167ffffffffffffffff16115b1561145f576040517f6f312cbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152600960205260409020805433919060ff861690811061148557611485614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16146114de576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600282015482546000918291829161154b916fffffffffffffffffffffffffffffffff82169160ff7c010000000000000000000000000000000000000000000000000000000090910481169188917401000000000000000000000000000000000000000090910416613c5a565b9250925092508560ff16811461158d576040517fc6e1dd1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818061159e575061159e8387613dbd565b156115d5576040517f477383f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115df8387613de3565b611615576040517ff6fa148200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61161f8387613e1c565b6002860180546fffffffffffffffffffffffffffffffff929092167fffffff00ffffffffffffffffffffffff00000000000000000000000000000000909216919091177c010000000000000000000000000000000000000000000000000000000060ff891602177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004267ffffffffffffffff1602178082556018906116f8907801000000000000000000000000000000000000000000000000900463ffffffff16614dd2565b91906101000a81548163ffffffff021916908363ffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16877f537f4d0c896772cdfaaac608ab3c4705fe48cc44b3a2e63451b4a7eb9808ba1b60405160405180910390a350505050505050565b600082815260096020526040812080548390811061178357611783614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690505b92915050565b600454600090612710906117d4906fffffffffffffffffffffffffffffffff1682614da9565b6117f0906fffffffffffffffffffffffffffffffff1684614df5565b6117a89190614e3b565b600081600281111561180e5761180e6149d6565b60ff16600754901c6001166001149050919050565b60035473ffffffffffffffffffffffffffffffffffffffff163314611874576040517f48f5c3ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e86fffffffffffffffffffffffffffffffff821611156118c2576040517ff4df6ae500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b60035473ffffffffffffffffffffffffffffffffffffffff163314611956576040517f48f5c3ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000601983836040516020016119e292919091825260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016602082015260280190565b6040516020818303038152906040528051906020012060001c611a059190614e4f565b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611a8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401611390565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000858152600b602052604090206002810154700100000000000000000000000000000000900467ffffffffffffffff16333214611b3e576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201546fffffffffffffffffffffffffffffffff16600003611b8e576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81161580611bae5750428167ffffffffffffffff16115b15611be5576040517f6f312cbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000878152600960205260409020805433919060ff8916908110611c0b57611c0b614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1614611c64576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201547801000000000000000000000000000000000000000000000000900463ffffffff1615801590611cc8575060018254760100000000000000000000000000000000000000000000900460ff166001811115611cc657611cc66149d6565b145b15611e9b5760028201547801000000000000000000000000000000000000000000000000900463ffffffff1660408601358814611d31576040517ff5d2101e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8116611d486040880160208901614e63565b67ffffffffffffffff1614611d89576040517f63e0897a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dd7611d9b36889003880188614b34565b86868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e3f92505050565b8254611e0b90899063ffffffff841690760100000000000000000000000000000000000000000000900460ff168935613eb5565b15611e99576002830154611e55906fffffffffffffffffffffffffffffffff8116907c0100000000000000000000000000000000000000000000000000000000900460ff16613e1c565b6002840180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff929092169190911790555b505b600282015482546000918291611f05916fffffffffffffffffffffffffffffffff81169160ff7c0100000000000000000000000000000000000000000000000000000000909204821691879174010000000000000000000000000000000000000000900416613c5a565b5091509150611f148289613de3565b611f4a576040517ff6fa148200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015611f5f5750611f5d8289613dbd565b155b15611f96576040517fa63065ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180850180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008116909155855473ffffffffffffffffffffffffffffffffffffffff908116600090815260086020908152604080832080546fffffffffffffffffffffffffffffffff90961695869003905589548f8452600b835281842080547fffffffffffffffffff0000000000000000000000000000000000000000000000168155968701849055600290960180547fffffff00000000000000000000000000000000000000000000000000000000001690556009909152812092939091169161208391614548565b73ffffffffffffffffffffffffffffffffffffffff81166120e25760405133906fffffffffffffffffffffffffffffffff841680156108fc02916000818181858888f193505050501580156120dc573d6000803e3d6000fd5b5061218c565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526fffffffffffffffffffffffffffffffff8316602482015273ffffffffffffffffffffffffffffffffffffffff82169063a9059cbb906044016020604051808303816000875af1158015612166573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218a9190614d45565b505b6040516fffffffffffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff82169033908d907f4fc9df21c274d2396432490df6fcda723ce987bc1b640015d34b26ca0ed980639060200160405180910390a45050505050505050505050565b600081600181111561220e5761220e6149d6565b61180e906080614e7e565b60006060808280808361224d7f546f6e74696e65000000000000000000000000000000000000000000000000076001613f8d565b6122787f31000000000000000000000000000000000000000000000000000000000000016002613f8d565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b3233146122f7576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600260ff8416108061230c5750606460ff8416115b15612343576040517f59e59f3f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836fffffffffffffffffffffffffffffffff1660000361238f576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612398826117fa565b15806123aa57506123a8816121fa565b155b156123e1576040517fa0042b1700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166000908152600a602052604090205460ff16612440576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546000908152600b60205260409020805460ff851674010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090911673ffffffffffffffffffffffffffffffffffffffff88161717808255839082907fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000008360028111156124fd576124fd6149d6565b02179055508054829082907fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff16760100000000000000000000000000000000000000000000836001811115612554576125546149d6565b02179055506fffffffffffffffffffffffffffffffff851670010000000000000000000000000000000081027fffffffffffffffffffffffffffffffff0000000000000000000000000000000090811682176001808501919091556002840180549092166f8000000000000000000000000000000017909155600580546000908152600960209081526040808320805480870182559084528284200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000163390811790915573ffffffffffffffffffffffffffffffffffffffff8d16845260089092528220805490950190945581548301918290559192600c92916126ab9190612661908690614d03565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905573ffffffffffffffffffffffffffffffffffffffff861661275357846fffffffffffffffffffffffffffffffff16341461274e576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61283b565b341561278b576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526fffffffffffffffffffffffffffffffff8616604482015273ffffffffffffffffffffffffffffffffffffffff8716906323b872dd906064016020604051808303816000875af1158015612815573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128399190614d45565b505b8573ffffffffffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff1660016005546128749190614d03565b6040517f7bc3e781453a761be4ae9daaacb192000aff04f1c45c3bf9ca9e118b4c9e2e9c90600090a460055433906128ae90600190614d03565b6040517fc0081eeafa0d002abcd7d58d84b6e688d1526b9c3d915a282212ba08c49a17a790600090a3505050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff841660208083019190915281830184905282518083038401815260609092019092528051910120600090611a05565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600a602052604090205460ff16612986576040517fc891add200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116612a085760035473ffffffffffffffffffffffffffffffffffffffff8281166000908152600860205260409020549116906108fc906129dc9047614d03565b6040518115909202916000818181858888f19350505050158015612a04573d6000803e3d6000fd5b5050565b60035473ffffffffffffffffffffffffffffffffffffffff828116600081815260086020526040908190205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919363a9059cbb93169184906370a0823190602401602060405180830381865afa158015612a91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab59190614e97565b612abf9190614d03565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303816000875af1158015612b2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a049190614d45565b50565b600081815260096020908152604091829020805483518184028101840190945280845260609392830182828015612bc357602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612b98575b50505050509050919050565b612c256040805161014081018252600080825260208201819052909182019081526020016000815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b6000828152600b6020908152604091829020825161014081018452815473ffffffffffffffffffffffffffffffffffffffff8116825260ff74010000000000000000000000000000000000000000820481169483019490945290939192918401917501000000000000000000000000000000000000000000909104166002811115612cb257612cb26149d6565b6002811115612cc357612cc36149d6565b81528154602090910190760100000000000000000000000000000000000000000000900460ff166001811115612cfb57612cfb6149d6565b6001811115612d0c57612d0c6149d6565b815260018201546fffffffffffffffffffffffffffffffff808216602084015270010000000000000000000000000000000091829004811660408401526002909301549283166060830152820467ffffffffffffffff1660808201527801000000000000000000000000000000000000000000000000820463ffffffff1660a08201527c010000000000000000000000000000000000000000000000000000000090910460ff1660c09091015292915050565b60006117a87fd133bbc7f95acfb85dec01a77edf5a7c3665782f8a05b72294c6bdf7fb8b115c836000015184602001518560400151604051602001612e279493929190938452602084019290925267ffffffffffffffff166040830152606082015260800190565b60405160208183030381529060405280519060200120614038565b6000868152600b602052604090206002810154700100000000000000000000000000000000900467ffffffffffffffff16333214612eac576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81161580612ecc5750428167ffffffffffffffff16115b15612f03576040517f6f312cbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000888152600960205260409020805433919060ff8a16908110612f2957612f29614ca5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1614612f82576040517fc394a43300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028201547801000000000000000000000000000000000000000000000000900463ffffffff1615801590612fe6575060018254760100000000000000000000000000000000000000000000900460ff166001811115612fe457612fe46149d6565b145b1561317d5760028201547801000000000000000000000000000000000000000000000000900463ffffffff166040860135891461304f576040517ff5d2101e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff81166130666040880160208901614e63565b67ffffffffffffffff16146130a7576040517f63e0897a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6130b9611d9b36889003880188614b34565b82546130ed908a9063ffffffff841690760100000000000000000000000000000000000000000000900460ff168935613eb5565b1561317b576002830154613137906fffffffffffffffffffffffffffffffff8116907c0100000000000000000000000000000000000000000000000000000000900460ff16613e1c565b6002840180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff929092169190911790555b505b60028201548254600091829182916131ea916fffffffffffffffffffffffffffffffff82169160ff7c010000000000000000000000000000000000000000000000000000000090910481169188917401000000000000000000000000000000000000000090910416613c5a565b9250925092508960ff16811461322c576040517fc6e1dd1d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818061323d575061323d838b613dbd565b15613274576040517f477383f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018501546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000009091048116908a16108015906132e05750600185547501000000000000000000000000000000000000000000900460ff1660028111156132de576132de6149d6565b145b1561331c576001850180546fffffffffffffffffffffffffffffffff808c167001000000000000000000000000000000000291161790556133d6565b600285547501000000000000000000000000000000000000000000900460ff16600281111561334d5761334d6149d6565b036133d6576127106133616103e882614eb0565b6001870154613396919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16614ed9565b6133a09190614f05565b6001860180546fffffffffffffffffffffffffffffffff9283167001000000000000000000000000000000000292169190911790555b600185015460009061340d9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16613c20565b60018701805491925082916000906134389084906fffffffffffffffffffffffffffffffff16614eb0565b82546101009290920a6fffffffffffffffffffffffffffffffff818102199093169183160217909155875473ffffffffffffffffffffffffffffffffffffffff166000908152600860205260408120805492851693509161349a908490614f34565b90915550506002860180547fffffff00ffffffff0000000000000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004267ffffffffffffffff16027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff16177c010000000000000000000000000000000000000000000000000000000060ff8e1602177fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff861617808255601890613593907801000000000000000000000000000000000000000000000000900463ffffffff16614dd2565b825463ffffffff9182166101009390930a9283029190920219909116179055855473ffffffffffffffffffffffffffffffffffffffff1661363657600186015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff163414613631576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61373a565b341561366e576040517faa82224900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855460018701546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201527001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16604482015273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303816000875af1158015613714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137389190614d45565b505b60018601546040517001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16815233908d907f34e592c5d9452abe7b4df38f36795ed86fbcded8a266d5bdeeaaf5375a8cf0d39060200160405180910390a3505050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461382b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401611390565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b60005473ffffffffffffffffffffffffffffffffffffffff16331461391c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401611390565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152600a6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b6000828152600b60209081526040808320815161014081018352815473ffffffffffffffffffffffffffffffffffffffff8116825260ff74010000000000000000000000000000000000000000820481169583019590955285948594859492908401917501000000000000000000000000000000000000000000909104166002811115613a0157613a016149d6565b6002811115613a1257613a126149d6565b81528154602090910190760100000000000000000000000000000000000000000000900460ff166001811115613a4a57613a4a6149d6565b6001811115613a5b57613a5b6149d6565b815260018201546fffffffffffffffffffffffffffffffff808216602084015270010000000000000000000000000000000091829004811660408401526002909301549283166060830152820467ffffffffffffffff90811660808301527801000000000000000000000000000000000000000000000000830463ffffffff1660a08301527c010000000000000000000000000000000000000000000000000000000090920460ff1660c09091015260e08201519192508116600003613b2e575060c00151925060009150819050613bd7565b600082610100015163ffffffff16118015613b5e5750600182606001516001811115613b5c57613b5c6149d6565b145b15613bb257613b828783610100015163ffffffff1684606001518960000135613eb5565b15613bb257613b9a8260c00151836101200151613e1c565b6fffffffffffffffffffffffffffffffff1660c08301525b613bce8260c0015183610120015160ff16838560200151613c5a565b94509450945050505b9250925092565b6000814442604051602001613bfd929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c6117a89190614f47565b60045460009061271090613c46906fffffffffffffffffffffffffffffffff1682614da9565b613c509084614ed9565b6117a89190614f05565b60008060008360ff16600003613c7857506000915081905080613db3565b428567ffffffffffffffff161115613cbc5786600060ff8616613c9c896001614d81565b613ca69190614f69565b919450925067ffffffffffffffff169050613db3565b60006063613cd467ffffffffffffffff881642614d03565b613cde9190614e3b565b905060015b818111613d6d57600060ff8716613d048367ffffffffffffffff8c16614f34565b613d0e9190614e4f565b9050613d1a8a82613de3565b15613d5357613d298a82613dbd565b15613d42578995506001945060ff169250613db3915050565b613d4c8a82613e1c565b9950613d5a565b8260010192505b5080613d6581614f90565b915050613ce3565b50600060ff8616613d888367ffffffffffffffff8b16614f34565b613d93906001614f34565b613d9d9190614e4f565b905088613daa8a83613dbd565b90955093509150505b9450945094915050565b6000613dc98383613e1c565b6fffffffffffffffffffffffffffffffff16159392505050565b600080613df183607f614fc8565b60ff166001901b90508381166fffffffffffffffffffffffffffffffff166000141591505092915050565b600080613e2a83607f614fc8565b600160ff919091161b19841691505092915050565b6000613e4a83612dbf565b90506000613e588284614080565b60065490915073ffffffffffffffffffffffffffffffffffffffff808316911614613eaf576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b60006001836001811115613ecb57613ecb6149d6565b03613f8157600082604051602001613ee591815260200190565b6040516020818303038152906040528051906020012060001c9050600060198787604051602001613f4592919091825260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016602082015260280190565b6040516020818303038152906040528051906020012060001c613f689190614e4f565b905080613f76606484614e4f565b111592505050613f85565b5060005b949350505050565b606060ff8314613fa757613fa0836140a4565b90506117a8565b818054613fb390614fe1565b80601f0160208091040260200160405190810160405280929190818152602001828054613fdf90614fe1565b801561402c5780601f106140015761010080835404028352916020019161402c565b820191906000526020600020905b81548152906001019060200180831161400f57829003601f168201915b505050505090506117a8565b60006117a86140456140e3565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b600080600061408f8585614220565b9150915061409c81614265565b509392505050565b606060006140b183614418565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b60003073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000a9f25bac1e81d8558a1d738bbb275299333742b1614801561414957507f000000000000000000000000000000000000000000000000000000000000000146145b1561417357507f7ad6e81c91da69a1ef5c13b5ca89de3cab3f92d86b1a199e96b9b99cee30524590565b61421b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f334e12643f9c85949b6ae031ea883c9b9d84f92aa0c86caecbc35f2fe550bf45918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b60008082516041036142565760208301516040840151606085015160001a61424a87828585614459565b9450945050505061425e565b506000905060025b9250929050565b6000816004811115614279576142796149d6565b036142815750565b6001816004811115614295576142956149d6565b036142fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611390565b6002816004811115614310576143106149d6565b03614377576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611390565b600381600481111561438b5761438b6149d6565b03612b53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611390565b600060ff8216601f8111156117a8576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614490575060009050600361453f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156144e4573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166145385760006001925092505061453f565b9150600090505b94509492505050565b5080546000825590600052602060002090810190612b5391905b808211156145765760008155600101614562565b5090565b803560ff8116811461458b57600080fd5b919050565b600080604083850312156145a357600080fd5b823591506145b36020840161457a565b90509250929050565b6000602082840312156145ce57600080fd5b5035919050565b600080604083850312156145e857600080fd5b50508035926020909101359150565b803573ffffffffffffffffffffffffffffffffffffffff8116811461458b57600080fd5b60006020828403121561462d57600080fd5b611a05826145f7565b80356003811061458b57600080fd5b60006020828403121561465757600080fd5b611a0582614636565b80356fffffffffffffffffffffffffffffffff8116811461458b57600080fd5b60006020828403121561469257600080fd5b611a0582614660565b803567ffffffffffffffff8116811461458b57600080fd5b600080604083850312156146c657600080fd5b823591506145b36020840161469b565b6000606082840312156146e857600080fd5b50919050565b60008083601f84011261470057600080fd5b50813567ffffffffffffffff81111561471857600080fd5b60208301915083602082850101111561425e57600080fd5b600080600080600060c0868803121561474857600080fd5b853594506147586020870161457a565b935061476787604088016146d6565b925060a086013567ffffffffffffffff81111561478357600080fd5b61478f888289016146ee565b969995985093965092949392505050565b80356002811061458b57600080fd5b6000602082840312156147c157600080fd5b611a05826147a0565b6000815180845260005b818110156147f0576020818501810151868301820152016147d4565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e08184015261486a60e084018a6147ca565b838103604085015261487c818a6147ca565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825283870192509083019060005b818110156148db578351835292840192918401916001016148bf565b50909c9b505050505050505050505050565b600080600080600060a0868803121561490557600080fd5b61490e866145f7565b945061491c60208701614660565b935061492a6040870161457a565b925061493860608701614636565b9150614946608087016147a0565b90509295509295909350565b6000806040838503121561496557600080fd5b61496e836145f7565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b818110156149ca57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101614998565b50909695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110614a1557614a156149d6565b9052565b60028110614a1557614a156149d6565b815173ffffffffffffffffffffffffffffffffffffffff16815261014081016020830151614a5c602084018260ff169052565b506040830151614a6f6040840182614a05565b506060830151614a826060840182614a19565b506080830151614aa660808401826fffffffffffffffffffffffffffffffff169052565b5060a0830151614aca60a08401826fffffffffffffffffffffffffffffffff169052565b5060c0830151614aee60c08401826fffffffffffffffffffffffffffffffff169052565b5060e0830151614b0a60e084018267ffffffffffffffff169052565b506101008381015163ffffffff16908301526101208084015160ff8116828501525b505092915050565b600060608284031215614b4657600080fd5b6040516060810181811067ffffffffffffffff82111715614b90577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358152614ba36020840161469b565b6020820152604083013560408201528091505092915050565b60008060008060008060e08789031215614bd557600080fd5b86359550614be56020880161457a565b9450614bf360408801614660565b9350614c0288606089016146d6565b925060c087013567ffffffffffffffff811115614c1e57600080fd5b614c2a89828a016146ee565b979a9699509497509295939492505050565b8015158114612b5357600080fd5b60008060408385031215614c5d57600080fd5b614c66836145f7565b91506020830135614c7681614c3c565b809150509250929050565b60008060808385031215614c9457600080fd5b823591506145b384602085016146d6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156117a8576117a8614cd4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600060208284031215614d5757600080fd5b8151611a0581614c3c565b600060ff821660ff8103614d7857614d78614cd4565b60010192915050565b67ffffffffffffffff818116838216019080821115614da257614da2614cd4565b5092915050565b6fffffffffffffffffffffffffffffffff828116828216039080821115614da257614da2614cd4565b600063ffffffff808316818103614deb57614deb614cd4565b6001019392505050565b80820281158282048414176117a8576117a8614cd4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614e4a57614e4a614e0c565b500490565b600082614e5e57614e5e614e0c565b500690565b600060208284031215614e7557600080fd5b611a058261469b565b60ff81811683821601908111156117a8576117a8614cd4565b600060208284031215614ea957600080fd5b5051919050565b6fffffffffffffffffffffffffffffffff818116838216019080821115614da257614da2614cd4565b6fffffffffffffffffffffffffffffffff818116838216028082169190828114614b2c57614b2c614cd4565b60006fffffffffffffffffffffffffffffffff80841680614f2857614f28614e0c565b92169190910492915050565b808201808211156117a8576117a8614cd4565b600060ff831680614f5a57614f5a614e0c565b8060ff84160691505092915050565b600067ffffffffffffffff80841680614f8457614f84614e0c565b92169190910692915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614fc157614fc1614cd4565b5060010190565b60ff82811682821603908111156117a8576117a8614cd4565b600181811c90821680614ff557607f821691505b6020821081036146e8577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fdfea26469706673582212201ede09760f66a31d17f5e2a9522175e60e0ca768cea14188a402a917d3a7a6c564736f6c63430008150033

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

000000000000000000000000a92bfe11459e75c994dc612d4f64b7f2935a732d000000000000000000000000a92bfe11459e75c994dc612d4f64b7f2935a732d0000000000000000000000006b572731fd736a4e78f7307e27d8a2cba622ca02

-----Decoded View---------------
Arg [0] : _admin (address): 0xa92bFe11459e75c994dc612D4F64b7f2935a732D
Arg [1] : _feeController (address): 0xa92bFe11459e75c994dc612D4F64b7f2935a732D
Arg [2] : _rngSource (address): 0x6B572731fd736a4e78F7307e27d8a2CbA622cA02

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000a92bfe11459e75c994dc612d4f64b7f2935a732d
Arg [1] : 000000000000000000000000a92bfe11459e75c994dc612d4f64b7f2935a732d
Arg [2] : 0000000000000000000000006b572731fd736a4e78f7307e27d8a2cba622ca02


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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