Goerli Testnet

Contract

0x9648cDC5D3D17A7DEc035ac2d0d943b511ECf8F6
Source Code
Transaction Hash
Method
Block
From
To
Value
Submit Challenge102471442023-12-21 10:01:1266 days 12 hrs ago1703152872IN
0x9648cD...11ECf8F6
0 ETH0.000129331.50000003
Reveal Fleets An...102471432023-12-21 10:01:0066 days 12 hrs ago1703152860IN
0x9648cD...11ECf8F6
0 ETH0.001146111.50000003
Accept Challenge102471422023-12-21 10:00:3666 days 12 hrs ago1703152836IN
0x9648cD...11ECf8F6
0 ETH0.000089721.50000003
Submit Challenge102470892023-12-21 9:45:0066 days 12 hrs ago1703151900IN
0x9648cD...11ECf8F6
0 ETH0.000129331.50000003
Reveal Fleets An...102470882023-12-21 9:44:4866 days 12 hrs ago1703151888IN
0x9648cD...11ECf8F6
0 ETH0.000788171.50000003
Accept Challenge102470852023-12-21 9:44:0066 days 12 hrs ago1703151840IN
0x9648cD...11ECf8F6
0 ETH0.000089411.50000003
Submit Challenge102470812023-12-21 9:42:4866 days 12 hrs ago1703151768IN
0x9648cD...11ECf8F6
0 ETH0.000129331.50000003
Accept Challenge102470622023-12-21 9:37:3666 days 12 hrs ago1703151456IN
0x9648cD...11ECf8F6
0 ETH0.000089411.50000003
Reveal Fleets An...102469592023-12-21 9:11:0066 days 13 hrs ago1703149860IN
0x9648cD...11ECf8F6
0 ETH0.000734821.50000002
Accept Challenge102469222023-12-21 9:00:2466 days 13 hrs ago1703149224IN
0x9648cD...11ECf8F6
0 ETH0.000089391.50000002
Submit Challenge102468742023-12-21 8:46:4866 days 13 hrs ago1703148408IN
0x9648cD...11ECf8F6
0 ETH0.000129351.50000003
Accept Challenge102418432023-12-20 9:56:2467 days 12 hrs ago1703066184IN
0x9648cD...11ECf8F6
0 ETH0.000089721.50000001
Submit Challenge100310172023-11-12 14:40:24105 days 7 hrs ago1699800024IN
0x9648cD...11ECf8F6
0 ETH0.000129331.5
Reveal Fleets An...100310152023-11-12 14:39:48105 days 7 hrs ago1699799988IN
0x9648cD...11ECf8F6
0 ETH0.001187771.5
Accept Challenge100310132023-11-12 14:39:24105 days 7 hrs ago1699799964IN
0x9648cD...11ECf8F6
0 ETH0.000089411.5
Submit Challenge99160082023-10-23 7:37:00125 days 14 hrs ago1698046620IN
0x9648cD...11ECf8F6
0 ETH0.000130281.51104552
Reveal Fleets An...99160062023-10-23 7:36:12125 days 14 hrs ago1698046572IN
0x9648cD...11ECf8F6
0 ETH0.001168551.51049325
Accept Challenge99160052023-10-23 7:35:48125 days 14 hrs ago1698046548IN
0x9648cD...11ECf8F6
0 ETH0.000090061.51097836
Submit Challenge99159992023-10-23 7:34:00125 days 14 hrs ago1698046440IN
0x9648cD...11ECf8F6
0 ETH0.000130241.51049375
Reveal Fleets An...99159982023-10-23 7:33:48125 days 14 hrs ago1698046428IN
0x9648cD...11ECf8F6
0 ETH0.001094721.51007759
Accept Challenge99159972023-10-23 7:33:36125 days 14 hrs ago1698046416IN
0x9648cD...11ECf8F6
0 ETH0.000090061.51084885
Submit Challenge99159902023-10-23 7:32:12125 days 14 hrs ago1698046332IN
0x9648cD...11ECf8F6
0 ETH0.000161071.51431489
Submit Challenge99159852023-10-23 7:31:12125 days 14 hrs ago1698046272IN
0x9648cD...11ECf8F6
0 ETH0.000130731.51625059
Reveal Fleets An...99159842023-10-23 7:30:48125 days 14 hrs ago1698046248IN
0x9648cD...11ECf8F6
0 ETH0.001010671.51500059
Accept Challenge99159832023-10-23 7:30:24125 days 14 hrs ago1698046224IN
0x9648cD...11ECf8F6
0 ETH0.000090321.51521043
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Txn Hash Block From To Value
102471442023-12-21 10:01:1266 days 12 hrs ago1703152872
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
102471432023-12-21 10:01:0066 days 12 hrs ago1703152860
0x9648cD...11ECf8F6
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Game

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 5 : Game.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "./generals/IGeneral.sol";
import "./utils/Attacks.sol";
import "./utils/Fleet.sol";
import "./utils/Board.sol";

error ChallengeDoesNotExist();
error ChallengeAldreadyExists();
error ChallengeAldreadyLocked();
error ChallengeNeedsToBeLocked();
error ChallengerDoesNotWantToPlayAgainstYou();
error FaciliatorPercentageUnitsWrong();
error FleetsNeedToHaveBeenRevealed();
error NotYourGeneral();
error NotEnoughEth();
error InvalidChallengeIndex();
error InvalidFleetHash();
error InvalidMaxTurns();

/**
 * @title 0xShip: On-Chain Battleship Game
 * @author Nazar Ilamanov <@nazar_ilamanov>
 * @notice Here is how the game works at a high level:
 * 1. A challenger submits a Challenge py picking a general (the contract
 *    that has playing logic) and the fleet.
 * 2. Anyone can then then accept this challenge. (To accept the challenge
 *    you need to provide your own general and fleet). Accepting the
 *    challenge locks a battle between the challenger and the caller.
 * 3. At this point, nothing can be modified about the game. Next step is
 *    to reveal your fleet and start the battle. Both of these operations
 *    can be performed by a 3rd party facilitator that will be compensated
 *    by a percentage of game bid.

 * (Fleet reveal is necessary because fleet is initially obfuscated by
 * providing only the hash of the fleet. This is so that opponents don't
 * know each other's fleet before the battle begins).
 */
contract Game {
    // -------------------------------------------- EVENTS --------------------------------------------

    event ChallengeSubmitted(
        bytes32 indexed challengeHash,
        uint256 indexed bidAmount,
        address indexed general,
        uint96 fleetHash,
        uint96 facilitatorPercentage,
        address preferredOpponent
    );
    event ChallengeAccepted(
        bytes32 indexed challengeHash,
        address indexed general,
        uint96 fleetHash
    );
    event ChallengeModified(
        bytes32 indexed challengeHash,
        uint256 indexed bidAmount,
        uint96 facilitatorPercentage,
        address preferredOpponent
    );
    event ChallengeWithdrawn(bytes32 indexed challengeHash);
    event FleetRevealed(
        uint96 indexed fleetHash,
        uint256 indexed fleet,
        bytes32 salt
    );
    event BattleConcluded(
        bytes32 indexed challengeHash,
        address indexed challengerGeneral,
        address indexed callerGeneral,
        uint256 challengerBoard,
        uint256 callerBoard,
        uint256 bidAmount,
        uint256 facilitatorPercentage,
        uint256 winnerIdx,
        uint256 outcome,
        uint256[] gameHistory,
        uint256 maxTurns
    );

    // ---------------- LOGIC FOR SUBMITTING/ACCEPTING/MODIFYING/WITHDRAWING CHALLENGES ----------------

    struct Challenge {
        Gear challenger; // initiator of the challenge
        Gear caller; // acceptor of the challenge
        // The rest is optional
        uint256 bidAmount; // can be 0 if not bidding any ETH
        uint96 facilitatorPercentage; // percentage of the bid rewarded for calling startBattle
        IGeneral preferredOpponent; // set by the challenger. If non-zero, then only preferredOpponent can accept the challenge
    }

    // gear brought to the game
    struct Gear {
        IGeneral general; // the general that contains the playing logic
        uint96 fleetHash; // hash of the fleet
        // hash is necessary so that players don't know each other fleet.
        // see revealFleet to understand how hash is computed.
        // using 96 bits for the hash so that entire struct is one word.
    }

    // challengeHash to Challenge
    mapping(bytes32 => Challenge) public challenges;
    // all challengeHashes
    bytes32[] public challengeHashes;

    function getAllChallenges()
        external
        view
        returns (Challenge[] memory allChallenges)
    {
        allChallenges = new Challenge[](challengeHashes.length);
        for (uint256 i = 0; i < allChallenges.length; i++) {
            allChallenges[i] = challenges[challengeHashes[i]];
        }
    }

    function _hashChallenge(Gear memory challengerGear)
        private
        pure
        returns (bytes32)
    {
        // challenger's gear is hashed as a way to "lock" the selected general and fleetHash.
        // but, the internal logic of the general can still be modified
        return
            keccak256(
                abi.encodePacked(
                    challengerGear.general,
                    challengerGear.fleetHash
                )
            );
    }

    modifier onlyOwnerOfGeneral(IGeneral general) {
        // check so players dont use other ppls code. Credit: 0xBeans
        if (general.owner() != msg.sender) revert NotYourGeneral();
        _;
    }

    function isChallengerGeneralSet(bytes32 challengeHash)
        private
        view
        returns (bool)
    {
        return
            address(challenges[challengeHash].challenger.general) != address(0);
    }

    function isCallerGeneralSet(bytes32 challengeHash)
        private
        view
        returns (bool)
    {
        return address(challenges[challengeHash].caller.general) != address(0);
    }

    // constant to scale uints into percentages (1e4 == 100%)
    uint96 private constant PERCENTAGE_SCALE = 1e4;

    function submitChallenge(
        Gear calldata gear,
        uint96 facilitatorPercentage,
        IGeneral preferredOpponent
    ) external payable onlyOwnerOfGeneral(gear.general) {
        bytes32 challengeHash = _hashChallenge(gear);
        if (isChallengerGeneralSet(challengeHash))
            revert ChallengeAldreadyExists();
        if (facilitatorPercentage > PERCENTAGE_SCALE)
            revert FaciliatorPercentageUnitsWrong();

        challenges[challengeHash].challenger = gear;
        challenges[challengeHash].bidAmount = msg.value;
        challenges[challengeHash].facilitatorPercentage = facilitatorPercentage;
        challenges[challengeHash].preferredOpponent = preferredOpponent;
        challengeHashes.push(challengeHash);

        emit ChallengeSubmitted(
            challengeHash,
            msg.value,
            address(gear.general),
            gear.fleetHash,
            facilitatorPercentage,
            address(preferredOpponent)
        );
    }

    function acceptChallenge(Gear calldata gear, bytes32 challengeHash)
        external
        payable
        onlyOwnerOfGeneral(gear.general)
    {
        if (!isChallengerGeneralSet(challengeHash))
            revert ChallengeDoesNotExist();
        if (isCallerGeneralSet(challengeHash)) revert ChallengeAldreadyLocked();

        IGeneral preferredOpponent = challenges[challengeHash]
            .preferredOpponent;
        if (
            address(preferredOpponent) != address(0) &&
            preferredOpponent != gear.general
        ) revert ChallengerDoesNotWantToPlayAgainstYou();

        if (msg.value < challenges[challengeHash].bidAmount)
            revert NotEnoughEth();

        // lock the challenge by making caller non-null
        challenges[challengeHash].caller = gear;

        emit ChallengeAccepted(
            challengeHash,
            address(gear.general),
            gear.fleetHash
        );
    }

    function modifyChallenge(
        Gear calldata oldGear,
        uint256 newBidAmount,
        uint96 newFacilitatorPercentage,
        IGeneral newPreferredOpponent
    ) external payable onlyOwnerOfGeneral(oldGear.general) {
        bytes32 challengeHash = _hashChallenge(oldGear);
        if (!isChallengerGeneralSet(challengeHash))
            revert ChallengeDoesNotExist();
        if (isCallerGeneralSet(challengeHash)) revert ChallengeAldreadyLocked();
        if (newFacilitatorPercentage > PERCENTAGE_SCALE)
            revert FaciliatorPercentageUnitsWrong();

        uint256 oldBidAmount = challenges[challengeHash].bidAmount;
        if (newBidAmount > oldBidAmount) {
            if (msg.value < (newBidAmount - oldBidAmount))
                revert NotEnoughEth();
        } else if (newBidAmount < oldBidAmount) {
            payable(msg.sender).transfer(oldBidAmount - newBidAmount);
        }
        challenges[challengeHash].bidAmount = newBidAmount;
        challenges[challengeHash]
            .facilitatorPercentage = newFacilitatorPercentage;
        challenges[challengeHash].preferredOpponent = newPreferredOpponent;

        emit ChallengeModified(
            challengeHash,
            newBidAmount,
            newFacilitatorPercentage,
            address(newPreferredOpponent)
        );
    }

    function withdrawChallenge(Gear calldata gear, uint256 challengeIdx)
        external
        onlyOwnerOfGeneral(gear.general)
    {
        bytes32 challengeHash = _hashChallenge(gear);
        if (isCallerGeneralSet(challengeHash)) revert ChallengeAldreadyLocked();

        uint256 bidAmount = challenges[challengeHash].bidAmount;
        if (bidAmount > 0) {
            payable(msg.sender).transfer(bidAmount);
        }

        if (challengeHashes[challengeIdx] != challengeHash)
            revert InvalidChallengeIndex();

        delete challenges[challengeHash];
        challengeHashes[challengeIdx] = challengeHashes[
            challengeHashes.length - 1
        ];
        challengeHashes.pop();

        emit ChallengeWithdrawn(challengeHash);
    }

    // -------------------------------------- LOGIC FOR REVEALING FLEET --------------------------------------

    // fleet uses 64 bits. See Fleet library for the layout of bits
    using Fleet for uint256;
    // board uses 192 bits. See Board library for the layout of bits
    using Board for uint256;

    struct FleetAndBoard {
        // in storage we only store what's necessary in order to bit-pack
        uint64 fleet;
        uint192 board;
    }

    // fleetHash to FleetAndBoard
    mapping(uint96 => FleetAndBoard) public fleetsAndBoards;

    function revealFleetsAndStartBattle(
        uint96 fleetHash1,
        uint256 fleet1,
        bytes32 salt1,
        uint96 fleetHash2,
        uint256 fleet2,
        bytes32 salt2,
        bytes32 challengeHash,
        uint256 challengeIdx,
        uint256 maxTurns,
        address facilitatorFeeAddress
    ) external {
        revealFleet(fleetHash1, fleet1, salt1);
        revealFleet(fleetHash2, fleet2, salt2);
        startBattle(
            challengeHash,
            challengeIdx,
            maxTurns,
            facilitatorFeeAddress
        );
    }

    function revealFleet(
        uint96 fleetHash,
        uint256 fleet,
        bytes32 salt
    ) public {
        // In order to obfuscate the fleet that a player starts with from the opponent,
        // we use fleetHash when commiting a/to Challenge.
        // After the challenge is accepted, the game is locked. Nothing can be changed about it.
        // So, now it's safe to reveal the fleet

        // in order to prevent memoization of fleet (rainbow attack), an optional salt can be
        // used to make it harder to reverse the hashing operation. salt can be any data.

        if (
            fleetHash !=
            uint96(uint256(keccak256(abi.encodePacked(fleet, salt))))
        ) revert InvalidFleetHash();

        // this function not only makes sure that the revealed fleet actually corresponds to the
        // provided fleetHash earlier, but also makes sure that fleet obeys the rules of the game,
        // i.e. correct number of ships, correct placement, etc.
        uint256 board = fleet.validateFleetAndConvertToBoard();

        // also store the board representation of the fleet for faster lookup
        fleetsAndBoards[fleetHash] = FleetAndBoard(
            uint64(fleet),
            uint192(board)
        );

        emit FleetRevealed(fleetHash, fleet, salt);
    }

    // ------------------------------------ LOGIC FOR PLAYING THE GAME ------------------------------------

    // attacks are represented using 192 bits. See Attacks library for the layout of bits
    using Attacks for uint256;

    uint256 internal constant OUTCOME_DRAW = 5;
    uint256 internal constant OUTCOME_ELIMINATED_OPPONENT = 1;
    uint256 internal constant OUTCOME_INFLICTED_MORE_DAMAGE = 2;
    uint256 internal constant NO_WINNER = 5;

    // To avoid stack too deep errors, use a struct to pack all game vars into one var
    // https://medium.com/1milliondevs/compilererror-stack-too-deep-try-removing-local-variables-solved-a6bcecc16231
    struct GameState {
        // The first 3 arrays are constant
        // TODO is it possible to shave off gas due to them being constant?
        IGeneral[2] generals;
        uint256[2] fleets;
        uint256[2] boards;
        // everything else is not constant
        uint256[2] attacks;
        uint256[2] lastMoves;
        uint256[2] opponentsDiscoveredFleet;
        uint256[5][2] remainingCells;
        uint256 currentPlayerIdx;
        uint256 otherPlayerIdx;
        uint256 winnerIdx;
        uint256 outcome;
        uint256[] gameHistory;
    }

    function startBattle(
        bytes32 challengeHash,
        uint256 challengeIdx,
        uint256 maxTurns,
        address facilitatorFeeAddress
    ) public {
        GameState memory gs = _getInitialGameState(challengeHash, maxTurns);

        if (address(gs.generals[1]) == address(0))
            revert ChallengeNeedsToBeLocked();
        if (gs.fleets[0] == 0 || gs.fleets[1] == 0)
            revert FleetsNeedToHaveBeenRevealed();
        if (
            ((maxTurns % 2) != 0) || (maxTurns < 21) // 21 is the least amount of moves to win the game
        ) revert InvalidMaxTurns();

        uint256 i;
        for (; i < maxTurns; i++) {
            gs.otherPlayerIdx = (gs.currentPlayerIdx + 1) % 2;

            uint256 returnedVal;
            uint256 cellToFire;
            try
                gs.generals[gs.currentPlayerIdx].fire{gas: 5_000}(
                    gs.boards[gs.currentPlayerIdx],
                    gs.attacks[gs.currentPlayerIdx],
                    gs.attacks[gs.otherPlayerIdx],
                    gs.lastMoves[gs.currentPlayerIdx],
                    gs.lastMoves[gs.otherPlayerIdx],
                    gs.opponentsDiscoveredFleet[gs.currentPlayerIdx]
                )
            returns (uint256 ret) {
                returnedVal = ret;
                cellToFire = ret & 63; // take last 6 bits only. 63 is 111111 in binary
            } catch {}

            gs.gameHistory[i + 1] = cellToFire;
            gs.lastMoves[gs.currentPlayerIdx] = returnedVal;

            // duplicate moves are ok
            if (
                !gs.attacks[gs.currentPlayerIdx].isOfType(
                    cellToFire,
                    Attacks.UNTOUCHED
                )
            ) {
                gs.currentPlayerIdx = gs.otherPlayerIdx;
                continue;
            }

            uint256 hitShipType = gs.boards[gs.otherPlayerIdx].getShipAt(
                cellToFire
            );

            if (hitShipType == Fleet.EMPTY) {
                gs.attacks[gs.currentPlayerIdx] = gs
                    .attacks[gs.currentPlayerIdx]
                    .markAs(cellToFire, Attacks.MISS);
                gs.currentPlayerIdx = gs.otherPlayerIdx;
                continue;
            }

            // it's a hit
            gs.attacks[gs.currentPlayerIdx] = gs
                .attacks[gs.currentPlayerIdx]
                .markAs(cellToFire, Attacks.HIT);

            // decrement number of cells remaining for the hit ship
            uint256 hitShipRemainingCells = --gs.remainingCells[
                gs.otherPlayerIdx
            ][hitShipType - 1];

            if (hitShipRemainingCells == 0) {
                // ship destroyed

                if (gs.attacks[gs.currentPlayerIdx].hasWon()) {
                    gs.winnerIdx = gs.currentPlayerIdx;
                    gs.outcome = OUTCOME_ELIMINATED_OPPONENT;
                    gs.gameHistory[i + 2] = 255;
                    break;
                }

                gs.opponentsDiscoveredFleet[gs.currentPlayerIdx] = gs
                    .fleets[gs.otherPlayerIdx]
                    .copyShipTo(
                        gs.opponentsDiscoveredFleet[gs.currentPlayerIdx],
                        uint8(hitShipType)
                    );
            }

            gs.currentPlayerIdx = gs.otherPlayerIdx;
        }

        if (gs.winnerIdx == NO_WINNER) {
            // game terminated due to maxTotalTurns
            gs.gameHistory[i + 1] = 255;

            uint256 numberOfShipDestroyed0 = _getNumberOfDestroyedShips(
                gs.remainingCells,
                0
            );
            uint256 numberOfShipDestroyed1 = _getNumberOfDestroyedShips(
                gs.remainingCells,
                1
            );

            if (numberOfShipDestroyed0 > numberOfShipDestroyed1) {
                gs.winnerIdx = 0;
                gs.outcome = OUTCOME_INFLICTED_MORE_DAMAGE;
            } else if (numberOfShipDestroyed0 < numberOfShipDestroyed1) {
                gs.winnerIdx = 1;
                gs.outcome = OUTCOME_INFLICTED_MORE_DAMAGE;
            } // else draw
        }

        distributeFunds(challengeHash, gs, maxTurns, facilitatorFeeAddress);

        if (challengeHashes[challengeIdx] != challengeHash)
            revert InvalidChallengeIndex();

        delete challenges[challengeHash];
        challengeHashes[challengeIdx] = challengeHashes[
            challengeHashes.length - 1
        ];
        challengeHashes.pop();
    }

    function distributeFunds(
        bytes32 challengeHash,
        GameState memory gs,
        uint256 maxTurns,
        address facilitatorFeeAddress
    ) private {
        uint256 bidAmount = challenges[challengeHash].bidAmount;
        uint256 amountToSplit = 2 * bidAmount;
        uint256 facilitatorPercentage = challenges[challengeHash]
            .facilitatorPercentage;

        if (amountToSplit > 0) {
            uint256 facilitatorFee = (amountToSplit * facilitatorPercentage) /
                PERCENTAGE_SCALE;
            payable(facilitatorFeeAddress).transfer(facilitatorFee);

            if (gs.winnerIdx == NO_WINNER) {
                amountToSplit = (amountToSplit - facilitatorFee) / 2;
                payable(gs.generals[0].owner()).transfer(amountToSplit);
                payable(gs.generals[1].owner()).transfer(amountToSplit);
            } else {
                amountToSplit -= facilitatorFee;
                payable(gs.generals[gs.winnerIdx].owner()).transfer(
                    amountToSplit
                );
            }
        }

        emit BattleConcluded(
            challengeHash,
            address(gs.generals[0]),
            address(gs.generals[1]),
            gs.boards[0],
            gs.boards[1],
            bidAmount,
            facilitatorPercentage,
            gs.winnerIdx,
            gs.outcome,
            gs.gameHistory,
            maxTurns
        );
    }

    function _getInitialGameState(bytes32 challengeHash, uint256 maxTurns)
        private
        view
        returns (GameState memory initialGameState)
    {
        initialGameState.generals = [
            challenges[challengeHash].challenger.general,
            challenges[challengeHash].caller.general
        ];

        {
            FleetAndBoard[2] memory fleetAndBoardsCached = [
                fleetsAndBoards[challenges[challengeHash].challenger.fleetHash],
                fleetsAndBoards[challenges[challengeHash].caller.fleetHash]
            ];
            initialGameState.fleets = [
                uint256(fleetAndBoardsCached[0].fleet),
                uint256(fleetAndBoardsCached[1].fleet)
            ];
            initialGameState.boards = [
                uint256(fleetAndBoardsCached[0].board),
                uint256(fleetAndBoardsCached[1].board)
            ];
        }

        initialGameState.attacks = [
            Attacks.EMPTY_ATTACKS,
            Attacks.EMPTY_ATTACKS
        ];
        initialGameState.lastMoves = [uint256(255), uint256(255)]; // initialize with 255 which indicates start of the game.
        //                                        valid moves are indicies into the 8x8 board, i.e. [0, 64)
        initialGameState.opponentsDiscoveredFleet = [
            Fleet.EMPTY_FLEET,
            Fleet.EMPTY_FLEET
        ];
        initialGameState.remainingCells = [
            [
                Fleet.PATROL_LENGTH,
                Fleet.DESTROYER_LENGTH,
                Fleet.DESTROYER_LENGTH,
                Fleet.CARRIER_LENGTH * Fleet.CARRIER_WIDTH,
                Fleet.BATTLESHIP_LENGTH
            ],
            [
                Fleet.PATROL_LENGTH,
                Fleet.DESTROYER_LENGTH,
                Fleet.DESTROYER_LENGTH,
                Fleet.CARRIER_LENGTH * Fleet.CARRIER_WIDTH,
                Fleet.BATTLESHIP_LENGTH
            ]
        ];

        // need to randomly choose first general to start firing.
        // use the timestamp as random input
        initialGameState.currentPlayerIdx = uint256(block.timestamp) % 2;

        initialGameState.winnerIdx = NO_WINNER;
        initialGameState.outcome = OUTCOME_DRAW;

        // used for emitting gameHistory in the event. first item in the history is the
        // idx of the first player to fire. Last item is 255 which indicates end of game.
        // the rest of the items are cells fired by players
        initialGameState.gameHistory = new uint256[](maxTurns + 2);
        initialGameState.gameHistory[0] = initialGameState.currentPlayerIdx;
    }

    function _getNumberOfDestroyedShips(
        uint256[5][2] memory remainingCells,
        uint256 playerIdx
    ) private pure returns (uint256 numberOfDestroyedShips) {
        for (uint256 i = 0; i < 5; i++) {
            if (remainingCells[playerIdx][i] == 0) {
                numberOfDestroyedShips++;
            }
        }
    }
}

File 2 of 5 : IGeneral.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IGeneral {
    // nickname of your general
    function name() external view returns (string memory);

    // must be the address you will be calling the Game contract from.
    // this value is to check that no one else is using your code to play. credit: 0xBeans
    function owner() external view returns (address);

    // this function needs to return an index into the 8x8 board, i.e. a value between [0 and 64).
    // a shell will be fired at this location. if you return >= 64, you're TKO'd
    // you're constrained by gas in this function. Check Game contract for max_gas
    // check Board library for the layout of bits of myBoard
    // check Attacks library for the layout of bits of attacks
    // check Fleet library for the layout of bits of fleet. Non-discovered fleet will have both,
    // the start and end coords =0
    function fire(
        uint256 myBoard,
        uint256 myAttacks,
        uint256 opponentsAttacks,
        uint256 myLastMove,
        uint256 opponentsLastMove,
        uint256 opponentsDiscoveredFleet
    ) external returns (uint256);
}

File 3 of 5 : Attacks.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * Attacks are represented using 256 bits but only the rightmost 192 bits are used.
 * Bit layout of the 192 bits:
 * [64 bits: whether a cell is a hit | 64 bits: whether a cell is a miss | 64 bits: whether a cell is untouched]
 * Each 64-bit piece has a layout like this:
 * [bit for 63rd cell | bit for 62nd cell | bit for 0th cell]
 */
library Attacks {
    // initializer for new battle. All cells are untouched
    uint256 internal constant EMPTY_ATTACKS = 0xFFFFFFFFFFFFFFFF;

    uint256 internal constant UNTOUCHED = 0;
    uint256 internal constant MISS = 1;
    uint256 internal constant HIT = 2;

    // zero_mask is same as [...63 zeros ... 1 ... 63 zeros ... 1 ... 63 zeros ... 1]
    uint256 private constant FIRST_CELL = 0x100000000000000010000000000000001;

    function isOfType(
        uint256 attacks,
        uint256 cellIdx,
        uint256 attackType
    ) internal pure returns (bool) {
        // cell of interest is at shiftBy = attackType * 64 + cellIdx = (attackType << 6) + cellIdx
        // need to check whether attacks & (1 << shiftBy) is non-zero
        return (attacks & (1 << ((attackType << 6) + cellIdx))) > 0;
    }

    function markAs(
        uint256 attacks,
        uint256 cellIdx,
        uint256 attackType
    ) internal pure returns (uint256 updatedAttacks) {
        // zeroMask is for zeroing out all attackTypes for a given cell
        uint256 zeroMask = ~(FIRST_CELL << cellIdx);
        // oneMask is for setting bit at attackType to 1
        uint256 oneMask = 1 << ((attackType << 6) + cellIdx);
        return (attacks & zeroMask) | oneMask;
    }

    function numberOfEmptyCells(uint256 attacks)
        internal
        pure
        returns (uint256)
    {
        return
            hammingDistance64(
                (attacks >> (UNTOUCHED << 6)) & 0xFFFFFFFFFFFFFFFF
            );
    }

    function numberOfMisses(uint256 attacks) internal pure returns (uint256) {
        return hammingDistance64((attacks >> (MISS << 6)) & 0xFFFFFFFFFFFFFFFF);
    }

    function numberOfHits(uint256 attacks) internal pure returns (uint256) {
        return hammingDistance64((attacks >> (HIT << 6)) & 0xFFFFFFFFFFFFFFFF);
    }

    function hasWon(uint256 attacks) internal pure returns (bool) {
        // hasWon is when numberOfHits == 21
        return
            hammingDistance64((attacks >> (HIT << 6)) & 0xFFFFFFFFFFFFFFFF) ==
            21; // 21 is total number of cells occupied by ships
    }

    function hammingDistance64(uint256 x) internal pure returns (uint256) {
        // Computes hamming distance (weight) of a 64-bit number.
        // Hamming distance is number of bits that are set to 1. See:
        // - https://en.wikipedia.org/wiki/Hamming_weight
        // - https://stackoverflow.com/questions/2709430/count-number-of-bits-in-a-64-bit-long-big-integer
        // and for 32-bit version see:
        // - http://graphics.stanford.edu/~seander/bithacks.html (Counting bits set, in parallel section)
        // - https://stackoverflow.com/questions/14555607/number-of-bits-set-in-a-number
        // - https://stackoverflow.com/questions/15233121/calculating-hamming-weight-in-o1

        // implementation from https://en.wikipedia.org/wiki/Hamming_weight
        x -= (x >> 1) & 0x5555555555555555; // put count of each 2 bits into those 2 bits
        x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); // put count of each 4 bits into those 4 bits
        x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f; // put count of each 8 bits into those 8 bits
        return uint64(x * 0x0101010101010101) >> 56; // returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
    }
}

File 4 of 5 : Fleet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "./Board.sol";

error CoordsAreNotSorted(uint256 shipType);
error NotRightSizeOrOrientation(uint256 shipType);

/**
 * Board is represented using 256 bits, but only rightmost 64 bits are used. Bit layout of the 64 bits:
 * [4 empty bits | battleship start coord | battleship end coord | carrier start coord | ... | patrol end coord]
 * Each coord is 6 bits and it's an index into the 8x8 board, i.e. [0, 64)
 */
library Fleet {
    // initializer for undiscovered fleet
    uint256 internal constant EMPTY_FLEET = 0;

    // ship types
    uint256 internal constant EMPTY = 0;
    uint256 internal constant PATROL = 1;
    uint256 internal constant DESTROYER1 = 2;
    uint256 internal constant DESTROYER2 = 3;
    uint256 internal constant CARRIER = 4;
    uint256 internal constant BATTLESHIP = 5;

    // ship sizes
    uint256 internal constant PATROL_LENGTH = 2;
    uint256 internal constant DESTROYER_LENGTH = 3;
    uint256 internal constant CARRIER_LENGTH = 4;
    uint256 internal constant CARRIER_WIDTH = 2;
    uint256 internal constant BATTLESHIP_LENGTH = 5;

    function getCoords(uint256 fleet, uint256 shipType)
        internal
        pure
        returns (uint256)
    {
        // each ship takes up 2*6=12 bits. Need to shift right by
        // correct number of bits and take only the remaining 12 bits (0xFFF is 12 ones)
        return (fleet >> (12 * (shipType - 1))) & 0xFFF;
    }

    // populate toFleet with the coords of shipType from fromFleet
    function copyShipTo(
        uint256 fromFleet,
        uint256 toFleet,
        uint256 shipType
    ) internal pure returns (uint256) {
        uint256 selectShip = 0xFFF << (shipType - 1); // 0xFFF is 12 ones
        return (fromFleet & selectShip) | (toFleet & ~selectShip);
    }

    using Board for Board.BuildData;

    // This struct is only used to avoid stack too deep errors, use a struct to pack all vars into one var
    // https://medium.com/1milliondevs/compilererror-stack-too-deep-try-removing-local-variables-solved-a6bcecc16231
    struct Coords {
        uint256 start;
        uint256 end;
        uint256 diff;
        uint256 horizontal;
        uint256 vertical;
    }

    // only used in Game.revealBoard to validate the fleet and store the board representation
    // of the fleet for faster lookup
    function validateFleetAndConvertToBoard(uint256 fleet)
        internal
        pure
        returns (uint256)
    {
        // make sure that the fleet is properly formatted: ships have the right size and
        // orientation (either horizontal or vertical)

        // ------------------------------ PATROL ship validation ------------------------------
        Coords memory patrolCoords;
        uint256 tmpCoords = getCoords(fleet, PATROL);
        patrolCoords.start = tmpCoords >> 6;
        patrolCoords.end = tmpCoords & 0x3F;
        // start and end are guaranteed to be within [0, 64)
        // because we mask out the leading bits in getCoords[X]
        if (patrolCoords.start >= patrolCoords.end)
            revert CoordsAreNotSorted(PATROL);

        patrolCoords.diff = patrolCoords.end - patrolCoords.start;
        patrolCoords.horizontal = PATROL_LENGTH - 1;
        // required difference between end and start coords of Patrol ship in horizontal orientation
        patrolCoords.vertical = patrolCoords.horizontal << 3; // "<< 3" is same as "* 8"
        // required difference in vertical orientation (because indices wrap around the 8x8 board)
        if (
            !(patrolCoords.diff == patrolCoords.horizontal ||
                patrolCoords.diff == patrolCoords.vertical)
        ) revert NotRightSizeOrOrientation(PATROL);

        // ------------------------------ DESTROYER1 ship validation ------------------------------
        Coords memory destroyer1Coords;
        tmpCoords = getCoords(fleet, DESTROYER1);
        destroyer1Coords.start = tmpCoords >> 6;
        destroyer1Coords.end = tmpCoords & 0x3F;
        if (destroyer1Coords.start >= destroyer1Coords.end)
            revert CoordsAreNotSorted(DESTROYER1);

        destroyer1Coords.diff = destroyer1Coords.end - destroyer1Coords.start;
        destroyer1Coords.horizontal = DESTROYER_LENGTH - 1;
        destroyer1Coords.vertical = destroyer1Coords.horizontal << 3;
        if (
            !(destroyer1Coords.diff == destroyer1Coords.horizontal ||
                destroyer1Coords.diff == destroyer1Coords.vertical)
        ) revert NotRightSizeOrOrientation(DESTROYER1);

        // ------------------------------ DESTROYER2 ship validation ------------------------------
        Coords memory destroyer2Coords;
        tmpCoords = getCoords(fleet, DESTROYER2);
        destroyer2Coords.start = tmpCoords >> 6;
        destroyer2Coords.end = tmpCoords & 0x3F;
        if (destroyer2Coords.start >= destroyer2Coords.end)
            revert CoordsAreNotSorted(DESTROYER2);

        destroyer2Coords.diff = destroyer2Coords.end - destroyer2Coords.start;
        if (
            !(destroyer2Coords.diff == destroyer1Coords.horizontal ||
                destroyer2Coords.diff == destroyer1Coords.vertical)
        ) revert NotRightSizeOrOrientation(DESTROYER2);

        // ------------------------------ BATTLESHIP ship validation ------------------------------
        Coords memory battleshipCoords;
        tmpCoords = getCoords(fleet, BATTLESHIP);
        battleshipCoords.start = tmpCoords >> 6;
        battleshipCoords.end = tmpCoords & 0x3F;
        if (battleshipCoords.start >= battleshipCoords.end)
            revert CoordsAreNotSorted(BATTLESHIP);

        battleshipCoords.diff = battleshipCoords.end - battleshipCoords.start;
        battleshipCoords.horizontal = BATTLESHIP_LENGTH - 1;
        battleshipCoords.vertical = battleshipCoords.horizontal << 3;
        if (
            !(battleshipCoords.diff == battleshipCoords.horizontal ||
                battleshipCoords.diff == battleshipCoords.vertical)
        ) revert NotRightSizeOrOrientation(BATTLESHIP);

        // ------------------------------ CARRIER ship validation ------------------------------
        Coords memory carrierCoords;
        tmpCoords = getCoords(fleet, CARRIER);
        carrierCoords.start = tmpCoords >> 6;
        carrierCoords.end = tmpCoords & 0x3F;
        if (carrierCoords.start >= carrierCoords.end)
            revert CoordsAreNotSorted(CARRIER);

        carrierCoords.diff = carrierCoords.end - carrierCoords.start;
        carrierCoords.horizontal =
            (CARRIER_LENGTH - 1) +
            ((CARRIER_WIDTH - 1) << 3); // carrier size is 4x2
        carrierCoords.vertical =
            ((CARRIER_LENGTH - 1) << 3) +
            (CARRIER_WIDTH - 1);
        if (
            !(carrierCoords.diff == carrierCoords.horizontal ||
                carrierCoords.diff == carrierCoords.vertical)
        ) revert NotRightSizeOrOrientation(CARRIER);

        // ------------------------------ building the board ------------------------------
        // This piece converts the fleet into 8x8 board representation.
        // It also validates that ships don't overlap and that
        // ships are not too close.

        // construct the board from fleet info
        Board.BuildData memory constructedBoard = Board.BuildData(0, 0);
        constructedBoard.placeShip(
            patrolCoords.start,
            patrolCoords.end,
            PATROL
        );
        constructedBoard.placeShip(
            destroyer1Coords.start,
            destroyer1Coords.end,
            DESTROYER1
        );
        constructedBoard.placeShip(
            destroyer2Coords.start,
            destroyer2Coords.end,
            DESTROYER2
        );
        constructedBoard.placeShip(
            carrierCoords.start,
            carrierCoords.end,
            CARRIER
        );
        constructedBoard.placeShip(
            battleshipCoords.start,
            battleshipCoords.end,
            BATTLESHIP
        );
        return constructedBoard.board;
    }
}

File 5 of 5 : Board.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

error CantWrapShip(uint256 shipType);
error ShipCollidesOrTooClose(uint256 shipType);

/**
 * Board is represented using 256 bits, but only rightmost 192 bits are used. Bit layout of the 192 bits:
 * [3 bits: 63rd cell | 3 bits: 62nd cell | ... | 3 bits: 0th cell]
 * Each cell is 3 bits and it indicates shipType (1,2,3,4,5) or empty (0) at that cell.
 * The shipType values are from Fleet.PATROL, Fleet.DESTROYER1, etc.
 */
library Board {
    function getShipAt(uint256 board, uint256 cellIdx)
        internal
        pure
        returns (uint256)
    {
        return (board >> (cellIdx * 3)) & 0x7;
    }

    // ---------- Helper to build a board from its fleet representation ----------
    struct BuildData {
        uint256 board;
        uint256 occupancyGrid; // all 3 bits at each cell will be 1 if there is a ship at a given cell
    }

    // assumes startCoord < endCoord
    function placeShip(
        BuildData memory buildData,
        uint256 startCoord,
        uint256 endCoord,
        uint256 shipType
    ) internal pure {
        // TODO is there a constant time bit operation that we can use instead of a loop in the function?
        uint256 startY = startCoord >> 3; // divide by 8
        uint256 endY = endCoord >> 3;
        uint256 startX = startCoord % 8;
        uint256 endX = endCoord % 8;

        if (startX > endX) revert CantWrapShip(shipType);

        uint256 ship; // cells occupied by this ship
        for (uint256 y = startY; y <= endY; y++) {
            for (uint256 x = startX; x <= endX; x++) {
                // to mark the cell we need to do shipType << (3 * cellIdx)
                // where cellIdx = y * 8 + x = (y << 3) + x
                ship |= shipType << (3 * ((y << 3) + x));
            }
        }
        if (ship & buildData.occupancyGrid != 0)
            revert ShipCollidesOrTooClose(shipType);

        if (startX > 0) {
            startX--;
        }
        if (startY > 0) {
            startY--;
        }
        if (endX < 7) {
            endX++;
        }
        if (endY < 7) {
            endY++;
        }
        uint256 shipAndAdjacent; // cells occupied by this ship and a padding of 1
        // You can't place another ship close than this padding
        for (uint256 y = startY; y <= endY; y++) {
            for (uint256 x = startX; x <= endX; x++) {
                // we do the same as in the above loop but use 111=0x7 instead of shipType
                shipAndAdjacent |= 0x7 << (3 * ((y << 3) + x));
            }
        }

        buildData.board |= ship;
        buildData.occupancyGrid |= shipAndAdjacent;
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"shipType","type":"uint256"}],"name":"CantWrapShip","type":"error"},{"inputs":[],"name":"ChallengeAldreadyExists","type":"error"},{"inputs":[],"name":"ChallengeAldreadyLocked","type":"error"},{"inputs":[],"name":"ChallengeDoesNotExist","type":"error"},{"inputs":[],"name":"ChallengeNeedsToBeLocked","type":"error"},{"inputs":[],"name":"ChallengerDoesNotWantToPlayAgainstYou","type":"error"},{"inputs":[{"internalType":"uint256","name":"shipType","type":"uint256"}],"name":"CoordsAreNotSorted","type":"error"},{"inputs":[],"name":"FaciliatorPercentageUnitsWrong","type":"error"},{"inputs":[],"name":"FleetsNeedToHaveBeenRevealed","type":"error"},{"inputs":[],"name":"InvalidChallengeIndex","type":"error"},{"inputs":[],"name":"InvalidFleetHash","type":"error"},{"inputs":[],"name":"InvalidMaxTurns","type":"error"},{"inputs":[],"name":"NotEnoughEth","type":"error"},{"inputs":[{"internalType":"uint256","name":"shipType","type":"uint256"}],"name":"NotRightSizeOrOrientation","type":"error"},{"inputs":[],"name":"NotYourGeneral","type":"error"},{"inputs":[{"internalType":"uint256","name":"shipType","type":"uint256"}],"name":"ShipCollidesOrTooClose","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"challengeHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"challengerGeneral","type":"address"},{"indexed":true,"internalType":"address","name":"callerGeneral","type":"address"},{"indexed":false,"internalType":"uint256","name":"challengerBoard","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"callerBoard","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bidAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"facilitatorPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"winnerIdx","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outcome","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"gameHistory","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"maxTurns","type":"uint256"}],"name":"BattleConcluded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"challengeHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"general","type":"address"},{"indexed":false,"internalType":"uint96","name":"fleetHash","type":"uint96"}],"name":"ChallengeAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"challengeHash","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"bidAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"facilitatorPercentage","type":"uint96"},{"indexed":false,"internalType":"address","name":"preferredOpponent","type":"address"}],"name":"ChallengeModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"challengeHash","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"bidAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"general","type":"address"},{"indexed":false,"internalType":"uint96","name":"fleetHash","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"facilitatorPercentage","type":"uint96"},{"indexed":false,"internalType":"address","name":"preferredOpponent","type":"address"}],"name":"ChallengeSubmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"challengeHash","type":"bytes32"}],"name":"ChallengeWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"fleetHash","type":"uint96"},{"indexed":true,"internalType":"uint256","name":"fleet","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"FleetRevealed","type":"event"},{"inputs":[{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"gear","type":"tuple"},{"internalType":"bytes32","name":"challengeHash","type":"bytes32"}],"name":"acceptChallenge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"challengeHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"challenges","outputs":[{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"challenger","type":"tuple"},{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"caller","type":"tuple"},{"internalType":"uint256","name":"bidAmount","type":"uint256"},{"internalType":"uint96","name":"facilitatorPercentage","type":"uint96"},{"internalType":"contract IGeneral","name":"preferredOpponent","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"","type":"uint96"}],"name":"fleetsAndBoards","outputs":[{"internalType":"uint64","name":"fleet","type":"uint64"},{"internalType":"uint192","name":"board","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllChallenges","outputs":[{"components":[{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"challenger","type":"tuple"},{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"caller","type":"tuple"},{"internalType":"uint256","name":"bidAmount","type":"uint256"},{"internalType":"uint96","name":"facilitatorPercentage","type":"uint96"},{"internalType":"contract IGeneral","name":"preferredOpponent","type":"address"}],"internalType":"struct Game.Challenge[]","name":"allChallenges","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"oldGear","type":"tuple"},{"internalType":"uint256","name":"newBidAmount","type":"uint256"},{"internalType":"uint96","name":"newFacilitatorPercentage","type":"uint96"},{"internalType":"contract IGeneral","name":"newPreferredOpponent","type":"address"}],"name":"modifyChallenge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint96","name":"fleetHash","type":"uint96"},{"internalType":"uint256","name":"fleet","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"revealFleet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"fleetHash1","type":"uint96"},{"internalType":"uint256","name":"fleet1","type":"uint256"},{"internalType":"bytes32","name":"salt1","type":"bytes32"},{"internalType":"uint96","name":"fleetHash2","type":"uint96"},{"internalType":"uint256","name":"fleet2","type":"uint256"},{"internalType":"bytes32","name":"salt2","type":"bytes32"},{"internalType":"bytes32","name":"challengeHash","type":"bytes32"},{"internalType":"uint256","name":"challengeIdx","type":"uint256"},{"internalType":"uint256","name":"maxTurns","type":"uint256"},{"internalType":"address","name":"facilitatorFeeAddress","type":"address"}],"name":"revealFleetsAndStartBattle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"challengeHash","type":"bytes32"},{"internalType":"uint256","name":"challengeIdx","type":"uint256"},{"internalType":"uint256","name":"maxTurns","type":"uint256"},{"internalType":"address","name":"facilitatorFeeAddress","type":"address"}],"name":"startBattle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"gear","type":"tuple"},{"internalType":"uint96","name":"facilitatorPercentage","type":"uint96"},{"internalType":"contract IGeneral","name":"preferredOpponent","type":"address"}],"name":"submitChallenge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IGeneral","name":"general","type":"address"},{"internalType":"uint96","name":"fleetHash","type":"uint96"}],"internalType":"struct Game.Gear","name":"gear","type":"tuple"},{"internalType":"uint256","name":"challengeIdx","type":"uint256"}],"name":"withdrawChallenge","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50612a07806100206000396000f3fe60806040526004361061009c5760003560e01c8063c1e69b6611610064578063c1e69b6614610142578063dc645241146101e6578063dde8ab7814610206578063e3f5675814610226578063e7db485c14610246578063f698bfe3146102b757600080fd5b80636090e61b146100a157806371c3c892146100b6578063829ef1ac146100c9578063a8af8a8d146100f4578063b7dde4d714610122575b600080fd5b6100b46100af366004612453565b6102ca565b005b6100b46100c43660046124a5565b610527565b3480156100d557600080fd5b506100de61075d565b6040516100eb91906124f1565b60405180910390f35b34801561010057600080fd5b5061011461010f366004612583565b6108ec565b6040519081526020016100eb565b34801561012e57600080fd5b506100b461013d36600461259c565b61090d565b34801561014e57600080fd5b506101d561015d366004612583565b600060208181529181526040908190208151808301835281546001600160a01b0380821683526001600160601b03600160a01b92839004811684880152855180870190965260018501548083168752929092048216958501959095526002830154600390930154919490821691600160601b90041685565b6040516100eb95949392919061262d565b3480156101f257600080fd5b506100b4610201366004612676565b61093b565b34801561021257600080fd5b506100b46102213660046124a5565b610a32565b34801561023257600080fd5b506100b46102413660046126ab565b610c71565b34801561025257600080fd5b5061028f6102613660046126e1565b60026020526000908152604090205467ffffffffffffffff811690600160401b90046001600160c01b031682565b6040805167ffffffffffffffff90931683526001600160c01b039091166020830152016100eb565b6100b46102c5366004612705565b611307565b6102d7602085018561274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561031f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610343919061276c565b6001600160a01b03161461036a5760405163388d0a1f60e01b815260040160405180910390fd5b600061038361037e3688900388018861279f565b611524565b6000818152602081905260409020549091506001600160a01b03166103ba57604051623126e360e41b815260040160405180910390fd5b6000818152602081905260409020600101546001600160a01b0316156103f35760405163123af06b60e01b815260040160405180910390fd5b6127106001600160601b038516111561041f576040516361d2bb0360e01b815260040160405180910390fd5b60008181526020819052604090206002015480861115610468576104438187612822565b3410156104635760405163f14a42b760e01b815260040160405180910390fd5b6104a8565b808610156104a857336108fc61047e8884612822565b6040518115909202916000818181858888f193505050501580156104a6573d6000803e3d6000fd5b505b60008281526020818152604091829020600281018990556001600160601b038816600160601b6001600160a01b0389169081028217600390930192909255835190815291820152879184917f6887717706805f48aaf66cb6e4877e11e8eb9270a65f625d62a0b47121851427910160405180910390a350505050505050565b610534602083018361274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561057c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a0919061276c565b6001600160a01b0316146105c75760405163388d0a1f60e01b815260040160405180910390fd5b6000828152602081905260409020546001600160a01b03166105fb57604051623126e360e41b815260040160405180910390fd5b6000828152602081905260409020600101546001600160a01b0316156106345760405163123af06b60e01b815260040160405180910390fd5b600082815260208190526040902060030154600160601b90046001600160a01b03168015801590610683575061066d602085018561274f565b6001600160a01b0316816001600160a01b031614155b156106a157604051630f06503560e41b815260040160405180910390fd5b6000838152602081905260409020600201543410156106d35760405163f14a42b760e01b815260040160405180910390fd5b600083815260208190526040902084906001016106f08282612835565b506107009050602085018561274f565b6001600160a01b0316837f6ee50ac97c0c46c8b2867cb72d830af9312101598bd23b5585679d7a0d01c06061073b60408801602089016126e1565b6040516001600160601b0390911681526020015b60405180910390a350505050565b60015460609067ffffffffffffffff81111561077b5761077b612789565b6040519080825280602002602001820160405280156107f257816020015b6040805160e081018252600060a0820181815260c0830182905282528251808401845281815260208082018390528301529181018290526060810182905260808101919091528152602001906001900390816107995790505b50905060005b81518110156108e8576000806001838154811061081757610817612877565b600091825260208083209091015483528281019390935260409182019020815160e08101835281546001600160a01b0380821660a084019081526001600160601b03600160a01b93849004811660c0860152908452855180870187526001860154808416825293909304811683880152958301919091526002830154938201939093526003909101549283166060820152600160601b90920416608082015282518390839081106108ca576108ca612877565b602002602001018190525080806108e09061288d565b9150506107f8565b5090565b600181815481106108fc57600080fd5b600091825260209091200154905081565b6109188a8a8a61093b565b61092387878761093b565b61092f84848484610c71565b50505050505050505050565b60408051602081018490529081018290526060016040516020818303038152906040528051906020012060001c6001600160601b0316836001600160601b031614610999576040516329077eab60e11b815260040160405180910390fd5b60006109a483611588565b60408051808201825267ffffffffffffffff80871682526001600160c01b0380851660208085019182526001600160601b038b16600081815260029092529086902094519151909216600160401b02921691909117909155905191925084917fccfa43dde42b308c80e0eea01887c538073ac753b3958d9fc5f1acfc75e9216a9061074f9086815260200190565b610a3f602083018361274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aab919061276c565b6001600160a01b031614610ad25760405163388d0a1f60e01b815260040160405180910390fd5b6000610ae661037e3686900386018661279f565b6000818152602081905260409020600101549091506001600160a01b031615610b225760405163123af06b60e01b815260040160405180910390fd5b6000818152602081905260409020600201548015610b6957604051339082156108fc029083906000818181858888f19350505050158015610b67573d6000803e3d6000fd5b505b8160018581548110610b7d57610b7d612877565b906000526020600020015414610ba657604051634bfe8d9f60e01b815260040160405180910390fd5b60008281526020819052604081208181556001808201839055600282018390556003909101919091558054610bdc908290612822565b81548110610bec57610bec612877565b906000526020600020015460018581548110610c0a57610c0a612877565b6000918252602090912001556001805480610c2757610c276128a6565b60019003818190600052602060002001600090559055817fa5c028458c27d7cc75621d18a58fe289c65a143f70064736b1ff38def064fb6460405160405180910390a25050505050565b6000610c7d85846119c2565b8051602001519091506001600160a01b0316610cac57604051633cdf589160e21b815260040160405180910390fd5b6020810151511580610cc357506020818101510151155b15610ce157604051630fbc4d2d60e01b815260040160405180910390fd5b610cec6002846128d2565b151580610cf95750601583105b15610d175760405163c7c2ccaf60e01b815260040160405180910390fd5b60005b838110156111835760028260e001516001610d3591906128e6565b610d3f91906128d2565b610100830152815160e0830151600091829160028110610d6157610d61612877565b60200201516001600160a01b031663436cc79561138886604001518760e0015160028110610d9157610d91612877565b602002015187606001518860e0015160028110610db057610db0612877565b6020020151886060015189610100015160028110610dd057610dd0612877565b602002015189608001518a60e0015160028110610def57610def612877565b60200201518a608001518b610100015160028110610e0f57610e0f612877565b60200201518b60a001518c60e0015160028110610e2e57610e2e612877565b60200201516040516001600160e01b031960e08a901b1681526004810196909652602486019490945260448501929092526064840152608483015260a482015260c40160206040518083038160008887f193505050508015610ead575060408051601f3d908101601f19168201909252610eaa918101906128f9565b60015b15610eba57915050603f81165b6101608401518190610ecd8560016128e6565b81518110610edd57610edd612877565b6020026020010181815250508184608001518560e0015160028110610f0457610f04612877565b602002018181525050610f3981600086606001518760e0015160028110610f2d57610f2d612877565b60200201519190611c1f565b610f4f57505061010082015160e0830152611171565b6000610f7b82866040015187610100015160028110610f7057610f70612877565b602002015190611c40565b905080610fe057610fae82600187606001518860e0015160028110610fa257610fa2612877565b60200201519190611c5c565b85606001518660e0015160028110610fc857610fc8612877565b602002015250505061010082015160e0830152611171565b61100082600287606001518860e0015160028110610fa257610fa2612877565b85606001518660e001516002811061101a5761101a612877565b602002015260c0850151610100860151600091906002811061103e5761103e612877565b602002015161104e600184612822565b6005811061105e5761105e612877565b60200201805161106d90612912565b9081815250905080600003611161576110a386606001518760e001516002811061109957611099612877565b6020020151611c93565b156110f45760e0860151610120870152600161014087015261016086015160ff906110cf8760026128e6565b815181106110df576110df612877565b60200260200101818152505050505050611183565b6111418660a001518760e001516002811061111157611111612877565b60200201518360ff1688602001518961010001516002811061113557611135612877565b60200201519190611cb5565b8660a001518760e001516002811061115b5761115b612877565b60200201525b50505061010083015160e0840152505b8061117b8161288d565b915050610d1a565b60058261012001510361121f5761016082015160ff906111a48360016128e6565b815181106111b4576111b4612877565b60200260200101818152505060006111d18360c001516000611cda565b905060006111e48460c001516001611cda565b905080821115611203576000610120850152600261014085015261121c565b8082101561121c57600161012085015260026101408501525b50505b61122b86838686611d42565b856001868154811061123f5761123f612877565b90600052602060002001541461126857604051634bfe8d9f60e01b815260040160405180910390fd5b6000868152602081905260408120818155600180820183905560028201839055600390910191909155805461129e908290612822565b815481106112ae576112ae612877565b9060005260206000200154600186815481106112cc576112cc612877565b60009182526020909120015560018054806112e9576112e96128a6565b60019003818190600052602060002001600090559055505050505050565b611314602084018461274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561135c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611380919061276c565b6001600160a01b0316146113a75760405163388d0a1f60e01b815260040160405180910390fd5b60006113bb61037e3687900387018761279f565b6000818152602081905260409020549091506001600160a01b0316156113f457604051634c93111160e01b815260040160405180910390fd5b6127106001600160601b0385161115611420576040516361d2bb0360e01b815260040160405180910390fd5b6000818152602081905260409020859061143a8282612835565b505060008181526020818152604082203460028201556001600160a01b038616600160601b026001600160601b0388161760039091015560018054808201825592527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf69091018290556114af9086018661274f565b6001600160a01b031634827f87143ee8969c7635cf96224732f26903c7736d877f7ec60bc44dec67dc88a5146114eb60408a0160208b016126e1565b604080516001600160601b039283168152918a1660208301526001600160a01b0389169082015260600160405180910390a45050505050565b60008160000151826020015160405160200161156b92919060609290921b6bffffffffffffffffffffffff1916825260a01b6001600160a01b031916601482015260200190565b604051602081830303815290604052805190602001209050919050565b60006115926122eb565b600061159f8460016120a3565b600681901c808452603f821660208501819052919250106115db576040516325e3b13160e21b8152600160048201526024015b60405180910390fd5b815160208301516115ec9190612822565b60408301526115fd60016002612822565b60608301819052600381901b608084015260408301511480611626575081608001518260400151145b61164657604051630ac662cf60e41b8152600160048201526024016115d2565b61164e6122eb565b6116598560026120a3565b600681901c808352603f82166020840181905291935010611690576040516325e3b13160e21b8152600260048201526024016115d2565b805160208201516116a19190612822565b60408201526116b260016003612822565b60608201819052600381901b6080830152604082015114806116db575080608001518160400151145b6116fb57604051630ac662cf60e41b8152600260048201526024016115d2565b6117036122eb565b61170e8660036120a3565b600681901c808352603f82166020840181905291945010611745576040516325e3b13160e21b8152600360048201526024016115d2565b805160208201516117569190612822565b6040820181905260608301511480611775575081608001518160400151145b61179557604051630ac662cf60e41b8152600360048201526024016115d2565b61179d6122eb565b6117a88760056120a3565b600681901c808352603f821660208401819052919550106117df576040516325e3b13160e21b8152600560048201526024016115d2565b805160208201516117f09190612822565b604082015261180160016005612822565b60608201819052600381901b60808301526040820151148061182a575080608001518160400151145b61184a57604051630ac662cf60e41b8152600560048201526024016115d2565b6118526122eb565b61185d8860046120a3565b600681901c808352603f82166020840181905291965010611893576040516325e3b13160e21b81526004818101526024016115d2565b805160208201516118a49190612822565b604082015260036118b760016002612822565b901b6118c560016004612822565b6118cf91906128e6565b60608201526118e060016002612822565b60036118ee60016004612822565b6118f992911b6128e6565b608082015260608101516040820151148061191b575080608001518160400151145b61193a57604051630ac662cf60e41b81526004818101526024016115d2565b60408051808201909152600080825260208083019190915287519088015161196591839160016120ca565b8451602086015161197991839160026120ca565b8351602085015161198d91839160036120ca565b815160208301516119a191839160046120ca565b825160208401516119b591839160056120ca565b5198975050505050505050565b6119ca61231a565b6040805180820182526000858152602081815283822080546001600160a01b0380821686526001909201549182168386015293865284516001600160601b03600160a01b9586900481168552600280855287862060808085018a52905467ffffffffffffffff808216868c019081526001600160c01b03600160401b9384900481166060808a01919091529188529a90970490941688528287528988208a51808c018c5290548086168252919091048916818801528487019081528951808b018b52855151851681528151518516818901528b8801528951808b018b529451870151891685525186015190971683860152888801929092528651808801885281815280850191909152828801528551808701875260ff8082528185015285880152855180870187528481529283019390935260a080870192909252845160e08101865294850183815260039186018290529385015283929190830190611b31906004612929565b8152602001600581525081526020016040518060a0016040528060028152602001600381526020016003815260200160026004611b6e9190612929565b81526005602090910152905260c0820152611b8a6002426128d2565b60e082015260056101208201819052610140820152611baa8260026128e6565b67ffffffffffffffff811115611bc257611bc2612789565b604051908082528060200260200182016040528015611beb578160200160208202803683370190505b50610160820181905260e08201518151909190600090611c0d57611c0d612877565b60200260200101818152505092915050565b600080611c3084600685901b6128e6565b6001901b85161190509392505050565b6000611c4d826003612929565b83901c60071690505b92915050565b6000700100000000000000010000000000000001831b1981611c8285600686901b6128e6565b6001901b9190951617949350505050565b6000611cac67ffffffffffffffff608084901c16612267565b60151492915050565b600080611cc3600184612822565b610fff901b85811690198516179150509392505050565b6000805b6005811015611d3b57838360028110611cf957611cf9612877565b60200201518160058110611d0f57611d0f612877565b6020020151600003611d295781611d258161288d565b9250505b80611d338161288d565b915050611cde565b5092915050565b60008481526020819052604081206002908101549190611d63908390612929565b6000878152602081905260409020600301549091506001600160601b03168115612014576000612710611d968385612929565b611da09190612940565b6040519091506001600160a01b0386169082156108fc029083906000818181858888f19350505050158015611dd9573d6000803e3d6000fd5b50600587610120015103611f4b576002611df38285612822565b611dfd9190612940565b8751909350600060200201516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6a919061276c565b6001600160a01b03166108fc849081150290604051600060405180830381858888f19350505050158015611ea2573d6000803e3d6000fd5b508651600160200201516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ee9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0d919061276c565b6001600160a01b03166108fc849081150290604051600060405180830381858888f19350505050158015611f45573d6000803e3d6000fd5b50612012565b611f558184612822565b9250866000015187610120015160028110611f7257611f72612877565b60200201516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd8919061276c565b6001600160a01b03166108fc849081150290604051600060405180830381858888f19350505050158015612010573d6000803e3d6000fd5b505b505b85516020810151905160408801516001600160a01b03928316929091169089907fdec5d867ed84be6d953aedc46aefecffb72ee1c2a55a7fe1a3a5fd5be6198225906000602002015160408b01516001602002015188878d61012001518e61014001518f61016001518f604051612092989796959493929190612954565b60405180910390a450505050505050565b60006120b0600183612822565b6120bb90600c612929565b83901c610fff16905092915050565b600383811c9083901c60006120e06008876128d2565b905060006120ef6008876128d2565b905080821115612115576040516319b6e55960e11b8152600481018690526024016115d2565b6000845b84811161216f57835b83811161215c5761213781600384901b6128e6565b612142906003612929565b88901b8317925080806121549061288d565b915050612122565b50806121678161288d565b915050612119565b50602089015181161561219857604051635624d5ff60e11b8152600481018790526024016115d2565b82156121ac57826121a881612912565b9350505b84156121c057846121bc81612912565b9550505b60078210156121d757816121d38161288d565b9250505b60078410156121ee57836121ea8161288d565b9450505b6000855b85811161224957845b8481116122365761221081600384901b6128e6565b61221b906003612929565b6007901b83179250808061222e9061288d565b9150506121fb565b50806122418161288d565b9150506121f2565b50895190911789526020909801805190981790975250505050505050565b6000612281675555555555555555600184901c1683612822565b915061229e673333333333333333600284901c81169084166128e6565b91506122ae600483901c836128e6565b670f0f0f0f0f0f0f0f16915060386122ce83670101010101010101612929565b67ffffffffffffffff16901c67ffffffffffffffff169050919050565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405180610180016040528061232e6123a5565b815260200161233b6123a5565b81526020016123486123a5565b81526020016123556123a5565b81526020016123626123a5565b815260200161236f6123a5565b815260200161237c6123c3565b815260200160008152602001600081526020016000815260200160008152602001606081525090565b60405180604001604052806002906020820280368337509192915050565b60405180604001604052806002905b6123da6123f0565b8152602001906001900390816123d25790505090565b6040518060a001604052806005906020820280368337509192915050565b60006040828403121561242057600080fd5b50919050565b6001600160601b038116811461243b57600080fd5b50565b6001600160a01b038116811461243b57600080fd5b60008060008060a0858703121561246957600080fd5b612473868661240e565b935060408501359250606085013561248a81612426565b9150608085013561249a8161243e565b939692955090935050565b600080606083850312156124b857600080fd5b6124c2848461240e565b946040939093013593505050565b80516001600160a01b031682526020908101516001600160601b0316910152565b602080825282518282018190526000919060409081850190868401855b828110156125765781516125238582516124d0565b86810151612533878701826124d0565b508086015160808681019190915260608201516001600160601b031660a087015201516001600160a01b031660c085015260e0909301929085019060010161250e565b5091979650505050505050565b60006020828403121561259557600080fd5b5035919050565b6000806000806000806000806000806101408b8d0312156125bc57600080fd5b8a356125c781612426565b995060208b0135985060408b0135975060608b01356125e581612426565b965060808b0135955060a08b0135945060c08b0135935060e08b013592506101008b013591506101208b013561261a8161243e565b809150509295989b9194979a5092959850565b60e0810161263b82886124d0565b61264860408301876124d0565b60808201949094526001600160601b039290921660a08301526001600160a01b031660c09091015292915050565b60008060006060848603121561268b57600080fd5b833561269681612426565b95602085013595506040909401359392505050565b600080600080608085870312156126c157600080fd5b843593506020850135925060408501359150606085013561249a8161243e565b6000602082840312156126f357600080fd5b81356126fe81612426565b9392505050565b60008060006080848603121561271a57600080fd5b612724858561240e565b9250604084013561273481612426565b915060608401356127448161243e565b809150509250925092565b60006020828403121561276157600080fd5b81356126fe8161243e565b60006020828403121561277e57600080fd5b81516126fe8161243e565b634e487b7160e01b600052604160045260246000fd5b6000604082840312156127b157600080fd5b6040516040810181811067ffffffffffffffff821117156127e257634e487b7160e01b600052604160045260246000fd5b60405282356127f08161243e565b8152602083013561280081612426565b60208201529392505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611c5657611c5661280c565b81356128408161243e565b81546001600160a01b03199081166001600160a01b03929092169182178355602084013561286d81612426565b60a01b1617905550565b634e487b7160e01b600052603260045260246000fd5b60006001820161289f5761289f61280c565b5060010190565b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826128e1576128e16128bc565b500690565b80820180821115611c5657611c5661280c565b60006020828403121561290b57600080fd5b5051919050565b6000816129215761292161280c565b506000190190565b8082028115828204841417611c5657611c5661280c565b60008261294f5761294f6128bc565b500490565b60006101008083018b845260208b818601528a60408601528960608601528860808601528760a08601528260c0860152819250865180835261012086019350818801925060005b818110156129b75783518552938201939282019260010161299b565b5050505060e092909201929092529897505050505050505056fea26469706673582212204e5900840ccb02dbc0aa3beafff8822aebd569f25dfccd400c96e50b56ee394664736f6c63430008110033

Deployed Bytecode

0x60806040526004361061009c5760003560e01c8063c1e69b6611610064578063c1e69b6614610142578063dc645241146101e6578063dde8ab7814610206578063e3f5675814610226578063e7db485c14610246578063f698bfe3146102b757600080fd5b80636090e61b146100a157806371c3c892146100b6578063829ef1ac146100c9578063a8af8a8d146100f4578063b7dde4d714610122575b600080fd5b6100b46100af366004612453565b6102ca565b005b6100b46100c43660046124a5565b610527565b3480156100d557600080fd5b506100de61075d565b6040516100eb91906124f1565b60405180910390f35b34801561010057600080fd5b5061011461010f366004612583565b6108ec565b6040519081526020016100eb565b34801561012e57600080fd5b506100b461013d36600461259c565b61090d565b34801561014e57600080fd5b506101d561015d366004612583565b600060208181529181526040908190208151808301835281546001600160a01b0380821683526001600160601b03600160a01b92839004811684880152855180870190965260018501548083168752929092048216958501959095526002830154600390930154919490821691600160601b90041685565b6040516100eb95949392919061262d565b3480156101f257600080fd5b506100b4610201366004612676565b61093b565b34801561021257600080fd5b506100b46102213660046124a5565b610a32565b34801561023257600080fd5b506100b46102413660046126ab565b610c71565b34801561025257600080fd5b5061028f6102613660046126e1565b60026020526000908152604090205467ffffffffffffffff811690600160401b90046001600160c01b031682565b6040805167ffffffffffffffff90931683526001600160c01b039091166020830152016100eb565b6100b46102c5366004612705565b611307565b6102d7602085018561274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561031f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610343919061276c565b6001600160a01b03161461036a5760405163388d0a1f60e01b815260040160405180910390fd5b600061038361037e3688900388018861279f565b611524565b6000818152602081905260409020549091506001600160a01b03166103ba57604051623126e360e41b815260040160405180910390fd5b6000818152602081905260409020600101546001600160a01b0316156103f35760405163123af06b60e01b815260040160405180910390fd5b6127106001600160601b038516111561041f576040516361d2bb0360e01b815260040160405180910390fd5b60008181526020819052604090206002015480861115610468576104438187612822565b3410156104635760405163f14a42b760e01b815260040160405180910390fd5b6104a8565b808610156104a857336108fc61047e8884612822565b6040518115909202916000818181858888f193505050501580156104a6573d6000803e3d6000fd5b505b60008281526020818152604091829020600281018990556001600160601b038816600160601b6001600160a01b0389169081028217600390930192909255835190815291820152879184917f6887717706805f48aaf66cb6e4877e11e8eb9270a65f625d62a0b47121851427910160405180910390a350505050505050565b610534602083018361274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561057c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a0919061276c565b6001600160a01b0316146105c75760405163388d0a1f60e01b815260040160405180910390fd5b6000828152602081905260409020546001600160a01b03166105fb57604051623126e360e41b815260040160405180910390fd5b6000828152602081905260409020600101546001600160a01b0316156106345760405163123af06b60e01b815260040160405180910390fd5b600082815260208190526040902060030154600160601b90046001600160a01b03168015801590610683575061066d602085018561274f565b6001600160a01b0316816001600160a01b031614155b156106a157604051630f06503560e41b815260040160405180910390fd5b6000838152602081905260409020600201543410156106d35760405163f14a42b760e01b815260040160405180910390fd5b600083815260208190526040902084906001016106f08282612835565b506107009050602085018561274f565b6001600160a01b0316837f6ee50ac97c0c46c8b2867cb72d830af9312101598bd23b5585679d7a0d01c06061073b60408801602089016126e1565b6040516001600160601b0390911681526020015b60405180910390a350505050565b60015460609067ffffffffffffffff81111561077b5761077b612789565b6040519080825280602002602001820160405280156107f257816020015b6040805160e081018252600060a0820181815260c0830182905282528251808401845281815260208082018390528301529181018290526060810182905260808101919091528152602001906001900390816107995790505b50905060005b81518110156108e8576000806001838154811061081757610817612877565b600091825260208083209091015483528281019390935260409182019020815160e08101835281546001600160a01b0380821660a084019081526001600160601b03600160a01b93849004811660c0860152908452855180870187526001860154808416825293909304811683880152958301919091526002830154938201939093526003909101549283166060820152600160601b90920416608082015282518390839081106108ca576108ca612877565b602002602001018190525080806108e09061288d565b9150506107f8565b5090565b600181815481106108fc57600080fd5b600091825260209091200154905081565b6109188a8a8a61093b565b61092387878761093b565b61092f84848484610c71565b50505050505050505050565b60408051602081018490529081018290526060016040516020818303038152906040528051906020012060001c6001600160601b0316836001600160601b031614610999576040516329077eab60e11b815260040160405180910390fd5b60006109a483611588565b60408051808201825267ffffffffffffffff80871682526001600160c01b0380851660208085019182526001600160601b038b16600081815260029092529086902094519151909216600160401b02921691909117909155905191925084917fccfa43dde42b308c80e0eea01887c538073ac753b3958d9fc5f1acfc75e9216a9061074f9086815260200190565b610a3f602083018361274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aab919061276c565b6001600160a01b031614610ad25760405163388d0a1f60e01b815260040160405180910390fd5b6000610ae661037e3686900386018661279f565b6000818152602081905260409020600101549091506001600160a01b031615610b225760405163123af06b60e01b815260040160405180910390fd5b6000818152602081905260409020600201548015610b6957604051339082156108fc029083906000818181858888f19350505050158015610b67573d6000803e3d6000fd5b505b8160018581548110610b7d57610b7d612877565b906000526020600020015414610ba657604051634bfe8d9f60e01b815260040160405180910390fd5b60008281526020819052604081208181556001808201839055600282018390556003909101919091558054610bdc908290612822565b81548110610bec57610bec612877565b906000526020600020015460018581548110610c0a57610c0a612877565b6000918252602090912001556001805480610c2757610c276128a6565b60019003818190600052602060002001600090559055817fa5c028458c27d7cc75621d18a58fe289c65a143f70064736b1ff38def064fb6460405160405180910390a25050505050565b6000610c7d85846119c2565b8051602001519091506001600160a01b0316610cac57604051633cdf589160e21b815260040160405180910390fd5b6020810151511580610cc357506020818101510151155b15610ce157604051630fbc4d2d60e01b815260040160405180910390fd5b610cec6002846128d2565b151580610cf95750601583105b15610d175760405163c7c2ccaf60e01b815260040160405180910390fd5b60005b838110156111835760028260e001516001610d3591906128e6565b610d3f91906128d2565b610100830152815160e0830151600091829160028110610d6157610d61612877565b60200201516001600160a01b031663436cc79561138886604001518760e0015160028110610d9157610d91612877565b602002015187606001518860e0015160028110610db057610db0612877565b6020020151886060015189610100015160028110610dd057610dd0612877565b602002015189608001518a60e0015160028110610def57610def612877565b60200201518a608001518b610100015160028110610e0f57610e0f612877565b60200201518b60a001518c60e0015160028110610e2e57610e2e612877565b60200201516040516001600160e01b031960e08a901b1681526004810196909652602486019490945260448501929092526064840152608483015260a482015260c40160206040518083038160008887f193505050508015610ead575060408051601f3d908101601f19168201909252610eaa918101906128f9565b60015b15610eba57915050603f81165b6101608401518190610ecd8560016128e6565b81518110610edd57610edd612877565b6020026020010181815250508184608001518560e0015160028110610f0457610f04612877565b602002018181525050610f3981600086606001518760e0015160028110610f2d57610f2d612877565b60200201519190611c1f565b610f4f57505061010082015160e0830152611171565b6000610f7b82866040015187610100015160028110610f7057610f70612877565b602002015190611c40565b905080610fe057610fae82600187606001518860e0015160028110610fa257610fa2612877565b60200201519190611c5c565b85606001518660e0015160028110610fc857610fc8612877565b602002015250505061010082015160e0830152611171565b61100082600287606001518860e0015160028110610fa257610fa2612877565b85606001518660e001516002811061101a5761101a612877565b602002015260c0850151610100860151600091906002811061103e5761103e612877565b602002015161104e600184612822565b6005811061105e5761105e612877565b60200201805161106d90612912565b9081815250905080600003611161576110a386606001518760e001516002811061109957611099612877565b6020020151611c93565b156110f45760e0860151610120870152600161014087015261016086015160ff906110cf8760026128e6565b815181106110df576110df612877565b60200260200101818152505050505050611183565b6111418660a001518760e001516002811061111157611111612877565b60200201518360ff1688602001518961010001516002811061113557611135612877565b60200201519190611cb5565b8660a001518760e001516002811061115b5761115b612877565b60200201525b50505061010083015160e0840152505b8061117b8161288d565b915050610d1a565b60058261012001510361121f5761016082015160ff906111a48360016128e6565b815181106111b4576111b4612877565b60200260200101818152505060006111d18360c001516000611cda565b905060006111e48460c001516001611cda565b905080821115611203576000610120850152600261014085015261121c565b8082101561121c57600161012085015260026101408501525b50505b61122b86838686611d42565b856001868154811061123f5761123f612877565b90600052602060002001541461126857604051634bfe8d9f60e01b815260040160405180910390fd5b6000868152602081905260408120818155600180820183905560028201839055600390910191909155805461129e908290612822565b815481106112ae576112ae612877565b9060005260206000200154600186815481106112cc576112cc612877565b60009182526020909120015560018054806112e9576112e96128a6565b60019003818190600052602060002001600090559055505050505050565b611314602084018461274f565b336001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561135c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611380919061276c565b6001600160a01b0316146113a75760405163388d0a1f60e01b815260040160405180910390fd5b60006113bb61037e3687900387018761279f565b6000818152602081905260409020549091506001600160a01b0316156113f457604051634c93111160e01b815260040160405180910390fd5b6127106001600160601b0385161115611420576040516361d2bb0360e01b815260040160405180910390fd5b6000818152602081905260409020859061143a8282612835565b505060008181526020818152604082203460028201556001600160a01b038616600160601b026001600160601b0388161760039091015560018054808201825592527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf69091018290556114af9086018661274f565b6001600160a01b031634827f87143ee8969c7635cf96224732f26903c7736d877f7ec60bc44dec67dc88a5146114eb60408a0160208b016126e1565b604080516001600160601b039283168152918a1660208301526001600160a01b0389169082015260600160405180910390a45050505050565b60008160000151826020015160405160200161156b92919060609290921b6bffffffffffffffffffffffff1916825260a01b6001600160a01b031916601482015260200190565b604051602081830303815290604052805190602001209050919050565b60006115926122eb565b600061159f8460016120a3565b600681901c808452603f821660208501819052919250106115db576040516325e3b13160e21b8152600160048201526024015b60405180910390fd5b815160208301516115ec9190612822565b60408301526115fd60016002612822565b60608301819052600381901b608084015260408301511480611626575081608001518260400151145b61164657604051630ac662cf60e41b8152600160048201526024016115d2565b61164e6122eb565b6116598560026120a3565b600681901c808352603f82166020840181905291935010611690576040516325e3b13160e21b8152600260048201526024016115d2565b805160208201516116a19190612822565b60408201526116b260016003612822565b60608201819052600381901b6080830152604082015114806116db575080608001518160400151145b6116fb57604051630ac662cf60e41b8152600260048201526024016115d2565b6117036122eb565b61170e8660036120a3565b600681901c808352603f82166020840181905291945010611745576040516325e3b13160e21b8152600360048201526024016115d2565b805160208201516117569190612822565b6040820181905260608301511480611775575081608001518160400151145b61179557604051630ac662cf60e41b8152600360048201526024016115d2565b61179d6122eb565b6117a88760056120a3565b600681901c808352603f821660208401819052919550106117df576040516325e3b13160e21b8152600560048201526024016115d2565b805160208201516117f09190612822565b604082015261180160016005612822565b60608201819052600381901b60808301526040820151148061182a575080608001518160400151145b61184a57604051630ac662cf60e41b8152600560048201526024016115d2565b6118526122eb565b61185d8860046120a3565b600681901c808352603f82166020840181905291965010611893576040516325e3b13160e21b81526004818101526024016115d2565b805160208201516118a49190612822565b604082015260036118b760016002612822565b901b6118c560016004612822565b6118cf91906128e6565b60608201526118e060016002612822565b60036118ee60016004612822565b6118f992911b6128e6565b608082015260608101516040820151148061191b575080608001518160400151145b61193a57604051630ac662cf60e41b81526004818101526024016115d2565b60408051808201909152600080825260208083019190915287519088015161196591839160016120ca565b8451602086015161197991839160026120ca565b8351602085015161198d91839160036120ca565b815160208301516119a191839160046120ca565b825160208401516119b591839160056120ca565b5198975050505050505050565b6119ca61231a565b6040805180820182526000858152602081815283822080546001600160a01b0380821686526001909201549182168386015293865284516001600160601b03600160a01b9586900481168552600280855287862060808085018a52905467ffffffffffffffff808216868c019081526001600160c01b03600160401b9384900481166060808a01919091529188529a90970490941688528287528988208a51808c018c5290548086168252919091048916818801528487019081528951808b018b52855151851681528151518516818901528b8801528951808b018b529451870151891685525186015190971683860152888801929092528651808801885281815280850191909152828801528551808701875260ff8082528185015285880152855180870187528481529283019390935260a080870192909252845160e08101865294850183815260039186018290529385015283929190830190611b31906004612929565b8152602001600581525081526020016040518060a0016040528060028152602001600381526020016003815260200160026004611b6e9190612929565b81526005602090910152905260c0820152611b8a6002426128d2565b60e082015260056101208201819052610140820152611baa8260026128e6565b67ffffffffffffffff811115611bc257611bc2612789565b604051908082528060200260200182016040528015611beb578160200160208202803683370190505b50610160820181905260e08201518151909190600090611c0d57611c0d612877565b60200260200101818152505092915050565b600080611c3084600685901b6128e6565b6001901b85161190509392505050565b6000611c4d826003612929565b83901c60071690505b92915050565b6000700100000000000000010000000000000001831b1981611c8285600686901b6128e6565b6001901b9190951617949350505050565b6000611cac67ffffffffffffffff608084901c16612267565b60151492915050565b600080611cc3600184612822565b610fff901b85811690198516179150509392505050565b6000805b6005811015611d3b57838360028110611cf957611cf9612877565b60200201518160058110611d0f57611d0f612877565b6020020151600003611d295781611d258161288d565b9250505b80611d338161288d565b915050611cde565b5092915050565b60008481526020819052604081206002908101549190611d63908390612929565b6000878152602081905260409020600301549091506001600160601b03168115612014576000612710611d968385612929565b611da09190612940565b6040519091506001600160a01b0386169082156108fc029083906000818181858888f19350505050158015611dd9573d6000803e3d6000fd5b50600587610120015103611f4b576002611df38285612822565b611dfd9190612940565b8751909350600060200201516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6a919061276c565b6001600160a01b03166108fc849081150290604051600060405180830381858888f19350505050158015611ea2573d6000803e3d6000fd5b508651600160200201516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ee9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0d919061276c565b6001600160a01b03166108fc849081150290604051600060405180830381858888f19350505050158015611f45573d6000803e3d6000fd5b50612012565b611f558184612822565b9250866000015187610120015160028110611f7257611f72612877565b60200201516001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd8919061276c565b6001600160a01b03166108fc849081150290604051600060405180830381858888f19350505050158015612010573d6000803e3d6000fd5b505b505b85516020810151905160408801516001600160a01b03928316929091169089907fdec5d867ed84be6d953aedc46aefecffb72ee1c2a55a7fe1a3a5fd5be6198225906000602002015160408b01516001602002015188878d61012001518e61014001518f61016001518f604051612092989796959493929190612954565b60405180910390a450505050505050565b60006120b0600183612822565b6120bb90600c612929565b83901c610fff16905092915050565b600383811c9083901c60006120e06008876128d2565b905060006120ef6008876128d2565b905080821115612115576040516319b6e55960e11b8152600481018690526024016115d2565b6000845b84811161216f57835b83811161215c5761213781600384901b6128e6565b612142906003612929565b88901b8317925080806121549061288d565b915050612122565b50806121678161288d565b915050612119565b50602089015181161561219857604051635624d5ff60e11b8152600481018790526024016115d2565b82156121ac57826121a881612912565b9350505b84156121c057846121bc81612912565b9550505b60078210156121d757816121d38161288d565b9250505b60078410156121ee57836121ea8161288d565b9450505b6000855b85811161224957845b8481116122365761221081600384901b6128e6565b61221b906003612929565b6007901b83179250808061222e9061288d565b9150506121fb565b50806122418161288d565b9150506121f2565b50895190911789526020909801805190981790975250505050505050565b6000612281675555555555555555600184901c1683612822565b915061229e673333333333333333600284901c81169084166128e6565b91506122ae600483901c836128e6565b670f0f0f0f0f0f0f0f16915060386122ce83670101010101010101612929565b67ffffffffffffffff16901c67ffffffffffffffff169050919050565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405180610180016040528061232e6123a5565b815260200161233b6123a5565b81526020016123486123a5565b81526020016123556123a5565b81526020016123626123a5565b815260200161236f6123a5565b815260200161237c6123c3565b815260200160008152602001600081526020016000815260200160008152602001606081525090565b60405180604001604052806002906020820280368337509192915050565b60405180604001604052806002905b6123da6123f0565b8152602001906001900390816123d25790505090565b6040518060a001604052806005906020820280368337509192915050565b60006040828403121561242057600080fd5b50919050565b6001600160601b038116811461243b57600080fd5b50565b6001600160a01b038116811461243b57600080fd5b60008060008060a0858703121561246957600080fd5b612473868661240e565b935060408501359250606085013561248a81612426565b9150608085013561249a8161243e565b939692955090935050565b600080606083850312156124b857600080fd5b6124c2848461240e565b946040939093013593505050565b80516001600160a01b031682526020908101516001600160601b0316910152565b602080825282518282018190526000919060409081850190868401855b828110156125765781516125238582516124d0565b86810151612533878701826124d0565b508086015160808681019190915260608201516001600160601b031660a087015201516001600160a01b031660c085015260e0909301929085019060010161250e565b5091979650505050505050565b60006020828403121561259557600080fd5b5035919050565b6000806000806000806000806000806101408b8d0312156125bc57600080fd5b8a356125c781612426565b995060208b0135985060408b0135975060608b01356125e581612426565b965060808b0135955060a08b0135945060c08b0135935060e08b013592506101008b013591506101208b013561261a8161243e565b809150509295989b9194979a5092959850565b60e0810161263b82886124d0565b61264860408301876124d0565b60808201949094526001600160601b039290921660a08301526001600160a01b031660c09091015292915050565b60008060006060848603121561268b57600080fd5b833561269681612426565b95602085013595506040909401359392505050565b600080600080608085870312156126c157600080fd5b843593506020850135925060408501359150606085013561249a8161243e565b6000602082840312156126f357600080fd5b81356126fe81612426565b9392505050565b60008060006080848603121561271a57600080fd5b612724858561240e565b9250604084013561273481612426565b915060608401356127448161243e565b809150509250925092565b60006020828403121561276157600080fd5b81356126fe8161243e565b60006020828403121561277e57600080fd5b81516126fe8161243e565b634e487b7160e01b600052604160045260246000fd5b6000604082840312156127b157600080fd5b6040516040810181811067ffffffffffffffff821117156127e257634e487b7160e01b600052604160045260246000fd5b60405282356127f08161243e565b8152602083013561280081612426565b60208201529392505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611c5657611c5661280c565b81356128408161243e565b81546001600160a01b03199081166001600160a01b03929092169182178355602084013561286d81612426565b60a01b1617905550565b634e487b7160e01b600052603260045260246000fd5b60006001820161289f5761289f61280c565b5060010190565b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826128e1576128e16128bc565b500690565b80820180821115611c5657611c5661280c565b60006020828403121561290b57600080fd5b5051919050565b6000816129215761292161280c565b506000190190565b8082028115828204841417611c5657611c5661280c565b60008261294f5761294f6128bc565b500490565b60006101008083018b845260208b818601528a60408601528960608601528860808601528760a08601528260c0860152819250865180835261012086019350818801925060005b818110156129b75783518552938201939282019260010161299b565b5050505060e092909201929092529897505050505050505056fea26469706673582212204e5900840ccb02dbc0aa3beafff8822aebd569f25dfccd400c96e50b56ee394664736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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