ETH Price: $2,408.23 (-2.58%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040175782422023-06-28 13:53:59495 days ago1687960439IN
 Create: Rollup
0 ETH0.1333601525.36228202

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Rollup

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 33 : Rollup.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * This file was changed in accordance to Apache License, Version 2.0.
 *
 * Copyright 2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./challenge/Challenge.sol";
import "./challenge/ChallengeLib.sol";
import "./AssertionMap.sol";
import "./IRollup.sol";
import "./RollupLib.sol";
import "./WhiteList.sol";
import "./verifier/IVerifier.sol";
import {Lib_AddressResolver} from "../../libraries/resolver/Lib_AddressResolver.sol";
import {Lib_AddressManager} from "../../libraries/resolver/Lib_AddressManager.sol";
import {Lib_BVMCodec} from "../../libraries/codec/Lib_BVMCodec.sol";


abstract contract RollupBase is IRollup, Initializable {
    // Config parameters
    uint256 public minimumAssertionPeriod; // number of L1 blocks
    uint256 public baseStakeAmount; // number of stake tokens

    IERC20 public stakeToken;
    AssertionMap public override assertions;
    IVerifierEntry public verifier;

    // slot place hold
    uint256[50] rollupBaseGap;

    struct Staker {
        bool isStaked;
        uint256 amountStaked;
        uint256 assertionID; // latest staked assertion ID
        address operator; // operator
        address currentChallenge; // address(0) if none
    }

    struct Zombie {
        address stakerAddress;
        uint256 lastAssertionID;
    }

    struct ChallengeCtx {
        bool completed;
        address challengeAddress;
        address defenderAddress;
        address challengerAddress;
        uint256 defenderAssertionID;
        uint256 challengerAssertionID;
    }
}

contract Rollup is Lib_AddressResolver, RollupBase, Whitelist {
    modifier stakedOnly() {
        if (!isStaked(msg.sender)) {
            revert("NotStaked");
        }
        _;
    }

    modifier operatorOnly() {
        if (registers[msg.sender] == address(0)) {
            revert("NotOperator");
        }
        _;
    }

    // Assertion state
    uint256 public lastResolvedAssertionID;
    uint256 public lastConfirmedAssertionID;
    uint256 public lastCreatedAssertionID;

    // Staking state
    uint256 public numStakers; // current total number of stakers
    mapping(address => Staker) public stakers; // mapping from staker addresses to corresponding stakers
    mapping(address => address) public registers; // register info for operator => staker
    mapping(address => uint256) public withdrawableFunds; // mapping from addresses to withdrawable funds (won in challenge)
    Zombie[] public zombies; // stores stakers that lost a challenge
    ChallengeCtx public challengeCtx;  // stores challenge context
    constructor() Lib_AddressResolver(address(0)) {
        _disableInitializers();
    }

    function initialize(
        address _owner,
        address _verifier,
        address _stakeToken,
        address _libAddressManager,
        address _assertionMap,
        uint256 _minimumAssertionPeriod,
        uint256 _baseStakeAmount,
        bytes32 _initialVMhash,
        address[] calldata stakerWhitelists,
        address[] calldata operatorWhitelists
    ) public initializer {
        if (_owner == address(0) || _verifier == address(0)) {
            revert("ZeroAddress");
        }
        owner = _owner;
        stakeToken = IERC20(_stakeToken);
        verifier = IVerifierEntry(_verifier);

        if (address(libAddressManager) != address(0)) {
            revert("RedundantInitialized");
        }
        libAddressManager = Lib_AddressManager(_libAddressManager);

        if (address(assertions) != address(0)) {
            revert("RedundantInitialized");
        }
        assertions = AssertionMap(_assertionMap);

        minimumAssertionPeriod = _minimumAssertionPeriod;
        baseStakeAmount = _baseStakeAmount;

        assertions.setRollupAddress(address(this));
        lastResolvedAssertionID = 0;
        lastConfirmedAssertionID = 0;
        lastCreatedAssertionID = 0;

        assertions.createAssertion(
            lastResolvedAssertionID, // assertionID
            _initialVMhash,
            0, // inboxSize (genesis)
            0, // parentID
            block.number // deadline (unchallengeable)
        );

        addToStakerWhitelist(stakerWhitelists);
        addToOperatorWhitelist(operatorWhitelists);
    }

    /// @inheritdoc IRollup
    function isStaked(address addr) public view override returns (bool) {
        return stakers[addr].isStaked;
    }

    /// @inheritdoc IRollup
    function currentRequiredStake() public view override returns (uint256) {
        return baseStakeAmount;
    }

    /// @inheritdoc IRollup
    function confirmedInboxSize() public view override returns (uint256) {
        return assertions.getInboxSize(lastConfirmedAssertionID);
    }

    /// @inheritdoc IRollup
    function stake(uint256 stakeAmount, address operator) external override
        stakerWhitelistOnly(msg.sender)
        operatorWhitelistOnly(operator)
    {
        // send erc20 token to staking contract, need user approve first
        require(
            IERC20(stakeToken).transferFrom(msg.sender, address(this), stakeAmount),
            "transfer erc20 token failed"
        );

        if (isStaked(msg.sender)) {
            require(
            stakers[msg.sender].operator == operator,
                "staker => operator mapping not unique"
            );
            stakers[msg.sender].amountStaked += stakeAmount;
        } else {
            require(registers[operator] == address(0), "operator is occupied");

            if (stakeAmount < baseStakeAmount) {
                revert("InsufficientStake");
            }

            stakers[msg.sender] = Staker(true, stakeAmount, 0, operator, address(0));
            registers[operator] = msg.sender;
            numStakers++;
            stakeOnAssertion(msg.sender, lastConfirmedAssertionID);
        }
    }

    /// @inheritdoc IRollup
    function unstake(uint256 stakeAmount) external override {
        requireStaked(msg.sender);
        // Require that staker is staked on a confirmed assertion.
        Staker storage staker = stakers[msg.sender];
        if (staker.assertionID > lastConfirmedAssertionID) {
            revert("StakedOnUnconfirmedAssertion");
        }
        if (stakeAmount > staker.amountStaked - currentRequiredStake()) {
            revert("InsufficientStake");
        }
        staker.amountStaked -= stakeAmount;
        // send erc20 token to user
        require(
            IERC20(stakeToken).transfer(msg.sender, stakeAmount),
            "transfer erc20 token failed"
        );
    }

    /// @inheritdoc IRollup
    function removeStake(address stakerAddress) onlyOwner external override {
        requireStaked(stakerAddress);
        // Require that staker is staked on a confirmed assertion.
        Staker storage staker = stakers[stakerAddress];
        if (staker.assertionID > lastConfirmedAssertionID) {
            revert("StakedOnUnconfirmedAssertion");
        }
        uint256 amountToSent = staker.amountStaked;
        deleteStaker(stakerAddress);
        // send erc20 token to user
        require(
            IERC20(stakeToken).transfer(stakerAddress, amountToSent),
            "transfer erc20 token failed"
        );
    }

    /// @inheritdoc IRollup
    function advanceStake(uint256 assertionID) external override operatorOnly {
        address stakerAddr = registers[msg.sender];
        Staker storage staker = stakers[stakerAddr];
        if (assertionID <= staker.assertionID || assertionID > lastCreatedAssertionID) {
            revert("AssertionOutOfRange");
        }
        // TODO: allow arbitrary descendant of current staked assertionID, not just child.
        if (staker.assertionID != assertions.getParentID(assertionID)) {
            revert("ParentAssertionUnstaked");
        }
        stakeOnAssertion(stakerAddr, assertionID);
    }

    /// @inheritdoc IRollup
    function withdraw() external override operatorOnly {
        uint256 withdrawableFund = withdrawableFunds[msg.sender];
        withdrawableFunds[msg.sender] = 0;
        require(
            IERC20(stakeToken).transfer(msg.sender, withdrawableFund),
            "transfer erc20 token failed"
        );
    }

    /// @inheritdoc IRollup
    function createAssertion(
        bytes32 vmHash,
        uint256 inboxSize
    ) public override operatorOnly {
        address stakerAddr = registers[msg.sender];
        require(stakers[stakerAddr].currentChallenge == address(0),"can not create assertion when staker in challenge");
        uint256 parentID = stakers[stakerAddr].assertionID;
        // Require that enough time has passed since the last assertion.
        if (block.number - assertions.getProposalTime(parentID) < minimumAssertionPeriod) {
            revert("MinimumAssertionPeriodNotPassed");
        }
        // Require that the assertion at least includes one transaction
        if (inboxSize <= assertions.getInboxSize(parentID)) {
            revert("EmptyAssertion");
        }

        // Initialize assertion.
        lastCreatedAssertionID++;
        emit AssertionCreated(lastCreatedAssertionID, msg.sender, vmHash, inboxSize);
        assertions.createAssertion(
            lastCreatedAssertionID, vmHash, inboxSize, parentID, newAssertionDeadline()
        );

        // Update stake.
        stakeOnAssertion(stakerAddr, lastCreatedAssertionID);
        // confirmed this assertion instantly
        lastResolvedAssertionID++;
        lastConfirmedAssertionID = lastResolvedAssertionID;
        emit AssertionConfirmed(lastResolvedAssertionID);
    }

    /// @inheritdoc IRollup
    function createAssertionWithStateBatch(
        bytes32 vmHash,
        uint256 inboxSize,
        bytes32[] calldata _batch,
        uint256 _shouldStartAtElement,
        bytes calldata _signature
        ) external override operatorOnly {
        // permissions only allow rollup proposer to submit assertion, only allow RollupContract to append new batch
        require(msg.sender == resolve("BVM_Rolluper"), "msg.sender is not rollup proposer, can't append batch");
        // create assertion
        createAssertion(vmHash, inboxSize);
        // append state batch
        address scc = resolve("StateCommitmentChain");
        (bool success, ) = scc.call(
            abi.encodeWithSignature("appendStateBatch(bytes32[],uint256,bytes)", _batch, _shouldStartAtElement, _signature)
        );
        require(success, "scc append state batch failed, revert all");
    }

    function challengeAssertion(address[2] calldata players, uint256[2] calldata assertionIDs)
        external
        override
        operatorOnly
        returns (address)
    {
        uint256 defenderAssertionID = assertionIDs[0];
        uint256 challengerAssertionID = assertionIDs[1];
        // Require IDs ordered and in-range.
        if (defenderAssertionID >= challengerAssertionID) {
            revert("WrongOrder");
        }
        if (challengerAssertionID > lastCreatedAssertionID) {
            revert("UnproposedAssertion");
        }
        if (lastConfirmedAssertionID >= defenderAssertionID) {
            revert("AssertionAlreadyResolved");
        }
        // Require that players have attested to sibling assertions.
        uint256 parentID = assertions.getParentID(defenderAssertionID);
        if (parentID != assertions.getParentID(challengerAssertionID)) {
            revert("DifferentParent");
        }
        // Require that neither player is currently engaged in a challenge.
        address defender = players[0];
        address challenger = players[1];
        require(defender != challenger, "defender and challenge must not equal");
        address defenderStaker = registers[defender];
        address challengerStaker = registers[challenger];
        requireUnchallengedStaker(defenderStaker);
        requireUnchallengedStaker(challengerStaker);

        // TODO: Calculate upper limit for allowed node proposal time.

        // Initialize challenge.
        Challenge challenge = new Challenge();
        address challengeAddr = address(challenge);
        stakers[challengerStaker].currentChallenge = challengeAddr;
        stakers[defenderStaker].currentChallenge = challengeAddr;

        challengeCtx = ChallengeCtx(false,challengeAddr,defender,challenger,defenderAssertionID,challengerAssertionID);
        emit AssertionChallenged(defenderAssertionID, challengeAddr);
        uint256 inboxSize = assertions.getInboxSize(parentID);
        bytes32 parentStateHash = assertions.getStateHash(parentID);
        bytes32 defenderStateHash = assertions.getStateHash(defenderAssertionID);
        challenge.initialize(
            defender,
            challenger,
            verifier,
            address(this),
            inboxSize,
            parentStateHash,
            defenderStateHash
        );
        return challengeAddr;
    }

    /// @inheritdoc IRollup
    function confirmFirstUnresolvedAssertion() public override operatorOnly {
        if (lastResolvedAssertionID >= lastCreatedAssertionID) {
            revert("NoUnresolvedAssertion");
        }

        // (1) there is at least one staker, and
        if (numStakers <= 0) revert("NoStaker");

        uint256 lastUnresolvedID = lastResolvedAssertionID + 1;

        // (2) challenge period has passed
        if (block.timestamp < assertions.getDeadline(lastUnresolvedID)) {
            revert("ChallengePeriodPending");
        }

        // (3) predecessor has been confirmed
        if (assertions.getParentID(lastUnresolvedID) != lastConfirmedAssertionID) {
            revert("InvalidParent");
        }

        // Remove old zombies
        // removeOldZombies();

        // (4) all stakers are staked on the block.
        // if (assertions.getNumStakers(lastUnresolvedID) != numStakers) {
        //    revert("NotAllStaked");
        // }

        // there is no slashing mechanism currently,
        // we can not handle offline staker if we sum up zombies and numStakers,
        // in which case a offline validator can block confirmation progress.
        // if (assertions.getNumStakers(lastUnresolvedID) != countStakedZombies(lastUnresolvedID) + numStakers) {
        //    revert NotAllStaked();
        // }

        // Confirm assertion.
        // assertions.deleteAssertion(lastConfirmedAssertionID);
        lastResolvedAssertionID++;
        lastConfirmedAssertionID = lastResolvedAssertionID;
        emit AssertionConfirmed(lastResolvedAssertionID);
    }

    /// @inheritdoc IRollup
    function rejectFirstUnresolvedAssertion() external override operatorOnly {
        if (lastResolvedAssertionID >= lastCreatedAssertionID) {
            revert("NoUnresolvedAssertion");
        }

        uint256 firstUnresolvedAssertionID = lastResolvedAssertionID + 1;

        // First case - parent of first unresolved is last confirmed (`if` condition below). e.g.
        // [1] <- [3]           | valid chain ([1] is last confirmed, [3] is stakerAddress's unresolved assertion)
        //  ^---- [2]           | invalid chain ([2] is firstUnresolved)
        // Second case (trivial) - parent of first unresolved is not last confirmed. i.e.:
        //   parent is previous rejected, e.g.
        //   [1] <- [4]           | valid chain ([1] is last confirmed, [4] is stakerAddress's unresolved assertion)
        //   [2] <- [3]           | invalid chain ([3] is firstUnresolved)
        //   OR
        //   parent is previous confirmed, e.g.
        //   [1] <- [2] <- [4]    | valid chain ([2] is last confirmed, [4] is stakerAddress's unresolved assertion)
        //    ^---- [3]           | invalid chain ([3] is firstUnresolved)
        if (assertions.getParentID(firstUnresolvedAssertionID) == lastConfirmedAssertionID) {
            // 1a. challenge period has passed.
            if (block.timestamp < assertions.getDeadline(firstUnresolvedAssertionID)) {
                revert("ChallengePeriodPending");
            }
            // 1b. at least one staker exists (on a sibling)
            // - stakerAddress is indeed a staker
            // requireStaked(stakerAddress);
            // - staker's assertion can't be a ancestor of firstUnresolved (because staker's assertion is also unresolved)
            // if (stakers[stakerAddress].assertionID < firstUnresolvedAssertionID) {
            //    revert("AssertionAlreadyResolved");
            // }
            // - staker's assertion can't be a descendant of firstUnresolved (because staker has never staked on firstUnresolved)
            // if (assertions.isStaker(firstUnresolvedAssertionID, stakerAddress)) {
            //    revert("StakerStakedOnTarget");
            // }
            // If a staker is staked on an assertion that is neither an ancestor nor a descendant of firstUnresolved, it must be a sibling, QED

            // 1c. no staker is staked on this assertion
            // removeOldZombies();
            if (assertions.getNumStakers(firstUnresolvedAssertionID) != countStakedZombies(firstUnresolvedAssertionID))
            {
                revert("StakersPresent");
            }
        }

        // Reject assertion.
        lastResolvedAssertionID++;
        emit AssertionRejected(lastResolvedAssertionID);
        assertions.deleteAssertion(lastResolvedAssertionID);
    }

/// @inheritdoc IRollup
    function rejectLatestCreatedAssertionWithBatch(Lib_BVMCodec.ChainBatchHeader memory _batchHeader) external override onlyOwner {
        address scc = resolve("StateCommitmentChain");

        // batch shift
        (, bytes memory data) = scc.call(
            abi.encodeWithSignature("getTotalBatches()")
        );
        uint256 totalBatches = uint256(bytes32(data));
        require(totalBatches-_batchHeader.batchIndex == 1, "delete batch with gap is not allowed");

        // Delete state batch
        (bool success, ) = scc.call(
            abi.encodeWithSignature("deleteStateBatch((uint256,bytes32,uint256,uint256,bytes,bytes))", _batchHeader)
        );
        require(success, "scc delete state batch failed, revert all");

        // Reject assertion.
        require(lastCreatedAssertionID >= lastResolvedAssertionID, "delete assertion before last resolved in error");
        emit AssertionRejected(lastCreatedAssertionID);
        assertions.deleteAssertionForBatch(lastCreatedAssertionID);
        lastCreatedAssertionID--;
        lastResolvedAssertionID--;
        lastConfirmedAssertionID--;

        // Revert status
        for (uint i = 0; i < stakerslist.length; i++) {
            if (stakers[stakerslist[i]].assertionID > lastCreatedAssertionID) {
                stakers[stakerslist[i]].assertionID = lastCreatedAssertionID;
            }
        }
    }

    /// @inheritdoc IRollup
    function completeChallenge(address winner, address loser) external override operatorOnly {
        address winnerStaker = registers[winner];
        address loserStaker = registers[loser];
        requireStaked(loserStaker);

        address challenge = getChallenge(winnerStaker, loserStaker);
        if (msg.sender != challenge) {
            revert("NotChallenge");
        }
        uint256 amountWon;
        uint256 loserStake = stakers[loserStaker].amountStaked;
        // uint256 winnerStake = stakers[winnerStaker].amountStaked;
        if (loserStake > baseStakeAmount) {
            // If loser has a higher stake than the base stake amount, refund the difference.
            // Loser gets deleted anyways, so maybe unnecessary to set amountStaked.
            // stakers[loser].amountStaked = winnerStake;
            withdrawableFunds[loserStaker] += (loserStake - baseStakeAmount);
            amountWon = baseStakeAmount;
        } else {
            amountWon = loserStake;
        }
        // Reward the winner with winner amount
        stakers[winnerStaker].amountStaked += amountWon; // why +stake instead of +withdrawable?
        stakers[winnerStaker].currentChallenge = address(0);
        // Turning loser into zombie renders the loser's remaining stake inaccessible.
        uint256 assertionID = stakers[loserStaker].assertionID;
        deleteStaker(loserStaker);
        // Track as zombie so we can account for it during assertion resolution.
        zombies.push(Zombie(loserStaker, assertionID));
        challengeCtx.completed = true;
    }

    /// @inheritdoc IRollup
    function rollbackL2Chain(uint256 _shouldRollBack, uint256 _shouldStartAtElement, bytes memory _signature) external override onlyOwner {
        address scc = resolve("StateCommitmentChain");

        // batch shift
        (bool success, ) = scc.call(
            abi.encodeWithSignature("rollBackL2Chain(uint256,uint256,bytes)", _shouldRollBack, _shouldStartAtElement, _signature)
        );
        require(success, "call rollBackL2Chain failed");
    }

    /**
     * @notice Updates staker and assertion metadata.
     * @param stakerAddress Address of existing staker.
     * @param assertionID ID of existing assertion to stake on.
     */
    function stakeOnAssertion(address stakerAddress, uint256 assertionID) private {
        stakers[stakerAddress].assertionID = assertionID;
        assertions.stakeOnAssertion(assertionID, stakerAddress);
        emit StakerStaked(stakerAddress, assertionID);
    }

    /**
     * @notice Deletes the staker from global state. Does not touch assertion staker state.
     * @param stakerAddress Address of the staker to delete
     */
    function deleteStaker(address stakerAddress) private {
        numStakers--;
        address operator = stakers[stakerAddress].operator;
        delete stakers[stakerAddress];
        delete registers[operator];
    }

    /**
     * @notice Checks to see whether the two stakers are in the same challenge
     * @param staker1Address Address of the first staker
     * @param staker2Address Address of the second staker
     * @return Address of the challenge that the two stakers are in
     */
    function getChallenge(address staker1Address, address staker2Address) private view returns (address) {
        Staker storage staker1 = stakers[staker1Address];
        Staker storage staker2 = stakers[staker2Address];
        address challenge = staker1.currentChallenge;
        if (challenge == address(0)) {
            revert("NotInChallenge");
        }
        if (challenge != staker2.currentChallenge) {
            revert("InDifferentChallenge");
        }
        return challenge;
    }

    function newAssertionDeadline() private returns (uint256) {
        // TODO: account for prev assertion, gas
        // return block.number + confirmationPeriod;
        address scc = resolve("StateCommitmentChain");
        (bool success, bytes memory data) = scc.call(
            abi.encodeWithSignature("FRAUD_PROOF_WINDOW()")
        );
        require(success,"call FRAUD_PROOF_WINDOW() failed");
        uint256 confirmationWindow = uint256(bytes32(data));
        return block.timestamp + confirmationWindow;
    }

    // *****************
    // zombie processing
    // *****************

    function removeOldZombies() external operatorOnly {
        delete zombies;
    }
    /**
     * @notice Removes any zombies whose latest stake is earlier than the first unresolved assertion.
     * @dev Uses pop() instead of delete to prevent gaps, although order is not preserved
     */
    // function removeOldZombies() private {
    // }

    /**
     * @notice Counts the number of zombies staked on an assertion.
     * @dev O(n), where n is # of zombies (but is expected to be small).
     * This function could be uncallable if there are too many zombies. However,
     * removeOldZombies() can be used to remove any zombies that exist so that this
     * will then be callable.
     * @param assertionID The assertion on which to count staked zombies
     * @return The number of zombies staked on the assertion
     */
    function countStakedZombies(uint256 assertionID) private view returns (uint256) {
        uint256 numStakedZombies = 0;
        for (uint256 i = 0; i < zombies.length; i++) {
            if (assertions.isStaker(assertionID, zombies[i].stakerAddress)) {
                numStakedZombies++;
            }
        }
        return numStakedZombies;
    }

    // ************
    // requirements
    // ************

    function requireStaked(address stakerAddress) private view {
        if (!isStaked(stakerAddress)) {
            revert("NotStaked");
        }
    }

    function requireUnchallengedStaker(address stakerAddress) private view {
        requireStaked(stakerAddress);
        if (stakers[stakerAddress].currentChallenge != address(0)) {
            revert("ChallengedStaker");
        }
    }
}

File 2 of 33 : AssertionMap.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./libraries/Errors.sol";

// Exists only to reduce size of Rollup contract (maybe revert since Rollup fits under optimized compilation).
contract AssertionMap is Initializable {
    error ChildInboxSizeMismatch();

    error SiblingStateHashExists();

    struct Assertion {
        bytes32 stateHash; // Hash of execution state associated with assertion (see `RollupLib.stateHash`)
        uint256 inboxSize; // Inbox size this assertion advanced to
        uint256 parent; // Parent assertion ID
        uint256 deadline; // Confirmation deadline (L1 block timestamp)
        uint256 proposalTime; // L1 block number at which assertion was proposed
        // Staking state
        uint256 numStakers; // total number of stakers that have ever staked on this assertion. increasing only.
        // Child state
        uint256 childInboxSize; // child assertion inbox state
    }

    struct AssertionState {
        mapping(address => bool) stakers; // all stakers that have ever staked on this assertion.
        mapping(bytes32 => bool) childStateHashes; // child assertion vm hashes
    }

    mapping(uint256 => Assertion) public assertions;
    mapping(uint256 => AssertionState) private assertionStates; // mapping from assertionID to assertion state
    address public rollupAddress;

    modifier rollupOnly() {
        if (msg.sender != rollupAddress) {
            revert NotRollup(msg.sender, rollupAddress);
        }
        _;
    }

    constructor() {
        _disableInitializers();
    }

    function initialize() public initializer {}

    function setRollupAddress(address _rollupAddress) public {
        require(
            address(rollupAddress) == address(0),
            "rollupAddress already initialized."
        );
        require(_rollupAddress != address(0), "ZERO_ADDRESS");
        rollupAddress = _rollupAddress;
    }

    function getStateHash(uint256 assertionID) external view returns (bytes32) {
        return assertions[assertionID].stateHash;
    }

    function getInboxSize(uint256 assertionID) external view returns (uint256) {
        return assertions[assertionID].inboxSize;
    }

    function getParentID(uint256 assertionID) external view returns (uint256) {
        return assertions[assertionID].parent;
    }

    function getDeadline(uint256 assertionID) external view returns (uint256) {
        return assertions[assertionID].deadline;
    }

    function getProposalTime(uint256 assertionID) external view returns (uint256) {
        return assertions[assertionID].proposalTime;
    }

    function getNumStakers(uint256 assertionID) external view returns (uint256) {
        return assertions[assertionID].numStakers;
    }

    function isStaker(uint256 assertionID, address stakerAddress) external view returns (bool) {
        return assertionStates[assertionID].stakers[stakerAddress];
    }

    function createAssertion(
        uint256 assertionID,
        bytes32 stateHash,
        uint256 inboxSize,
        uint256 parentID,
        uint256 deadline
    ) external rollupOnly {
        Assertion storage parentAssertion = assertions[parentID];
        AssertionState storage parentAssertionState = assertionStates[parentID];
        // Child assertions must have same inbox size
        uint256 parentChildInboxSize = parentAssertion.childInboxSize;
        if (parentChildInboxSize == 0) {
            parentAssertion.childInboxSize = inboxSize;
        } else {
            if (inboxSize != parentChildInboxSize) {
                revert("ChildInboxSizeMismatch");
            }
        }
        if (parentAssertionState.childStateHashes[stateHash]) {
            revert("SiblingStateHashExists");
        }

        parentAssertionState.childStateHashes[stateHash] = true;

        assertions[assertionID] = Assertion(
            stateHash,
            inboxSize,
            parentID,
            deadline,
            block.number, // proposal time
            0, // numStakers
            0 // childInboxSize
        );
    }

    function stakeOnAssertion(uint256 assertionID, address stakerAddress) external rollupOnly {
        Assertion storage assertion = assertions[assertionID];
        assertionStates[assertionID].stakers[stakerAddress] = true;
        assertion.numStakers++;
    }

    function deleteAssertion(uint256 assertionID) external rollupOnly {
        delete assertions[assertionID];
    }

    function deleteAssertionForBatch(uint256 assertionID) external rollupOnly {
        bytes32 stateHash = assertions[assertionID].stateHash;
        uint256 parentID = assertions[assertionID].parent;
        delete assertions[assertionID];
        assertions[parentID].childInboxSize = 0;
        assertionStates[parentID].childStateHashes[stateHash] = false;
    }
}

File 3 of 33 : WhiteList.sol
// SPDX-License-Identifier: Apache-2.0

abstract contract Whitelist {
    modifier onlyOwner() {
        require(msg.sender == owner, "Ownable: caller is not the owner");
        _;
    }

    modifier stakerWhitelistOnly(address _checkAddress) {
        require(stakerslist[stakerWhitelist[_checkAddress]] == _checkAddress, "NOT_IN_STAKER_WHITELIST");
        _;
    }

    modifier operatorWhitelistOnly(address _checkAddress) {
        require(operatorslist[operatorWhitelist[_checkAddress]] == _checkAddress, "NOT_IN_OPERATOR_WHITELIST");
        _;
    }

    address public owner;
    mapping(address => uint256) public stakerWhitelist;
    address[] public stakerslist;
    mapping(address => uint256) public operatorWhitelist;
    address[] public operatorslist;

    // slot place hold
    uint256[50] whitelistGap;

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

    /**
     * @notice Add to staker whitelist
     */
    function addToStakerWhitelist(address[] calldata toAddAddresses) public onlyOwner {
        uint256 lens = stakerslist.length;
        for (uint i = 0; i < toAddAddresses.length; i++) {
            stakerWhitelist[toAddAddresses[i]] = lens+i;
            stakerslist.push(toAddAddresses[i]);
        }
    }

    /**
     * @notice Remove from whitelist
     */
    function removeFromStakerWhitelist(address[] calldata toRemoveAddresses) public onlyOwner {
        for (uint i = 0; i < toRemoveAddresses.length; i++) {
            uint256 index = stakerWhitelist[toRemoveAddresses[i]];
            stakerWhitelist[stakerslist[stakerslist.length-1]] = index;
            stakerslist[index] = stakerslist[stakerslist.length-1];
            stakerslist.pop();
            delete stakerWhitelist[toRemoveAddresses[i]];
        }
    }

    /**
 * @notice Add to whitelist
     */
    function addToOperatorWhitelist(address[] calldata toAddAddresses) public onlyOwner {
        uint256 lens = operatorslist.length;
        for (uint i = 0; i < toAddAddresses.length; i++) {
            operatorWhitelist[toAddAddresses[i]] = lens+i;
            operatorslist.push(toAddAddresses[i]);
        }
    }

    /**
     * @notice Remove from whitelist
     */
    function removeFromOperatorWhitelist(address[] calldata toRemoveAddresses) public onlyOwner {
        for (uint i = 0; i < toRemoveAddresses.length; i++) {
            uint256 index = operatorWhitelist[toRemoveAddresses[i]];
            operatorWhitelist[operatorslist[operatorslist.length-1]] = index;
            operatorslist[index] = operatorslist[operatorslist.length-1];
            operatorslist.pop();
            delete operatorWhitelist[toRemoveAddresses[i]];
        }
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

File 4 of 33 : IRollup.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * This file was changed in accordance to Apache License, Version 2.0.
 *
 * Copyright 2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./AssertionMap.sol";
import {Lib_BVMCodec} from "../../libraries/codec/Lib_BVMCodec.sol";

interface IRollup {
    event AssertionCreated(
        uint256 assertionID, address asserterAddr, bytes32 vmHash, uint256 inboxSize
    );

    event AssertionChallenged(uint256 assertionID, address challengeAddr);

    event AssertionConfirmed(uint256 assertionID);

    event AssertionRejected(uint256 assertionID);

    event StakerStaked(address stakerAddr, uint256 assertionID);

    /// @dev Thrown when address that have not staked any token calls a only-staked function
    error NotStaked();

    /// @dev Thrown when the function is called with Insufficient Stake
    error InsufficientStake();

    /// @dev Thrown when the caller is staked on unconfirmed assertion.
    error StakedOnUnconfirmedAssertion();

    /// @dev Thrown when transfer fails
    error TransferFailed();

    /// @dev Thrown when a staker tries to advance stake to invalid assertionId.
    error AssertionOutOfRange();

    /// @dev Thrown when a staker tries to advance stake to non-child assertion
    error ParentAssertionUnstaked();

    /// @dev Thrown when a sender tries to create assertion before the minimum assertion time period
    error MinimumAssertionPeriodNotPassed();

    /// @dev Thrown when parent's statehash is not equal to the start state(or previous state)/
    error PreviousStateHash();

    /// @dev Thrown when a sender tries to create assertion without any tx.
    error EmptyAssertion();

    /// @dev Thrown when the requested assertion read past the end of current Inbox.
    error InboxReadLimitExceeded();

    /// @dev Thrown when the challenge assertion Id is not ordered or in range.
    error WrongOrder();

    /// @dev Thrown when the challenger tries to challenge an unproposed assertion
    error UnproposedAssertion();

    /// @dev Thrown when the assertion is already resolved
    error AssertionAlreadyResolved();

    /// @dev Thrown when there is no unresolved assertion
    error NoUnresolvedAssertion();

    /// @dev Thrown when the challenge period has not passed
    error ChallengePeriodPending();

    /// @dev Thrown when the challenger and defender didn't attest to sibling assertions
    error DifferentParent();

    /// @dev Thrown when the assertion's parent is not the last confirmed assertion
    error InvalidParent();

    /// @dev Thrown when the staker is not in a challenge
    error NotInChallenge();

    /// @dev Thrown when the two stakers are in different challenge
    /// @param staker1Challenge challenge address of staker 1
    /// @param staker2Challenge challenge address of staker 2
    error InDifferentChallenge(address staker1Challenge, address staker2Challenge);

    /// @dev Thrown when the staker is currently in Challenge
    error ChallengedStaker();

    /// @dev Thrown when all the stakers are not staked
    error NotAllStaked();

    /// @dev Thrown staker's assertion is descendant of firstUnresolved assertion
    error StakerStakedOnTarget();

    /// @dev Thrown when there are staker's present on the assertion
    error StakersPresent();

    /// @dev Thrown when there are zero stakers
    error NoStaker();

    /// @dev Thrown when slot is not blank in initialize step
    error RedundantInitialized();

    /// @dev Thrown when function is called with a zero address argument
    error ZeroAddress();

    function assertions() external view returns (AssertionMap);

    /**
     * @param addr User address.
     * @return True if address is staked, else False.
     */
    function isStaked(address addr) external view returns (bool);

    /**
     * @return The current required stake amount.
     */
    function currentRequiredStake() external view returns (uint256);

    /**
     * @return confirmedInboxSize size of inbox confirmed
     */
    function confirmedInboxSize() external view returns (uint256);

    /**
     * @notice Deposits stake on staker's current assertion (or the last confirmed assertion if not currently staked).
     * @notice currently use MNT to stake; stakeAmount Token amount to deposit. Must be > than defined threshold if this is a new stake.
     */
     function stake(uint256 stakeAmount, address operator) external;

    /**
     * @notice Withdraws stakeAmount from staker's stake by if assertion it is staked on is confirmed.
     * @param stakeAmount Token amount to withdraw. Must be <= sender's current stake minus the current required stake.
     */
    function unstake(uint256 stakeAmount) external;

    /**
     * @notice Removes stakerAddress from the set of stakers and withdraws the full stake amount to stakerAddress.
     * This can be called by anyone since it is currently necessary to keep the chain progressing.
     * @param stakerAddress Address of staker for which to unstake.
     */
    function removeStake(address stakerAddress) external;

    /**
     * @notice Advances msg.sender's existing sake to assertionID.
     * @param assertionID ID of assertion to advance stake to. Currently this must be a child of the current assertion.
     * TODO: generalize to arbitrary descendants.
     */
    function advanceStake(uint256 assertionID) external;

    /**
     * @notice Withdraws all of msg.sender's withdrawable funds.
     */
    function withdraw() external;

    /**
     * @notice Creates a new DA representing the rollup state after executing a block of transactions (sequenced in SequencerInbox).
     * Block is represented by all transactions in range [prevInboxSize, inboxSize]. The latest staked DA of the sender
     * is considered to be the predecessor. Moves sender stake onto the new DA.
     *
     * The new DA stores the hash of the parameters: vmHash
     *
     * @param vmHash New VM hash.
     * @param inboxSize Size of inbox corresponding to assertion (number of transactions).
     */
    function createAssertion(
        bytes32 vmHash,
        uint256 inboxSize
    ) external;

    /**
     *
     * @notice create assertion with scc state batch
     *
     * @param vmHash New VM hash.
     * @param inboxSize Size of inbox corresponding to assertion (number of transactions).
     * @param _batch Batch of state roots.
     * @param _shouldStartAtElement Index of the element at which this batch should start.
     * @param _signature tss group signature of state batches.
     */
    function createAssertionWithStateBatch(
        bytes32 vmHash,
        uint256 inboxSize,
        bytes32[] calldata _batch,
        uint256 _shouldStartAtElement,
        bytes calldata _signature
    ) external;


    /**
     * @notice Initiates a dispute between a defender and challenger on an unconfirmed DA.
     * @param players Defender (first) and challenger (second) addresses. Must be staked on DAs on different branches.
     * @param assertionIDs Assertion IDs of the players engaged in the challenge. The first ID should be the earlier-created and is the one being challenged.
     * @return Newly created challenge contract address.
     */
    function challengeAssertion(address[2] calldata players, uint256[2] calldata assertionIDs)
        external
        returns (address);

    /**
     * @notice Confirms first unresolved assertion. Assertion is confirmed if and only if:
     * (1) there is at least one staker, and
     * (2) challenge period has passed, and
     * (3) predecessor has been confirmed, and
     * (4) all stakers are staked on the assertion.
     */
    function confirmFirstUnresolvedAssertion() external;

    /**
     * @notice Rejects first unresolved assertion. Assertion is rejected if and only if:
     * (1) all of the following are true:
     * (a) challenge period has passed, and
     * (b) at least one staker exists, and
     * (c) no staker remains staked on the assertion (all have been destroyed).
     * OR
     * (2) predecessor has been rejected
     */
    function rejectFirstUnresolvedAssertion() external;

    //* @param stakerAddress Address of a staker staked on a different branch to the first unresolved assertion.
    //* If the first unresolved assertion's parent is confirmed, this parameter is used to establish that a staker exists
    //* on a different branch of the assertion chain. This parameter is ignored when the parent of the first unresolved
    //* assertion is not the last confirmed assertion.
    function rejectLatestCreatedAssertionWithBatch(Lib_BVMCodec.ChainBatchHeader memory _batchHeader) external;

    /**
     * @notice Completes ongoing challenge. Callback, called by a challenge contract.
     * @param winner Address of winning staker.
     * @param loser Address of losing staker.
     */
    function completeChallenge(address winner, address loser) external;

    /**
     * Emit event to notify sequencers to roll back.
     * @param _shouldRollBack roll back to should start.
     * @param _shouldStartAtElement Index of the element at which this batch should start
     * @param _signature signature of rollback message
     */
    function rollbackL2Chain(uint256 _shouldRollBack, uint256 _shouldStartAtElement, bytes memory _signature) external;
}

File 5 of 33 : RollupLib.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * This file was changed in accordance to Apache License, Version 2.0.
 *
 * Copyright 2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./challenge/ChallengeLib.sol";

// TODO: move into ChallengeLib.
library RollupLib {
    struct ExecutionState {
        bytes32 vmHash;
    }

    /**
     * @notice Computes the hash of `execState`.
     */
    function stateHash(ExecutionState memory execState) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(execState.vmHash));
    }
}

File 6 of 33 : Lib_AddressResolver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/* Library Imports */
import { Lib_AddressManager } from "./Lib_AddressManager.sol";

/**
 * @title Lib_AddressResolver
 */
abstract contract Lib_AddressResolver {
    /*************
     * Variables *
     *************/

    Lib_AddressManager public libAddressManager;

    /***************
     * Constructor *
     ***************/

    /**
     * @param _libAddressManager Address of the Lib_AddressManager.
     */
    constructor(address _libAddressManager) {
        libAddressManager = Lib_AddressManager(_libAddressManager);
    }

    /********************
     * Public Functions *
     ********************/

    /**
     * Resolves the address associated with a given name.
     * @param _name Name to resolve an address for.
     * @return Address associated with the given name.
     */
    function resolve(string memory _name) public view returns (address) {
        return libAddressManager.getAddress(_name);
    }
}

File 7 of 33 : Lib_AddressManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/* External Imports */
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title Lib_AddressManager
 */
contract Lib_AddressManager is Ownable {
    /**********
     * Events *
     **********/

    event AddressSet(string indexed _name, address _newAddress, address _oldAddress);

    /*************
     * Variables *
     *************/

    mapping(bytes32 => address) private addresses;

    /********************
     * Public Functions *
     ********************/

    /**
     * Changes the address associated with a particular name.
     * @param _name String name to associate an address with.
     * @param _address Address to associate with the name.
     */
    function setAddress(string memory _name, address _address) external onlyOwner {
        bytes32 nameHash = _getNameHash(_name);
        address oldAddress = addresses[nameHash];
        addresses[nameHash] = _address;

        emit AddressSet(_name, _address, oldAddress);
    }

    /**
     * Retrieves the address associated with a given name.
     * @param _name Name to retrieve an address for.
     * @return Address associated with the given name.
     */
    function getAddress(string memory _name) external view returns (address) {
        return addresses[_getNameHash(_name)];
    }

    /**********************
     * Internal Functions *
     **********************/

    /**
     * Computes the hash of a name.
     * @param _name Name to compute a hash for.
     * @return Hash of the given name.
     */
    function _getNameHash(string memory _name) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(_name));
    }
}

File 8 of 33 : Lib_BVMCodec.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/* Library Imports */
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
import { Lib_BytesUtils } from "../utils/Lib_BytesUtils.sol";
import { Lib_Bytes32Utils } from "../utils/Lib_Bytes32Utils.sol";

/**
 * @title Lib_BVMCodec
 */
library Lib_BVMCodec {
    /*********
     * Enums *
     *********/

    enum QueueOrigin {
        SEQUENCER_QUEUE,
        L1TOL2_QUEUE
    }

    /***********
     * Structs *
     ***********/

    struct EVMAccount {
        uint256 nonce;
        uint256 balance;
        bytes32 storageRoot;
        bytes32 codeHash;
    }

    struct ChainBatchHeader {
        uint256 batchIndex;
        bytes32 batchRoot;
        uint256 batchSize;
        uint256 prevTotalElements;
        bytes signature;
        bytes extraData;
    }

    struct ChainInclusionProof {
        uint256 index;
        bytes32[] siblings;
    }

    struct Transaction {
        uint256 timestamp;
        uint256 blockNumber;
        QueueOrigin l1QueueOrigin;
        address l1TxOrigin;
        address entrypoint;
        uint256 gasLimit;
        bytes data;
    }

    struct TransactionChainElement {
        bool isSequenced;
        uint256 queueIndex; // QUEUED TX ONLY
        uint256 timestamp; // SEQUENCER TX ONLY
        uint256 blockNumber; // SEQUENCER TX ONLY
        bytes txData; // SEQUENCER TX ONLY
    }

    struct QueueElement {
        bytes32 transactionHash;
        uint40 timestamp;
        uint40 blockNumber;
    }

    /**********************
     * Internal Functions *
     **********************/

    /**
     * Encodes a standard BVM transaction.
     * @param _transaction BVM transaction to encode.
     * @return Encoded transaction bytes.
     */
    function encodeTransaction(Transaction memory _transaction)
        internal
        pure
        returns (bytes memory)
    {
        return
            abi.encodePacked(
                _transaction.timestamp,
                _transaction.blockNumber,
                _transaction.l1QueueOrigin,
                _transaction.l1TxOrigin,
                _transaction.entrypoint,
                _transaction.gasLimit,
                _transaction.data
            );
    }

    /**
     * Hashes a standard BVM transaction.
     * @param _transaction BVM transaction to encode.
     * @return Hashed transaction
     */
    function hashTransaction(Transaction memory _transaction) internal pure returns (bytes32) {
        return keccak256(encodeTransaction(_transaction));
    }

    /**
     * @notice Decodes an RLP-encoded account state into a useful struct.
     * @param _encoded RLP-encoded account state.
     * @return Account state struct.
     */
    function decodeEVMAccount(bytes memory _encoded) internal pure returns (EVMAccount memory) {
        Lib_RLPReader.RLPItem[] memory accountState = Lib_RLPReader.readList(_encoded);

        return
            EVMAccount({
                nonce: Lib_RLPReader.readUint256(accountState[0]),
                balance: Lib_RLPReader.readUint256(accountState[1]),
                storageRoot: Lib_RLPReader.readBytes32(accountState[2]),
                codeHash: Lib_RLPReader.readBytes32(accountState[3])
            });
    }

    /**
     * Calculates a hash for a given batch header.
     * @param _batchHeader Header to hash.
     * @return Hash of the header.
     */
    function hashBatchHeader(Lib_BVMCodec.ChainBatchHeader memory _batchHeader)
        internal
        pure
        returns (bytes32)
    {
        return
            keccak256(
                abi.encode(
                    _batchHeader.batchRoot,
                    _batchHeader.batchSize,
                    _batchHeader.prevTotalElements,
                    _batchHeader.signature,
                    _batchHeader.extraData
                )
            );
    }
}

File 9 of 33 : Challenge.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * This file was changed in accordance to Apache License, Version 2.0.
 *
 * Copyright 2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./IChallenge.sol";
import "./ChallengeLib.sol";
import "../IRollup.sol";

contract Challenge is IChallenge {
    struct BisectedStore {
        bytes32 startState;
        bytes32 midState;
        bytes32 endState;
        uint256 blockNum;
        uint256 blockTime;
        uint256 challengedSegmentStart;
        uint256 challengedSegmentLength;
    }

    enum Turn {
        NoChallenge,
        Challenger,
        Defender
    }

    // Error codes

    // Can only initialize once
    string private constant CHAL_INIT_STATE = "CHAL_INIT_STATE";
    // deadline expired
    string private constant BIS_DEADLINE = "BIS_DEADLINE";
    // Only original asserter can continue bisect
    string private constant BIS_SENDER = "BIS_SENDER";
    // Incorrect previous state
    string private constant BIS_PREV = "BIS_PREV";
    // Can't timeout before deadline
    string private constant TIMEOUT_DEADLINE = "TIMEOUT_DEADLINE";

    bytes32 private constant UNREACHABLE_ASSERTION = bytes32(uint256(0));

    uint256 private constant MAX_BISECTION_DEGREE = 2;

    // Other contracts
    address internal resultReceiver;
    IVerifierEntry internal verifier;

    // Challenge state
    address public defender;
    address public challenger;
    uint256 public lastMoveBlockTime;
    uint256 public defenderTimeLeft;
    uint256 public challengerTimeLeft;

    Turn public turn;
    // See `ChallengeLib.computeBisectionHash` for the format of this commitment.
    bytes32 public bisectionHash;
    bytes32[3] public prevBisection;

    // Initial state used to initialize bisectionHash (write-once).
    bytes32 private startStateHash;
    bytes32 private endStateHash;

    address public winner;

    bool public rollback;
    uint256 public startInboxSize;

    BisectedStore public currentBisected;

    /**
     * @notice Pre-condition: `msg.sender` is correct and still has time remaining.
     * Post-condition: `turn` changes and `lastMoveBlock` set to current `block.number`.
     */
    modifier onlyOnTurn() {
        require(msg.sender == currentResponder(), BIS_SENDER);
        require(block.timestamp - lastMoveBlockTime <= currentResponderTimeLeft(), BIS_DEADLINE);

        _;

        if (turn == Turn.Challenger) {
            challengerTimeLeft = challengerTimeLeft - (block.timestamp- lastMoveBlockTime);
            turn = Turn.Defender;
        } else if (turn == Turn.Defender) {
            defenderTimeLeft = defenderTimeLeft - (block.timestamp - lastMoveBlockTime);
            turn = Turn.Challenger;
        }
        lastMoveBlockTime = block.timestamp;
    }

    /**
     * @notice Ensures challenge has been initialized.
     */
    modifier postInitialization() {
        require(bisectionHash != 0, "NOT_INITIALIZED");
        _;
    }

    modifier onlyDefender(){
        require(defender != address(0),"Defender not set");
        require(msg.sender==defender,"Caller not defender");
        _;
    }

    function initialize(
        address _defender,
        address _challenger,
        IVerifierEntry _verifier,
        address _resultReceiver,
        uint256 _startInboxSize,
        bytes32 _startStateHash,
        bytes32 _endStateHash
    ) external override {
        require(turn == Turn.NoChallenge, CHAL_INIT_STATE);
        require(_defender != address(0) && _challenger != address(0) && _resultReceiver != address(0), "ZERO_ADDRESS");
        defender = _defender;
        challenger = _challenger;
        verifier = _verifier;
        resultReceiver = _resultReceiver;
        startStateHash = _startStateHash;
        endStateHash = _endStateHash;

        turn = Turn.Defender;
        lastMoveBlockTime = block.timestamp;
        // TODO(ujval): initialize timeout
        defenderTimeLeft = 150;
        challengerTimeLeft = 150;
        prevBisection[0] = _startStateHash;
        prevBisection[1] = bytes32(0);
        prevBisection[2] = _endStateHash;

        startInboxSize = _startInboxSize;
    }

    function initializeChallengeLength(bytes32 checkStateHash, uint256 _numSteps) external override onlyOnTurn {
        require(bisectionHash == 0, CHAL_INIT_STATE);
        require(_numSteps > 0, "INVALID_NUM_STEPS");
        bisectionHash = ChallengeLib.computeBisectionHash(0, _numSteps);
        // TODO: consider emitting a different event?
        currentBisected = BisectedStore(startStateHash, checkStateHash, endStateHash, block.number, block.timestamp, 0, _numSteps);
        emit Bisected(startStateHash, checkStateHash, endStateHash, block.number, block.timestamp, 0, _numSteps);
    }

    function bisectExecution(
        bytes32[3] calldata bisection,
        uint256 challengedSegmentIndex,
        uint256 challengedSegmentStart,
        uint256 challengedSegmentLength,
        uint256 prevChallengedSegmentStart,
        uint256 prevChallengedSegmentLength
    ) external override onlyOnTurn postInitialization {
        // Verify provided prev bisection.
        bytes32 prevHash = ChallengeLib.computeBisectionHash(prevChallengedSegmentStart, prevChallengedSegmentLength);
        require(prevHash == bisectionHash, BIS_PREV);

        // Require agreed upon start state hash and disagreed upon end state hash.
        if (prevBisection[1] != bytes32(0)) {
            require(bisection[0] == prevBisection[0] || bisection[0] == prevBisection[1], "AMBIGUOUS_START");
        }
        require(bisection[2] != prevBisection[2], "INVALID_END");

        // Compute segment start/length.
        require(challengedSegmentLength > 0, "TOO_SHORT");

        // Compute new challenge state.
        prevBisection[0] = bisection[0];
        prevBisection[1] = bisection[1];
        prevBisection[2] = bisection[2];
        bisectionHash = ChallengeLib.computeBisectionHash(challengedSegmentStart, challengedSegmentLength);
        currentBisected = BisectedStore(bisection[0], bisection[1], bisection[2], block.number, block.timestamp, challengedSegmentStart, challengedSegmentLength);
        emit Bisected(bisection[0], bisection[1], bisection[2], block.number, block.timestamp, challengedSegmentStart, challengedSegmentLength);
    }

    function verifyOneStepProof(
        VerificationContext.Context calldata ctx,
        uint8 verifyType,
        bytes calldata proof,
        uint256 challengedStepIndex,
        uint256 prevChallengedSegmentStart,
        uint256 prevChallengedSegmentLength
    ) external override onlyOnTurn {
         // Verify provided prev bisection.
         bytes32 prevHash =
            ChallengeLib.computeBisectionHash(prevChallengedSegmentStart, prevChallengedSegmentLength);
         require(prevHash == bisectionHash, BIS_PREV);
         // require(challengedStepIndex > 0 && challengedStepIndex < prevBisection.length, "INVALID_INDEX");
         // Require that this is the last round.
         require(prevChallengedSegmentLength / MAX_BISECTION_DEGREE <= 1, "BISECTION_INCOMPLETE");

         // verify OSP
         // IVerificationContext ctx = <get ctx from sequenced txs>;

         bytes32 nextStateHash = verifier.verifyOneStepProof(
             ctx,
             verifyType,
             prevBisection[challengedStepIndex-1],
             proof
         );
         if (nextStateHash == prevBisection[challengedStepIndex]) {
             // osp verified, current win
             _currentWin(CompletionReason.OSP_VERIFIED);
         } else {
             _currentLose(CompletionReason.OSP_VERIFIED);
         }
    }

    function setRollback() public {
        if (rollback) {
            revert("ALREADY_SET_ROLLBACK");
        }
        rollback = true;
    }

    function timeout() external override {
        require(block.timestamp - lastMoveBlockTime > currentResponderTimeLeft(), TIMEOUT_DEADLINE);
        if (turn == Turn.Defender) {
            _challengerWin(CompletionReason.TIMEOUT);
        } else {
            _asserterWin(CompletionReason.TIMEOUT);
        }
    }

    function currentResponder() public view override returns (address) {
        if (turn == Turn.Defender) {
            return defender;
        } else if (turn == Turn.Challenger) {
            return challenger;
        } else {
            revert("NO_TURN");
        }
    }

    function currentResponderTimeLeft() public view override returns (uint256) {
        if (turn == Turn.Defender) {
            return defenderTimeLeft;
        } else if (turn == Turn.Challenger) {
            return challengerTimeLeft;
        } else {
            revert("NO_TURN");
        }
    }

    function _currentWin(CompletionReason reason) private {
        if (turn == Turn.Defender) {
            _asserterWin(reason);
        } else {
            winner = challenger;
            _challengerWin(reason);
        }
    }

    function _currentLose(CompletionReason reason) private {
        if (turn == Turn.Defender) {
            _challengerWin(reason);
        } else {
            _asserterWin(reason);
        }
    }

    function _asserterWin(CompletionReason reason) private {
        winner = defender;
        emit ChallengeCompleted(defender, challenger, reason);
    }

    function _challengerWin(CompletionReason reason) private {
        winner = challenger;
        emit ChallengeCompleted(challenger, defender, reason);
    }

    function completeChallenge(bool result) external onlyDefender{
        require(winner != address(0),"Do not have winner");

        if (winner == challenger) {
            if (result) {
                IRollup(resultReceiver).completeChallenge(challenger, defender);
                return;
            }
            winner = defender;
        }
        IRollup(resultReceiver).completeChallenge(defender, challenger);
    }
}

File 10 of 33 : ChallengeLib.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * This file was changed in accordance to Apache License, Version 2.0.
 *
 * Copyright 2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

library ChallengeLib {
    /**
     * @notice Computes the initial bisection hash.
     * @param numSteps Number of steps from the end of `startState` to the end of `endState`.
     */
    function initialBisectionHash(uint256 numSteps)
        internal
        pure
        returns (bytes32)
    {
        return ChallengeLib.computeBisectionHash(0, numSteps);
    }

    /**
     * @notice Computes H(bisection || segmentStart || segmentLength)
     * @param challengedSegmentStart The number of steps preceding `bisection[1]`, relative to the assertion being challenged.
     * @param challengedSegmentLength Length of bisected segment (in steps), from the start of bisection[1] to the end of bisection[-1].
     */
    function computeBisectionHash(
        uint256 challengedSegmentStart,
        uint256 challengedSegmentLength
    ) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(challengedSegmentStart, challengedSegmentLength));
    }

    /**
     * @notice Returns length of first segment in a bisection.
     */
    function firstSegmentLength(uint256 length, uint256 bisectionDegree) internal pure returns (uint256) {
        return length / bisectionDegree + length % bisectionDegree;
    }

    /**
     * @notice Returns length of a segment (after first) in a bisection.
     */
    function otherSegmentLength(uint256 length, uint256 bisectionDegree) internal pure returns (uint256) {
        return length / bisectionDegree;
    }
}

File 11 of 33 : IVerifier.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./libraries/VerificationContext.sol";

interface IVerifier {
    function verifyOneStepProof(VerificationContext.Context memory ctx, bytes32 currStateHash, bytes calldata encoded)
        external
        pure
        returns (bytes32);
}

File 12 of 33 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 13 of 33 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }
}

File 14 of 33 : Errors.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

/// @dev Thrown when unauthorized (!rollup) address calls an only-rollup function
/// @param sender Address of the caller
/// @param rollup The rollup address authorized to call this function
error NotRollup(address sender, address rollup);

/// @dev Thrown when unauthorized (!challenge) address calls an only-challenge function
/// @param sender Address of the caller
/// @param challenge The challenge address authorized to call this function
error NotChallenge(address sender, address challenge);

/// @dev Thrown when unauthorized (!sequencer) address calls an only-sequencer function
/// @param sender Address of the caller
/// @param sequencer The sequencer address authorized to call this function
error NotSequencer(address sender, address sequencer);

/// @dev Thrown when function is called with a zero address argument
error ZeroAddress();

/// @dev Thrown when function is called with a zero address argument
error RedundantInitialized();

File 15 of 33 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

File 16 of 33 : Lib_RLPReader.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/**
 * @title Lib_RLPReader
 * @dev Adapted from "RLPReader" by Hamdi Allam ([email protected]).
 */
library Lib_RLPReader {
    /*************
     * Constants *
     *************/

    uint256 internal constant MAX_LIST_LENGTH = 32;

    /*********
     * Enums *
     *********/

    enum RLPItemType {
        DATA_ITEM,
        LIST_ITEM
    }

    /***********
     * Structs *
     ***********/

    struct RLPItem {
        uint256 length;
        uint256 ptr;
    }

    /**********************
     * Internal Functions *
     **********************/

    /**
     * Converts bytes to a reference to memory position and length.
     * @param _in Input bytes to convert.
     * @return Output memory reference.
     */
    function toRLPItem(bytes memory _in) internal pure returns (RLPItem memory) {
        uint256 ptr;
        assembly {
            ptr := add(_in, 32)
        }

        return RLPItem({ length: _in.length, ptr: ptr });
    }

    /**
     * Reads an RLP list value into a list of RLP items.
     * @param _in RLP list value.
     * @return Decoded RLP list items.
     */
    function readList(RLPItem memory _in) internal pure returns (RLPItem[] memory) {
        (uint256 listOffset, , RLPItemType itemType) = _decodeLength(_in);

        require(itemType == RLPItemType.LIST_ITEM, "Invalid RLP list value.");

        // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by
        // writing to the length. Since we can't know the number of RLP items without looping over
        // the entire input, we'd have to loop twice to accurately size this array. It's easier to
        // simply set a reasonable maximum list length and decrease the size before we finish.
        RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH);

        uint256 itemCount = 0;
        uint256 offset = listOffset;
        while (offset < _in.length) {
            require(itemCount < MAX_LIST_LENGTH, "Provided RLP list exceeds max list length.");

            (uint256 itemOffset, uint256 itemLength, ) = _decodeLength(
                RLPItem({ length: _in.length - offset, ptr: _in.ptr + offset })
            );

            out[itemCount] = RLPItem({ length: itemLength + itemOffset, ptr: _in.ptr + offset });

            itemCount += 1;
            offset += itemOffset + itemLength;
        }

        // Decrease the array size to match the actual item count.
        assembly {
            mstore(out, itemCount)
        }

        return out;
    }

    /**
     * Reads an RLP list value into a list of RLP items.
     * @param _in RLP list value.
     * @return Decoded RLP list items.
     */
    function readList(bytes memory _in) internal pure returns (RLPItem[] memory) {
        return readList(toRLPItem(_in));
    }

    /**
     * Reads an RLP bytes value into bytes.
     * @param _in RLP bytes value.
     * @return Decoded bytes.
     */
    function readBytes(RLPItem memory _in) internal pure returns (bytes memory) {
        (uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in);

        require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes value.");

        return _copy(_in.ptr, itemOffset, itemLength);
    }

    /**
     * Reads an RLP bytes value into bytes.
     * @param _in RLP bytes value.
     * @return Decoded bytes.
     */
    function readBytes(bytes memory _in) internal pure returns (bytes memory) {
        return readBytes(toRLPItem(_in));
    }

    /**
     * Reads an RLP string value into a string.
     * @param _in RLP string value.
     * @return Decoded string.
     */
    function readString(RLPItem memory _in) internal pure returns (string memory) {
        return string(readBytes(_in));
    }

    /**
     * Reads an RLP string value into a string.
     * @param _in RLP string value.
     * @return Decoded string.
     */
    function readString(bytes memory _in) internal pure returns (string memory) {
        return readString(toRLPItem(_in));
    }

    /**
     * Reads an RLP bytes32 value into a bytes32.
     * @param _in RLP bytes32 value.
     * @return Decoded bytes32.
     */
    function readBytes32(RLPItem memory _in) internal pure returns (bytes32) {
        require(_in.length <= 33, "Invalid RLP bytes32 value.");

        (uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in);

        require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes32 value.");

        uint256 ptr = _in.ptr + itemOffset;
        bytes32 out;
        assembly {
            out := mload(ptr)

            // Shift the bytes over to match the item size.
            if lt(itemLength, 32) {
                out := div(out, exp(256, sub(32, itemLength)))
            }
        }

        return out;
    }

    /**
     * Reads an RLP bytes32 value into a bytes32.
     * @param _in RLP bytes32 value.
     * @return Decoded bytes32.
     */
    function readBytes32(bytes memory _in) internal pure returns (bytes32) {
        return readBytes32(toRLPItem(_in));
    }

    /**
     * Reads an RLP uint256 value into a uint256.
     * @param _in RLP uint256 value.
     * @return Decoded uint256.
     */
    function readUint256(RLPItem memory _in) internal pure returns (uint256) {
        return uint256(readBytes32(_in));
    }

    /**
     * Reads an RLP uint256 value into a uint256.
     * @param _in RLP uint256 value.
     * @return Decoded uint256.
     */
    function readUint256(bytes memory _in) internal pure returns (uint256) {
        return readUint256(toRLPItem(_in));
    }

    /**
     * Reads an RLP bool value into a bool.
     * @param _in RLP bool value.
     * @return Decoded bool.
     */
    function readBool(RLPItem memory _in) internal pure returns (bool) {
        require(_in.length == 1, "Invalid RLP boolean value.");

        uint256 ptr = _in.ptr;
        uint256 out;
        assembly {
            out := byte(0, mload(ptr))
        }

        require(out == 0 || out == 1, "Lib_RLPReader: Invalid RLP boolean value, must be 0 or 1");

        return out != 0;
    }

    /**
     * Reads an RLP bool value into a bool.
     * @param _in RLP bool value.
     * @return Decoded bool.
     */
    function readBool(bytes memory _in) internal pure returns (bool) {
        return readBool(toRLPItem(_in));
    }

    /**
     * Reads an RLP address value into a address.
     * @param _in RLP address value.
     * @return Decoded address.
     */
    function readAddress(RLPItem memory _in) internal pure returns (address) {
        if (_in.length == 1) {
            return address(0);
        }

        require(_in.length == 21, "Invalid RLP address value.");

        return address(uint160(readUint256(_in)));
    }

    /**
     * Reads an RLP address value into a address.
     * @param _in RLP address value.
     * @return Decoded address.
     */
    function readAddress(bytes memory _in) internal pure returns (address) {
        return readAddress(toRLPItem(_in));
    }

    /**
     * Reads the raw bytes of an RLP item.
     * @param _in RLP item to read.
     * @return Raw RLP bytes.
     */
    function readRawBytes(RLPItem memory _in) internal pure returns (bytes memory) {
        return _copy(_in);
    }

    /*********************
     * Private Functions *
     *********************/

    /**
     * Decodes the length of an RLP item.
     * @param _in RLP item to decode.
     * @return Offset of the encoded data.
     * @return Length of the encoded data.
     * @return RLP item type (LIST_ITEM or DATA_ITEM).
     */
    function _decodeLength(RLPItem memory _in)
        private
        pure
        returns (
            uint256,
            uint256,
            RLPItemType
        )
    {
        require(_in.length > 0, "RLP item cannot be null.");

        uint256 ptr = _in.ptr;
        uint256 prefix;
        assembly {
            prefix := byte(0, mload(ptr))
        }

        if (prefix <= 0x7f) {
            // Single byte.

            return (0, 1, RLPItemType.DATA_ITEM);
        } else if (prefix <= 0xb7) {
            // Short string.

            // slither-disable-next-line variable-scope
            uint256 strLen = prefix - 0x80;

            require(_in.length > strLen, "Invalid RLP short string.");

            return (1, strLen, RLPItemType.DATA_ITEM);
        } else if (prefix <= 0xbf) {
            // Long string.
            uint256 lenOfStrLen = prefix - 0xb7;

            require(_in.length > lenOfStrLen, "Invalid RLP long string length.");

            uint256 strLen;
            assembly {
                // Pick out the string length.
                strLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfStrLen)))
            }

            require(_in.length > lenOfStrLen + strLen, "Invalid RLP long string.");

            return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
        } else if (prefix <= 0xf7) {
            // Short list.
            // slither-disable-next-line variable-scope
            uint256 listLen = prefix - 0xc0;

            require(_in.length > listLen, "Invalid RLP short list.");

            return (1, listLen, RLPItemType.LIST_ITEM);
        } else {
            // Long list.
            uint256 lenOfListLen = prefix - 0xf7;

            require(_in.length > lenOfListLen, "Invalid RLP long list length.");

            uint256 listLen;
            assembly {
                // Pick out the list length.
                listLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfListLen)))
            }

            require(_in.length > lenOfListLen + listLen, "Invalid RLP long list.");

            return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM);
        }
    }

    /**
     * Copies the bytes from a memory location.
     * @param _src Pointer to the location to read from.
     * @param _offset Offset to start reading from.
     * @param _length Number of bytes to read.
     * @return Copied bytes.
     */
    function _copy(
        uint256 _src,
        uint256 _offset,
        uint256 _length
    ) private pure returns (bytes memory) {
        bytes memory out = new bytes(_length);
        if (out.length == 0) {
            return out;
        }

        uint256 src = _src + _offset;
        uint256 dest;
        assembly {
            dest := add(out, 32)
        }

        // Copy over as many complete words as we can.
        for (uint256 i = 0; i < _length / 32; i++) {
            assembly {
                mstore(dest, mload(src))
            }

            src += 32;
            dest += 32;
        }

        // Pick out the remaining bytes.
        uint256 mask;
        unchecked {
            mask = 256**(32 - (_length % 32)) - 1;
        }

        assembly {
            mstore(dest, or(and(mload(src), not(mask)), and(mload(dest), mask)))
        }
        return out;
    }

    /**
     * Copies an RLP item into bytes.
     * @param _in RLP item to copy.
     * @return Copied bytes.
     */
    function _copy(RLPItem memory _in) private pure returns (bytes memory) {
        return _copy(_in.ptr, 0, _in.length);
    }
}

File 17 of 33 : Lib_RLPWriter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/**
 * @title Lib_RLPWriter
 * @author Bakaoh (with modifications)
 */
library Lib_RLPWriter {
    /**********************
     * Internal Functions *
     **********************/

    /**
     * RLP encodes a byte string.
     * @param _in The byte string to encode.
     * @return The RLP encoded string in bytes.
     */
    function writeBytes(bytes memory _in) internal pure returns (bytes memory) {
        bytes memory encoded;

        if (_in.length == 1 && uint8(_in[0]) < 128) {
            encoded = _in;
        } else {
            encoded = abi.encodePacked(_writeLength(_in.length, 128), _in);
        }

        return encoded;
    }

    /**
     * RLP encodes a list of RLP encoded byte byte strings.
     * @param _in The list of RLP encoded byte strings.
     * @return The RLP encoded list of items in bytes.
     */
    function writeList(bytes[] memory _in) internal pure returns (bytes memory) {
        bytes memory list = _flatten(_in);
        return abi.encodePacked(_writeLength(list.length, 192), list);
    }

    /**
     * RLP encodes a string.
     * @param _in The string to encode.
     * @return The RLP encoded string in bytes.
     */
    function writeString(string memory _in) internal pure returns (bytes memory) {
        return writeBytes(bytes(_in));
    }

    /**
     * RLP encodes an address.
     * @param _in The address to encode.
     * @return The RLP encoded address in bytes.
     */
    function writeAddress(address _in) internal pure returns (bytes memory) {
        return writeBytes(abi.encodePacked(_in));
    }

    /**
     * RLP encodes a uint.
     * @param _in The uint256 to encode.
     * @return The RLP encoded uint256 in bytes.
     */
    function writeUint(uint256 _in) internal pure returns (bytes memory) {
        return writeBytes(_toBinary(_in));
    }

    /**
     * RLP encodes a bool.
     * @param _in The bool to encode.
     * @return The RLP encoded bool in bytes.
     */
    function writeBool(bool _in) internal pure returns (bytes memory) {
        bytes memory encoded = new bytes(1);
        encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80));
        return encoded;
    }

    /*********************
     * Private Functions *
     *********************/

    /**
     * Encode the first byte, followed by the `len` in binary form if `length` is more than 55.
     * @param _len The length of the string or the payload.
     * @param _offset 128 if item is string, 192 if item is list.
     * @return RLP encoded bytes.
     */
    function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory) {
        bytes memory encoded;

        if (_len < 56) {
            encoded = new bytes(1);
            encoded[0] = bytes1(uint8(_len) + uint8(_offset));
        } else {
            uint256 lenLen;
            uint256 i = 1;
            while (_len / i != 0) {
                lenLen++;
                i *= 256;
            }

            encoded = new bytes(lenLen + 1);
            encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55);
            for (i = 1; i <= lenLen; i++) {
                encoded[i] = bytes1(uint8((_len / (256**(lenLen - i))) % 256));
            }
        }

        return encoded;
    }

    /**
     * Encode integer in big endian binary form with no leading zeroes.
     * @notice TODO: This should be optimized with assembly to save gas costs.
     * @param _x The integer to encode.
     * @return RLP encoded bytes.
     */
    function _toBinary(uint256 _x) private pure returns (bytes memory) {
        bytes memory b = abi.encodePacked(_x);

        uint256 i = 0;
        for (; i < 32; i++) {
            if (b[i] != 0) {
                break;
            }
        }

        bytes memory res = new bytes(32 - i);
        for (uint256 j = 0; j < res.length; j++) {
            res[j] = b[i++];
        }

        return res;
    }

    /**
     * Copies a piece of memory to another location.
     * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol.
     * @param _dest Destination location.
     * @param _src Source location.
     * @param _len Length of memory to copy.
     */
    function _memcpy(
        uint256 _dest,
        uint256 _src,
        uint256 _len
    ) private pure {
        uint256 dest = _dest;
        uint256 src = _src;
        uint256 len = _len;

        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        uint256 mask;
        unchecked {
            mask = 256**(32 - len) - 1;
        }
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    /**
     * Flattens a list of byte strings into one byte string.
     * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol.
     * @param _list List of byte strings to flatten.
     * @return The flattened byte string.
     */
    function _flatten(bytes[] memory _list) private pure returns (bytes memory) {
        if (_list.length == 0) {
            return new bytes(0);
        }

        uint256 len;
        uint256 i = 0;
        for (; i < _list.length; i++) {
            len += _list[i].length;
        }

        bytes memory flattened = new bytes(len);
        uint256 flattenedPtr;
        assembly {
            flattenedPtr := add(flattened, 0x20)
        }

        for (i = 0; i < _list.length; i++) {
            bytes memory item = _list[i];

            uint256 listPtr;
            assembly {
                listPtr := add(item, 0x20)
            }

            _memcpy(flattenedPtr, listPtr, item.length);
            flattenedPtr += _list[i].length;
        }

        return flattened;
    }
}

File 18 of 33 : Lib_Bytes32Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/**
 * @title Lib_Byte32Utils
 */
library Lib_Bytes32Utils {
    /**********************
     * Internal Functions *
     **********************/

    /**
     * Converts a bytes32 value to a boolean. Anything non-zero will be converted to "true."
     * @param _in Input bytes32 value.
     * @return Bytes32 as a boolean.
     */
    function toBool(bytes32 _in) internal pure returns (bool) {
        return _in != 0;
    }

    /**
     * Converts a boolean to a bytes32 value.
     * @param _in Input boolean value.
     * @return Boolean as a bytes32.
     */
    function fromBool(bool _in) internal pure returns (bytes32) {
        return bytes32(uint256(_in ? 1 : 0));
    }

    /**
     * Converts a bytes32 value to an address. Takes the *last* 20 bytes.
     * @param _in Input bytes32 value.
     * @return Bytes32 as an address.
     */
    function toAddress(bytes32 _in) internal pure returns (address) {
        return address(uint160(uint256(_in)));
    }

    /**
     * Converts an address to a bytes32.
     * @param _in Input address value.
     * @return Address as a bytes32.
     */
    function fromAddress(address _in) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(_in)));
    }
}

File 19 of 33 : Lib_BytesUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/**
 * @title Lib_BytesUtils
 */
library Lib_BytesUtils {
    /**********************
     * Internal Functions *
     **********************/

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_start + _length >= _start, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)

                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) {
        if (_start >= _bytes.length) {
            return bytes("");
        }

        return slice(_bytes, _start, _bytes.length - _start);
    }

    function toBytes32(bytes memory _bytes) internal pure returns (bytes32) {
        if (_bytes.length < 32) {
            bytes32 ret;
            assembly {
                ret := mload(add(_bytes, 32))
            }
            return ret;
        }

        return abi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes
    }

    function toUint256(bytes memory _bytes) internal pure returns (uint256) {
        return uint256(toBytes32(_bytes));
    }

    function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) {
        bytes memory nibbles = new bytes(_bytes.length * 2);

        for (uint256 i = 0; i < _bytes.length; i++) {
            nibbles[i * 2] = _bytes[i] >> 4;
            nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16);
        }

        return nibbles;
    }

    function fromNibbles(bytes memory _bytes) internal pure returns (bytes memory) {
        bytes memory ret = new bytes(_bytes.length / 2);

        for (uint256 i = 0; i < ret.length; i++) {
            ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]);
        }

        return ret;
    }

    function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) {
        return keccak256(_bytes) == keccak256(_other);
    }
}

File 20 of 33 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 21 of 33 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

File 22 of 33 : IChallenge.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Modifications Copyright 2022, Specular contributors
 *
 * This file was changed in accordance to Apache License, Version 2.0.
 *
 * Copyright 2021, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../verifier/IVerifierEntry.sol";

/**
 * @notice Protocol execution:
 * `initialize` (challenger, via Rollup) ->
 * `initializeChallengeLength` (defender) ->
 * `bisectExecution` (challenger, defender -- alternating) ->
 * `verifyOneStepProof`
 */
interface IChallenge {
    enum CompletionReason {
        OSP_VERIFIED, // OSP verified by winner.
        TIMEOUT // Loser timed out before completing their round.
    }

    event ChallengeCompleted(address winner, address loser, CompletionReason reason);

    event Bisected(bytes32 startState, bytes32 midState, bytes32 endState, uint256 blockNum, uint256 blockTime, uint256 challengedSegmentStart, uint256 challengedSegmentLength);

    /**
     * @notice Initializes contract.
     * @param _defender Defending party.
     * @param _challenger Challenging party. Challenger starts.
     * @param _verifier Address of the verifier contract.
     * @param _resultReceiver Address of contract that will receive the outcome (via callback `completeChallenge`).
     * @param _startStateHash Bisection root being challenged.
     * @param _endStateHash Bisection root being challenged.
     */
    function initialize(
        address _defender,
        address _challenger,
        IVerifierEntry _verifier,
        address _resultReceiver,
        uint256 _startInboxSize,
        bytes32 _startStateHash,
        bytes32 _endStateHash
    ) external;

    /**
     * @notice Initializes the length of the challenge. Must be called by defender before bisection rounds begin.
     * @param _numSteps Number of steps executed from the start of the assertion to its end.
     * If this parameter is incorrect, the defender will be slashed (assuming successful execution of the protocol by the challenger).
     */
    function initializeChallengeLength(bytes32 checkStateHash, uint256 _numSteps) external;

    /**
     * @notice Bisects a segment. The challenged segment is defined by: {`challengedSegmentStart`, `challengedSegmentLength`, `bisection[0]`, `oldEndHash`}
     * @param bisection Bisection of challenged segment. Each element is a state hash (see `ChallengeLib.stateHash`).
     * The first element is the last agreed upon state hash. Must be of length MAX_BISECTION_LENGTH for all rounds except the last.
     * In the last round, the bisection segments must be single steps.
     * @param challengedSegmentIndex Index into `prevBisection`. Must be greater than 0 (since the first is agreed upon).
     * @param challengedSegmentStart Offset of the segment challenged in the preceding round (in steps).
     * @param challengedSegmentLength Length of the segment challenged in the preceding round (in steps).
     * @param prevChallengedSegmentStart Offset of the segment challenged in the preceding round (in steps).
     * Note: this is relative to the assertion being challenged (i.e. always between 0 and the initial `numSteps`).
     * @param prevChallengedSegmentLength Length of the segment challenged in the preceding round (in steps).
     */
    function bisectExecution(
        bytes32[3] calldata bisection,
        uint256 challengedSegmentIndex,
        uint256 challengedSegmentStart,
        uint256 challengedSegmentLength,
        uint256 prevChallengedSegmentStart,
        uint256 prevChallengedSegmentLength
    ) external;

    /**
     * @notice Verifies one step proof and completes challenge protocol.
     * @param ctx execution context.
     * @param verifyType Index into `prevBisection`. Must be greater than 0 (since the first is agreed upon).
     * @param proof one step proof.
     * @param prevChallengedSegmentStart Offset of the segment challenged in the preceding round (in steps).
     * Note: this is relative to the assertion being challenged (i.e. always between 0 and the initial `numSteps`).
     * @param prevChallengedSegmentLength Length of the segment challenged in the preceding round (in steps).
     */
    function verifyOneStepProof(
        VerificationContext.Context calldata ctx,
        uint8 verifyType,
        bytes calldata proof,
        uint256 challengedStepIndex,
        uint256 prevChallengedSegmentStart,
        uint256 prevChallengedSegmentLength
    ) external;

    function setRollback() external;

    /**
     * @notice Triggers completion of challenge protocol if a responder timed out.
     */
    function timeout() external;

    function currentResponder() external view returns (address);

    function currentResponderTimeLeft() external view returns (uint256);

    function completeChallenge(bool) external;
}

File 23 of 33 : IVerifierEntry.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./libraries/VerificationContext.sol";

interface IVerifierEntry {
    function verifyOneStepProof(
        VerificationContext.Context memory ctx,
        uint8 verifier,
        bytes32 currStateHash,
        bytes calldata encoded
    ) external view returns (bytes32);
}

File 24 of 33 : VerificationContext.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/DeserializationLib.sol";
import "../../libraries/BytesLib.sol";
import "../../libraries/MerkleLib.sol";

import "./MemoryLib.sol";
import "./EVMTypesLib.sol";

library VerificationContext {
    using BytesLib for bytes;
    using EVMTypesLib for EVMTypesLib.Transaction;

    struct Context {
        address coinbase;
        uint256 timestamp;
        uint256 number;
        address origin;
        EVMTypesLib.Transaction transaction;
        bytes32 inputRoot;
        bytes32 txHash;
    }

    function newContext(bytes calldata proof) internal view returns (Context memory ctx) {
//        inbox.verifyTxInclusion(proof);
//        ctx.coinbase = inbox.sequencerAddress();
        ctx.coinbase = address(0); // TODO FIXME
        uint256 offset = 0;
        uint256 txDataLength;
        (offset, ctx.origin) = DeserializationLib.deserializeAddress(proof, offset);
        (offset, ctx.number) = DeserializationLib.deserializeUint256(proof, offset);
        (offset, ctx.timestamp) = DeserializationLib.deserializeUint256(proof, offset);
        (offset, txDataLength) = DeserializationLib.deserializeUint256(proof, offset);
        bytes memory txData = bytes(proof[offset:txDataLength]);
        ctx.transaction = EVMTypesLib.decodeTransaction(txData);
    }

    function getCoinbase(Context memory ctx) internal pure returns (address) {
        return ctx.coinbase;
    }

    function getTimestamp(Context memory ctx) internal pure returns (uint256) {
        return ctx.timestamp;
    }

    function getBlockNumber(Context memory ctx) internal pure returns (uint256) {
        return ctx.number;
    }

    function getDifficulty(Context memory) internal pure returns (uint256) {
        return 1;
    }

    function getGasLimit(Context memory) internal pure returns (uint64) {
        return 80000000;
    }

    function getChainID(Context memory) internal pure returns (uint256) {
        return 13527;
    }

    // Transaction
    function getOrigin(Context memory ctx) internal pure returns (address) {
        return ctx.origin;
    }

    function getRecipient(Context memory ctx) internal pure returns (address) {
        return ctx.transaction.to;
    }

    function getValue(Context memory ctx) internal pure returns (uint256) {
        return ctx.transaction.value;
    }

    function getGas(Context memory ctx) internal pure returns (uint64) {
        return ctx.transaction.gas;
    }

    function getGasPrice(Context memory ctx) internal pure returns (uint256) {
        return ctx.transaction.gasPrice;
    }

    function getInput(Context memory ctx) internal pure returns (bytes memory) {
        return ctx.transaction.data;
    }

    function getInputSize(Context memory ctx) internal pure returns (uint64) {
        return uint64(ctx.transaction.data.length);
    }

    function getInputRoot(Context memory ctx) internal pure returns (bytes32) {
        if (ctx.inputRoot == 0x0) {
            ctx.inputRoot = MemoryLib.getMemoryRoot(ctx.transaction.data);
        }
        return ctx.inputRoot;
    }

    function getTxHash(Context memory ctx) internal pure returns (bytes32) {
        if (ctx.txHash == 0x0) {
            ctx.txHash = ctx.transaction.hashTransaction();
        }
        return ctx.transaction.hashTransaction();
    }
}

File 25 of 33 : DeserializationLib.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./BytesLib.sol";

library DeserializationLib {
    function deserializeAddress(bytes memory data, uint256 startOffset) internal pure returns (uint256, address) {
        return (startOffset + 20, BytesLib.toAddress(data, startOffset));
    }

    function deserializeUint256(bytes memory data, uint256 startOffset) internal pure returns (uint256, uint256) {
        require(data.length >= startOffset && data.length - startOffset >= 32, "too short");
        return (startOffset + 32, BytesLib.toUint256(data, startOffset));
    }

    function deserializeBytes32(bytes memory data, uint256 startOffset) internal pure returns (uint256, bytes32) {
        require(data.length >= startOffset && data.length - startOffset >= 32, "too short");
        return (startOffset + 32, BytesLib.toBytes32(data, startOffset));
    }
}

File 26 of 33 : MerkleLib.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library MerkleLib {
    // Hashes a and b in the order they are passed
    function hash_node(bytes32 a, bytes32 b) internal pure returns (bytes32 hash) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            hash := keccak256(0x00, 0x40)
        }
    }

    // Hashes a and b in order define by boolean
    function hash_pair(bytes32 a, bytes32 b, bool order) internal pure returns (bytes32 hash) {
        hash = order ? hash_node(a, b) : hash_node(b, a);
    }

    // Counts number of set bits (1's) in 32-bit unsigned integer
    function bit_count_32(uint32 n) internal pure returns (uint32) {
        n = n - ((n >> 1) & 0x55555555);
        n = (n & 0x33333333) + ((n >> 2) & 0x33333333);

        return (((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
    }

    // Round 32-bit unsigned integer up to the nearest power of 2
    function round_up_to_power_of_2(uint32 n) internal pure returns (uint32) {
        if (bit_count_32(n) == 1) return n;

        n |= n >> 1;
        n |= n >> 2;
        n |= n >> 4;
        n |= n >> 8;
        n |= n >> 16;

        return n + 1;
    }

    // Get the Element Merkle Root for a tree with just a single bytes32 element in memory
    function get_root_from_one(bytes32 element) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(bytes1(0), element));
    }

    // Get nodes (parent of leafs) from bytes32 elements in memory
    function get_nodes_from_elements(bytes32[] memory elements) internal pure returns (bytes32[] memory nodes) {
        uint256 element_count = elements.length;
        uint256 node_count = (element_count >> 1) + (element_count & 1);
        nodes = new bytes32[](node_count);
        uint256 write_index;
        uint256 left_index;

        while (write_index < node_count) {
            left_index = write_index << 1;

            if (left_index == element_count - 1) {
                nodes[write_index] = keccak256(abi.encodePacked(bytes1(0), elements[left_index]));
                break;
            }

            nodes[write_index++] = hash_node(
                keccak256(abi.encodePacked(bytes1(0), elements[left_index])),
                keccak256(abi.encodePacked(bytes1(0), elements[left_index + 1]))
            );
        }
    }

    // Get the Element Merkle Root given nodes (parent of leafs)
    function get_root_from_nodes(bytes32[] memory nodes) internal pure returns (bytes32) {
        uint256 node_count = nodes.length;
        uint256 write_index;
        uint256 left_index;

        while (node_count > 1) {
            left_index = write_index << 1;

            if (left_index == node_count - 1) {
                nodes[write_index] = nodes[left_index];
                write_index = 0;
                node_count = (node_count >> 1) + (node_count & 1);
                continue;
            }

            if (left_index >= node_count) {
                write_index = 0;
                node_count = (node_count >> 1) + (node_count & 1);
                continue;
            }

            nodes[write_index++] = hash_node(nodes[left_index], nodes[left_index + 1]);
        }

        return nodes[0];
    }

    // Get the Element Merkle Root for a tree with several bytes32 elements in memory
    function get_root_from_many(bytes32[] memory elements) internal pure returns (bytes32) {
        return get_root_from_nodes(get_nodes_from_elements(elements));
    }

    // Get the original Element Merkle Root, given a Size Proof
    function get_root_from_size_proof(uint256 element_count, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 hash)
    {
        uint256 proof_index = bit_count_32(uint32(element_count)) - 1;
        hash = proof[proof_index];

        while (proof_index > 0) {
            hash = hash_node(proof[--proof_index], hash);
        }
    }

    // Get the original Element Merkle Root, given an index, a leaf, and a Single Proof
    function get_root_from_leaf_and_single_proof(uint256 index, bytes32 leaf, bytes32[] memory proof)
        internal
        pure
        returns (bytes32)
    {
        uint256 proof_index = proof.length - 1;
        uint256 upper_bound = uint256(proof[0]) - 1;

        while (proof_index > 0) {
            if (index != upper_bound || (index & 1 == 1)) {
                leaf = (index & 1 == 1) ? hash_node(proof[proof_index], leaf) : hash_node(leaf, proof[proof_index]);
                proof_index -= 1;
            }

            index >>= 1;
            upper_bound >>= 1;
        }

        return leaf;
    }

    // Get the original Element Merkle Root, given an index, a bytes32 element, and a Single Proof
    function get_root_from_single_proof(uint256 index, bytes32 element, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 hash)
    {
        hash = keccak256(abi.encodePacked(bytes1(0), element));
        hash = get_root_from_leaf_and_single_proof(index, hash, proof);
    }

    // Get the original and updated Element Merkle Root, given an index, a leaf, an update leaf, and a Single Proof
    function get_roots_from_leaf_and_single_proof_update(
        uint256 index,
        bytes32 leaf,
        bytes32 update_leaf,
        bytes32[] memory proof
    ) internal pure returns (bytes32 scratch, bytes32) {
        uint256 proof_index = proof.length - 1;
        uint256 upper_bound = uint256(proof[0]) - 1;

        while (proof_index > 0) {
            if ((index != upper_bound) || (index & 1 == 1)) {
                scratch = proof[proof_index];
                proof_index -= 1;
                leaf = (index & 1 == 1) ? hash_node(scratch, leaf) : hash_node(leaf, scratch);
                update_leaf = (index & 1 == 1) ? hash_node(scratch, update_leaf) : hash_node(update_leaf, scratch);
            }

            index >>= 1;
            upper_bound >>= 1;
        }

        return (leaf, update_leaf);
    }

    // Get the original and updated Element Merkle Root, given an index, a bytes32 element, a bytes32 update element, and a Single Proof
    function get_roots_from_single_proof_update(
        uint256 index,
        bytes32 element,
        bytes32 update_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 hash, bytes32 update_hash) {
        hash = keccak256(abi.encodePacked(bytes1(0), element));
        update_hash = keccak256(abi.encodePacked(bytes1(0), update_element));
        return get_roots_from_leaf_and_single_proof_update(index, hash, update_hash, proof);
    }

    // Get the indices of the elements being proven, given an Existence Multi Proof
    function get_indices_from_multi_proof(uint256 element_count, bytes32 flags, bytes32 skips, bytes32 orders)
        internal
        pure
        returns (uint256[] memory indices)
    {
        indices = new uint256[](element_count);
        uint256[] memory bits_pushed = new uint256[](element_count);
        bool[] memory grouped_with_next = new bool[](element_count);
        element_count -= 1;
        uint256 index = element_count;
        bytes32 bit_check = 0x0000000000000000000000000000000000000000000000000000000000000001;
        bytes32 flag;
        bytes32 skip;
        bytes32 order;
        uint256 bits_to_push;

        while (true) {
            flag = flags & bit_check;
            skip = skips & bit_check;
            order = orders & bit_check;
            bits_to_push = 1 << bits_pushed[index];

            if (skip == bit_check) {
                if (flag == bit_check) return indices;

                while (true) {
                    bits_pushed[index]++;

                    if (index == 0) {
                        index = element_count;
                        break;
                    }

                    if (!grouped_with_next[index--]) break;
                }

                bit_check <<= 1;
                continue;
            }

            if (flag == bit_check) {
                while (true) {
                    if (order == bit_check) {
                        indices[index] |= bits_to_push;
                    }

                    bits_pushed[index]++;

                    if (index == 0) {
                        index = element_count;
                        break;
                    }

                    if (!grouped_with_next[index]) {
                        grouped_with_next[index--] = true;
                        break;
                    }

                    grouped_with_next[index--] = true;
                }
            }

            while (true) {
                if (order != bit_check) {
                    indices[index] |= bits_to_push;
                }

                bits_pushed[index]++;

                if (index == 0) {
                    index = element_count;
                    break;
                }

                if (!grouped_with_next[index--]) break;
            }

            bit_check <<= 1;
        }
    }

    // Get leafs from bytes32 elements in memory, in reverse order
    function get_reversed_leafs_from_elements(bytes32[] memory elements)
        internal
        pure
        returns (bytes32[] memory leafs)
    {
        uint256 element_count = elements.length;
        leafs = new bytes32[](element_count);
        // uint256 read_index = element_count - 1;
        // uint256 write_index;

        for (uint64 i = 0; i < element_count; i++) {
            leafs[i] = keccak256(abi.encodePacked(bytes1(0), elements[element_count - 1 - i]));
        }

        // while (write_index < element_count) {
        //     leafs[write_index] = keccak256(abi.encodePacked(bytes1(0), elements[read_index]));
        //     write_index += 1;
        //     read_index -= 1;
        // }
    }

    // Get the original Element Merkle Root, given leafs and an Existence Multi Proof
    function get_root_from_leafs_and_multi_proof(bytes32[] memory leafs, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 right)
    {
        uint256 leaf_count = leafs.length;
        uint256 read_index;
        uint256 write_index;
        uint256 proof_index = 4;
        bytes32 bit_check = 0x0000000000000000000000000000000000000000000000000000000000000001;
        bytes32 flags = proof[1];
        bytes32 skips = proof[2];
        bytes32 orders = proof[3];

        while (true) {
            if (skips & bit_check == bit_check) {
                if (flags & bit_check == bit_check) return leafs[(write_index == 0 ? leaf_count : write_index) - 1];

                leafs[write_index] = leafs[read_index];

                read_index = (read_index + 1) % leaf_count;
                write_index = (write_index + 1) % leaf_count;
                bit_check <<= 1;
                continue;
            }

            right = (flags & bit_check == bit_check) ? leafs[read_index++] : proof[proof_index++];

            read_index %= leaf_count;

            leafs[write_index] = hash_pair(leafs[read_index], right, orders & bit_check == bit_check);

            read_index = (read_index + 1) % leaf_count;
            write_index = (write_index + 1) % leaf_count;
            bit_check <<= 1;
        }
    }

    // Get the original Element Merkle Root, given bytes32 memory in memory and an Existence Multi Proof
    function get_root_from_multi_proof(bytes32[] memory elements, bytes32[] memory proof)
        internal
        pure
        returns (bytes32)
    {
        return get_root_from_leafs_and_multi_proof(get_reversed_leafs_from_elements(elements), proof);
    }

    // Get current and update leafs from current bytes32 elements in memory and update bytes32 elements in memory, in reverse order
    function get_reversed_leafs_from_current_and_update_elements(
        bytes32[] memory elements,
        bytes32[] memory update_elements
    ) internal pure returns (bytes32[] memory leafs, bytes32[] memory update_leafs) {
        uint256 element_count = elements.length;
        require(update_elements.length == element_count, "LENGTH_MISMATCH");

        leafs = new bytes32[](element_count);
        update_leafs = new bytes32[](element_count);
        // uint256 read_index = element_count - 1;
        // uint256 write_index;

        // while (write_index < element_count) {
        //     leafs[write_index] = keccak256(abi.encodePacked(bytes1(0), elements[read_index]));
        //     update_leafs[write_index] = keccak256(abi.encodePacked(bytes1(0), update_elements[read_index]));
        //     write_index += 1;
        //     read_index -= 1;
        // }

        for (uint64 i = 0; i < element_count; i++) {
            leafs[i] = keccak256(abi.encodePacked(bytes1(0), elements[element_count - 1 - i]));
            update_leafs[i] = keccak256(abi.encodePacked(bytes1(0), update_elements[element_count - 1 - i]));
        }
    }

    // Get the original and updated Element Merkle Root, given leafs, update leafs, and an Existence Multi Proof
    function get_roots_from_leafs_and_multi_proof_update(
        bytes32[] memory leafs,
        bytes32[] memory update_leafs,
        bytes32[] memory proof
    ) internal pure returns (bytes32 flags, bytes32 skips) {
        uint256 leaf_count = update_leafs.length;
        uint256 read_index;
        uint256 write_index;
        uint256 proof_index = 4;
        bytes32 bit_check = 0x0000000000000000000000000000000000000000000000000000000000000001;
        flags = proof[1];
        skips = proof[2];
        bytes32 orders = proof[3];
        bytes32 scratch;
        uint256 scratch_2;

        while (true) {
            if (skips & bit_check == bit_check) {
                if (flags & bit_check == bit_check) {
                    read_index = (write_index == 0 ? leaf_count : write_index) - 1;

                    return (leafs[read_index], update_leafs[read_index]);
                }

                leafs[write_index] = leafs[read_index];
                update_leafs[write_index] = update_leafs[read_index];

                read_index = (read_index + 1) % leaf_count;
                write_index = (write_index + 1) % leaf_count;
                bit_check <<= 1;
                continue;
            }

            if (flags & bit_check == bit_check) {
                scratch_2 = (read_index + 1) % leaf_count;

                leafs[write_index] = hash_pair(leafs[scratch_2], leafs[read_index], orders & bit_check == bit_check);
                update_leafs[write_index] =
                    hash_pair(update_leafs[scratch_2], update_leafs[read_index], orders & bit_check == bit_check);

                read_index += 2;
            } else {
                scratch = proof[proof_index++];

                leafs[write_index] = hash_pair(leafs[read_index], scratch, orders & bit_check == bit_check);
                update_leafs[write_index] =
                    hash_pair(update_leafs[read_index], scratch, orders & bit_check == bit_check);

                read_index += 1;
            }

            read_index %= leaf_count;
            write_index = (write_index + 1) % leaf_count;
            bit_check <<= 1;
        }
    }

    // Get the original and updated Element Merkle Root,
    // given bytes32 elements in memory, bytes32 update elements in memory, and an Existence Multi Proof
    function get_roots_from_multi_proof_update(
        bytes32[] memory elements,
        bytes32[] memory update_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32, bytes32) {
        (bytes32[] memory leafs, bytes32[] memory update_leafs) =
            get_reversed_leafs_from_current_and_update_elements(elements, update_elements);
        return get_roots_from_leafs_and_multi_proof_update(leafs, update_leafs, proof);
    }

    // Get the original Element Merkle Root, given an Append Proof
    function get_root_from_append_proof(bytes32[] memory proof) internal pure returns (bytes32 hash) {
        uint256 proof_index = bit_count_32(uint32(uint256(proof[0])));
        hash = proof[proof_index];

        while (proof_index > 1) {
            proof_index -= 1;
            hash = hash_node(proof[proof_index], hash);
        }
    }

    // Get the original and updated Element Merkle Root, given append leaf and an Append Proof
    function get_roots_from_leaf_and_append_proof_single_append(bytes32 append_leaf, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 hash, bytes32 scratch)
    {
        uint256 proof_index = bit_count_32(uint32(uint256(proof[0])));
        hash = proof[proof_index];
        append_leaf = hash_node(hash, append_leaf);

        while (proof_index > 1) {
            proof_index -= 1;
            scratch = proof[proof_index];
            append_leaf = hash_node(scratch, append_leaf);
            hash = hash_node(scratch, hash);
        }

        return (hash, append_leaf);
    }

    // Get the original and updated Element Merkle Root, given a bytes32 append element in memory and an Append Proof
    function get_roots_from_append_proof_single_append(bytes32 append_element, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 append_leaf, bytes32)
    {
        append_leaf = keccak256(abi.encodePacked(bytes1(0), append_element));
        return get_roots_from_leaf_and_append_proof_single_append(append_leaf, proof);
    }

    // Get leafs from bytes32 elements in memory
    function get_leafs_from_elements(bytes32[] memory elements) internal pure returns (bytes32[] memory leafs) {
        uint256 element_count = elements.length;
        leafs = new bytes32[](element_count);

        while (element_count > 0) {
            element_count -= 1;
            leafs[element_count] = keccak256(abi.encodePacked(bytes1(0), elements[element_count]));
        }
    }

    // Get the original and updated Element Merkle Root, given append leafs and an Append Proof
    function get_roots_from_leafs_and_append_proof_multi_append(bytes32[] memory append_leafs, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 hash, bytes32)
    {
        uint256 leaf_count = append_leafs.length;
        uint256 write_index;
        uint256 read_index;
        uint256 offset = uint256(proof[0]);
        uint256 index = offset;

        // reuse leaf_count variable as upper_bound, since leaf_count no longer needed
        leaf_count += offset;
        leaf_count -= 1;
        uint256 proof_index = bit_count_32(uint32(offset));
        hash = proof[proof_index];

        while (leaf_count > 0) {
            if ((write_index == 0) && (index & 1 == 1)) {
                append_leafs[0] = hash_node(proof[proof_index], append_leafs[read_index]);
                proof_index -= 1;
                read_index += 1;

                if (proof_index > 0) {
                    hash = hash_node(proof[proof_index], hash);
                }

                write_index = 1;
                index += 1;
            } else if (index < leaf_count) {
                append_leafs[write_index++] = hash_node(append_leafs[read_index++], append_leafs[read_index]);
                read_index += 1;
                index += 2;
            }

            if (index >= leaf_count) {
                if (index == leaf_count) {
                    append_leafs[write_index] = append_leafs[read_index];
                }

                read_index = 0;
                write_index = 0;
                leaf_count >>= 1;
                offset >>= 1;
                index = offset;
            }
        }

        return (hash, append_leafs[0]);
    }

    // Get the original and updated Element Merkle Root, given bytes32 append elements in memory and an Append Proof
    function get_roots_from_append_proof_multi_append(bytes32[] memory append_elements, bytes32[] memory proof)
        internal
        pure
        returns (bytes32, bytes32)
    {
        return get_roots_from_leafs_and_append_proof_multi_append(get_leafs_from_elements(append_elements), proof);
    }

    // Get the updated Element Merkle Root, given an append leaf and an Append Proof
    function get_new_root_from_leafs_and_append_proof_single_append(bytes32 append_leaf, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 append_hash)
    {
        uint256 proof_index = bit_count_32(uint32(uint256(proof[0])));
        append_hash = hash_node(proof[proof_index], append_leaf);

        while (proof_index > 1) {
            proof_index -= 1;
            append_hash = hash_node(proof[proof_index], append_hash);
        }
    }

    // Get the updated Element Merkle Root, given a bytes32 append elements in memory and an Append Proof
    function get_new_root_from_append_proof_single_append(bytes32 append_element, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 append_leaf)
    {
        append_leaf = keccak256(abi.encodePacked(bytes1(0), append_element));
        return get_new_root_from_leafs_and_append_proof_single_append(append_leaf, proof);
    }

    // Get the updated Element Merkle Root, given append leafs and an Append Proof
    function get_new_root_from_leafs_and_append_proof_multi_append(
        bytes32[] memory append_leafs,
        bytes32[] memory proof
    ) internal pure returns (bytes32) {
        uint256 leaf_count = append_leafs.length;
        uint256 write_index;
        uint256 read_index;
        uint256 offset = uint256(proof[0]);
        uint256 index = offset;

        // reuse leaf_count variable as upper_bound, since leaf_count no longer needed
        leaf_count += offset;
        leaf_count -= 1;
        uint256 proof_index = proof.length - 1;

        while (leaf_count > 0) {
            if ((write_index == 0) && (index & 1 == 1)) {
                append_leafs[0] = hash_node(proof[proof_index], append_leafs[read_index]);

                read_index += 1;
                proof_index -= 1;
                write_index = 1;
                index += 1;
            } else if (index < leaf_count) {
                append_leafs[write_index++] = hash_node(append_leafs[read_index++], append_leafs[read_index++]);

                index += 2;
            }

            if (index >= leaf_count) {
                if (index == leaf_count) {
                    append_leafs[write_index] = append_leafs[read_index];
                }

                read_index = 0;
                write_index = 0;
                leaf_count >>= 1;
                offset >>= 1;
                index = offset;
            }
        }

        return append_leafs[0];
    }

    // Get the updated Element Merkle Root, given bytes32 append elements in memory and an Append Proof
    function get_new_root_from_append_proof_multi_append(bytes32[] memory append_elements, bytes32[] memory proof)
        internal
        pure
        returns (bytes32)
    {
        return get_new_root_from_leafs_and_append_proof_multi_append(get_leafs_from_elements(append_elements), proof);
    }

    // Get the original Element Merkle Root and derive Append Proof, given an index, an append leaf, and a Single Proof
    function get_append_proof_from_leaf_and_single_proof(uint256 index, bytes32 leaf, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 append_hash, bytes32[] memory append_proof)
    {
        uint256 proof_index = proof.length - 1;
        uint256 append_node_index = uint256(proof[0]);
        uint256 upper_bound = append_node_index - 1;
        uint256 append_proof_index = bit_count_32(uint32(append_node_index)) + 1;
        append_proof = new bytes32[](append_proof_index);
        append_proof[0] = bytes32(append_node_index);
        bytes32 scratch;

        while (proof_index > 0) {
            if (index != upper_bound || (index & 1 == 1)) {
                scratch = proof[proof_index];

                leaf = (index & 1 == 1) ? hash_node(scratch, leaf) : hash_node(leaf, scratch);

                if (append_node_index & 1 == 1) {
                    append_proof_index -= 1;
                    append_proof[append_proof_index] = scratch;
                    append_hash = hash_node(scratch, append_hash);
                }

                proof_index -= 1;
            } else if (append_node_index & 1 == 1) {
                append_proof_index -= 1;
                append_proof[append_proof_index] = leaf;
                append_hash = leaf;
            }

            index >>= 1;
            upper_bound >>= 1;
            append_node_index >>= 1;
        }

        require(append_proof_index == 2 || append_hash == leaf, "INVALID_PROOF");

        if (append_proof_index == 2) {
            append_proof[1] = leaf;
        }
    }

    // Get the original Element Merkle Root and derive Append Proof, given an index, a bytes32 element, and a Single Proof
    function get_append_proof_from_single_proof(uint256 index, bytes32 element, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 leaf, bytes32[] memory)
    {
        leaf = keccak256(abi.encodePacked(bytes1(0), element));
        return get_append_proof_from_leaf_and_single_proof(index, leaf, proof);
    }

    // Get the original Element Merkle Root and derive Append Proof, given an index, a leaf, an update leaf, and a Single Proof
    function get_append_proof_from_leaf_and_single_proof_update(
        uint256 index,
        bytes32 leaf,
        bytes32 update_leaf,
        bytes32[] memory proof
    ) internal pure returns (bytes32 append_hash, bytes32[] memory append_proof) {
        uint256 proof_index = proof.length - 1;
        uint256 append_node_index = uint256(proof[0]);
        uint256 upper_bound = append_node_index - 1;
        uint256 append_proof_index = bit_count_32(uint32(append_node_index)) + 1;
        append_proof = new bytes32[](append_proof_index);
        append_proof[0] = bytes32(append_node_index);
        bytes32 scratch;

        while (proof_index > 0) {
            if (index != upper_bound || (index & 1 == 1)) {
                scratch = proof[proof_index];

                leaf = (index & 1 == 1) ? hash_node(scratch, leaf) : hash_node(leaf, scratch);

                update_leaf = (index & 1 == 1) ? hash_node(scratch, update_leaf) : hash_node(update_leaf, scratch);

                if (append_node_index & 1 == 1) {
                    append_proof_index -= 1;
                    append_proof[append_proof_index] = scratch;
                    append_hash = hash_node(scratch, append_hash);
                }

                proof_index -= 1;
            } else if (append_node_index & 1 == 1) {
                append_proof_index -= 1;
                append_proof[append_proof_index] = update_leaf;
                append_hash = leaf;
            }

            index >>= 1;
            upper_bound >>= 1;
            append_node_index >>= 1;
        }

        require(append_proof_index == 2 || append_hash == leaf, "INVALID_PROOF");

        if (append_proof_index == 2) {
            append_proof[1] = update_leaf;
        }
    }

    // Get the original Element Merkle Root and derive Append Proof,
    // given an index, a bytes32 element, a bytes32 update element, and a Single Proof
    function get_append_proof_from_single_proof_update(
        uint256 index,
        bytes32 element,
        bytes32 update_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 leaf, bytes32[] memory) {
        leaf = keccak256(abi.encodePacked(bytes1(0), element));
        bytes32 update_leaf = keccak256(abi.encodePacked(bytes1(0), update_element));
        return get_append_proof_from_leaf_and_single_proof_update(index, leaf, update_leaf, proof);
    }

    // Hashes leaf at read index and next index (circular) to write index
    function hash_within_leafs(
        bytes32[] memory leafs,
        uint256 write_index,
        uint256 read_index,
        uint256 leaf_count,
        bool order
    ) internal pure {
        leafs[write_index] = order
            ? hash_node(leafs[(read_index + 1) % leaf_count], leafs[read_index])
            : hash_node(leafs[read_index], leafs[(read_index + 1) % leaf_count]);
    }

    // Hashes value with leaf at read index to write index
    function hash_with_leafs(bytes32[] memory leafs, bytes32 value, uint256 write_index, uint256 read_index, bool order)
        internal
        pure
    {
        leafs[write_index] = order ? hash_node(leafs[read_index], value) : hash_node(value, leafs[read_index]);
    }

    // Get the original Element Merkle Root and derive Append Proof, given leafs and an Existence Multi Proof
    function get_append_proof_from_leafs_and_multi_proof(bytes32[] memory leafs, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 append_hash, bytes32[] memory append_proof)
    {
        uint256 leaf_count = leafs.length;
        uint256 read_index;
        uint256 write_index;
        uint256 proof_index = 4;
        uint256 append_node_index = uint256(proof[0]);
        uint256 append_proof_index = uint256(bit_count_32(uint32(append_node_index))) + 1;
        append_proof = new bytes32[](append_proof_index);
        append_proof[0] = bytes32(append_node_index);
        bytes32 bit_check = 0x0000000000000000000000000000000000000000000000000000000000000001;
        bytes32 skips = proof[2];
        uint256 read_index_of_append_node;
        bool scratch;

        while (true) {
            if (skips & bit_check == bit_check) {
                if (proof[1] & bit_check == bit_check) {
                    read_index = (write_index == 0 ? leaf_count : write_index) - 1;

                    // reuse bit_check as scratch variable
                    bit_check = leafs[read_index];

                    require(append_proof_index == 2 || append_hash == bit_check, "INVALID_PROOF");

                    if (append_proof_index == 2) {
                        append_proof[1] = bit_check;
                    }

                    return (append_hash, append_proof);
                }

                if (append_node_index & 1 == 1) {
                    append_proof_index -= 1;
                    append_hash = leafs[read_index]; // TODO scratch this leafs[read_index] above
                    append_proof[append_proof_index] = leafs[read_index];
                }

                read_index_of_append_node = write_index;
                append_node_index >>= 1;

                leafs[write_index] = leafs[read_index];

                read_index = (read_index + 1) % leaf_count;
                write_index = (write_index + 1) % leaf_count;
                bit_check <<= 1;
                continue;
            }

            scratch = proof[1] & bit_check == bit_check;

            if (read_index_of_append_node == read_index) {
                if (append_node_index & 1 == 1) {
                    append_proof_index -= 1;

                    if (scratch) {
                        // reuse read_index_of_append_node as temporary scratch variable
                        read_index_of_append_node = (read_index + 1) % leaf_count;

                        append_hash = hash_node(leafs[read_index_of_append_node], append_hash);
                        append_proof[append_proof_index] = leafs[read_index_of_append_node];
                    } else {
                        append_hash = hash_node(proof[proof_index], append_hash);
                        append_proof[append_proof_index] = proof[proof_index];
                    }
                }

                read_index_of_append_node = write_index;
                append_node_index >>= 1;
            }

            if (scratch) {
                scratch = proof[3] & bit_check == bit_check;
                hash_within_leafs(leafs, write_index, read_index, leaf_count, scratch);
                read_index += 2;
            } else {
                scratch = proof[3] & bit_check == bit_check;
                hash_with_leafs(leafs, proof[proof_index], write_index, read_index, scratch);
                proof_index += 1;
                read_index += 1;
            }

            read_index %= leaf_count;
            write_index = (write_index + 1) % leaf_count;
            bit_check <<= 1;
        }
    }

    // Get the original Element Merkle Root and derive Append Proof, given bytes32 elements in memory and an Existence Multi Proof
    function get_append_proof_from_multi_proof(bytes32[] memory elements, bytes32[] memory proof)
        internal
        pure
        returns (bytes32, bytes32[] memory)
    {
        return get_append_proof_from_leafs_and_multi_proof(get_reversed_leafs_from_elements(elements), proof);
    }

    // Get combined current and update leafs from current bytes32 elements in memory and update bytes32 elements in memory, in reverse order
    function get_reversed_combined_leafs_from_current_and_update_elements(
        bytes32[] memory elements,
        bytes32[] memory update_elements
    ) internal pure returns (bytes32[] memory combined_leafs) {
        uint256 element_count = elements.length;
        require(update_elements.length == element_count, "LENGTH_MISMATCH");

        combined_leafs = new bytes32[](element_count << 1);
        // uint256 read_index = element_count - 1;
        // uint256 write_index;

        // while (write_index < element_count) {
        //     combined_leafs[write_index] = keccak256(abi.encodePacked(bytes1(0), elements[read_index]));
        //     combined_leafs[element_count + write_index] =
        //         keccak256(abi.encodePacked(bytes1(0), update_elements[read_index]));
        //     write_index += 1;
        //     read_index -= 1;
        // }

        for (uint64 i = 0; i < element_count; i++) {
            combined_leafs[i] = keccak256(abi.encodePacked(bytes1(0), elements[element_count - 1 - i]));
            combined_leafs[element_count + i] =
                keccak256(abi.encodePacked(bytes1(0), update_elements[element_count - 1 - i]));
        }
    }

    // Copy leaf and update leaf at read indices and to write indices
    function copy_within_combined_leafs(
        bytes32[] memory combined_leafs,
        uint256 write_index,
        uint256 read_index,
        uint256 leaf_count
    ) internal pure {
        combined_leafs[write_index] = combined_leafs[read_index];
        combined_leafs[leaf_count + write_index] = combined_leafs[leaf_count + read_index];
    }

    // Hashes leaf and update leaf at read indices and next indices (circular) to write indices
    function hash_within_combined_leafs(
        bytes32[] memory combined_leafs,
        uint256 write_index,
        uint256 read_index,
        uint256 leaf_count,
        bool order
    ) internal pure {
        uint256 scratch = (read_index + 1) % leaf_count;

        combined_leafs[write_index] = order
            ? hash_node(combined_leafs[scratch], combined_leafs[read_index])
            : hash_node(combined_leafs[read_index], combined_leafs[scratch]);

        combined_leafs[leaf_count + write_index] = order
            ? hash_node(combined_leafs[leaf_count + scratch], combined_leafs[leaf_count + read_index])
            : hash_node(combined_leafs[leaf_count + read_index], combined_leafs[leaf_count + scratch]);
    }

    // Hashes value with leaf and update leaf at read indices to write indices
    function hash_with_combined_leafs(
        bytes32[] memory combined_leafs,
        bytes32 value,
        uint256 write_index,
        uint256 read_index,
        uint256 leaf_count,
        bool order
    ) internal pure {
        combined_leafs[write_index] =
            order ? hash_node(combined_leafs[read_index], value) : hash_node(value, combined_leafs[read_index]);

        combined_leafs[leaf_count + write_index] = order
            ? hash_node(combined_leafs[leaf_count + read_index], value)
            : hash_node(value, combined_leafs[leaf_count + read_index]);
    }

    // Get the original Element Merkle Root and derive Append Proof, given combined leafs and update leafs and an Existence Multi Proof
    function get_append_proof_from_leafs_and_multi_proof_update(bytes32[] memory combined_leafs, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 append_hash, bytes32[] memory append_proof)
    {
        uint256 leaf_count = combined_leafs.length >> 1;
        uint256 read_index;
        uint256 write_index;
        uint256 read_index_of_append_node;
        uint256 proof_index = 4;
        uint256 append_node_index = uint256(proof[0]);
        uint256 append_proof_index = bit_count_32(uint32(append_node_index)) + 1;
        append_proof = new bytes32[](append_proof_index);
        append_proof[0] = bytes32(append_node_index);
        bytes32 bit_check = 0x0000000000000000000000000000000000000000000000000000000000000001;
        bool scratch;

        while (true) {
            if (proof[2] & bit_check == bit_check) {
                if (proof[1] & bit_check == bit_check) {
                    read_index = (write_index == 0 ? leaf_count : write_index) - 1;

                    // reuse bit_check as scratch variable
                    bit_check = combined_leafs[read_index];

                    require(append_proof_index == 2 || append_hash == bit_check, "INVALID_PROOF");

                    if (append_proof_index == 2) {
                        append_proof[1] = combined_leafs[leaf_count + read_index];
                    }

                    return (bit_check, append_proof);
                }

                if (append_node_index & 1 == 1) {
                    append_proof_index -= 1;
                    append_hash = combined_leafs[read_index];
                    append_proof[append_proof_index] = combined_leafs[leaf_count + read_index];
                }

                read_index_of_append_node = write_index;
                append_node_index >>= 1;

                copy_within_combined_leafs(combined_leafs, write_index, read_index, leaf_count);

                read_index = (read_index + 1) % leaf_count;
                write_index = (write_index + 1) % leaf_count;
                bit_check <<= 1;
                continue;
            }

            scratch = proof[1] & bit_check == bit_check;

            if (read_index_of_append_node == read_index) {
                if (append_node_index & 1 == 1) {
                    append_proof_index -= 1;

                    if (scratch) {
                        // use read_index_of_append_node as temporary scratch
                        read_index_of_append_node = (read_index + 1) % leaf_count;

                        append_hash = hash_node(combined_leafs[read_index_of_append_node], append_hash);
                        append_proof[append_proof_index] = combined_leafs[leaf_count + read_index_of_append_node];
                    } else {
                        append_hash = hash_node(proof[proof_index], append_hash);
                        append_proof[append_proof_index] = proof[proof_index];
                    }
                }

                read_index_of_append_node = write_index;
                append_node_index >>= 1;
            }

            if (scratch) {
                scratch = proof[3] & bit_check == bit_check;

                hash_within_combined_leafs(combined_leafs, write_index, read_index, leaf_count, scratch);

                read_index += 2;
            } else {
                scratch = proof[3] & bit_check == bit_check;

                hash_with_combined_leafs(
                    combined_leafs, proof[proof_index], write_index, read_index, leaf_count, scratch
                );

                proof_index += 1;
                read_index += 1;
            }

            read_index %= leaf_count;
            write_index = (write_index + 1) % leaf_count;
            bit_check <<= 1;
        }
    }

    // Get the original Element Merkle Root and derive Append Proof,
    // given bytes32 elements in memory, bytes32 update elements in memory, and an Existence Multi Proof
    function get_append_proof_from_multi_proof_update(
        bytes32[] memory elements,
        bytes32[] memory update_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32, bytes32[] memory) {
        return get_append_proof_from_leafs_and_multi_proof_update(
            get_reversed_combined_leafs_from_current_and_update_elements(elements, update_elements), proof
        );
    }

    // INTERFACE: Check if bytes32 element exists at index, given a root and a Single Proof
    function element_exists(bytes32 root, uint256 index, bytes32 element, bytes32[] memory proof)
        internal
        pure
        returns (bool)
    {
        return hash_node(proof[0], get_root_from_single_proof(index, element, proof)) == root;
    }

    // INTERFACE: Check if bytes32 elements in memory exist, given a root and a Single Proof
    function elements_exist(bytes32 root, bytes32[] memory elements, bytes32[] memory proof)
        internal
        pure
        returns (bool)
    {
        return hash_node(proof[0], get_root_from_multi_proof(elements, proof)) == root;
    }

    // INTERFACE: Get the indices of the bytes32 elements in memory, given an Existence Multi Proof
    function get_indices(bytes32[] memory elements, bytes32[] memory proof) internal pure returns (uint256[] memory) {
        return get_indices_from_multi_proof(elements.length, proof[1], proof[2], proof[3]);
    }

    // INTERFACE: Check tree size, given a Size Proof
    function verify_size_with_proof(bytes32 root, uint256 size, bytes32[] memory proof) internal pure returns (bool) {
        if (root == bytes32(0) && size == 0) return true;

        return hash_node(bytes32(size), get_root_from_size_proof(size, proof)) == root;
    }

    // INTERFACE: Check tree size, given a the Element Merkle Root
    function verify_size(bytes32 root, uint256 size, bytes32 element_root) internal pure returns (bool) {
        if (root == bytes32(0) && size == 0) return true;

        return hash_node(bytes32(size), element_root) == root;
    }

    // INTERFACE: Try to update a bytes32 element, given a root, and index, an bytes32 element, and a Single Proof
    function try_update_one(
        bytes32 root,
        uint256 index,
        bytes32 element,
        bytes32 update_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 new_element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32 old_element_root;
        (old_element_root, new_element_root) = get_roots_from_single_proof_update(index, element, update_element, proof);

        require(hash_node(total_element_count, old_element_root) == root, "INVALID_PROOF");

        return hash_node(total_element_count, new_element_root);
    }

    // INTERFACE: Try to update bytes32 elements in memory, given a root, bytes32 elements in memory, and an Existence Multi Proof
    function try_update_many(
        bytes32 root,
        bytes32[] memory elements,
        bytes32[] memory update_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32 new_element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32 old_element_root;
        (old_element_root, new_element_root) = get_roots_from_multi_proof_update(elements, update_elements, proof);

        require(hash_node(total_element_count, old_element_root) == root, "INVALID_PROOF");

        return hash_node(total_element_count, new_element_root);
    }

    // INTERFACE: Try to append a bytes32 element, given a root and an Append Proof
    function try_append_one(bytes32 root, bytes32 append_element, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 new_element_root)
    {
        bytes32 total_element_count = proof[0];

        require((root == bytes32(0)) == (total_element_count == bytes32(0)), "INVALID_TREE");

        if (root == bytes32(0)) return hash_node(bytes32(uint256(1)), get_root_from_one(append_element));

        bytes32 old_element_root;
        (old_element_root, new_element_root) = get_roots_from_append_proof_single_append(append_element, proof);

        require(hash_node(total_element_count, old_element_root) == root, "INVALID_PROOF");

        return hash_node(bytes32(uint256(total_element_count) + 1), new_element_root);
    }

    // INTERFACE: Try to append bytes32 elements in memory, given a root and an Append Proof
    function try_append_many(bytes32 root, bytes32[] memory append_elements, bytes32[] memory proof)
        internal
        pure
        returns (bytes32 new_element_root)
    {
        bytes32 total_element_count = proof[0];

        require((root == bytes32(0)) == (total_element_count == bytes32(0)), "INVALID_TREE");

        if (root == bytes32(0)) {
            return hash_node(bytes32(append_elements.length), get_root_from_many(append_elements));
        }

        bytes32 old_element_root;
        (old_element_root, new_element_root) = get_roots_from_append_proof_multi_append(append_elements, proof);

        require(hash_node(total_element_count, old_element_root) == root, "INVALID_PROOF");

        return hash_node(bytes32(uint256(total_element_count) + append_elements.length), new_element_root);
    }

    // INTERFACE: Try to append a bytes32 element, given a root, an index, a bytes32 element, and a Single Proof
    function try_append_one_using_one(
        bytes32 root,
        uint256 index,
        bytes32 element,
        bytes32 append_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_single_proof(index, element, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_single_append(append_element, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + 1), element_root);
    }

    // INTERFACE: Try to append bytes32 elements in memory, given a root, an index, a bytes32 element, and a Single Proof
    function try_append_many_using_one(
        bytes32 root,
        uint256 index,
        bytes32 element,
        bytes32[] memory append_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_single_proof(index, element, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_multi_append(append_elements, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + append_elements.length), element_root);
    }

    // INTERFACE: Try to append a bytes32 element, given a root, bytes32 elements in memory, and an Existence Multi Proof
    function try_append_one_using_many(
        bytes32 root,
        bytes32[] memory elements,
        bytes32 append_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_multi_proof(elements, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_single_append(append_element, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + 1), element_root);
    }

    // INTERFACE: Try to append bytes32 elements in memory, given a root, bytes32 elements in memory, and an Existence Multi Proof
    function try_append_many_using_many(
        bytes32 root,
        bytes32[] memory elements,
        bytes32[] memory append_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_multi_proof(elements, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_multi_append(append_elements, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + append_elements.length), element_root);
    }

    // INTERFACE: Try to update a bytes32 element and append a bytes32 element,
    // given a root, an index, a bytes32 element, and a Single Proof
    function try_update_one_and_append_one(
        bytes32 root,
        uint256 index,
        bytes32 element,
        bytes32 update_element,
        bytes32 append_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_single_proof_update(index, element, update_element, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_single_append(append_element, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + 1), element_root);
    }

    // INTERFACE: Try to update a bytes32 element and append bytes32 elements in memory,
    // given a root, an index, a bytes32 element, and a Single Proof
    function try_update_one_and_append_many(
        bytes32 root,
        uint256 index,
        bytes32 element,
        bytes32 update_element,
        bytes32[] memory append_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_single_proof_update(index, element, update_element, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_multi_append(append_elements, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + append_elements.length), element_root);
    }

    // INTERFACE: Try to update bytes32 elements in memory and append a bytes32 element,
    // given a root, bytes32 elements in memory, and a Single Proof
    function try_update_many_and_append_one(
        bytes32 root,
        bytes32[] memory elements,
        bytes32[] memory update_elements,
        bytes32 append_element,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_multi_proof_update(elements, update_elements, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_single_append(append_element, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + 1), element_root);
    }

    // INTERFACE: Try to update bytes32 elements in memory and append bytes32 elements in memory,
    // given a root, bytes32 elements in memory, and an Existence Multi Proof
    function try_update_many_and_append_many(
        bytes32 root,
        bytes32[] memory elements,
        bytes32[] memory update_elements,
        bytes32[] memory append_elements,
        bytes32[] memory proof
    ) internal pure returns (bytes32 element_root) {
        bytes32 total_element_count = proof[0];

        require(root != bytes32(0) || total_element_count == bytes32(0), "EMPTY_TREE");

        bytes32[] memory append_proof;
        (element_root, append_proof) = get_append_proof_from_multi_proof_update(elements, update_elements, proof);

        require(hash_node(total_element_count, element_root) == root, "INVALID_PROOF");

        element_root = get_new_root_from_append_proof_multi_append(append_elements, append_proof);

        return hash_node(bytes32(uint256(total_element_count) + append_elements.length), element_root);
    }

    // INTERFACE: Create a tree and return the root, given a bytes32 element
    function create_from_one(bytes32 element) internal pure returns (bytes32 new_element_root) {
        return hash_node(bytes32(uint256(1)), get_root_from_one(element));
    }

    // INTERFACE: Create a tree and return the root, given bytes32 elements in memory
    function create_from_many(bytes32[] memory elements) internal pure returns (bytes32 new_element_root) {
        return hash_node(bytes32(elements.length), get_root_from_many(elements));
    }
}

File 27 of 33 : BytesLib.sol
// SPDX-License-Identifier: Unlicense

/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 * @custom:attribution https://github.com/GNSPS/solidity-bytes-utils
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */

pragma solidity >=0.8.0 <0.9.0;

library BytesLib {
    function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for { let cc := add(_postBytes, 0x20) } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } { mstore(mc, mload(cc)) }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(
                0x40,
                and(
                    add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                    not(31) // Round down to the nearest 32 bytes.
                )
            )
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } { sstore(sc, mload(mc)) }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } { sstore(sc, mload(mc)) }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toBytes32Pad(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        bytes32 result;

        assembly {
            result := mload(add(add(_bytes, 0x20), _start))
        }

        if (_bytes.length < _start + 32) {
            uint256 pad = 32 + _start - _bytes.length;
            result = result >> pad << pad;
        }

        return result;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for { let cc := add(_postBytes, 0x20) }
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

File 28 of 33 : MemoryLib.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/DeserializationLib.sol";
import "../../libraries/MerkleLib.sol";
import "../../libraries/BytesLib.sol";
import "./OneStepProof.sol";

library MemoryLib {
    using BytesLib for bytes;

    function calcCellNum(uint64 offset, uint64 length) internal pure returns (uint64) {
        return (offset + length + 31) / 32 - offset / 32;
    }

    function getMemoryRoot(bytes memory content) internal pure returns (bytes32) {
        uint64 cellNum = MemoryLib.calcCellNum(0, uint64(content.length));
        bytes32[] memory elements = new bytes32[](cellNum);
        for (uint256 i = 0; i < cellNum - 1; i++) {
            elements[i] = content.toBytes32(i * 32);
        }
        elements[cellNum - 1] = content.toBytes32Pad((cellNum - 1) * 32);
        return MerkleLib.create_from_many(elements);
    }

    function decodeAndVerifyMemoryReadProof(
        OneStepProof.StateProof memory stateProof,
        bytes calldata encoded,
        uint64 offset,
        uint64 memoryOffset,
        uint64 memoryReadLength
    ) internal pure returns (uint64, bytes memory) {
        if (stateProof.memSize == 0 || memoryReadLength == 0) {
            return (offset, new bytes(memoryReadLength));
        }
        uint64 startCell = memoryOffset / 32;
        uint64 cellNum = calcCellNum(memoryOffset, memoryReadLength);
        uint64 memoryCell = calcCellNum(0, stateProof.memSize);
        OneStepProof.MemoryMerkleProof memory merkleProof;
        {
            if (memoryCell <= startCell) {
                cellNum += startCell - memoryCell;
                OneStepProof.MemoryAppendProof memory appendProof;
                (offset, appendProof) = OneStepProof.decodeMemoryAppendProof(encoded, offset, cellNum);
                (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
                stateProof.memRoot =
                    MerkleLib.try_append_many(stateProof.memRoot, appendProof.appendCells, merkleProof.proof);
                if (memoryOffset + memoryReadLength > stateProof.memSize) {
                    stateProof.memSize = (memoryOffset + memoryReadLength + 31) / 32 * 32; // Expand by words
                }
                bytes memory readContent = new bytes(memoryReadLength);
                return (offset, readContent);
            }
        }
        {
            if (memoryCell >= startCell + cellNum) {
                OneStepProof.MemoryReadProof memory readProof;
                (offset, readProof) = OneStepProof.decodeMemoryReadProof(encoded, offset, cellNum);
                (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
                if (cellNum == 1) {
                    MerkleLib.element_exists(stateProof.memRoot, startCell, readProof.cells[0], merkleProof.proof);
                    require(
                        MerkleLib.element_exists(stateProof.memRoot, startCell, readProof.cells[0], merkleProof.proof),
                        "IMP"
                    );
                } else {
                    {
                        // avoid stack too deep
                        uint256[] memory indices = MerkleLib.get_indices(readProof.cells, merkleProof.proof);
                        for (uint64 i = 0; i < cellNum; i++) {
                            require(indices[i] == startCell + i, "IMP");
                        }
                    }
                    MerkleLib.elements_exist(stateProof.memRoot, readProof.cells, merkleProof.proof);
                    require(MerkleLib.elements_exist(stateProof.memRoot, readProof.cells, merkleProof.proof), "IMP");
                }
                bytes memory readContent = abi.encodePacked(readProof.cells).slice(memoryOffset % 32, memoryReadLength);
                return (offset, readContent);
            }
        }
        uint64 existCellNum = memoryCell - startCell;
        OneStepProof.MemoryCombinedReadProof memory combinedReadProof;
        (offset, combinedReadProof) =
            OneStepProof.decodeMemoryCombinedReadProof(encoded, offset, existCellNum, cellNum - existCellNum);
        (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
        if (existCellNum == 1) {
            stateProof.memRoot = MerkleLib.try_append_many_using_one(
                stateProof.memRoot,
                startCell,
                combinedReadProof.cells[0],
                combinedReadProof.appendCells,
                merkleProof.proof
            );
        } else {
            {
                // avoid stack too deep
                uint256[] memory indices = MerkleLib.get_indices(combinedReadProof.cells, merkleProof.proof);
                for (uint64 i = 0; i < existCellNum; i++) {
                    require(indices[i] == startCell + i, "IMP");
                }
            }
            stateProof.memRoot = MerkleLib.try_append_many_using_many(
                stateProof.memRoot, combinedReadProof.cells, combinedReadProof.appendCells, merkleProof.proof
            );
        }
        if (memoryOffset + memoryReadLength > stateProof.memSize) {
            stateProof.memSize = (memoryOffset + memoryReadLength + 31) / 32 * 32; // Expand by words
        }
        bytes memory readContent = abi.encodePacked(combinedReadProof.cells, combinedReadProof.appendCells).slice(
            memoryOffset % 32, memoryReadLength
        );
        return (offset, readContent);
    }

    function decodeAndVerifyMemoryLikeReadProofNoAppend(
        bytes32 memoryLikeRoot,
        uint64 memoryLikeSize,
        bytes calldata encoded,
        uint64 offset,
        uint64 memoryLikeOffset,
        uint64 memoryLikeReadLength
    ) internal pure returns (uint64, bytes memory) {
        if (memoryLikeSize == 0 || memoryLikeReadLength == 0) {
            return (offset, new bytes(memoryLikeReadLength));
        }
        uint64 startCell = memoryLikeOffset / 32;
        uint64 cellNum = calcCellNum(memoryLikeOffset, memoryLikeReadLength);
        uint64 memoryCell = calcCellNum(0, memoryLikeSize);
        {
            if (memoryCell <= startCell) {
                bytes memory readContent;
                readContent = new bytes(memoryLikeReadLength);
                return (offset, readContent);
            }
        }
        {
            if (memoryCell >= startCell + cellNum) {
                bytes memory readContent;
                OneStepProof.MemoryReadProof memory readProof;
                OneStepProof.MemoryMerkleProof memory merkleProof;
                (offset, readProof) = OneStepProof.decodeMemoryReadProof(encoded, offset, cellNum);
                (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
                if (cellNum == 1) {
                    MerkleLib.element_exists(memoryLikeRoot, startCell, readProof.cells[0], merkleProof.proof);
                    require(
                        MerkleLib.element_exists(memoryLikeRoot, startCell, readProof.cells[0], merkleProof.proof),
                        "IMP"
                    );
                } else {
                    {
                        uint256[] memory indices = MerkleLib.get_indices(readProof.cells, merkleProof.proof);
                        for (uint64 i = 0; i < cellNum; i++) {
                            require(indices[i] == startCell + i, "IMP2");
                        }
                    }
                    MerkleLib.elements_exist(memoryLikeRoot, readProof.cells, merkleProof.proof);
                    require(MerkleLib.elements_exist(memoryLikeRoot, readProof.cells, merkleProof.proof), "IMP");
                }
                readContent = abi.encodePacked(readProof.cells).slice(memoryLikeOffset % 32, memoryLikeReadLength);
                return (offset, readContent);
            }
        }
        uint64 existCellNum = memoryCell - startCell;
        OneStepProof.MemoryReadProof memory readProof;
        OneStepProof.MemoryMerkleProof memory merkleProof;
        (offset, readProof) = OneStepProof.decodeMemoryReadProof(encoded, offset, existCellNum);
        (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
        if (existCellNum == 1) {
            MerkleLib.element_exists(memoryLikeRoot, startCell, readProof.cells[0], merkleProof.proof);
            require(MerkleLib.element_exists(memoryLikeRoot, startCell, readProof.cells[0], merkleProof.proof), "IMP");
        } else {
            {
                uint256[] memory indices = MerkleLib.get_indices(readProof.cells, merkleProof.proof);
                for (uint64 i = 0; i < cellNum; i++) {
                    require(indices[i] == startCell + i, "IMP");
                }
            }
            MerkleLib.elements_exist(memoryLikeRoot, readProof.cells, merkleProof.proof);
            require(MerkleLib.elements_exist(memoryLikeRoot, readProof.cells, merkleProof.proof), "IMP");
        }
        bytes memory padding = new bytes(32 * (cellNum - existCellNum));
        bytes memory readContent;
        readContent = abi.encodePacked(readProof.cells, padding).slice(memoryLikeOffset % 32, memoryLikeReadLength);
        return (offset, readContent);
    }

    function decodeAndVerifyMemoryWriteProof(
        OneStepProof.StateProof memory stateProof,
        bytes calldata encoded,
        uint64 offset,
        uint64 memoryOffset,
        uint64 memoryWriteLength
    ) internal pure returns (uint64, bytes memory) {
        if (memoryWriteLength == 0) {
            return (offset, new bytes(0));
        }
        if (stateProof.memSize == 0) {
            // Don't call decodeMemoryWriteProof if memory is empty
            // Instead, update memory root and size directly
            revert();
        }
        uint64 startCell = memoryOffset / 32;
        uint64 cellNum = calcCellNum(memoryOffset, memoryWriteLength);
        uint64 memoryCell = calcCellNum(0, stateProof.memSize);
        OneStepProof.MemoryMerkleProof memory merkleProof;

        {
            if (memoryCell <= startCell) {
                cellNum += startCell - memoryCell;
                OneStepProof.MemoryAppendProof memory appendProof;
                (offset, appendProof) = OneStepProof.decodeMemoryAppendProof(encoded, offset, cellNum);
                (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
                if (cellNum == 1) {
                    stateProof.memRoot =
                        MerkleLib.try_append_one(stateProof.memRoot, appendProof.appendCells[0], merkleProof.proof);
                } else {
                    stateProof.memRoot =
                        MerkleLib.try_append_many(stateProof.memRoot, appendProof.appendCells, merkleProof.proof);
                }
                if (memoryOffset + memoryWriteLength > stateProof.memSize) {
                    stateProof.memSize = (memoryOffset + memoryWriteLength + 31) / 32 * 32; // Expand by words
                }
                bytes memory writeContent =
                    abi.encodePacked(appendProof.appendCells).slice(memoryOffset % 32, memoryWriteLength);
                return (offset, writeContent);
            }
        }
        {
            if (memoryCell >= startCell + cellNum) {
                OneStepProof.MemoryWriteProof memory writeProof;
                (offset, writeProof) = OneStepProof.decodeMemoryWriteProof(encoded, offset, cellNum);
                (offset, merkleProof) = OneStepProof.decodeMemoryMerkleProof(encoded, offset);
                if (cellNum == 1) {
                    stateProof.memRoot = MerkleLib.try_update_one(
                        stateProof.memRoot,
                        startCell,
                        writeProof.cells[0],
                        writeProof.updatedCells[0],
                        merkleProof.proof
                    );
                } else {
                    {
                        // Avoid stack too deep
                        uint256[] memory indices = MerkleLib.get_indices(writeProof.cells, merkleProof.proof);
                        for (uint64 i = 0; i < cellNum; i++) {
                            require(indices[i] == startCell + i, "IMP");
                        }
                    }
                    stateProof.memRoot = MerkleLib.try_update_many(
                        stateProof.memRoot, writeProof.cells, writeProof.updatedCells, merkleProof.proof
                    );
                }
                bytes memory writeContent =
                    abi.encodePacked(writeProof.updatedCells).slice(memoryOffset % 32, memoryWriteLength);
                return (offset, writeContent);
            }
        }
        uint64 existCellNum = memoryCell - startCell;
        OneStepProof.MemoryCombinedWriteProof memory combinedWriteProof;
        (offset, combinedWriteProof) =
            OneStepProof.decodeMemoryCombinedWriteProof(encoded, offset, existCellNum, cellNum - existCellNum);
        if (cellNum == 1) {
            stateProof.memRoot = MerkleLib.try_update_one_and_append_many(
                stateProof.memRoot,
                startCell,
                combinedWriteProof.cells[0],
                combinedWriteProof.updatedCells[0],
                combinedWriteProof.appendCells,
                merkleProof.proof
            );
        } else {
            {
                // avoid stack too deep
                uint256[] memory indices = MerkleLib.get_indices(combinedWriteProof.cells, merkleProof.proof);
                for (uint64 i = 0; i < cellNum; i++) {
                    require(indices[i] == startCell + i, "IMP");
                }
            }
            stateProof.memRoot = MerkleLib.try_update_many_and_append_many(
                stateProof.memRoot,
                combinedWriteProof.cells,
                combinedWriteProof.updatedCells,
                combinedWriteProof.appendCells,
                merkleProof.proof
            );
        }
        if (memoryOffset + memoryWriteLength > stateProof.memSize) {
            stateProof.memSize = (memoryOffset + memoryWriteLength + 31) / 32 * 32; // Expand by words
        }
        bytes memory writeContent = abi.encodePacked(combinedWriteProof.updatedCells, combinedWriteProof.appendCells)
            .slice(memoryOffset % 32, memoryWriteLength);
        return (offset, writeContent);
    }
}

File 29 of 33 : EVMTypesLib.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/BytesLib.sol";
import "../../libraries/RLPReader.sol";
import "../../libraries/RLPWriter.sol";
import "./BloomLib.sol";

library EVMTypesLib {
    using BytesLib for bytes;
    using RLPReader for bytes;
    using RLPReader for RLPReader.RLPItem;

    struct BlockHeader {
        bytes32 parentHash;
        bytes32 ommerHash;
        address beneficiary;
        bytes32 stateRoot;
        bytes32 transactionRoot;
        bytes32 receiptsRoot;
        uint256 difficulty;
        uint256 number;
        uint64 gasLimit;
        uint64 gasUsed;
        uint64 timestamp;
        BloomLib.Bloom logsBloom;
    }

    function hashBlockHeader(BlockHeader memory header) internal pure returns (bytes32) {
        bytes[] memory raw = new bytes[](15);
        raw[0] = RLPWriter.writeBytes(abi.encodePacked(header.parentHash));
        raw[1] = RLPWriter.writeBytes(abi.encodePacked(header.ommerHash));
        raw[2] = RLPWriter.writeAddress(header.beneficiary);
        raw[3] = RLPWriter.writeBytes(abi.encodePacked(header.stateRoot));
        raw[4] = RLPWriter.writeBytes(abi.encodePacked(header.transactionRoot));
        raw[5] = RLPWriter.writeBytes(abi.encodePacked(header.receiptsRoot));
        raw[6] = RLPWriter.writeBytes(abi.encodePacked(header.logsBloom.data));
        raw[7] = RLPWriter.writeUint(header.difficulty);
        raw[8] = RLPWriter.writeUint(header.number);
        raw[9] = RLPWriter.writeUint(uint256(header.gasLimit));
        raw[10] = RLPWriter.writeUint(uint256(header.gasUsed));
        raw[11] = RLPWriter.writeUint(uint256(header.timestamp));
        raw[12] = RLPWriter.writeBytes(""); // Extra
        raw[13] = RLPWriter.writeBytes(abi.encodePacked(bytes32(0))); // MixDigest
        raw[14] = RLPWriter.writeBytes(abi.encodePacked(bytes8(0))); // Nonce
        return keccak256(RLPWriter.writeList(raw));
    }

    struct Transaction {
        uint64 nonce;
        uint256 gasPrice;
        uint64 gas;
        address to;
        uint256 value;
        bytes data;
        uint256 v;
        uint256 r;
        uint256 s;
    }

    function decodeTransaction(bytes memory data) internal pure returns (Transaction memory transaction) {
        RLPReader.RLPItem[] memory decoded = data.toRlpItem().toList();
        transaction.nonce = uint64(decoded[0].toUint());
        transaction.gasPrice = decoded[1].toUint();
        transaction.gas = uint64(decoded[2].toUint());
        transaction.to = address(uint160(decoded[3].toUint()));
        transaction.value = decoded[4].toUint();
        transaction.data = decoded[5].toBytes();
        transaction.v = decoded[6].toUint();
        transaction.r = decoded[7].toUint();
        transaction.s = decoded[8].toUint();
    }

    function hashTransaction(Transaction memory txn) internal pure returns (bytes32) {
        bytes[] memory raw = new bytes[](9);
        raw[0] = RLPWriter.writeUint(uint256(txn.nonce));
        raw[1] = RLPWriter.writeUint(txn.gasPrice);
        raw[2] = RLPWriter.writeUint(uint256(txn.gas));
        raw[3] = RLPWriter.writeAddress(txn.to);
        raw[4] = RLPWriter.writeUint(txn.value);
        raw[5] = RLPWriter.writeBytes(txn.data);
        raw[6] = RLPWriter.writeUint(txn.v);
        raw[7] = RLPWriter.writeUint(txn.r);
        raw[8] = RLPWriter.writeUint(txn.s);
        return keccak256(RLPWriter.writeList(raw));
    }

    struct Account {
        uint64 nonce;
        uint256 balance;
        bytes32 storageRoot;
        bytes32 codeHash;
    }

    function decodeAccount(RLPReader.RLPItem memory encoded) internal pure returns (Account memory proof) {
        RLPReader.RLPItem[] memory items = encoded.toList();
        require(items.length == 4, "Invalid Account");
        proof.nonce = uint64(items[0].toUint());
        proof.balance = items[1].toUint();
        proof.storageRoot = bytes32(items[2].toUint());
        proof.codeHash = bytes32(items[3].toUint());
    }

    function encodeRLP(Account memory account) internal pure returns (bytes memory) {
        bytes[] memory raw = new bytes[](4);
        raw[0] = RLPWriter.writeUint(uint256(account.nonce));
        raw[1] = RLPWriter.writeUint(account.balance);
        raw[2] = RLPWriter.writeBytes(abi.encodePacked(account.storageRoot));
        raw[3] = RLPWriter.writeBytes(abi.encodePacked(account.codeHash));
        return RLPWriter.writeList(raw);
    }

    function hashLogEntry(address addr, uint256[] memory topics, bytes memory data) internal pure returns (bytes32) {
        bytes[] memory topicRaw = new bytes[](topics.length);
        for (uint256 i = 0; i < topics.length; i++) {
            topicRaw[i] = RLPWriter.writeBytes(abi.encodePacked(bytes32(topics[i])));
        }
        bytes[] memory raw = new bytes[](3);
        raw[0] = RLPWriter.writeAddress(addr);
        raw[1] = RLPWriter.writeBytes(RLPWriter.writeList(topicRaw));
        raw[2] = RLPWriter.writeBytes(data);
        return keccak256(RLPWriter.writeList(raw));
    }
}

File 30 of 33 : OneStepProof.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/RLPReader.sol";
import "../../libraries/BytesLib.sol";
import "./BloomLib.sol";
import "./VerificationContext.sol";

library OneStepProof {
    using BytesLib for bytes;
    using RLPReader for RLPReader.RLPItem;
    using RLPReader for RLPReader.Iterator;
    using RLPReader for bytes;
    using VerificationContext for VerificationContext.Context;

    // [encode rule]
    struct StateProof {
        uint64 blockNumber; // Block number of current transaction [always]
        uint64 transactionIdx; // Transaction index in block [always]
        uint16 depth; // Current call depth [always]
        uint64 gas; // Gas left in the current call [always]
        uint64 refund; // Gas refund accumulated in the current transaction [always]
        bytes32 lastDepthHash; // The state hash of the last depth call frame [always]
        address contractAddress; // Current executing contract address [depth > 1]
        address caller; // Current caller [depth > 1]
        uint256 value; // Current call value [depth > 1]
        uint8 callFlag; // Current call type [depth > 1]
        uint64 out; // Offset of the return data of current call to be copied to the last depth call frame [depth > 1]
        uint64 outSize; // Size of the return data of current call to be copied to the last depth call frame [depth > 1]
        uint64 pc; // Current program counter [always]
        uint8 opCode; // Current opcode to be executed [always]
        bytes32 codeHash; // Current executing contract code hash [always]
        uint64 stackSize; // Size of the stack [always]
        bytes32 stackHash; // Commitment of the stack [always]
        uint64 memSize; // Size of the memory [always]
        bytes32 memRoot; // Commitment of the memory [memSize > 0]
        uint64 inputDataSize; // Size of the call data [depth > 1]
        bytes32 inputDataRoot; // Commitment of the return data [depth > 1 && inputDataSize > 0]
        uint64 returnDataSize; // Size of the return data [always]
        bytes32 returnDataRoot; // Commitment of the return data [returnDataSize > 0]
        bytes32 committedGlobalStateRoot; // Commitment of the global MPT state at the start of transaction [always]
        bytes32 globalStateRoot; // Commitment of the global MPT state [always]
        bytes32 selfDestructAcc; // Commitment of the self destructed contracts in the current transaction [always]
        bytes32 logAcc; // Commitment of the logs emitted in the current transaction [always]
        bytes32 blockHashRoot; // Commitment of the 256 previous blockhash in the current block [always]
        bytes32 accessListRoot; // Commitment of the access list in the current transaction [always]
    }

    function decodeStateProof(VerificationContext.Context memory ctx, bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, StateProof memory proof)
    {
        uint64 remainLength = uint64(encoded.length) - offset;
        uint64 stateProofLen = 323;
        require(remainLength >= stateProofLen, "Proof Underflow (State)");
        proof.blockNumber = encoded.toUint64(offset);
        proof.transactionIdx = encoded.toUint64(offset + 8);
        proof.depth = encoded.toUint16(offset + 16);
        proof.gas = encoded.toUint64(offset + 18);
        proof.refund = encoded.toUint64(offset + 26);
        proof.lastDepthHash = encoded.toBytes32(offset + 34);
        offset = offset + 66;
        if (proof.depth > 1) {
            stateProofLen += 97;
            require(remainLength >= stateProofLen, "Proof Underflow (State)");
            proof.contractAddress = encoded.toAddress(offset);
            proof.caller = encoded.toAddress(offset + 20);
            proof.value = encoded.toUint256(offset + 40);
            proof.callFlag = encoded.toUint8(offset + 72);
            proof.out = encoded.toUint64(offset + 73);
            proof.outSize = encoded.toUint64(offset + 81);
            offset += 89;
        } else {
            proof.contractAddress = ctx.getRecipient();
            proof.caller = ctx.getOrigin();
            proof.value = ctx.getValue();
            if (ctx.getRecipient() == address(0)) {
                proof.callFlag = 4;
            } else {
                proof.callFlag = 0;
            }
        }
        proof.pc = encoded.toUint64(offset);
        proof.opCode = encoded.toUint8(offset + 8);
        proof.codeHash = encoded.toBytes32(offset + 9);
        proof.stackSize = encoded.toUint64(offset + 41);
        offset += 49;
        if (proof.stackSize != 0) {
            stateProofLen += 32;
            require(remainLength >= stateProofLen, "Proof Underflow (State)");
            proof.stackHash = encoded.toBytes32(offset);
            offset += 32;
        }
        proof.memSize = encoded.toUint64(offset);
        offset += 8;
        if (proof.memSize != 0) {
            stateProofLen += 32;
            require(remainLength >= stateProofLen, "Proof Underflow (State)");
            proof.memRoot = encoded.toBytes32(offset);
            offset += 32;
        }
        if (proof.depth > 1) {
            proof.inputDataSize = encoded.toUint64(offset);
            offset += 8;
            if (proof.inputDataSize != 0) {
                stateProofLen += 32;
                require(remainLength >= stateProofLen, "Proof Underflow (State)");
                proof.inputDataRoot = encoded.toBytes32(offset);
                offset += 32;
            }
        } else {
            proof.inputDataSize = ctx.getInputSize();
            proof.inputDataRoot = ctx.getInputRoot();
        }
        proof.returnDataSize = encoded.toUint64(offset);
        offset += 8;
        if (proof.returnDataSize != 0) {
            stateProofLen += 32;
            require(remainLength >= stateProofLen, "Proof Underflow (State)");
            proof.returnDataRoot = encoded.toBytes32(offset);
            offset += 32;
        }
        proof.committedGlobalStateRoot = encoded.toBytes32(offset);
        proof.globalStateRoot = encoded.toBytes32(offset + 32);
        proof.selfDestructAcc = encoded.toBytes32(offset + 64);
        proof.logAcc = encoded.toBytes32(offset + 96);
        proof.blockHashRoot = encoded.toBytes32(offset + 128);
        proof.accessListRoot = encoded.toBytes32(offset + 160);
        return (offset + 192, proof);
    }

    function encodeStateProof(StateProof memory proof) internal pure returns (bytes memory encoded) {
        encoded = encoded.concat(abi.encodePacked(proof.blockNumber));
        encoded = encoded.concat(abi.encodePacked(proof.transactionIdx));
        encoded = encoded.concat(abi.encodePacked(proof.depth));
        encoded = encoded.concat(abi.encodePacked(proof.gas));
        encoded = encoded.concat(abi.encodePacked(proof.refund));
        encoded = encoded.concat(abi.encodePacked(proof.lastDepthHash));
        if (proof.depth > 1) {
            encoded = encoded.concat(abi.encodePacked(proof.contractAddress));
            encoded = encoded.concat(abi.encodePacked(proof.caller));
            encoded = encoded.concat(abi.encodePacked(proof.value));
            encoded = encoded.concat(abi.encodePacked(proof.callFlag));
            encoded = encoded.concat(abi.encodePacked(proof.out));
            encoded = encoded.concat(abi.encodePacked(proof.outSize));
        }
        encoded = encoded.concat(abi.encodePacked(proof.pc));
        encoded = encoded.concat(abi.encodePacked(proof.opCode));
        encoded = encoded.concat(abi.encodePacked(proof.codeHash));
        encoded = encoded.concat(abi.encodePacked(proof.stackSize));
        if (proof.stackSize != 0) {
            encoded = encoded.concat(abi.encodePacked(proof.stackHash));
        }
        encoded = encoded.concat(abi.encodePacked(proof.memSize));
        if (proof.memSize != 0) {
            encoded = encoded.concat(abi.encodePacked(proof.memRoot));
        }
        if (proof.depth > 1) {
            encoded = encoded.concat(abi.encodePacked(proof.inputDataSize));
            if (proof.inputDataSize != 0) {
                encoded = encoded.concat(abi.encodePacked(proof.inputDataRoot));
            }
        }
        encoded = encoded.concat(abi.encodePacked(proof.returnDataSize));
        if (proof.returnDataSize != 0) {
            encoded = encoded.concat(abi.encodePacked(proof.returnDataRoot));
        }
        encoded = encoded.concat(abi.encodePacked(proof.committedGlobalStateRoot));
        encoded = encoded.concat(abi.encodePacked(proof.globalStateRoot));
        encoded = encoded.concat(abi.encodePacked(proof.selfDestructAcc));
        encoded = encoded.concat(abi.encodePacked(proof.logAcc));
        encoded = encoded.concat(abi.encodePacked(proof.blockHashRoot));
        encoded = encoded.concat(abi.encodePacked(proof.accessListRoot));
    }

    function hashStateProof(StateProof memory proof) internal pure returns (bytes32) {
        if (proof.depth == 0) {
            // When returning/reverting from depth 1, we can't directly return an InterStateProof
            // Therefore we reuse some of the fields in the IntraStateProof to store an InterStateProof
            // The field mappings are as follows:
            InterStateProof memory interProof;
            interProof.blockNumber = proof.blockNumber;
            interProof.transactionIdx = proof.transactionIdx;
            interProof.globalStateRoot = proof.globalStateRoot;
            interProof.cumulativeGasUsed = proof.value;
            interProof.blockGasUsed = uint256(proof.lastDepthHash);
            interProof.blockHashRoot = proof.blockHashRoot;
            interProof.transactionTrieRoot = proof.selfDestructAcc;
            interProof.receiptTrieRoot = proof.logAcc;
            return hashInterStateProof(interProof);
        }
        return keccak256(encodeStateProof(proof));
    }

    struct InterStateProof {
        uint64 blockNumber;
        uint64 transactionIdx;
        bytes32 globalStateRoot;
        uint256 cumulativeGasUsed;
        uint256 blockGasUsed;
        bytes32 blockHashRoot;
        bytes32 transactionTrieRoot;
        bytes32 receiptTrieRoot;
        BloomLib.Bloom logsBloom;
    }

    function decodeInterStateProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, InterStateProof memory proof)
    {
        require(encoded.length - offset >= 464, "Proof Underflow (Inter)");
        proof.blockNumber = encoded.toUint64(offset);
        proof.transactionIdx = encoded.toUint64(offset + 8);
        proof.globalStateRoot = encoded.toBytes32(offset + 16);
        proof.cumulativeGasUsed = encoded.toUint64(offset + 48);
        proof.blockGasUsed = encoded.toUint64(offset + 80);
        proof.blockHashRoot = encoded.toBytes32(offset + 112);
        proof.transactionTrieRoot = encoded.toBytes32(offset + 144);
        proof.receiptTrieRoot = encoded.toBytes32(offset + 176);
        proof.logsBloom = BloomLib.decodeBloom(encoded, offset + 208);
        return (offset + 464, proof);
    }

    function encodeInterStateProof(InterStateProof memory proof) internal pure returns (bytes memory encoded) {
        encoded = encoded.concat(abi.encodePacked(proof.blockNumber));
        encoded = encoded.concat(abi.encodePacked(proof.transactionIdx));
        encoded = encoded.concat(abi.encodePacked(proof.globalStateRoot));
        encoded = encoded.concat(abi.encodePacked(proof.cumulativeGasUsed));
        encoded = encoded.concat(abi.encodePacked(proof.blockGasUsed));
        encoded = encoded.concat(abi.encodePacked(proof.blockHashRoot));
        encoded = encoded.concat(abi.encodePacked(proof.transactionTrieRoot));
        encoded = encoded.concat(abi.encodePacked(proof.receiptTrieRoot));
        encoded = encoded.concat(abi.encodePacked(proof.logsBloom.data));
    }

    function hashInterStateProof(InterStateProof memory proof) internal pure returns (bytes32) {
        return keccak256(encodeInterStateProof(proof));
    }

    struct BlockStateProof {
        uint64 blockNumber;
        bytes32 globalStateRoot;
        uint256 cumulativeGasUsed;
        bytes32 blockHashRoot;
    }

    function decodeBlockStateProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, BlockStateProof memory proof)
    {
        require(encoded.length - offset >= 104, "Proof Underflow (Block)");
        proof.blockNumber = encoded.toUint64(offset);
        proof.globalStateRoot = encoded.toBytes32(offset + 8);
        proof.cumulativeGasUsed = encoded.toUint64(offset + 40);
        proof.blockHashRoot = encoded.toBytes32(offset + 72);
        return (offset + 104, proof);
    }

    function encodeBlockStateProof(BlockStateProof memory proof) internal pure returns (bytes memory encoded) {
        encoded = encoded.concat(abi.encodePacked(proof.blockNumber));
        encoded = encoded.concat(abi.encodePacked(proof.globalStateRoot));
        encoded = encoded.concat(abi.encodePacked(proof.cumulativeGasUsed));
        encoded = encoded.concat(abi.encodePacked(proof.blockHashRoot));
    }

    function hashBlockStateProof(BlockStateProof memory proof) internal pure returns (bytes32) {
        return keccak256(encodeBlockStateProof(proof));
    }

    struct CodeProof {
        uint64 ptr;
        uint64 size;
    }

    function decodeCodeProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, CodeProof memory proof)
    {
        require(encoded.length - offset >= 8, "Proof Underflow (Code)");
        // Decode bytecode size in bytes
        uint64 contentSize = encoded.toUint64(offset);
        require(encoded.length - offset >= 8 + contentSize, "Proof Underflow (Code)");
        offset += 8;
        proof.ptr = offset;
        proof.size = contentSize;
        return (offset + contentSize, proof);
    }

    function getOpCodeAt(CodeProof memory proof, bytes calldata encoded, uint64 idx) internal pure returns (uint8) {
        if (idx >= proof.size) {
            return 0;
        }
        return uint8(encoded[proof.ptr + idx]);
    }

    function getCodeSlice(CodeProof memory proof, bytes calldata encoded, uint64 offset, uint64 size)
        internal
        pure
        returns (bytes memory)
    {
        if (offset + size > proof.size) {
            return encoded.slice(proof.ptr + offset, size).concat(new bytes(size - (proof.size - offset)));
        }
        return encoded.slice(proof.ptr + offset, size);
    }

    function hashCodeProof(CodeProof memory proof, bytes calldata encoded) internal pure returns (bytes32) {
        return keccak256(encoded[proof.ptr:proof.ptr + proof.size]);
    }

    struct StackProof {
        // The elements popped in the step
        uint256[] pops;
        // The stack hash after popping above elements
        bytes32 stackHashAfterPops;
    }

    function decodeStackProof(bytes calldata encoded, uint64 offset, uint64 popNum)
        internal
        pure
        returns (uint64, StackProof memory proof)
    {
        if (popNum == 0) {
            // No StackProof needed for popNum == 0
            return (offset, proof);
        }
        require(encoded.length - offset >= 32 * (popNum + 1), "Proof Underflow (Stack)");
        proof.pops = new uint256[](popNum);
        // Decode popped elements
        for (uint64 i = 0; i < popNum; i++) {
            proof.pops[i] = encoded.toUint256(offset);
            offset += 32;
        }
        // Decode stackHashAfterPops
        proof.stackHashAfterPops = encoded.toBytes32(offset);
        offset += 32;
        return (offset, proof);
    }

    function encodeStackProof(StackProof memory proof) internal pure returns (bytes memory encoded) {
        for (uint64 i = 0; i < proof.pops.length; i++) {
            encoded = encoded.concat(abi.encodePacked(proof.pops[i]));
        }
        encoded = encoded.concat(abi.encodePacked(proof.stackHashAfterPops));
    }

    struct MemoryMerkleProof {
        bytes32[] proof;
    }

    function decodeMemoryMerkleProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, MemoryMerkleProof memory proof)
    {
        require(encoded.length - offset >= 8, "Proof Underflow");
        uint64 len = encoded.toUint64(offset);
        offset += 8;
        require(encoded.length - offset >= 32 * len, "Proof Underflow");
        proof.proof = new bytes32[](len);
        for (uint64 i = 0; i < len; i++) {
            proof.proof[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    struct MemoryReadProof {
        bytes32[] cells;
    }

    function decodeMemoryReadProof(bytes calldata encoded, uint64 offset, uint64 cellNum)
        internal
        pure
        returns (uint64, MemoryReadProof memory proof)
    {
        require(encoded.length - offset >= 32 * cellNum, "Proof Underflow");
        proof.cells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.cells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    struct MemoryWriteProof {
        bytes32[] cells;
        bytes32[] updatedCells;
    }

    function decodeMemoryWriteProof(bytes calldata encoded, uint64 offset, uint64 cellNum)
        internal
        pure
        returns (uint64, MemoryWriteProof memory proof)
    {
        require(encoded.length - offset >= 64 * cellNum, "Proof Underflow");
        proof.cells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.cells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        proof.updatedCells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.updatedCells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    struct MemoryAppendProof {
        bytes32[] appendCells;
    }

    function decodeMemoryAppendProof(bytes calldata encoded, uint64 offset, uint64 cellNum)
        internal
        pure
        returns (uint64, MemoryAppendProof memory proof)
    {
        require(encoded.length - offset >= 32 * cellNum, "Proof Underflow");
        proof.appendCells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.appendCells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    struct MemoryCombinedReadProof {
        bytes32[] cells;
        bytes32[] appendCells;
    }

    function decodeMemoryCombinedReadProof(bytes calldata encoded, uint64 offset, uint64 cellNum, uint64 appendCellNum)
        internal
        pure
        returns (uint64, MemoryCombinedReadProof memory proof)
    {
        require(encoded.length - offset >= 32 * (cellNum + appendCellNum), "Proof Underflow");
        proof.cells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.cells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        proof.appendCells = new bytes32[](appendCellNum);
        for (uint64 i = 0; i < appendCellNum; i++) {
            proof.appendCells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    struct MemoryCombinedWriteProof {
        bytes32[] cells;
        bytes32[] updatedCells;
        bytes32[] appendCells;
    }

    function decodeMemoryCombinedWriteProof(bytes calldata encoded, uint64 offset, uint64 cellNum, uint64 appendCellNum)
        internal
        pure
        returns (uint64, MemoryCombinedWriteProof memory proof)
    {
        require(encoded.length - offset >= 32 * (2 * cellNum + appendCellNum), "Proof Underflow");
        proof.cells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.cells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        proof.updatedCells = new bytes32[](cellNum);
        for (uint64 i = 0; i < cellNum; i++) {
            proof.updatedCells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        proof.appendCells = new bytes32[](appendCellNum);
        for (uint64 i = 0; i < appendCellNum; i++) {
            proof.appendCells[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    // For MPT proof, receipt proof
    struct RLPProof {
        RLPReader.RLPItem proof;
    }

    function decodeRLPProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, RLPProof memory proof)
    {
        require(encoded.length - offset >= 8, "Proof Underflow");
        uint64 len = encoded.toUint64(offset);
        offset += 8;
        require(encoded.length - offset >= len, "Proof Underflow");
        proof.proof = encoded.slice(offset, len).toRlpItem();
        return (offset + len, proof);
    }

    struct BlockHashProof {
        bytes32 blockHash;
    }

    function decodeBlockHashProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, BlockHashProof memory proof)
    {
        require(encoded.length - offset >= 32, "Proof Underflow");
        proof.blockHash = encoded.toBytes32(offset);
        return (offset + 32, proof);
    }

    struct BlockHashMerkleProof {
        uint64 path;
        bytes32[] proof;
    }

    function decodeBlockHashMerkleProof(bytes memory encoded, uint64 offset)
        internal
        pure
        returns (uint64, BlockHashMerkleProof memory proof)
    {
        require(encoded.length - offset >= 9, "Proof Underflow");
        proof.path = encoded.toUint64(offset);
        uint8 len = encoded.toUint8(offset + 8);
        offset += 9;
        require(encoded.length - offset >= 32 * len, "Proof Underflow");
        proof.proof = new bytes32[](len);
        for (uint64 i = 0; i < len; i++) {
            proof.proof[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return (offset, proof);
    }

    struct LogProof {
        bytes32 accumulateHash;
        BloomLib.Bloom bloom;
    }

    function decodeLogProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, LogProof memory proof)
    {
        require(encoded.length - offset >= 288, "Proof Underflow");
        proof.accumulateHash = encoded.toBytes32(offset);
        proof.bloom = BloomLib.decodeBloom(encoded, offset + 32);
        return (offset + 288, proof);
    }

    function hashLogProof(LogProof memory proof) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(proof.accumulateHash, proof.bloom.data));
    }

    struct SelfDestructSetProof {
        address[] contracts;
    }

    function decodeSelfDestructSetProof(bytes calldata encoded, uint64 offset)
        internal
        pure
        returns (uint64, SelfDestructSetProof memory proof)
    {
        require(encoded.length - offset >= 8, "Proof Underflow");
        uint64 len = encoded.toUint64(offset);
        offset += 8;
        require(encoded.length - offset >= 20 * len, "Proof Underflow");
        proof.contracts = new address[](len);
        for (uint64 i = 0; i < len; i++) {
            proof.contracts[i] = encoded.toAddress(offset);
            offset += 20;
        }
        return (offset, proof);
    }
}

File 31 of 33 : RLPReader.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * @author Hamdi Allam [email protected]
 * Please reach out with any questions or concerns
 */

pragma solidity ^0.8.0;

library RLPReader {
    uint8 constant STRING_SHORT_START = 0x80;
    uint8 constant STRING_LONG_START = 0xb8;
    uint8 constant LIST_SHORT_START = 0xc0;
    uint8 constant LIST_LONG_START = 0xf8;
    uint8 constant WORD_SIZE = 32;

    struct RLPItem {
        uint256 len;
        uint256 memPtr;
    }

    struct Iterator {
        RLPItem item; // Item that's being iterated over.
        uint256 nextPtr; // Position of the next item in the list.
    }

    /*
    * @dev Returns the next element in the iteration. Reverts if it has not next element.
    * @param self The iterator.
    * @return The next element in the iteration.
    */
    function next(Iterator memory self) internal pure returns (RLPItem memory) {
        require(hasNext(self));

        uint256 ptr = self.nextPtr;
        uint256 itemLength = _itemLength(ptr);
        self.nextPtr = ptr + itemLength;

        return RLPItem(itemLength, ptr);
    }

    /*
    * @dev Returns true if the iteration has more elements.
    * @param self The iterator.
    * @return true if the iteration has more elements.
    */
    function hasNext(Iterator memory self) internal pure returns (bool) {
        RLPItem memory item = self.item;
        return self.nextPtr < item.memPtr + item.len;
    }

    /*
    * @param item RLP encoded bytes
    */
    function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
        uint256 memPtr;
        assembly {
            memPtr := add(item, 0x20)
        }

        return RLPItem(item.length, memPtr);
    }

    /*
    * @dev Create an iterator. Reverts if item is not a list.
    * @param self The RLP item.
    * @return An 'Iterator' over the item.
    */
    function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
        require(isList(self));

        uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
        return Iterator(self, ptr);
    }

    /*
    * @param the RLP item.
    */
    function rlpLen(RLPItem memory item) internal pure returns (uint256) {
        return item.len;
    }

    /*
     * @param the RLP item.
     * @return (memPtr, len) pair: location of the item's payload in memory.
     */
    function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) {
        uint256 offset = _payloadOffset(item.memPtr);
        uint256 memPtr = item.memPtr + offset;
        uint256 len = item.len - offset; // data length
        return (memPtr, len);
    }

    /*
     * @param the RLP item.
     * @return RLPItem of (memPtr, len) pair: location of the item's payload in memory.
     */
    function payloadToRlpItem(RLPItem memory item) internal pure returns (RLPItem memory) {
        uint256 offset = _payloadOffset(item.memPtr);
        uint256 memPtr = item.memPtr + offset;
        uint256 len = item.len - offset; // data length
        return RLPItem(memPtr, len);
    }

    /*
    * @param the RLP item.
    */
    function payloadLen(RLPItem memory item) internal pure returns (uint256) {
        (, uint256 len) = payloadLocation(item);
        return len;
    }

    /*
    * @param the RLP item containing the encoded list.
    */
    function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
        require(isList(item));

        uint256 items = numItems(item);
        RLPItem[] memory result = new RLPItem[](items);

        uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 dataLen;
        for (uint256 i = 0; i < items; i++) {
            dataLen = _itemLength(memPtr);
            result[i] = RLPItem(dataLen, memPtr);
            memPtr = memPtr + dataLen;
        }

        return result;
    }

    // @return indicator whether encoded payload is a list. negate this function call for isData.
    function isList(RLPItem memory item) internal pure returns (bool) {
        if (item.len == 0) return false;

        uint8 byte0;
        uint256 memPtr = item.memPtr;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < LIST_SHORT_START) {
            return false;
        }
        return true;
    }

    /*
     * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
     * @return keccak256 hash of RLP encoded bytes.
     */
    function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        uint256 ptr = item.memPtr;
        uint256 len = item.len;
        bytes32 result;
        assembly {
            result := keccak256(ptr, len)
        }
        return result;
    }

    /*
     * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
     * @return keccak256 hash of the item payload.
     */
    function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        (uint256 memPtr, uint256 len) = payloadLocation(item);
        bytes32 result;
        assembly {
            result := keccak256(memPtr, len)
        }
        return result;
    }

    /**
     * RLPItem conversions into data types *
     */

    // @returns raw rlp encoding in bytes
    function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
        bytes memory result = new bytes(item.len);
        if (result.length == 0) return result;

        uint256 ptr;
        assembly {
            ptr := add(0x20, result)
        }

        copy(item.memPtr, ptr, item.len);
        return result;
    }

    // any non-zero byte except "0x80" is considered true
    function toBoolean(RLPItem memory item) internal pure returns (bool) {
        require(item.len == 1);
        uint256 result;
        uint256 memPtr = item.memPtr;
        assembly {
            result := byte(0, mload(memPtr))
        }

        // SEE Github Issue #5.
        // Summary: Most commonly used RLP libraries (i.e Geth) will encode
        // "0" as "0x80" instead of as "0". We handle this edge case explicitly
        // here.
        if (result == 0 || result == STRING_SHORT_START) {
            return false;
        } else {
            return true;
        }
    }

    function toAddress(RLPItem memory item) internal pure returns (address) {
        // 1 byte for the length prefix
        require(item.len == 21);

        return address(uint160(toUint(item)));
    }

    function toUint(RLPItem memory item) internal pure returns (uint256) {
        require(item.len > 0 && item.len <= 33);

        (uint256 memPtr, uint256 len) = payloadLocation(item);

        uint256 result;
        assembly {
            result := mload(memPtr)

            // shfit to the correct location if neccesary
            if lt(len, 32) { result := div(result, exp(256, sub(32, len))) }
        }

        return result;
    }

    // enforces 32 byte length
    function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
        // one byte prefix
        require(item.len == 33);

        uint256 result;
        uint256 memPtr = item.memPtr + 1;
        assembly {
            result := mload(memPtr)
        }

        return result;
    }

    function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
        require(item.len > 0);

        (uint256 memPtr, uint256 len) = payloadLocation(item);
        bytes memory result = new bytes(len);

        uint256 destPtr;
        assembly {
            destPtr := add(0x20, result)
        }

        copy(memPtr, destPtr, len);
        return result;
    }

    /*
    * Private Helpers
    */

    // @return number of payload items inside an encoded list.
    function numItems(RLPItem memory item) private pure returns (uint256) {
        if (item.len == 0) return 0;

        uint256 count = 0;
        uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 endPtr = item.memPtr + item.len;
        while (currPtr < endPtr) {
            currPtr = currPtr + _itemLength(currPtr); // skip over an item
            count++;
        }

        return count;
    }

    // @return entire rlp item byte length
    function _itemLength(uint256 memPtr) private pure returns (uint256) {
        uint256 itemLen;
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) {
            itemLen = 1;
        } else if (byte0 < STRING_LONG_START) {
            itemLen = byte0 - STRING_SHORT_START + 1;
        } else if (byte0 < LIST_SHORT_START) {
            assembly {
                let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                memPtr := add(memPtr, 1) // skip over the first byte

                /* 32 byte word size */
                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                itemLen := add(dataLen, add(byteLen, 1))
            }
        } else if (byte0 < LIST_LONG_START) {
            itemLen = byte0 - LIST_SHORT_START + 1;
        } else {
            assembly {
                let byteLen := sub(byte0, 0xf7)
                memPtr := add(memPtr, 1)

                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                itemLen := add(dataLen, add(byteLen, 1))
            }
        }

        return itemLen;
    }

    // @return number of bytes until the data
    function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) {
            return 0;
        } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) {
            return 1;
        } else if (
            byte0 < LIST_SHORT_START // being explicit
        ) {
            return byte0 - (STRING_LONG_START - 1) + 1;
        } else {
            return byte0 - (LIST_LONG_START - 1) + 1;
        }
    }

    /*
    * @param src Pointer to source
    * @param dest Pointer to destination
    * @param len Amount of memory to copy from the source
    */
    function copy(uint256 src, uint256 dest, uint256 len) private pure {
        if (len == 0) return;

        // copy as many word sizes as possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }

            src += WORD_SIZE;
            dest += WORD_SIZE;
        }

        if (len > 0) {
            // left over bytes. Mask is used to remove unwanted bytes from the word
            uint256 mask = 256 ** (WORD_SIZE - len) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask)) // zero out src
                let destpart := and(mload(dest), mask) // retrieve the bytes
                mstore(dest, or(destpart, srcpart))
            }
        }
    }
}

File 32 of 33 : BloomLib.sol
// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2022, Specular contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/BytesLib.sol";

library BloomLib {
    using BytesLib for bytes;

    struct Bloom {
        bytes32[8] data;
    }

    function emptyBloom() internal pure returns (Bloom memory b) {
        return b;
    }

    function decodeBloom(bytes calldata encoded, uint64 offset) internal pure returns (Bloom memory) {
        Bloom memory bloom;
        for (uint256 i = 0; i < 8; i++) {
            bloom.data[i] = encoded.toBytes32(offset);
            offset += 32;
        }
        return bloom;
    }

    function addHash(Bloom memory bloom, bytes32 h) internal pure {
        uint16 i1 = 255 - (uint16(uint256(h) >> 240) & 0x7ff) >> 3;
        uint8 v1 = uint8(1 << (uint8(h[1]) & 0x7));
        bloom.data[i1 >> 5] = bytes32(uint256(bloom.data[i1 >> 5]) | (uint256(v1) << 8 * (31 - (i1 & 0x1f))));
        uint16 i2 = 255 - (uint16(uint256(h) >> 224) & 0x7ff) >> 3;
        uint8 v2 = uint8(1 << (uint8(h[3]) & 0x7));
        bloom.data[i2 >> 5] = bytes32(uint256(bloom.data[i2 >> 5]) | (uint256(v2) << 8 * (31 - (i2 & 0x1f))));
        uint16 i3 = 255 - (uint16(uint256(h) >> 208) & 0x7ff) >> 3;
        uint8 v3 = uint8(1 << (uint8(h[5]) & 0x7));
        bloom.data[i3 >> 5] = bytes32(uint256(bloom.data[i3 >> 5]) | (uint256(v3) << 8 * (31 - (i3 & 0x1f))));
    }

    function add(Bloom memory bloom, bytes memory data) internal pure {
        bytes32 h;
        assembly {
            h := keccak256(add(data, 0x20), mload(data))
        }
        addHash(bloom, h);
    }

    function add(Bloom memory bloom, address data) internal pure {
        bytes32 h = keccak256(abi.encodePacked(data));
        addHash(bloom, h);
    }

    function add(Bloom memory bloom, bytes32 data) internal pure {
        bytes32 h = keccak256(abi.encodePacked(data));
        addHash(bloom, h);
    }
}

File 33 of 33 : RLPWriter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @custom:attribution https://github.com/bakaoh/solidity-rlp-encode
 * @title RLPWriter
 * @author RLPWriter is a library for encoding Solidity types to RLP bytes. Adapted from Bakaoh's
 *         RLPEncode library (https://github.com/bakaoh/solidity-rlp-encode) with minor
 *         modifications to improve legibility.
 */
library RLPWriter {
    /**
     * @notice RLP encodes a byte string.
     *
     * @param _in The byte string to encode.
     *
     * @return The RLP encoded string in bytes.
     */
    function writeBytes(bytes memory _in) internal pure returns (bytes memory) {
        bytes memory encoded;

        if (_in.length == 1 && uint8(_in[0]) < 128) {
            encoded = _in;
        } else {
            encoded = abi.encodePacked(_writeLength(_in.length, 128), _in);
        }

        return encoded;
    }

    /**
     * @notice RLP encodes a list of RLP encoded byte byte strings.
     *
     * @param _in The list of RLP encoded byte strings.
     *
     * @return The RLP encoded list of items in bytes.
     */
    function writeList(bytes[] memory _in) internal pure returns (bytes memory) {
        bytes memory list = _flatten(_in);
        return abi.encodePacked(_writeLength(list.length, 192), list);
    }

    /**
     * @notice RLP encodes a string.
     *
     * @param _in The string to encode.
     *
     * @return The RLP encoded string in bytes.
     */
    function writeString(string memory _in) internal pure returns (bytes memory) {
        return writeBytes(bytes(_in));
    }

    /**
     * @notice RLP encodes an address.
     *
     * @param _in The address to encode.
     *
     * @return The RLP encoded address in bytes.
     */
    function writeAddress(address _in) internal pure returns (bytes memory) {
        return writeBytes(abi.encodePacked(_in));
    }

    /**
     * @notice RLP encodes a uint.
     *
     * @param _in The uint256 to encode.
     *
     * @return The RLP encoded uint256 in bytes.
     */
    function writeUint(uint256 _in) internal pure returns (bytes memory) {
        return writeBytes(_toBinary(_in));
    }

    /**
     * @notice RLP encodes a bool.
     *
     * @param _in The bool to encode.
     *
     * @return The RLP encoded bool in bytes.
     */
    function writeBool(bool _in) internal pure returns (bytes memory) {
        bytes memory encoded = new bytes(1);
        encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80));
        return encoded;
    }

    /**
     * @notice Encode the first byte and then the `len` in binary form if `length` is more than 55.
     *
     * @param _len    The length of the string or the payload.
     * @param _offset 128 if item is string, 192 if item is list.
     *
     * @return RLP encoded bytes.
     */
    function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory) {
        bytes memory encoded;

        if (_len < 56) {
            encoded = new bytes(1);
            encoded[0] = bytes1(uint8(_len) + uint8(_offset));
        } else {
            uint256 lenLen;
            uint256 i = 1;
            while (_len / i != 0) {
                lenLen++;
                i *= 256;
            }

            encoded = new bytes(lenLen + 1);
            encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55);
            for (i = 1; i <= lenLen; i++) {
                encoded[i] = bytes1(uint8((_len / (256 ** (lenLen - i))) % 256));
            }
        }

        return encoded;
    }

    /**
     * @notice Encode integer in big endian binary form with no leading zeroes.
     *
     * @param _x The integer to encode.
     *
     * @return RLP encoded bytes.
     */
    function _toBinary(uint256 _x) private pure returns (bytes memory) {
        bytes memory b = abi.encodePacked(_x);

        uint256 i = 0;
        for (; i < 32; i++) {
            if (b[i] != 0) {
                break;
            }
        }

        bytes memory res = new bytes(32 - i);
        for (uint256 j = 0; j < res.length; j++) {
            res[j] = b[i++];
        }

        return res;
    }

    /**
     * @custom:attribution https://github.com/Arachnid/solidity-stringutils
     * @notice Copies a piece of memory to another location.
     *
     * @param _dest Destination location.
     * @param _src  Source location.
     * @param _len  Length of memory to copy.
     */
    function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure {
        uint256 dest = _dest;
        uint256 src = _src;
        uint256 len = _len;

        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        uint256 mask;
        unchecked {
            mask = 256 ** (32 - len) - 1;
        }
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    /**
     * @custom:attribution https://github.com/sammayo/solidity-rlp-encoder
     * @notice Flattens a list of byte strings into one byte string.
     *
     * @param _list List of byte strings to flatten.
     *
     * @return The flattened byte string.
     */
    function _flatten(bytes[] memory _list) private pure returns (bytes memory) {
        if (_list.length == 0) {
            return new bytes(0);
        }

        uint256 len;
        uint256 i = 0;
        for (; i < _list.length; i++) {
            len += _list[i].length;
        }

        bytes memory flattened = new bytes(len);
        uint256 flattenedPtr;
        assembly {
            flattenedPtr := add(flattened, 0x20)
        }

        for (i = 0; i < _list.length; i++) {
            bytes memory item = _list[i];

            uint256 listPtr;
            assembly {
                listPtr := add(item, 0x20)
            }

            _memcpy(flattenedPtr, listPtr, item.length);
            flattenedPtr += _list[i].length;
        }

        return flattened;
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AssertionAlreadyResolved","type":"error"},{"inputs":[],"name":"AssertionOutOfRange","type":"error"},{"inputs":[],"name":"ChallengePeriodPending","type":"error"},{"inputs":[],"name":"ChallengedStaker","type":"error"},{"inputs":[],"name":"DifferentParent","type":"error"},{"inputs":[],"name":"EmptyAssertion","type":"error"},{"inputs":[{"internalType":"address","name":"staker1Challenge","type":"address"},{"internalType":"address","name":"staker2Challenge","type":"address"}],"name":"InDifferentChallenge","type":"error"},{"inputs":[],"name":"InboxReadLimitExceeded","type":"error"},{"inputs":[],"name":"InsufficientStake","type":"error"},{"inputs":[],"name":"InvalidParent","type":"error"},{"inputs":[],"name":"MinimumAssertionPeriodNotPassed","type":"error"},{"inputs":[],"name":"NoStaker","type":"error"},{"inputs":[],"name":"NoUnresolvedAssertion","type":"error"},{"inputs":[],"name":"NotAllStaked","type":"error"},{"inputs":[],"name":"NotInChallenge","type":"error"},{"inputs":[],"name":"NotStaked","type":"error"},{"inputs":[],"name":"ParentAssertionUnstaked","type":"error"},{"inputs":[],"name":"PreviousStateHash","type":"error"},{"inputs":[],"name":"RedundantInitialized","type":"error"},{"inputs":[],"name":"StakedOnUnconfirmedAssertion","type":"error"},{"inputs":[],"name":"StakerStakedOnTarget","type":"error"},{"inputs":[],"name":"StakersPresent","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UnproposedAssertion","type":"error"},{"inputs":[],"name":"WrongOrder","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assertionID","type":"uint256"},{"indexed":false,"internalType":"address","name":"challengeAddr","type":"address"}],"name":"AssertionChallenged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assertionID","type":"uint256"}],"name":"AssertionConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assertionID","type":"uint256"},{"indexed":false,"internalType":"address","name":"asserterAddr","type":"address"},{"indexed":false,"internalType":"bytes32","name":"vmHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"inboxSize","type":"uint256"}],"name":"AssertionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assertionID","type":"uint256"}],"name":"AssertionRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"stakerAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"assertionID","type":"uint256"}],"name":"StakerStaked","type":"event"},{"inputs":[{"internalType":"address[]","name":"toAddAddresses","type":"address[]"}],"name":"addToOperatorWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"toAddAddresses","type":"address[]"}],"name":"addToStakerWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assertionID","type":"uint256"}],"name":"advanceStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"assertions","outputs":[{"internalType":"contract AssertionMap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseStakeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[2]","name":"players","type":"address[2]"},{"internalType":"uint256[2]","name":"assertionIDs","type":"uint256[2]"}],"name":"challengeAssertion","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"challengeCtx","outputs":[{"internalType":"bool","name":"completed","type":"bool"},{"internalType":"address","name":"challengeAddress","type":"address"},{"internalType":"address","name":"defenderAddress","type":"address"},{"internalType":"address","name":"challengerAddress","type":"address"},{"internalType":"uint256","name":"defenderAssertionID","type":"uint256"},{"internalType":"uint256","name":"challengerAssertionID","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"winner","type":"address"},{"internalType":"address","name":"loser","type":"address"}],"name":"completeChallenge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmFirstUnresolvedAssertion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmedInboxSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"vmHash","type":"bytes32"},{"internalType":"uint256","name":"inboxSize","type":"uint256"}],"name":"createAssertion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"vmHash","type":"bytes32"},{"internalType":"uint256","name":"inboxSize","type":"uint256"},{"internalType":"bytes32[]","name":"_batch","type":"bytes32[]"},{"internalType":"uint256","name":"_shouldStartAtElement","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"createAssertionWithStateBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentRequiredStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_verifier","type":"address"},{"internalType":"address","name":"_stakeToken","type":"address"},{"internalType":"address","name":"_libAddressManager","type":"address"},{"internalType":"address","name":"_assertionMap","type":"address"},{"internalType":"uint256","name":"_minimumAssertionPeriod","type":"uint256"},{"internalType":"uint256","name":"_baseStakeAmount","type":"uint256"},{"internalType":"bytes32","name":"_initialVMhash","type":"bytes32"},{"internalType":"address[]","name":"stakerWhitelists","type":"address[]"},{"internalType":"address[]","name":"operatorWhitelists","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isStaked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastConfirmedAssertionID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCreatedAssertionID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastResolvedAssertionID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"libAddressManager","outputs":[{"internalType":"contract Lib_AddressManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumAssertionPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numStakers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operatorWhitelist","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"operatorslist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"registers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rejectFirstUnresolvedAssertion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"batchIndex","type":"uint256"},{"internalType":"bytes32","name":"batchRoot","type":"bytes32"},{"internalType":"uint256","name":"batchSize","type":"uint256"},{"internalType":"uint256","name":"prevTotalElements","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct Lib_BVMCodec.ChainBatchHeader","name":"_batchHeader","type":"tuple"}],"name":"rejectLatestCreatedAssertionWithBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"toRemoveAddresses","type":"address[]"}],"name":"removeFromOperatorWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"toRemoveAddresses","type":"address[]"}],"name":"removeFromStakerWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeOldZombies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stakerAddress","type":"address"}],"name":"removeStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"}],"name":"resolve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shouldRollBack","type":"uint256"},{"internalType":"uint256","name":"_shouldStartAtElement","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"rollbackL2Chain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakeAmount","type":"uint256"},{"internalType":"address","name":"operator","type":"address"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakerWhitelist","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakers","outputs":[{"internalType":"bool","name":"isStaked","type":"bool"},{"internalType":"uint256","name":"amountStaked","type":"uint256"},{"internalType":"uint256","name":"assertionID","type":"uint256"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"currentChallenge","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakerslist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IVerifierEntry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawableFunds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"zombies","outputs":[{"internalType":"address","name":"stakerAddress","type":"address"},{"internalType":"uint256","name":"lastAssertionID","type":"uint256"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b50600080546001600160a01b03191690556200002c62000032565b62000101565b600054600160a81b900460ff1615620000a15760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160a01b90910481161015620000ff576000805460ff60a01b191660ff60a01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b615d9e80620001116000396000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c80636aabc35011610151578063b553ee84116100c3578063cfc5901d11610087578063cfc5901d14610641578063de574efc14610654578063e03c863214610667578063f2fde38b14610687578063fa7803e61461069a578063fe2ba848146106ad57600080fd5b8063b553ee84146105f7578063b6da898f14610600578063c8525c3e14610613578063c94b584714610626578063c97a59191461062e57600080fd5b80638c669739116101155780638c6697391461052a5780638da5cb5b146105325780639168ae72146105455780639efbea23146105c8578063a56ba93b146105db578063aca9a518146105e457600080fd5b80636aabc350146104c95780636c8b052a146104f257806371129559146104fb5780637acb7757146105045780638821b2ae1461051757600080fd5b806330b26075116101ea578063461a4478116101ae578063461a44781461043957806349cd30041461044c5780634d26732d1461045f57806351ed6a30146104675780636177fd181461047a5780636a368561146104b657600080fd5b806330b26075146103fa57806337d1fbdd146104025780633ccfd60b1461041557806340d9224b1461041d57806345e38b641461043057600080fd5b80632aa234f61161023c5780632aa234f61461036e5780632b7ac3f3146103815780632e17de78146103945780632f06d1b0146103a75780632f30cabd146103ba578063300a7161146103da57600080fd5b80630e456acf14610279578063107035a4146102f05780632052465e146103075780632906040e14610339578063299ca47814610343575b600080fd5b607754607854607954607a54607b546102ac9460ff8116946001600160a01b036101009092048216949082169391169186565b6040805196151587526001600160a01b0395861660208801529385169386019390935292166060840152608083019190915260a082015260c0015b60405180910390f35b6102f960715481565b6040519081526020016102e7565b61031a610315366004613bad565b6106c0565b604080516001600160a01b0390931683526020830191909152016102e7565b6103416106f8565b005b600054610356906001600160a01b031681565b6040516001600160a01b0390911681526020016102e7565b61035661037c366004613bad565b6109a8565b600554610356906001600160a01b031681565b6103416103a2366004613bad565b6109d2565b6103566103b5366004613bd7565b610b52565b6102f96103c8366004613c31565b60756020526000908152604090205481565b6102f96103e8366004613c31565b60396020526000908152604090205481565b6103416111cc565b610356610410366004613bad565b611518565b610341611528565b600454610356906001600160a01b031681565b6102f960015481565b610356610447366004613d0a565b611618565b61034161045a366004613da7565b611699565b6002546102f9565b600354610356906001600160a01b031681565b6104a6610488366004613c31565b6001600160a01b031660009081526073602052604090205460ff1690565b60405190151581526020016102e7565b6103416104c4366004613e61565b6118c6565b6103566104d7366004613c31565b6074602052600090815260409020546001600160a01b031681565b6102f960725481565b6102f960025481565b610341610512366004613ea3565b6119c0565b610341610525366004613bad565b611dac565b610341611f3a565b603854610356906001600160a01b031681565b610592610553366004613c31565b6073602052600090815260409020805460018201546002830154600384015460049094015460ff90931693919290916001600160a01b03908116911685565b6040805195151586526020860194909452928401919091526001600160a01b03908116606084015216608082015260a0016102e7565b6103416105d6366004613e61565b611f7d565b6102f960705481565b6103416105f2366004613e61565b612159565b6102f9606f5481565b61034161060e366004613ed3565b612335565b610341610621366004613e61565b6126f6565b6102f96127ea565b61034161063c366004613ef5565b61286f565b61034161064f366004614003565b612c57565b610341610662366004614053565b612da3565b6102f9610675366004613c31565b603b6020526000908152604090205481565b610341610695366004613c31565b6131f0565b6103416106a836600461410c565b613288565b6103416106bb366004613c31565b6134ca565b607681815481106106d057600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b336000908152607460205260409020546001600160a01b03166107365760405162461bcd60e51b815260040161072d9061413a565b60405180910390fd5b607154606f54106107815760405162461bcd60e51b81526020600482015260156024820152742737aab73932b9b7b63b32b220b9b9b2b93a34b7b760591b604482015260640161072d565b6000607254116107be5760405162461bcd60e51b81526020600482015260086024820152672737a9ba30b5b2b960c11b604482015260640161072d565b6000606f5460016107cf9190614175565b60048054604051638286227560e01b81529293506001600160a01b0316916382862275916108039185910190815260200190565b60206040518083038186803b15801561081b57600080fd5b505afa15801561082f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610853919061418d565b42101561089b5760405162461bcd60e51b81526020600482015260166024820152754368616c6c656e6765506572696f6450656e64696e6760501b604482015260640161072d565b6070546004805460405163030b947760e41b81529182018490526001600160a01b0316906330b947709060240160206040518083038186803b1580156108e057600080fd5b505afa1580156108f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610918919061418d565b146109555760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5914185c995b9d609a1b604482015260640161072d565b606f8054906000610965836141a6565b9091555050606f5460708190556040519081527f453430d123684340024ae0a229704bdab39c93dc48bb5a0b4bc83142d95d48ef9060200160405180910390a150565b603a81815481106109b857600080fd5b6000918252602090912001546001600160a01b0316905081565b6109db3361361b565b33600090815260736020526040902060705460028201541115610a405760405162461bcd60e51b815260206004820152601c60248201527f5374616b65644f6e556e636f6e6669726d6564417373657274696f6e00000000604482015260640161072d565b6002548160010154610a5291906141c1565b821115610a955760405162461bcd60e51b8152602060048201526011602482015270496e73756666696369656e745374616b6560781b604482015260640161072d565b81816001016000828254610aa991906141c1565b909155505060035460405163a9059cbb60e01b8152336004820152602481018490526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b158015610afa57600080fd5b505af1158015610b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3291906141d8565b610b4e5760405162461bcd60e51b815260040161072d906141fa565b5050565b336000908152607460205260408120546001600160a01b0316610b875760405162461bcd60e51b815260040161072d9061413a565b81356020830135808210610bca5760405162461bcd60e51b815260206004820152600a6024820152692bb937b733a7b93232b960b11b604482015260640161072d565b607154811115610c125760405162461bcd60e51b81526020600482015260136024820152722ab7383937b837b9b2b220b9b9b2b93a34b7b760691b604482015260640161072d565b8160705410610c635760405162461bcd60e51b815260206004820152601860248201527f417373657274696f6e416c72656164795265736f6c7665640000000000000000604482015260640161072d565b6004805460405163030b947760e41b81529182018490526000916001600160a01b03909116906330b947709060240160206040518083038186803b158015610caa57600080fd5b505afa158015610cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce2919061418d565b6004805460405163030b947760e41b81529293506001600160a01b0316916330b9477091610d169186910190815260200190565b60206040518083038186803b158015610d2e57600080fd5b505afa158015610d42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d66919061418d565b8114610da65760405162461bcd60e51b815260206004820152600f60248201526e111a5999995c995b9d14185c995b9d608a1b604482015260640161072d565b6000610db56020880188613c31565b90506000610dc96040890160208a01613c31565b9050806001600160a01b0316826001600160a01b03161415610e3b5760405162461bcd60e51b815260206004820152602560248201527f646566656e64657220616e64206368616c6c656e6765206d757374206e6f7420604482015264195c5d585b60da1b606482015260840161072d565b6001600160a01b038083166000908152607460205260408082205484841683529120549082169116610e6c8261366f565b610e758161366f565b6000604051610e8390613b5a565b604051809103906000f080158015610e9f573d6000803e3d6000fd5b506001600160a01b038084166000908152607360209081526040808320600490810180548688166001600160a01b031991821681179092558a871686528386209092018054831682179055825160c0810184529485529284018390528a8516848301819052948a1660608501819052608085018f905260a09094018d905260778054610100949094026001600160a81b031990941693909317909255607880548316909417909355607980549091169091179055607a8a9055607b8990555190915081907fd0ebe74b4f7d89a9b0fdc9d95f887a7b925c6c7300b5c4b2c3304d97925840fa90610fa4908b9084909182526001600160a01b0316602082015260400190565b60405180910390a160048054604051632b27e93b60e01b81529182018990526000916001600160a01b0390911690632b27e93b9060240160206040518083038186803b158015610ff357600080fd5b505afa158015611007573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102b919061418d565b60048054604051632a411f3360e11b81529182018b90529192506000916001600160a01b0316906354823e669060240160206040518083038186803b15801561107357600080fd5b505afa158015611087573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ab919061418d565b60048054604051632a411f3360e11b81529182018e90529192506000916001600160a01b0316906354823e669060240160206040518083038186803b1580156110f357600080fd5b505afa158015611107573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112b919061418d565b60055460405163abf4801360e01b81526001600160a01b038c811660048301528b8116602483015291821660448201523060648201526084810186905260a4810185905260c4810183905291925086169063abf480139060e401600060405180830381600087803b15801561119f57600080fd5b505af11580156111b3573d6000803e3d6000fd5b50959e5050505050505050505050505050505b92915050565b336000908152607460205260409020546001600160a01b03166112015760405162461bcd60e51b815260040161072d9061413a565b607154606f541061124c5760405162461bcd60e51b81526020600482015260156024820152742737aab73932b9b7b63b32b220b9b9b2b93a34b7b760591b604482015260640161072d565b6000606f54600161125d9190614175565b6070546004805460405163030b947760e41b815291820184905292935090916001600160a01b0316906330b947709060240160206040518083038186803b1580156112a757600080fd5b505afa1580156112bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112df919061418d565b14156114685760048054604051638286227560e01b81529182018390526001600160a01b03169063828622759060240160206040518083038186803b15801561132757600080fd5b505afa15801561133b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135f919061418d565b4210156113a75760405162461bcd60e51b81526020600482015260166024820152754368616c6c656e6765506572696f6450656e64696e6760501b604482015260640161072d565b6113b0816136d6565b6004805460405163366b2b6960e01b81529182018490526001600160a01b03169063366b2b699060240160206040518083038186803b1580156113f257600080fd5b505afa158015611406573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142a919061418d565b146114685760405162461bcd60e51b815260206004820152600e60248201526d14dd185ad95c9cd41c995cd95b9d60921b604482015260640161072d565b606f8054906000611478836141a6565b91905055507f5b24ab8ceb442373727ac5c559a027521cb52db451c74710ebed9faa5fe15a7c606f546040516114b091815260200190565b60405180910390a160048054606f5460405163d8a4e5af60e01b8152928301526001600160a01b03169063d8a4e5af90602401600060405180830381600087803b1580156114fd57600080fd5b505af1158015611511573d6000803e3d6000fd5b5050505050565b603c81815481106109b857600080fd5b336000908152607460205260409020546001600160a01b031661155d5760405162461bcd60e51b815260040161072d9061413a565b336000818152607560205260408082208054929055600354905163a9059cbb60e01b815260048101939093526024830182905290916001600160a01b039091169063a9059cbb90604401602060405180830381600087803b1580156115c157600080fd5b505af11580156115d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f991906141d8565b6116155760405162461bcd60e51b815260040161072d906141fa565b50565b6000805460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061164990859060040161429f565b60206040518083038186803b15801561166157600080fd5b505afa158015611675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c691906142b2565b336000908152607460205260409020546001600160a01b03166116ce5760405162461bcd60e51b815260040161072d9061413a565b6116fb6040518060400160405280600c81526020016b212b26afa937b6363ab832b960a11b815250611618565b6001600160a01b0316336001600160a01b0316146117795760405162461bcd60e51b815260206004820152603560248201527f6d73672e73656e646572206973206e6f7420726f6c6c75702070726f706f73656044820152740e45840c6c2dc4ee840c2e0e0cadcc840c4c2e8c6d605b1b606482015260840161072d565b6117838787612335565b60006117ba6040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b90506000816001600160a01b031687878787876040516024016117e19594939291906142cf565b60408051601f198184030181529181526020820180516001600160e01b0316632169f79f60e01b17905251611816919061433f565b6000604051808303816000865af19150503d8060008114611853576040519150601f19603f3d011682016040523d82523d6000602084013e611858565b606091505b50509050806118bb5760405162461bcd60e51b815260206004820152602960248201527f73636320617070656e64207374617465206261746368206661696c65642c2072604482015268195d995c9d08185b1b60ba1b606482015260840161072d565b505050505050505050565b6038546001600160a01b031633146118f05760405162461bcd60e51b815260040161072d9061435b565b603a5460005b828110156119ba576119088183614175565b6039600086868581811061191e5761191e614231565b90506020020160208101906119339190613c31565b6001600160a01b03168152602081019190915260400160002055603a84848381811061196157611961614231565b90506020020160208101906119769190613c31565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b03909216919091179055806119b2816141a6565b9150506118f6565b50505050565b33600081815260396020526040902054603a805483929081106119e5576119e5614231565b6000918252602090912001546001600160a01b031614611a475760405162461bcd60e51b815260206004820152601760248201527f4e4f545f494e5f5354414b45525f57484954454c495354000000000000000000604482015260640161072d565b6001600160a01b0382166000818152603b6020526040902054603c8054859392908110611a7657611a76614231565b6000918252602090912001546001600160a01b031614611ad85760405162461bcd60e51b815260206004820152601960248201527f4e4f545f494e5f4f50455241544f525f57484954454c49535400000000000000604482015260640161072d565b6003546040516323b872dd60e01b8152336004820152306024820152604481018690526001600160a01b03909116906323b872dd90606401602060405180830381600087803b158015611b2a57600080fd5b505af1158015611b3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6291906141d8565b611b7e5760405162461bcd60e51b815260040161072d906141fa565b3360009081526073602052604090205460ff1615611c3e57336000908152607360205260409020600301546001600160a01b03848116911614611c115760405162461bcd60e51b815260206004820152602560248201527f7374616b6572203d3e206f70657261746f72206d617070696e67206e6f7420756044820152646e6971756560d81b606482015260840161072d565b3360009081526073602052604081206001018054869290611c33908490614175565b909155506119ba9050565b6001600160a01b038381166000908152607460205260409020541615611c9d5760405162461bcd60e51b81526020600482015260146024820152731bdc195c985d1bdc881a5cc81bd8d8dd5c1a595960621b604482015260640161072d565b600254841015611ce35760405162461bcd60e51b8152602060048201526011602482015270496e73756666696369656e745374616b6560781b604482015260640161072d565b6040805160a0810182526001808252602080830188815260008486018181526001600160a01b03808b16606088018181526080890185815233808752607389528b87209a518b5490151560ff19909116178b559651988a01989098559251600289015591516003880180549183166001600160a01b03199283161790559551600490970180549790911696861696909617909555938452607490915292822080549091169092179091556072805491611d9b836141a6565b91905055506119ba336070546137ca565b336000908152607460205260409020546001600160a01b0316611de15760405162461bcd60e51b815260040161072d9061413a565b336000908152607460209081526040808320546001600160a01b03168084526073909252909120600281015483111580611e1c575060715483115b15611e5f5760405162461bcd60e51b8152602060048201526013602482015272417373657274696f6e4f75744f6652616e676560681b604482015260640161072d565b6004805460405163030b947760e41b81529182018590526001600160a01b0316906330b947709060240160206040518083038186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed9919061418d565b816002015414611f2b5760405162461bcd60e51b815260206004820152601760248201527f506172656e74417373657274696f6e556e7374616b6564000000000000000000604482015260640161072d565b611f3582846137ca565b505050565b336000908152607460205260409020546001600160a01b0316611f6f5760405162461bcd60e51b815260040161072d9061413a565b611f7b60766000613b67565b565b6038546001600160a01b03163314611fa75760405162461bcd60e51b815260040161072d9061435b565b60005b81811015611f35576000603b6000858585818110611fca57611fca614231565b9050602002016020810190611fdf9190613c31565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905080603b6000603c6001603c8054905061201b91906141c1565b8154811061202b5761202b614231565b60009182526020808320909101546001600160a01b03168352820192909252604001902055603c8054612060906001906141c1565b8154811061207057612070614231565b600091825260209091200154603c80546001600160a01b03909216918390811061209c5761209c614231565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550603c8054806120db576120db614390565b600082815260208120820160001990810180546001600160a01b0319169055909101909155603b9085858581811061211557612115614231565b905060200201602081019061212a9190613c31565b6001600160a01b0316815260208101919091526040016000908120555080612151816141a6565b915050611faa565b6038546001600160a01b031633146121835760405162461bcd60e51b815260040161072d9061435b565b60005b81811015611f35576000603960008585858181106121a6576121a6614231565b90506020020160208101906121bb9190613c31565b6001600160a01b03166001600160a01b031681526020019081526020016000205490508060396000603a6001603a805490506121f791906141c1565b8154811061220757612207614231565b60009182526020808320909101546001600160a01b03168352820192909252604001902055603a805461223c906001906141c1565b8154811061224c5761224c614231565b600091825260209091200154603a80546001600160a01b03909216918390811061227857612278614231565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550603a8054806122b7576122b7614390565b600082815260208120820160001990810180546001600160a01b03191690559091019091556039908585858181106122f1576122f1614231565b90506020020160208101906123069190613c31565b6001600160a01b031681526020810191909152604001600090812055508061232d816141a6565b915050612186565b336000908152607460205260409020546001600160a01b031661236a5760405162461bcd60e51b815260040161072d9061413a565b336000908152607460209081526040808320546001600160a01b039081168085526073909352922060040154909116156124005760405162461bcd60e51b815260206004820152603160248201527f63616e206e6f742063726561746520617373657274696f6e207768656e207374604482015270616b657220696e206368616c6c656e676560781b606482015260840161072d565b6001600160a01b038181166000908152607360205260409081902060020154600154600480549351634e04886d60e01b8152908101839052919390921690634e04886d9060240160206040518083038186803b15801561245f57600080fd5b505afa158015612473573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612497919061418d565b6124a190436141c1565b10156124ef5760405162461bcd60e51b815260206004820152601f60248201527f4d696e696d756d417373657274696f6e506572696f644e6f7450617373656400604482015260640161072d565b60048054604051632b27e93b60e01b81529182018390526001600160a01b031690632b27e93b9060240160206040518083038186803b15801561253157600080fd5b505afa158015612545573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612569919061418d565b83116125a85760405162461bcd60e51b815260206004820152600e60248201526d22b6b83a3ca0b9b9b2b93a34b7b760911b604482015260640161072d565b607180549060006125b8836141a6565b9091555050607154604080519182523360208301528101859052606081018490527f5c610f28399ecc14b66149012a0197a5e3257a8c397125afee95d1cf4b9507349060800160405180910390a16004546071546001600160a01b039091169063422815849086868561262961388f565b6040516001600160e01b031960e088901b1681526004810195909552602485019390935260448401919091526064830152608482015260a401600060405180830381600087803b15801561267c57600080fd5b505af1158015612690573d6000803e3d6000fd5b505050506126a0826071546137ca565b606f80549060006126b0836141a6565b9091555050606f5460708190556040519081527f453430d123684340024ae0a229704bdab39c93dc48bb5a0b4bc83142d95d48ef9060200160405180910390a150505050565b6038546001600160a01b031633146127205760405162461bcd60e51b815260040161072d9061435b565b603c5460005b828110156119ba576127388183614175565b603b600086868581811061274e5761274e614231565b90506020020160208101906127639190613c31565b6001600160a01b03168152602081019190915260400160002055603c84848381811061279157612791614231565b90506020020160208101906127a69190613c31565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b03909216919091179055806127e2816141a6565b915050612726565b60048054607054604051632b27e93b60e01b8152928301526000916001600160a01b0390911690632b27e93b9060240160206040518083038186803b15801561283257600080fd5b505afa158015612846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286a919061418d565b905090565b600054600160a81b900460ff161580801561289757506000546001600160a01b90910460ff16105b806128b85750303b1580156128b85750600054600160a01b900460ff166001145b61291b5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161072d565b6000805460ff60a01b1916600160a01b1790558015612948576000805460ff60a81b1916600160a81b1790555b6001600160a01b038d16158061296557506001600160a01b038c16155b156129a05760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b604482015260640161072d565b8c603860006101000a8154816001600160a01b0302191690836001600160a01b031602179055508a600360006101000a8154816001600160a01b0302191690836001600160a01b031602179055508b600560006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060006001600160a01b031660008054906101000a90046001600160a01b03166001600160a01b031614612a815760405162461bcd60e51b81526020600482015260146024820152731499591d5b99185b9d125b9a5d1a585b1a5e995960621b604482015260640161072d565b600080546001600160a01b0319166001600160a01b038c8116919091179091556004541615612ae95760405162461bcd60e51b81526020600482015260146024820152731499591d5b99185b9d125b9a5d1a585b1a5e995960621b604482015260640161072d565b600480546001600160a01b0319166001600160a01b038b16908117825560018a905560028990556040516303b31b8360e11b8152309281019290925290630766370690602401600060405180830381600087803b158015612b4957600080fd5b505af1158015612b5d573d6000803e3d6000fd5b50506000606f819055607081905560718190556004805460405163108a056160e21b8152918201839052602482018b90526044820183905260648201929092524360848201526001600160a01b0390911692506342281584915060a401600060405180830381600087803b158015612bd457600080fd5b505af1158015612be8573d6000803e3d6000fd5b50505050612bf685856118c6565b612c0083836126f6565b8015612c48576000805460ff60a81b19169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050505050505050565b6038546001600160a01b03163314612c815760405162461bcd60e51b815260040161072d9061435b565b6000612cb86040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b90506000816001600160a01b0316858585604051602401612cdb939291906143a6565b60408051601f198184030181529181526020820180516001600160e01b03166305f9daf960e11b17905251612d10919061433f565b6000604051808303816000865af19150503d8060008114612d4d576040519150601f19603f3d011682016040523d82523d6000602084013e612d52565b606091505b50509050806115115760405162461bcd60e51b815260206004820152601b60248201527f63616c6c20726f6c6c4261636b4c32436861696e206661696c65640000000000604482015260640161072d565b6038546001600160a01b03163314612dcd5760405162461bcd60e51b815260040161072d9061435b565b6000612e046040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b60408051600481526024810182526020810180516001600160e01b0316633958777760e21b17905290519192506000916001600160a01b03841691612e489161433f565b6000604051808303816000865af19150503d8060008114612e85576040519150601f19603f3d011682016040523d82523d6000602084013e612e8a565b606091505b50915050600081612e9a906143c5565b8451909150612ea990826141c1565b600114612f045760405162461bcd60e51b8152602060048201526024808201527f64656c657465206261746368207769746820676170206973206e6f7420616c6c6044820152631bddd95960e21b606482015260840161072d565b6000836001600160a01b031685604051602401612f2191906143ec565b60408051601f198184030181529181526020820180516001600160e01b0316632da6c87160e11b17905251612f56919061433f565b6000604051808303816000865af19150503d8060008114612f93576040519150601f19603f3d011682016040523d82523d6000602084013e612f98565b606091505b5050905080612ffb5760405162461bcd60e51b815260206004820152602960248201527f7363632064656c657465207374617465206261746368206661696c65642c2072604482015268195d995c9d08185b1b60ba1b606482015260840161072d565b606f5460715410156130665760405162461bcd60e51b815260206004820152602e60248201527f64656c65746520617373657274696f6e206265666f7265206c6173742072657360448201526d37b63b32b21034b71032b93937b960911b606482015260840161072d565b7f5b24ab8ceb442373727ac5c559a027521cb52db451c74710ebed9faa5fe15a7c60715460405161309991815260200190565b60405180910390a16004805460715460405163d0087d6160e01b8152928301526001600160a01b03169063d0087d6190602401600060405180830381600087803b1580156130e657600080fd5b505af11580156130fa573d6000803e3d6000fd5b50506071805492509050600061310f8361444d565b9091555050606f80549060006131248361444d565b9091555050607080549060006131398361444d565b919050555060005b603a548110156131e85760715460736000603a848154811061316557613165614231565b60009182526020808320909101546001600160a01b0316835282019290925260400190206002015411156131d65760715460736000603a84815481106131ad576131ad614231565b60009182526020808320909101546001600160a01b031683528201929092526040019020600201555b806131e0816141a6565b915050613141565b505050505050565b6038546001600160a01b0316331461321a5760405162461bcd60e51b815260040161072d9061435b565b6001600160a01b03811661327f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161072d565b611615816139c2565b336000908152607460205260409020546001600160a01b03166132bd5760405162461bcd60e51b815260040161072d9061413a565b6001600160a01b0380831660009081526074602052604080822054848416835291205490821691166132ee8161361b565b60006132fa8383613a14565b9050336001600160a01b038216146133435760405162461bcd60e51b815260206004820152600c60248201526b4e6f744368616c6c656e676560a01b604482015260640161072d565b6001600160a01b0382166000908152607360205260408120600101546002548111156133ad5760025461337690826141c1565b6001600160a01b0385166000908152607560205260408120805490919061339e908490614175565b909155505060025491506133b1565b8091505b6001600160a01b038516600090815260736020526040812060010180548492906133dc908490614175565b90915550506001600160a01b0380861660009081526073602052604080822060040180546001600160a01b03191690559186168152206002015461341f85613adf565b604080518082019091526001600160a01b03958616815260208101918252607680546001808201835560009290925291517fb5732705f5241370a28908c2fe1303cb223f03b90d857fd0573f003f79fefed4600290930292830180546001600160a01b031916919098161790965590517fb5732705f5241370a28908c2fe1303cb223f03b90d857fd0573f003f79fefed59091015550506077805460ff191690921790915550505050565b6038546001600160a01b031633146134f45760405162461bcd60e51b815260040161072d9061435b565b6134fd8161361b565b6001600160a01b03811660009081526073602052604090206070546002820154111561356b5760405162461bcd60e51b815260206004820152601c60248201527f5374616b65644f6e556e636f6e6669726d6564417373657274696f6e00000000604482015260640161072d565b600181015461357983613adf565b60035460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018490529091169063a9059cbb90604401602060405180830381600087803b1580156135c757600080fd5b505af11580156135db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ff91906141d8565b611f355760405162461bcd60e51b815260040161072d906141fa565b6001600160a01b03811660009081526073602052604090205460ff166116155760405162461bcd60e51b8152602060048201526009602482015268139bdd14dd185ad95960ba1b604482015260640161072d565b6136788161361b565b6001600160a01b0381811660009081526073602052604090206004015416156116155760405162461bcd60e51b815260206004820152601060248201526f21b430b63632b733b2b229ba30b5b2b960811b604482015260640161072d565b600080805b6076548110156137c357600454607680546001600160a01b039092169163873fd0899187918590811061371057613710614231565b600091825260209091206002909102015460405160e084901b6001600160e01b031916815260048101929092526001600160a01b0316602482015260440160206040518083038186803b15801561376657600080fd5b505afa15801561377a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379e91906141d8565b156137b157816137ad816141a6565b9250505b806137bb816141a6565b9150506136db565b5092915050565b6001600160a01b038281166000818152607360205260409081902060020184905560048054915163541961d760e11b815290810185905260248101929092529091169063a832c3ae90604401600060405180830381600087803b15801561383057600080fd5b505af1158015613844573d6000803e3d6000fd5b5050604080516001600160a01b0386168152602081018590527f617d31491414a4ab2bd831e566a31837fa7fb6582921c91dffbbe83fbca789f3935001905060405180910390a15050565b6000806138c76040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b60408051600481526024810182526020810180516001600160e01b031663c17b291b60e01b179052905191925060009182916001600160a01b0385169161390e919061433f565b6000604051808303816000865af19150503d806000811461394b576040519150601f19603f3d011682016040523d82523d6000602084013e613950565b606091505b5091509150816139a25760405162461bcd60e51b815260206004820181905260248201527f63616c6c2046524155445f50524f4f465f57494e444f572829206661696c6564604482015260640161072d565b60006139ad826143c5565b90506139b98142614175565b94505050505090565b603880546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b03808316600090815260736020526040808220848416835290822060048201549293919290911680613a805760405162461bcd60e51b815260206004820152600e60248201526d4e6f74496e4368616c6c656e676560901b604482015260640161072d565b60048201546001600160a01b03828116911614613ad65760405162461bcd60e51b8152602060048201526014602482015273496e446966666572656e744368616c6c656e676560601b604482015260640161072d565b95945050505050565b60728054906000613aef8361444d565b90915550506001600160a01b039081166000908152607360209081526040808320600381018054825460ff1916835560018301869055600283018690556001600160a01b03198082169092556004909201805482169055941683526074909152902080549091169055565b6119048061446583390190565b508054600082556002029060005260206000209081019061161591905b80821115613ba95780546001600160a01b031916815560006001820155600201613b84565b5090565b600060208284031215613bbf57600080fd5b5035919050565b80604081018310156111c657600080fd5b60008060808385031215613bea57600080fd5b613bf48484613bc6565b9150613c038460408501613bc6565b90509250929050565b6001600160a01b038116811461161557600080fd5b8035613c2c81613c0c565b919050565b600060208284031215613c4357600080fd5b8135613c4e81613c0c565b9392505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715613c8e57613c8e613c55565b60405290565b600067ffffffffffffffff80841115613caf57613caf613c55565b604051601f8501601f19908116603f01168101908282118183101715613cd757613cd7613c55565b81604052809350858152868686011115613cf057600080fd5b858560208301376000602087830101525050509392505050565b600060208284031215613d1c57600080fd5b813567ffffffffffffffff811115613d3357600080fd5b8201601f81018413613d4457600080fd5b613d5384823560208401613c94565b949350505050565b60008083601f840112613d6d57600080fd5b50813567ffffffffffffffff811115613d8557600080fd5b6020830191508360208260051b8501011115613da057600080fd5b9250929050565b600080600080600080600060a0888a031215613dc257600080fd5b8735965060208801359550604088013567ffffffffffffffff80821115613de857600080fd5b613df48b838c01613d5b565b909750955060608a0135945060808a0135915080821115613e1457600080fd5b818a0191508a601f830112613e2857600080fd5b813581811115613e3757600080fd5b8b6020828501011115613e4957600080fd5b60208301945080935050505092959891949750929550565b60008060208385031215613e7457600080fd5b823567ffffffffffffffff811115613e8b57600080fd5b613e9785828601613d5b565b90969095509350505050565b60008060408385031215613eb657600080fd5b823591506020830135613ec881613c0c565b809150509250929050565b60008060408385031215613ee657600080fd5b50508035926020909101359150565b6000806000806000806000806000806000806101408d8f031215613f1857600080fd5b613f218d613c21565b9b50613f2f60208e01613c21565b9a50613f3d60408e01613c21565b9950613f4b60608e01613c21565b9850613f5960808e01613c21565b975060a08d0135965060c08d0135955060e08d0135945067ffffffffffffffff6101008e01351115613f8a57600080fd5b613f9b8e6101008f01358f01613d5b565b909450925067ffffffffffffffff6101208e01351115613fba57600080fd5b613fcb8e6101208f01358f01613d5b565b81935080925050509295989b509295989b509295989b565b600082601f830112613ff457600080fd5b613c4e83833560208501613c94565b60008060006060848603121561401857600080fd5b8335925060208401359150604084013567ffffffffffffffff81111561403d57600080fd5b61404986828701613fe3565b9150509250925092565b60006020828403121561406557600080fd5b813567ffffffffffffffff8082111561407d57600080fd5b9083019060c0828603121561409157600080fd5b614099613c6b565b823581526020830135602082015260408301356040820152606083013560608201526080830135828111156140cd57600080fd5b6140d987828601613fe3565b60808301525060a0830135828111156140f157600080fd5b6140fd87828601613fe3565b60a08301525095945050505050565b6000806040838503121561411f57600080fd5b823561412a81613c0c565b91506020830135613ec881613c0c565b6020808252600b908201526a2737ba27b832b930ba37b960a91b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156141885761418861415f565b500190565b60006020828403121561419f57600080fd5b5051919050565b60006000198214156141ba576141ba61415f565b5060010190565b6000828210156141d3576141d361415f565b500390565b6000602082840312156141ea57600080fd5b81518015158114613c4e57600080fd5b6020808252601b908201527f7472616e7366657220657263323020746f6b656e206661696c65640000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60005b8381101561426257818101518382015260200161424a565b838111156119ba5750506000910152565b6000815180845261428b816020860160208601614247565b601f01601f19169290920160200192915050565b602081526000613c4e6020830184614273565b6000602082840312156142c457600080fd5b8151613c4e81613c0c565b6060808252810185905260006001600160fb1b038611156142ef57600080fd5b8560051b80886080850137602083018690528201828103608090810160408501528101849052838560a0830137600060a0858301015260a0601f19601f8601168201019150509695505050505050565b60008251614351818460208701614247565b9190910192915050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603160045260246000fd5b838152826020820152606060408201526000613ad66060830184614273565b805160208083015191908110156143e6576000198160200360031b1b821691505b50919050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201526000608083015160c060a084015261443060e0840182614273565b905060a0840151601f198483030160c0850152613ad68282614273565b60008161445c5761445c61415f565b50600019019056fe608060405234801561001057600080fd5b506118e4806100206000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80638b299903116100b8578063dfbf53ae1161007c578063dfbf53ae146102a3578063e87e3589146102b6578063ed5b1303146102be578063f03a7fcb146102c7578063f2858aa3146102da578063faeff41b146102ed57600080fd5b80638b2999031461022c5780638f2400a8146102465780639afd9d7814610259578063abf480131461027d578063afeae9651461029057600080fd5b8063631acced116100ff578063631acced146101aa57806370dea79a146101b2578063732e6961146101ba5780637f4c91c5146102115780638a8cd2181461022457600080fd5b806318ef160d1461013c5780632a51f6f71461015157806341e8510c1461016d578063534db0e2146101765780635f41e3d6146101a1575b600080fd5b61014f61014a366004611333565b6102f6565b005b61015a60085481565b6040519081526020015b60405180910390f35b61015a60065481565b600354610189906001600160a01b031681565b6040516001600160a01b039091168152602001610164565b61015a60055481565b61014f6105d0565b61014f610636565b6010546011546012546013546014546015546016546101dc9695949392919087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610164565b600254610189906001600160a01b031681565b6101896106c9565b6007546102399060ff1681565b604051610164919061136b565b61014f610254366004611385565b61075b565b600e5461026d90600160a01b900460ff1681565b6040519015158152602001610164565b61014f61028b3660046113fc565b610b55565b61015a61029e366004611473565b610cae565b600e54610189906001600160a01b031681565b61015a610cc5565b61015a60045481565b61014f6102d53660046114d5565b610d13565b61014f6102e8366004611579565b611018565b61015a600f5481565b6102fe6106c9565b6001600160a01b0316336001600160a01b0316146040518060400160405280600a8152602001692124a9afa9a2a72222a960b11b8152509061035c5760405162461bcd60e51b815260040161035391906115a2565b60405180910390fd5b50610365610cc5565b60045461037290426115f7565b11156040518060400160405280600c81526020016b4249535f444541444c494e4560a01b815250906103b75760405162461bcd60e51b815260040161035391906115a2565b5060085460408051808201909152600f81526e4348414c5f494e49545f535441544560881b602082015290156104005760405162461bcd60e51b815260040161035391906115a2565b50600081116104455760405162461bcd60e51b8152602060048201526011602482015270494e56414c49445f4e554d5f535445505360781b6044820152606401610353565b604080516000602080830191909152818301849052825180830384018152606090920190925280519101206008556040805160e08082018352600c548083526020808401879052600d54848601819052436060808701829052426080808901829052600060a0808b0182905260c09a8b018d9052601089905560118e9055601287905560138690556014849055601582905560168d90558b519889529688018d90529987019490945290850191909152908301528101939093529082018390527f71809f4d4f7bf3c208a85ccd3c922c984024f8e3cef51e3d03ae677e4217097d910160405180910390a1600160075460ff16600281111561054957610549611355565b141561057e5760045461055c90426115f7565b60065461056991906115f7565b6006556007805460ff191660021790556105c8565b600260075460ff16600281111561059757610597611355565b14156105c8576004546105aa90426115f7565b6005546105b791906115f7565b6005556007805460ff191660011790555b505042600455565b600e54600160a01b900460ff16156106215760405162461bcd60e51b8152602060048201526014602482015273414c52454144595f5345545f524f4c4c4241434b60601b6044820152606401610353565b600e805460ff60a01b1916600160a01b179055565b61063e610cc5565b60045461064b90426115f7565b116040518060400160405280601081526020016f54494d454f55545f444541444c494e4560801b815250906106935760405162461bcd60e51b815260040161035391906115a2565b50600260075460ff1660028111156106ad576106ad611355565b14156106bf576106bd60016111f4565b565b6106bd6001611257565b6000600260075460ff1660028111156106e4576106e4611355565b14156106fa57506002546001600160a01b031690565b600160075460ff16600281111561071357610713611355565b141561072957506003546001600160a01b031690565b60405162461bcd60e51b81526020600482015260076024820152662727afaa2aa92760c91b6044820152606401610353565b6107636106c9565b6001600160a01b0316336001600160a01b0316146040518060400160405280600a8152602001692124a9afa9a2a72222a960b11b815250906107b85760405162461bcd60e51b815260040161035391906115a2565b506107c1610cc5565b6004546107ce90426115f7565b11156040518060400160405280600c81526020016b4249535f444541444c494e4560a01b815250906108135760405162461bcd60e51b815260040161035391906115a2565b506008546108555760405162461bcd60e51b815260206004820152600f60248201526e1393d517d253925512505312569151608a1b6044820152606401610353565b604080516020808201859052818301849052825180830384018152606090920190925280519101206008548114604051806040016040528060088152602001672124a9afa82922ab60c11b815250906108c15760405162461bcd60e51b815260040161035391906115a2565b50600a541561091b57600954873514806108dd5750600a548735145b61091b5760405162461bcd60e51b815260206004820152600f60248201526e1053509251d553d554d7d4d5105495608a1b6044820152606401610353565b600b546040880135141561095f5760405162461bcd60e51b815260206004820152600b60248201526a1253959053125117d1539160aa1b6044820152606401610353565b6000841161099b5760405162461bcd60e51b81526020600482015260096024820152681513d3d7d4d213d49560ba1b6044820152606401610353565b8635600955602080880135600a55604080890135600b558051808301889052808201879052815180820383018152606090910190915280519101206008556040805160e081018252883581526020808a0135908201529081018860026020908102919091013582524382820181905242604080850182905260608086018c905260809586018b905286516010558685015160115586820151601255868101516013558686015160145560a08088015160155560c09788015160165582518f3581528f870135968101969096528e8301358684015290850193909352938301528101889052918201869052517f71809f4d4f7bf3c208a85ccd3c922c984024f8e3cef51e3d03ae677e4217097d9181900360e00190a150600160075460ff166002811115610aca57610aca611355565b1415610aff57600454610add90426115f7565b600654610aea91906115f7565b6006556007805460ff19166002179055610b49565b600260075460ff166002811115610b1857610b18611355565b1415610b4957600454610b2b90426115f7565b600554610b3891906115f7565b6005556007805460ff191660011790555b50504260045550505050565b600060075460ff166002811115610b6e57610b6e611355565b146040518060400160405280600f81526020016e4348414c5f494e49545f535441544560881b81525090610bb55760405162461bcd60e51b815260040161035391906115a2565b506001600160a01b03871615801590610bd657506001600160a01b03861615155b8015610bea57506001600160a01b03841615155b610c255760405162461bcd60e51b815260206004820152600c60248201526b5a45524f5f4144445245535360a01b6044820152606401610353565b600280546001600160a01b03199081166001600160a01b03998a16178255600380548216988a1698909817909755600180548816968916969096179095556000805490961693909616929092178455600c829055600d8590556007805460ff19169093179092554260045560966005819055600655600955600a91909155600b91909155600f55565b60098160038110610cbe57600080fd5b0154905081565b6000600260075460ff166002811115610ce057610ce0611355565b1415610ced575060055490565b600160075460ff166002811115610d0657610d06611355565b1415610729575060065490565b610d1b6106c9565b6001600160a01b0316336001600160a01b0316146040518060400160405280600a8152602001692124a9afa9a2a72222a960b11b81525090610d705760405162461bcd60e51b815260040161035391906115a2565b50610d79610cc5565b600454610d8690426115f7565b11156040518060400160405280600c81526020016b4249535f444541444c494e4560a01b81525090610dcb5760405162461bcd60e51b815260040161035391906115a2565b50604080516020808201859052818301849052825180830384018152606090920190925280519101206008548114604051806040016040528060088152602001672124a9afa82922ab60c11b81525090610e385760405162461bcd60e51b815260040161035391906115a2565b506001610e46600284611632565b1115610e8b5760405162461bcd60e51b8152602060048201526014602482015273424953454354494f4e5f494e434f4d504c45544560601b6044820152606401610353565b600180546000916001600160a01b039091169063625eb72e908b908b90600990610eb5908b6115f7565b60038110610ec557610ec561161c565b01548b8b6040518663ffffffff1660e01b8152600401610ee99594939291906117ac565b60206040518083038186803b158015610f0157600080fd5b505afa158015610f15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f39919061185f565b905060098560038110610f4e57610f4e61161c565b0154811415610f6657610f6160006112af565b610f70565b610f706000611302565b506001905060075460ff166002811115610f8c57610f8c611355565b1415610fc157600454610f9f90426115f7565b600654610fac91906115f7565b6006556007805460ff1916600217905561100b565b600260075460ff166002811115610fda57610fda611355565b141561100b57600454610fed90426115f7565b600554610ffa91906115f7565b6005556007805460ff191660011790555b5050426004555050505050565b6002546001600160a01b03166110635760405162461bcd60e51b815260206004820152601060248201526f111959995b99195c881b9bdd081cd95d60821b6044820152606401610353565b6002546001600160a01b031633146110b35760405162461bcd60e51b815260206004820152601360248201527221b0b63632b9103737ba103232b332b73232b960691b6044820152606401610353565b600e546001600160a01b03166111005760405162461bcd60e51b81526020600482015260126024820152712237903737ba103430bb32903bb4b73732b960711b6044820152606401610353565b600354600e546001600160a01b03908116911614156111b257801561118f57600054600354600254604051637d3c01f360e11b81526001600160a01b039283166004820152908216602482015291169063fa7803e6906044015b600060405180830381600087803b15801561117457600080fd5b505af1158015611188573d6000803e3d6000fd5b5050505050565b600254600e80546001600160a01b0319166001600160a01b039092169190911790555b600054600254600354604051637d3c01f360e11b81526001600160a01b039283166004820152908216602482015291169063fa7803e69060440161115a565b50565b600354600e80546001600160a01b0319166001600160a01b039283169081179091556002546040517f03f929a9a6b1f0aef5e43cb12b56f862da97ec3de3fda02a52e85f9f3974fb6a9361124c939216908590611878565b60405180910390a150565b600254600e80546001600160a01b0319166001600160a01b039283169081179091556003546040517f03f929a9a6b1f0aef5e43cb12b56f862da97ec3de3fda02a52e85f9f3974fb6a9361124c939216908590611878565b600260075460ff1660028111156112c8576112c8611355565b14156112d7576111f181611257565b600354600e80546001600160a01b0319166001600160a01b039092169190911790556111f1816111f4565b600260075460ff16600281111561131b5761131b611355565b141561132a576111f1816111f4565b6111f181611257565b6000806040838503121561134657600080fd5b50508035926020909101359150565b634e487b7160e01b600052602160045260246000fd5b602081016003831061137f5761137f611355565b91905290565b600080600080600080610100878903121561139f57600080fd5b60608701888111156113b057600080fd5b969896359750505060808701359460a0880135945060c0880135935060e088013592509050565b6001600160a01b03811681146111f157600080fd5b80356113f7816113d7565b919050565b600080600080600080600060e0888a03121561141757600080fd5b8735611422816113d7565b96506020880135611432816113d7565b95506040880135611442816113d7565b94506060880135611452816113d7565b9699959850939660808101359560a0820135955060c0909101359350915050565b60006020828403121561148557600080fd5b5035919050565b60008083601f84011261149e57600080fd5b50813567ffffffffffffffff8111156114b657600080fd5b6020830191508360208285010111156114ce57600080fd5b9250929050565b600080600080600080600060c0888a0312156114f057600080fd5b873567ffffffffffffffff8082111561150857600080fd5b9089019060e0828c03121561151c57600080fd5b90975060208901359060ff8216821461153457600080fd5b9096506040890135908082111561154a57600080fd5b506115578a828b0161148c565b989b979a50986060810135976080820135975060a09091013595509350505050565b60006020828403121561158b57600080fd5b8135801515811461159b57600080fd5b9392505050565b600060208083528351808285015260005b818110156115cf578581018301518582016040015282016115b3565b818111156115e1576000604083870101525b50601f01601f1916929092016040019392505050565b60008282101561161757634e487b7160e01b600052601160045260246000fd5b500390565b634e487b7160e01b600052603260045260246000fd5b60008261164f57634e487b7160e01b600052601260045260246000fd5b500490565b6000823561011e1983360301811261166b57600080fd5b90910192915050565b803567ffffffffffffffff811681146113f757600080fd5b6000808335601e198436030181126116a357600080fd5b830160208101925035905067ffffffffffffffff8111156116c357600080fd5b8036038313156114ce57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600061012067ffffffffffffffff61171284611674565b1684526020830135602085015261172b60408401611674565b67ffffffffffffffff166040850152611746606084016113ec565b6001600160a01b031660608501526080838101359085015261176b60a084018461168c565b8260a087015261177e83870182846116d2565b9250505060c083013560c085015260e083013560e08501526101008084013581860152508091505092915050565b60808152600086356117bd816113d7565b6001600160a01b039081166080840152602088013560a0840152604088013560c08401526060880135906117f0826113d7565b1660e08301526118036080880188611654565b60e06101008401526118196101608401826116fb565b60a089013561012085015260c089013561014085015260ff88166020850152905085604084015282810360608401526118538185876116d2565b98975050505050505050565b60006020828403121561187157600080fd5b5051919050565b6001600160a01b0384811682528316602082015260608101600283106118a0576118a0611355565b82604083015294935050505056fea2646970667358221220ccb7efc97baac554457c80cecdacd0363693ddc1ffd8120b287494f2e6e7e88b64736f6c63430008090033a2646970667358221220b2320bae11ac531fb45a91ab63f86f0979c9923b7d16f7e7b868965375a21be764736f6c63430008090033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102745760003560e01c80636aabc35011610151578063b553ee84116100c3578063cfc5901d11610087578063cfc5901d14610641578063de574efc14610654578063e03c863214610667578063f2fde38b14610687578063fa7803e61461069a578063fe2ba848146106ad57600080fd5b8063b553ee84146105f7578063b6da898f14610600578063c8525c3e14610613578063c94b584714610626578063c97a59191461062e57600080fd5b80638c669739116101155780638c6697391461052a5780638da5cb5b146105325780639168ae72146105455780639efbea23146105c8578063a56ba93b146105db578063aca9a518146105e457600080fd5b80636aabc350146104c95780636c8b052a146104f257806371129559146104fb5780637acb7757146105045780638821b2ae1461051757600080fd5b806330b26075116101ea578063461a4478116101ae578063461a44781461043957806349cd30041461044c5780634d26732d1461045f57806351ed6a30146104675780636177fd181461047a5780636a368561146104b657600080fd5b806330b26075146103fa57806337d1fbdd146104025780633ccfd60b1461041557806340d9224b1461041d57806345e38b641461043057600080fd5b80632aa234f61161023c5780632aa234f61461036e5780632b7ac3f3146103815780632e17de78146103945780632f06d1b0146103a75780632f30cabd146103ba578063300a7161146103da57600080fd5b80630e456acf14610279578063107035a4146102f05780632052465e146103075780632906040e14610339578063299ca47814610343575b600080fd5b607754607854607954607a54607b546102ac9460ff8116946001600160a01b036101009092048216949082169391169186565b6040805196151587526001600160a01b0395861660208801529385169386019390935292166060840152608083019190915260a082015260c0015b60405180910390f35b6102f960715481565b6040519081526020016102e7565b61031a610315366004613bad565b6106c0565b604080516001600160a01b0390931683526020830191909152016102e7565b6103416106f8565b005b600054610356906001600160a01b031681565b6040516001600160a01b0390911681526020016102e7565b61035661037c366004613bad565b6109a8565b600554610356906001600160a01b031681565b6103416103a2366004613bad565b6109d2565b6103566103b5366004613bd7565b610b52565b6102f96103c8366004613c31565b60756020526000908152604090205481565b6102f96103e8366004613c31565b60396020526000908152604090205481565b6103416111cc565b610356610410366004613bad565b611518565b610341611528565b600454610356906001600160a01b031681565b6102f960015481565b610356610447366004613d0a565b611618565b61034161045a366004613da7565b611699565b6002546102f9565b600354610356906001600160a01b031681565b6104a6610488366004613c31565b6001600160a01b031660009081526073602052604090205460ff1690565b60405190151581526020016102e7565b6103416104c4366004613e61565b6118c6565b6103566104d7366004613c31565b6074602052600090815260409020546001600160a01b031681565b6102f960725481565b6102f960025481565b610341610512366004613ea3565b6119c0565b610341610525366004613bad565b611dac565b610341611f3a565b603854610356906001600160a01b031681565b610592610553366004613c31565b6073602052600090815260409020805460018201546002830154600384015460049094015460ff90931693919290916001600160a01b03908116911685565b6040805195151586526020860194909452928401919091526001600160a01b03908116606084015216608082015260a0016102e7565b6103416105d6366004613e61565b611f7d565b6102f960705481565b6103416105f2366004613e61565b612159565b6102f9606f5481565b61034161060e366004613ed3565b612335565b610341610621366004613e61565b6126f6565b6102f96127ea565b61034161063c366004613ef5565b61286f565b61034161064f366004614003565b612c57565b610341610662366004614053565b612da3565b6102f9610675366004613c31565b603b6020526000908152604090205481565b610341610695366004613c31565b6131f0565b6103416106a836600461410c565b613288565b6103416106bb366004613c31565b6134ca565b607681815481106106d057600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b336000908152607460205260409020546001600160a01b03166107365760405162461bcd60e51b815260040161072d9061413a565b60405180910390fd5b607154606f54106107815760405162461bcd60e51b81526020600482015260156024820152742737aab73932b9b7b63b32b220b9b9b2b93a34b7b760591b604482015260640161072d565b6000607254116107be5760405162461bcd60e51b81526020600482015260086024820152672737a9ba30b5b2b960c11b604482015260640161072d565b6000606f5460016107cf9190614175565b60048054604051638286227560e01b81529293506001600160a01b0316916382862275916108039185910190815260200190565b60206040518083038186803b15801561081b57600080fd5b505afa15801561082f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610853919061418d565b42101561089b5760405162461bcd60e51b81526020600482015260166024820152754368616c6c656e6765506572696f6450656e64696e6760501b604482015260640161072d565b6070546004805460405163030b947760e41b81529182018490526001600160a01b0316906330b947709060240160206040518083038186803b1580156108e057600080fd5b505afa1580156108f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610918919061418d565b146109555760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5914185c995b9d609a1b604482015260640161072d565b606f8054906000610965836141a6565b9091555050606f5460708190556040519081527f453430d123684340024ae0a229704bdab39c93dc48bb5a0b4bc83142d95d48ef9060200160405180910390a150565b603a81815481106109b857600080fd5b6000918252602090912001546001600160a01b0316905081565b6109db3361361b565b33600090815260736020526040902060705460028201541115610a405760405162461bcd60e51b815260206004820152601c60248201527f5374616b65644f6e556e636f6e6669726d6564417373657274696f6e00000000604482015260640161072d565b6002548160010154610a5291906141c1565b821115610a955760405162461bcd60e51b8152602060048201526011602482015270496e73756666696369656e745374616b6560781b604482015260640161072d565b81816001016000828254610aa991906141c1565b909155505060035460405163a9059cbb60e01b8152336004820152602481018490526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b158015610afa57600080fd5b505af1158015610b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3291906141d8565b610b4e5760405162461bcd60e51b815260040161072d906141fa565b5050565b336000908152607460205260408120546001600160a01b0316610b875760405162461bcd60e51b815260040161072d9061413a565b81356020830135808210610bca5760405162461bcd60e51b815260206004820152600a6024820152692bb937b733a7b93232b960b11b604482015260640161072d565b607154811115610c125760405162461bcd60e51b81526020600482015260136024820152722ab7383937b837b9b2b220b9b9b2b93a34b7b760691b604482015260640161072d565b8160705410610c635760405162461bcd60e51b815260206004820152601860248201527f417373657274696f6e416c72656164795265736f6c7665640000000000000000604482015260640161072d565b6004805460405163030b947760e41b81529182018490526000916001600160a01b03909116906330b947709060240160206040518083038186803b158015610caa57600080fd5b505afa158015610cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce2919061418d565b6004805460405163030b947760e41b81529293506001600160a01b0316916330b9477091610d169186910190815260200190565b60206040518083038186803b158015610d2e57600080fd5b505afa158015610d42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d66919061418d565b8114610da65760405162461bcd60e51b815260206004820152600f60248201526e111a5999995c995b9d14185c995b9d608a1b604482015260640161072d565b6000610db56020880188613c31565b90506000610dc96040890160208a01613c31565b9050806001600160a01b0316826001600160a01b03161415610e3b5760405162461bcd60e51b815260206004820152602560248201527f646566656e64657220616e64206368616c6c656e6765206d757374206e6f7420604482015264195c5d585b60da1b606482015260840161072d565b6001600160a01b038083166000908152607460205260408082205484841683529120549082169116610e6c8261366f565b610e758161366f565b6000604051610e8390613b5a565b604051809103906000f080158015610e9f573d6000803e3d6000fd5b506001600160a01b038084166000908152607360209081526040808320600490810180548688166001600160a01b031991821681179092558a871686528386209092018054831682179055825160c0810184529485529284018390528a8516848301819052948a1660608501819052608085018f905260a09094018d905260778054610100949094026001600160a81b031990941693909317909255607880548316909417909355607980549091169091179055607a8a9055607b8990555190915081907fd0ebe74b4f7d89a9b0fdc9d95f887a7b925c6c7300b5c4b2c3304d97925840fa90610fa4908b9084909182526001600160a01b0316602082015260400190565b60405180910390a160048054604051632b27e93b60e01b81529182018990526000916001600160a01b0390911690632b27e93b9060240160206040518083038186803b158015610ff357600080fd5b505afa158015611007573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102b919061418d565b60048054604051632a411f3360e11b81529182018b90529192506000916001600160a01b0316906354823e669060240160206040518083038186803b15801561107357600080fd5b505afa158015611087573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ab919061418d565b60048054604051632a411f3360e11b81529182018e90529192506000916001600160a01b0316906354823e669060240160206040518083038186803b1580156110f357600080fd5b505afa158015611107573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112b919061418d565b60055460405163abf4801360e01b81526001600160a01b038c811660048301528b8116602483015291821660448201523060648201526084810186905260a4810185905260c4810183905291925086169063abf480139060e401600060405180830381600087803b15801561119f57600080fd5b505af11580156111b3573d6000803e3d6000fd5b50959e5050505050505050505050505050505b92915050565b336000908152607460205260409020546001600160a01b03166112015760405162461bcd60e51b815260040161072d9061413a565b607154606f541061124c5760405162461bcd60e51b81526020600482015260156024820152742737aab73932b9b7b63b32b220b9b9b2b93a34b7b760591b604482015260640161072d565b6000606f54600161125d9190614175565b6070546004805460405163030b947760e41b815291820184905292935090916001600160a01b0316906330b947709060240160206040518083038186803b1580156112a757600080fd5b505afa1580156112bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112df919061418d565b14156114685760048054604051638286227560e01b81529182018390526001600160a01b03169063828622759060240160206040518083038186803b15801561132757600080fd5b505afa15801561133b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135f919061418d565b4210156113a75760405162461bcd60e51b81526020600482015260166024820152754368616c6c656e6765506572696f6450656e64696e6760501b604482015260640161072d565b6113b0816136d6565b6004805460405163366b2b6960e01b81529182018490526001600160a01b03169063366b2b699060240160206040518083038186803b1580156113f257600080fd5b505afa158015611406573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142a919061418d565b146114685760405162461bcd60e51b815260206004820152600e60248201526d14dd185ad95c9cd41c995cd95b9d60921b604482015260640161072d565b606f8054906000611478836141a6565b91905055507f5b24ab8ceb442373727ac5c559a027521cb52db451c74710ebed9faa5fe15a7c606f546040516114b091815260200190565b60405180910390a160048054606f5460405163d8a4e5af60e01b8152928301526001600160a01b03169063d8a4e5af90602401600060405180830381600087803b1580156114fd57600080fd5b505af1158015611511573d6000803e3d6000fd5b5050505050565b603c81815481106109b857600080fd5b336000908152607460205260409020546001600160a01b031661155d5760405162461bcd60e51b815260040161072d9061413a565b336000818152607560205260408082208054929055600354905163a9059cbb60e01b815260048101939093526024830182905290916001600160a01b039091169063a9059cbb90604401602060405180830381600087803b1580156115c157600080fd5b505af11580156115d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f991906141d8565b6116155760405162461bcd60e51b815260040161072d906141fa565b50565b6000805460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061164990859060040161429f565b60206040518083038186803b15801561166157600080fd5b505afa158015611675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c691906142b2565b336000908152607460205260409020546001600160a01b03166116ce5760405162461bcd60e51b815260040161072d9061413a565b6116fb6040518060400160405280600c81526020016b212b26afa937b6363ab832b960a11b815250611618565b6001600160a01b0316336001600160a01b0316146117795760405162461bcd60e51b815260206004820152603560248201527f6d73672e73656e646572206973206e6f7420726f6c6c75702070726f706f73656044820152740e45840c6c2dc4ee840c2e0e0cadcc840c4c2e8c6d605b1b606482015260840161072d565b6117838787612335565b60006117ba6040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b90506000816001600160a01b031687878787876040516024016117e19594939291906142cf565b60408051601f198184030181529181526020820180516001600160e01b0316632169f79f60e01b17905251611816919061433f565b6000604051808303816000865af19150503d8060008114611853576040519150601f19603f3d011682016040523d82523d6000602084013e611858565b606091505b50509050806118bb5760405162461bcd60e51b815260206004820152602960248201527f73636320617070656e64207374617465206261746368206661696c65642c2072604482015268195d995c9d08185b1b60ba1b606482015260840161072d565b505050505050505050565b6038546001600160a01b031633146118f05760405162461bcd60e51b815260040161072d9061435b565b603a5460005b828110156119ba576119088183614175565b6039600086868581811061191e5761191e614231565b90506020020160208101906119339190613c31565b6001600160a01b03168152602081019190915260400160002055603a84848381811061196157611961614231565b90506020020160208101906119769190613c31565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b03909216919091179055806119b2816141a6565b9150506118f6565b50505050565b33600081815260396020526040902054603a805483929081106119e5576119e5614231565b6000918252602090912001546001600160a01b031614611a475760405162461bcd60e51b815260206004820152601760248201527f4e4f545f494e5f5354414b45525f57484954454c495354000000000000000000604482015260640161072d565b6001600160a01b0382166000818152603b6020526040902054603c8054859392908110611a7657611a76614231565b6000918252602090912001546001600160a01b031614611ad85760405162461bcd60e51b815260206004820152601960248201527f4e4f545f494e5f4f50455241544f525f57484954454c49535400000000000000604482015260640161072d565b6003546040516323b872dd60e01b8152336004820152306024820152604481018690526001600160a01b03909116906323b872dd90606401602060405180830381600087803b158015611b2a57600080fd5b505af1158015611b3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6291906141d8565b611b7e5760405162461bcd60e51b815260040161072d906141fa565b3360009081526073602052604090205460ff1615611c3e57336000908152607360205260409020600301546001600160a01b03848116911614611c115760405162461bcd60e51b815260206004820152602560248201527f7374616b6572203d3e206f70657261746f72206d617070696e67206e6f7420756044820152646e6971756560d81b606482015260840161072d565b3360009081526073602052604081206001018054869290611c33908490614175565b909155506119ba9050565b6001600160a01b038381166000908152607460205260409020541615611c9d5760405162461bcd60e51b81526020600482015260146024820152731bdc195c985d1bdc881a5cc81bd8d8dd5c1a595960621b604482015260640161072d565b600254841015611ce35760405162461bcd60e51b8152602060048201526011602482015270496e73756666696369656e745374616b6560781b604482015260640161072d565b6040805160a0810182526001808252602080830188815260008486018181526001600160a01b03808b16606088018181526080890185815233808752607389528b87209a518b5490151560ff19909116178b559651988a01989098559251600289015591516003880180549183166001600160a01b03199283161790559551600490970180549790911696861696909617909555938452607490915292822080549091169092179091556072805491611d9b836141a6565b91905055506119ba336070546137ca565b336000908152607460205260409020546001600160a01b0316611de15760405162461bcd60e51b815260040161072d9061413a565b336000908152607460209081526040808320546001600160a01b03168084526073909252909120600281015483111580611e1c575060715483115b15611e5f5760405162461bcd60e51b8152602060048201526013602482015272417373657274696f6e4f75744f6652616e676560681b604482015260640161072d565b6004805460405163030b947760e41b81529182018590526001600160a01b0316906330b947709060240160206040518083038186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed9919061418d565b816002015414611f2b5760405162461bcd60e51b815260206004820152601760248201527f506172656e74417373657274696f6e556e7374616b6564000000000000000000604482015260640161072d565b611f3582846137ca565b505050565b336000908152607460205260409020546001600160a01b0316611f6f5760405162461bcd60e51b815260040161072d9061413a565b611f7b60766000613b67565b565b6038546001600160a01b03163314611fa75760405162461bcd60e51b815260040161072d9061435b565b60005b81811015611f35576000603b6000858585818110611fca57611fca614231565b9050602002016020810190611fdf9190613c31565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905080603b6000603c6001603c8054905061201b91906141c1565b8154811061202b5761202b614231565b60009182526020808320909101546001600160a01b03168352820192909252604001902055603c8054612060906001906141c1565b8154811061207057612070614231565b600091825260209091200154603c80546001600160a01b03909216918390811061209c5761209c614231565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550603c8054806120db576120db614390565b600082815260208120820160001990810180546001600160a01b0319169055909101909155603b9085858581811061211557612115614231565b905060200201602081019061212a9190613c31565b6001600160a01b0316815260208101919091526040016000908120555080612151816141a6565b915050611faa565b6038546001600160a01b031633146121835760405162461bcd60e51b815260040161072d9061435b565b60005b81811015611f35576000603960008585858181106121a6576121a6614231565b90506020020160208101906121bb9190613c31565b6001600160a01b03166001600160a01b031681526020019081526020016000205490508060396000603a6001603a805490506121f791906141c1565b8154811061220757612207614231565b60009182526020808320909101546001600160a01b03168352820192909252604001902055603a805461223c906001906141c1565b8154811061224c5761224c614231565b600091825260209091200154603a80546001600160a01b03909216918390811061227857612278614231565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550603a8054806122b7576122b7614390565b600082815260208120820160001990810180546001600160a01b03191690559091019091556039908585858181106122f1576122f1614231565b90506020020160208101906123069190613c31565b6001600160a01b031681526020810191909152604001600090812055508061232d816141a6565b915050612186565b336000908152607460205260409020546001600160a01b031661236a5760405162461bcd60e51b815260040161072d9061413a565b336000908152607460209081526040808320546001600160a01b039081168085526073909352922060040154909116156124005760405162461bcd60e51b815260206004820152603160248201527f63616e206e6f742063726561746520617373657274696f6e207768656e207374604482015270616b657220696e206368616c6c656e676560781b606482015260840161072d565b6001600160a01b038181166000908152607360205260409081902060020154600154600480549351634e04886d60e01b8152908101839052919390921690634e04886d9060240160206040518083038186803b15801561245f57600080fd5b505afa158015612473573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612497919061418d565b6124a190436141c1565b10156124ef5760405162461bcd60e51b815260206004820152601f60248201527f4d696e696d756d417373657274696f6e506572696f644e6f7450617373656400604482015260640161072d565b60048054604051632b27e93b60e01b81529182018390526001600160a01b031690632b27e93b9060240160206040518083038186803b15801561253157600080fd5b505afa158015612545573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612569919061418d565b83116125a85760405162461bcd60e51b815260206004820152600e60248201526d22b6b83a3ca0b9b9b2b93a34b7b760911b604482015260640161072d565b607180549060006125b8836141a6565b9091555050607154604080519182523360208301528101859052606081018490527f5c610f28399ecc14b66149012a0197a5e3257a8c397125afee95d1cf4b9507349060800160405180910390a16004546071546001600160a01b039091169063422815849086868561262961388f565b6040516001600160e01b031960e088901b1681526004810195909552602485019390935260448401919091526064830152608482015260a401600060405180830381600087803b15801561267c57600080fd5b505af1158015612690573d6000803e3d6000fd5b505050506126a0826071546137ca565b606f80549060006126b0836141a6565b9091555050606f5460708190556040519081527f453430d123684340024ae0a229704bdab39c93dc48bb5a0b4bc83142d95d48ef9060200160405180910390a150505050565b6038546001600160a01b031633146127205760405162461bcd60e51b815260040161072d9061435b565b603c5460005b828110156119ba576127388183614175565b603b600086868581811061274e5761274e614231565b90506020020160208101906127639190613c31565b6001600160a01b03168152602081019190915260400160002055603c84848381811061279157612791614231565b90506020020160208101906127a69190613c31565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b03909216919091179055806127e2816141a6565b915050612726565b60048054607054604051632b27e93b60e01b8152928301526000916001600160a01b0390911690632b27e93b9060240160206040518083038186803b15801561283257600080fd5b505afa158015612846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286a919061418d565b905090565b600054600160a81b900460ff161580801561289757506000546001600160a01b90910460ff16105b806128b85750303b1580156128b85750600054600160a01b900460ff166001145b61291b5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161072d565b6000805460ff60a01b1916600160a01b1790558015612948576000805460ff60a81b1916600160a81b1790555b6001600160a01b038d16158061296557506001600160a01b038c16155b156129a05760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b604482015260640161072d565b8c603860006101000a8154816001600160a01b0302191690836001600160a01b031602179055508a600360006101000a8154816001600160a01b0302191690836001600160a01b031602179055508b600560006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060006001600160a01b031660008054906101000a90046001600160a01b03166001600160a01b031614612a815760405162461bcd60e51b81526020600482015260146024820152731499591d5b99185b9d125b9a5d1a585b1a5e995960621b604482015260640161072d565b600080546001600160a01b0319166001600160a01b038c8116919091179091556004541615612ae95760405162461bcd60e51b81526020600482015260146024820152731499591d5b99185b9d125b9a5d1a585b1a5e995960621b604482015260640161072d565b600480546001600160a01b0319166001600160a01b038b16908117825560018a905560028990556040516303b31b8360e11b8152309281019290925290630766370690602401600060405180830381600087803b158015612b4957600080fd5b505af1158015612b5d573d6000803e3d6000fd5b50506000606f819055607081905560718190556004805460405163108a056160e21b8152918201839052602482018b90526044820183905260648201929092524360848201526001600160a01b0390911692506342281584915060a401600060405180830381600087803b158015612bd457600080fd5b505af1158015612be8573d6000803e3d6000fd5b50505050612bf685856118c6565b612c0083836126f6565b8015612c48576000805460ff60a81b19169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050505050505050565b6038546001600160a01b03163314612c815760405162461bcd60e51b815260040161072d9061435b565b6000612cb86040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b90506000816001600160a01b0316858585604051602401612cdb939291906143a6565b60408051601f198184030181529181526020820180516001600160e01b03166305f9daf960e11b17905251612d10919061433f565b6000604051808303816000865af19150503d8060008114612d4d576040519150601f19603f3d011682016040523d82523d6000602084013e612d52565b606091505b50509050806115115760405162461bcd60e51b815260206004820152601b60248201527f63616c6c20726f6c6c4261636b4c32436861696e206661696c65640000000000604482015260640161072d565b6038546001600160a01b03163314612dcd5760405162461bcd60e51b815260040161072d9061435b565b6000612e046040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b60408051600481526024810182526020810180516001600160e01b0316633958777760e21b17905290519192506000916001600160a01b03841691612e489161433f565b6000604051808303816000865af19150503d8060008114612e85576040519150601f19603f3d011682016040523d82523d6000602084013e612e8a565b606091505b50915050600081612e9a906143c5565b8451909150612ea990826141c1565b600114612f045760405162461bcd60e51b8152602060048201526024808201527f64656c657465206261746368207769746820676170206973206e6f7420616c6c6044820152631bddd95960e21b606482015260840161072d565b6000836001600160a01b031685604051602401612f2191906143ec565b60408051601f198184030181529181526020820180516001600160e01b0316632da6c87160e11b17905251612f56919061433f565b6000604051808303816000865af19150503d8060008114612f93576040519150601f19603f3d011682016040523d82523d6000602084013e612f98565b606091505b5050905080612ffb5760405162461bcd60e51b815260206004820152602960248201527f7363632064656c657465207374617465206261746368206661696c65642c2072604482015268195d995c9d08185b1b60ba1b606482015260840161072d565b606f5460715410156130665760405162461bcd60e51b815260206004820152602e60248201527f64656c65746520617373657274696f6e206265666f7265206c6173742072657360448201526d37b63b32b21034b71032b93937b960911b606482015260840161072d565b7f5b24ab8ceb442373727ac5c559a027521cb52db451c74710ebed9faa5fe15a7c60715460405161309991815260200190565b60405180910390a16004805460715460405163d0087d6160e01b8152928301526001600160a01b03169063d0087d6190602401600060405180830381600087803b1580156130e657600080fd5b505af11580156130fa573d6000803e3d6000fd5b50506071805492509050600061310f8361444d565b9091555050606f80549060006131248361444d565b9091555050607080549060006131398361444d565b919050555060005b603a548110156131e85760715460736000603a848154811061316557613165614231565b60009182526020808320909101546001600160a01b0316835282019290925260400190206002015411156131d65760715460736000603a84815481106131ad576131ad614231565b60009182526020808320909101546001600160a01b031683528201929092526040019020600201555b806131e0816141a6565b915050613141565b505050505050565b6038546001600160a01b0316331461321a5760405162461bcd60e51b815260040161072d9061435b565b6001600160a01b03811661327f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161072d565b611615816139c2565b336000908152607460205260409020546001600160a01b03166132bd5760405162461bcd60e51b815260040161072d9061413a565b6001600160a01b0380831660009081526074602052604080822054848416835291205490821691166132ee8161361b565b60006132fa8383613a14565b9050336001600160a01b038216146133435760405162461bcd60e51b815260206004820152600c60248201526b4e6f744368616c6c656e676560a01b604482015260640161072d565b6001600160a01b0382166000908152607360205260408120600101546002548111156133ad5760025461337690826141c1565b6001600160a01b0385166000908152607560205260408120805490919061339e908490614175565b909155505060025491506133b1565b8091505b6001600160a01b038516600090815260736020526040812060010180548492906133dc908490614175565b90915550506001600160a01b0380861660009081526073602052604080822060040180546001600160a01b03191690559186168152206002015461341f85613adf565b604080518082019091526001600160a01b03958616815260208101918252607680546001808201835560009290925291517fb5732705f5241370a28908c2fe1303cb223f03b90d857fd0573f003f79fefed4600290930292830180546001600160a01b031916919098161790965590517fb5732705f5241370a28908c2fe1303cb223f03b90d857fd0573f003f79fefed59091015550506077805460ff191690921790915550505050565b6038546001600160a01b031633146134f45760405162461bcd60e51b815260040161072d9061435b565b6134fd8161361b565b6001600160a01b03811660009081526073602052604090206070546002820154111561356b5760405162461bcd60e51b815260206004820152601c60248201527f5374616b65644f6e556e636f6e6669726d6564417373657274696f6e00000000604482015260640161072d565b600181015461357983613adf565b60035460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018490529091169063a9059cbb90604401602060405180830381600087803b1580156135c757600080fd5b505af11580156135db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ff91906141d8565b611f355760405162461bcd60e51b815260040161072d906141fa565b6001600160a01b03811660009081526073602052604090205460ff166116155760405162461bcd60e51b8152602060048201526009602482015268139bdd14dd185ad95960ba1b604482015260640161072d565b6136788161361b565b6001600160a01b0381811660009081526073602052604090206004015416156116155760405162461bcd60e51b815260206004820152601060248201526f21b430b63632b733b2b229ba30b5b2b960811b604482015260640161072d565b600080805b6076548110156137c357600454607680546001600160a01b039092169163873fd0899187918590811061371057613710614231565b600091825260209091206002909102015460405160e084901b6001600160e01b031916815260048101929092526001600160a01b0316602482015260440160206040518083038186803b15801561376657600080fd5b505afa15801561377a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379e91906141d8565b156137b157816137ad816141a6565b9250505b806137bb816141a6565b9150506136db565b5092915050565b6001600160a01b038281166000818152607360205260409081902060020184905560048054915163541961d760e11b815290810185905260248101929092529091169063a832c3ae90604401600060405180830381600087803b15801561383057600080fd5b505af1158015613844573d6000803e3d6000fd5b5050604080516001600160a01b0386168152602081018590527f617d31491414a4ab2bd831e566a31837fa7fb6582921c91dffbbe83fbca789f3935001905060405180910390a15050565b6000806138c76040518060400160405280601481526020017329ba30ba32a1b7b6b6b4ba36b2b73a21b430b4b760611b815250611618565b60408051600481526024810182526020810180516001600160e01b031663c17b291b60e01b179052905191925060009182916001600160a01b0385169161390e919061433f565b6000604051808303816000865af19150503d806000811461394b576040519150601f19603f3d011682016040523d82523d6000602084013e613950565b606091505b5091509150816139a25760405162461bcd60e51b815260206004820181905260248201527f63616c6c2046524155445f50524f4f465f57494e444f572829206661696c6564604482015260640161072d565b60006139ad826143c5565b90506139b98142614175565b94505050505090565b603880546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b03808316600090815260736020526040808220848416835290822060048201549293919290911680613a805760405162461bcd60e51b815260206004820152600e60248201526d4e6f74496e4368616c6c656e676560901b604482015260640161072d565b60048201546001600160a01b03828116911614613ad65760405162461bcd60e51b8152602060048201526014602482015273496e446966666572656e744368616c6c656e676560601b604482015260640161072d565b95945050505050565b60728054906000613aef8361444d565b90915550506001600160a01b039081166000908152607360209081526040808320600381018054825460ff1916835560018301869055600283018690556001600160a01b03198082169092556004909201805482169055941683526074909152902080549091169055565b6119048061446583390190565b508054600082556002029060005260206000209081019061161591905b80821115613ba95780546001600160a01b031916815560006001820155600201613b84565b5090565b600060208284031215613bbf57600080fd5b5035919050565b80604081018310156111c657600080fd5b60008060808385031215613bea57600080fd5b613bf48484613bc6565b9150613c038460408501613bc6565b90509250929050565b6001600160a01b038116811461161557600080fd5b8035613c2c81613c0c565b919050565b600060208284031215613c4357600080fd5b8135613c4e81613c0c565b9392505050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715613c8e57613c8e613c55565b60405290565b600067ffffffffffffffff80841115613caf57613caf613c55565b604051601f8501601f19908116603f01168101908282118183101715613cd757613cd7613c55565b81604052809350858152868686011115613cf057600080fd5b858560208301376000602087830101525050509392505050565b600060208284031215613d1c57600080fd5b813567ffffffffffffffff811115613d3357600080fd5b8201601f81018413613d4457600080fd5b613d5384823560208401613c94565b949350505050565b60008083601f840112613d6d57600080fd5b50813567ffffffffffffffff811115613d8557600080fd5b6020830191508360208260051b8501011115613da057600080fd5b9250929050565b600080600080600080600060a0888a031215613dc257600080fd5b8735965060208801359550604088013567ffffffffffffffff80821115613de857600080fd5b613df48b838c01613d5b565b909750955060608a0135945060808a0135915080821115613e1457600080fd5b818a0191508a601f830112613e2857600080fd5b813581811115613e3757600080fd5b8b6020828501011115613e4957600080fd5b60208301945080935050505092959891949750929550565b60008060208385031215613e7457600080fd5b823567ffffffffffffffff811115613e8b57600080fd5b613e9785828601613d5b565b90969095509350505050565b60008060408385031215613eb657600080fd5b823591506020830135613ec881613c0c565b809150509250929050565b60008060408385031215613ee657600080fd5b50508035926020909101359150565b6000806000806000806000806000806000806101408d8f031215613f1857600080fd5b613f218d613c21565b9b50613f2f60208e01613c21565b9a50613f3d60408e01613c21565b9950613f4b60608e01613c21565b9850613f5960808e01613c21565b975060a08d0135965060c08d0135955060e08d0135945067ffffffffffffffff6101008e01351115613f8a57600080fd5b613f9b8e6101008f01358f01613d5b565b909450925067ffffffffffffffff6101208e01351115613fba57600080fd5b613fcb8e6101208f01358f01613d5b565b81935080925050509295989b509295989b509295989b565b600082601f830112613ff457600080fd5b613c4e83833560208501613c94565b60008060006060848603121561401857600080fd5b8335925060208401359150604084013567ffffffffffffffff81111561403d57600080fd5b61404986828701613fe3565b9150509250925092565b60006020828403121561406557600080fd5b813567ffffffffffffffff8082111561407d57600080fd5b9083019060c0828603121561409157600080fd5b614099613c6b565b823581526020830135602082015260408301356040820152606083013560608201526080830135828111156140cd57600080fd5b6140d987828601613fe3565b60808301525060a0830135828111156140f157600080fd5b6140fd87828601613fe3565b60a08301525095945050505050565b6000806040838503121561411f57600080fd5b823561412a81613c0c565b91506020830135613ec881613c0c565b6020808252600b908201526a2737ba27b832b930ba37b960a91b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156141885761418861415f565b500190565b60006020828403121561419f57600080fd5b5051919050565b60006000198214156141ba576141ba61415f565b5060010190565b6000828210156141d3576141d361415f565b500390565b6000602082840312156141ea57600080fd5b81518015158114613c4e57600080fd5b6020808252601b908201527f7472616e7366657220657263323020746f6b656e206661696c65640000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60005b8381101561426257818101518382015260200161424a565b838111156119ba5750506000910152565b6000815180845261428b816020860160208601614247565b601f01601f19169290920160200192915050565b602081526000613c4e6020830184614273565b6000602082840312156142c457600080fd5b8151613c4e81613c0c565b6060808252810185905260006001600160fb1b038611156142ef57600080fd5b8560051b80886080850137602083018690528201828103608090810160408501528101849052838560a0830137600060a0858301015260a0601f19601f8601168201019150509695505050505050565b60008251614351818460208701614247565b9190910192915050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603160045260246000fd5b838152826020820152606060408201526000613ad66060830184614273565b805160208083015191908110156143e6576000198160200360031b1b821691505b50919050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201526000608083015160c060a084015261443060e0840182614273565b905060a0840151601f198483030160c0850152613ad68282614273565b60008161445c5761445c61415f565b50600019019056fe608060405234801561001057600080fd5b506118e4806100206000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80638b299903116100b8578063dfbf53ae1161007c578063dfbf53ae146102a3578063e87e3589146102b6578063ed5b1303146102be578063f03a7fcb146102c7578063f2858aa3146102da578063faeff41b146102ed57600080fd5b80638b2999031461022c5780638f2400a8146102465780639afd9d7814610259578063abf480131461027d578063afeae9651461029057600080fd5b8063631acced116100ff578063631acced146101aa57806370dea79a146101b2578063732e6961146101ba5780637f4c91c5146102115780638a8cd2181461022457600080fd5b806318ef160d1461013c5780632a51f6f71461015157806341e8510c1461016d578063534db0e2146101765780635f41e3d6146101a1575b600080fd5b61014f61014a366004611333565b6102f6565b005b61015a60085481565b6040519081526020015b60405180910390f35b61015a60065481565b600354610189906001600160a01b031681565b6040516001600160a01b039091168152602001610164565b61015a60055481565b61014f6105d0565b61014f610636565b6010546011546012546013546014546015546016546101dc9695949392919087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610164565b600254610189906001600160a01b031681565b6101896106c9565b6007546102399060ff1681565b604051610164919061136b565b61014f610254366004611385565b61075b565b600e5461026d90600160a01b900460ff1681565b6040519015158152602001610164565b61014f61028b3660046113fc565b610b55565b61015a61029e366004611473565b610cae565b600e54610189906001600160a01b031681565b61015a610cc5565b61015a60045481565b61014f6102d53660046114d5565b610d13565b61014f6102e8366004611579565b611018565b61015a600f5481565b6102fe6106c9565b6001600160a01b0316336001600160a01b0316146040518060400160405280600a8152602001692124a9afa9a2a72222a960b11b8152509061035c5760405162461bcd60e51b815260040161035391906115a2565b60405180910390fd5b50610365610cc5565b60045461037290426115f7565b11156040518060400160405280600c81526020016b4249535f444541444c494e4560a01b815250906103b75760405162461bcd60e51b815260040161035391906115a2565b5060085460408051808201909152600f81526e4348414c5f494e49545f535441544560881b602082015290156104005760405162461bcd60e51b815260040161035391906115a2565b50600081116104455760405162461bcd60e51b8152602060048201526011602482015270494e56414c49445f4e554d5f535445505360781b6044820152606401610353565b604080516000602080830191909152818301849052825180830384018152606090920190925280519101206008556040805160e08082018352600c548083526020808401879052600d54848601819052436060808701829052426080808901829052600060a0808b0182905260c09a8b018d9052601089905560118e9055601287905560138690556014849055601582905560168d90558b519889529688018d90529987019490945290850191909152908301528101939093529082018390527f71809f4d4f7bf3c208a85ccd3c922c984024f8e3cef51e3d03ae677e4217097d910160405180910390a1600160075460ff16600281111561054957610549611355565b141561057e5760045461055c90426115f7565b60065461056991906115f7565b6006556007805460ff191660021790556105c8565b600260075460ff16600281111561059757610597611355565b14156105c8576004546105aa90426115f7565b6005546105b791906115f7565b6005556007805460ff191660011790555b505042600455565b600e54600160a01b900460ff16156106215760405162461bcd60e51b8152602060048201526014602482015273414c52454144595f5345545f524f4c4c4241434b60601b6044820152606401610353565b600e805460ff60a01b1916600160a01b179055565b61063e610cc5565b60045461064b90426115f7565b116040518060400160405280601081526020016f54494d454f55545f444541444c494e4560801b815250906106935760405162461bcd60e51b815260040161035391906115a2565b50600260075460ff1660028111156106ad576106ad611355565b14156106bf576106bd60016111f4565b565b6106bd6001611257565b6000600260075460ff1660028111156106e4576106e4611355565b14156106fa57506002546001600160a01b031690565b600160075460ff16600281111561071357610713611355565b141561072957506003546001600160a01b031690565b60405162461bcd60e51b81526020600482015260076024820152662727afaa2aa92760c91b6044820152606401610353565b6107636106c9565b6001600160a01b0316336001600160a01b0316146040518060400160405280600a8152602001692124a9afa9a2a72222a960b11b815250906107b85760405162461bcd60e51b815260040161035391906115a2565b506107c1610cc5565b6004546107ce90426115f7565b11156040518060400160405280600c81526020016b4249535f444541444c494e4560a01b815250906108135760405162461bcd60e51b815260040161035391906115a2565b506008546108555760405162461bcd60e51b815260206004820152600f60248201526e1393d517d253925512505312569151608a1b6044820152606401610353565b604080516020808201859052818301849052825180830384018152606090920190925280519101206008548114604051806040016040528060088152602001672124a9afa82922ab60c11b815250906108c15760405162461bcd60e51b815260040161035391906115a2565b50600a541561091b57600954873514806108dd5750600a548735145b61091b5760405162461bcd60e51b815260206004820152600f60248201526e1053509251d553d554d7d4d5105495608a1b6044820152606401610353565b600b546040880135141561095f5760405162461bcd60e51b815260206004820152600b60248201526a1253959053125117d1539160aa1b6044820152606401610353565b6000841161099b5760405162461bcd60e51b81526020600482015260096024820152681513d3d7d4d213d49560ba1b6044820152606401610353565b8635600955602080880135600a55604080890135600b558051808301889052808201879052815180820383018152606090910190915280519101206008556040805160e081018252883581526020808a0135908201529081018860026020908102919091013582524382820181905242604080850182905260608086018c905260809586018b905286516010558685015160115586820151601255868101516013558686015160145560a08088015160155560c09788015160165582518f3581528f870135968101969096528e8301358684015290850193909352938301528101889052918201869052517f71809f4d4f7bf3c208a85ccd3c922c984024f8e3cef51e3d03ae677e4217097d9181900360e00190a150600160075460ff166002811115610aca57610aca611355565b1415610aff57600454610add90426115f7565b600654610aea91906115f7565b6006556007805460ff19166002179055610b49565b600260075460ff166002811115610b1857610b18611355565b1415610b4957600454610b2b90426115f7565b600554610b3891906115f7565b6005556007805460ff191660011790555b50504260045550505050565b600060075460ff166002811115610b6e57610b6e611355565b146040518060400160405280600f81526020016e4348414c5f494e49545f535441544560881b81525090610bb55760405162461bcd60e51b815260040161035391906115a2565b506001600160a01b03871615801590610bd657506001600160a01b03861615155b8015610bea57506001600160a01b03841615155b610c255760405162461bcd60e51b815260206004820152600c60248201526b5a45524f5f4144445245535360a01b6044820152606401610353565b600280546001600160a01b03199081166001600160a01b03998a16178255600380548216988a1698909817909755600180548816968916969096179095556000805490961693909616929092178455600c829055600d8590556007805460ff19169093179092554260045560966005819055600655600955600a91909155600b91909155600f55565b60098160038110610cbe57600080fd5b0154905081565b6000600260075460ff166002811115610ce057610ce0611355565b1415610ced575060055490565b600160075460ff166002811115610d0657610d06611355565b1415610729575060065490565b610d1b6106c9565b6001600160a01b0316336001600160a01b0316146040518060400160405280600a8152602001692124a9afa9a2a72222a960b11b81525090610d705760405162461bcd60e51b815260040161035391906115a2565b50610d79610cc5565b600454610d8690426115f7565b11156040518060400160405280600c81526020016b4249535f444541444c494e4560a01b81525090610dcb5760405162461bcd60e51b815260040161035391906115a2565b50604080516020808201859052818301849052825180830384018152606090920190925280519101206008548114604051806040016040528060088152602001672124a9afa82922ab60c11b81525090610e385760405162461bcd60e51b815260040161035391906115a2565b506001610e46600284611632565b1115610e8b5760405162461bcd60e51b8152602060048201526014602482015273424953454354494f4e5f494e434f4d504c45544560601b6044820152606401610353565b600180546000916001600160a01b039091169063625eb72e908b908b90600990610eb5908b6115f7565b60038110610ec557610ec561161c565b01548b8b6040518663ffffffff1660e01b8152600401610ee99594939291906117ac565b60206040518083038186803b158015610f0157600080fd5b505afa158015610f15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f39919061185f565b905060098560038110610f4e57610f4e61161c565b0154811415610f6657610f6160006112af565b610f70565b610f706000611302565b506001905060075460ff166002811115610f8c57610f8c611355565b1415610fc157600454610f9f90426115f7565b600654610fac91906115f7565b6006556007805460ff1916600217905561100b565b600260075460ff166002811115610fda57610fda611355565b141561100b57600454610fed90426115f7565b600554610ffa91906115f7565b6005556007805460ff191660011790555b5050426004555050505050565b6002546001600160a01b03166110635760405162461bcd60e51b815260206004820152601060248201526f111959995b99195c881b9bdd081cd95d60821b6044820152606401610353565b6002546001600160a01b031633146110b35760405162461bcd60e51b815260206004820152601360248201527221b0b63632b9103737ba103232b332b73232b960691b6044820152606401610353565b600e546001600160a01b03166111005760405162461bcd60e51b81526020600482015260126024820152712237903737ba103430bb32903bb4b73732b960711b6044820152606401610353565b600354600e546001600160a01b03908116911614156111b257801561118f57600054600354600254604051637d3c01f360e11b81526001600160a01b039283166004820152908216602482015291169063fa7803e6906044015b600060405180830381600087803b15801561117457600080fd5b505af1158015611188573d6000803e3d6000fd5b5050505050565b600254600e80546001600160a01b0319166001600160a01b039092169190911790555b600054600254600354604051637d3c01f360e11b81526001600160a01b039283166004820152908216602482015291169063fa7803e69060440161115a565b50565b600354600e80546001600160a01b0319166001600160a01b039283169081179091556002546040517f03f929a9a6b1f0aef5e43cb12b56f862da97ec3de3fda02a52e85f9f3974fb6a9361124c939216908590611878565b60405180910390a150565b600254600e80546001600160a01b0319166001600160a01b039283169081179091556003546040517f03f929a9a6b1f0aef5e43cb12b56f862da97ec3de3fda02a52e85f9f3974fb6a9361124c939216908590611878565b600260075460ff1660028111156112c8576112c8611355565b14156112d7576111f181611257565b600354600e80546001600160a01b0319166001600160a01b039092169190911790556111f1816111f4565b600260075460ff16600281111561131b5761131b611355565b141561132a576111f1816111f4565b6111f181611257565b6000806040838503121561134657600080fd5b50508035926020909101359150565b634e487b7160e01b600052602160045260246000fd5b602081016003831061137f5761137f611355565b91905290565b600080600080600080610100878903121561139f57600080fd5b60608701888111156113b057600080fd5b969896359750505060808701359460a0880135945060c0880135935060e088013592509050565b6001600160a01b03811681146111f157600080fd5b80356113f7816113d7565b919050565b600080600080600080600060e0888a03121561141757600080fd5b8735611422816113d7565b96506020880135611432816113d7565b95506040880135611442816113d7565b94506060880135611452816113d7565b9699959850939660808101359560a0820135955060c0909101359350915050565b60006020828403121561148557600080fd5b5035919050565b60008083601f84011261149e57600080fd5b50813567ffffffffffffffff8111156114b657600080fd5b6020830191508360208285010111156114ce57600080fd5b9250929050565b600080600080600080600060c0888a0312156114f057600080fd5b873567ffffffffffffffff8082111561150857600080fd5b9089019060e0828c03121561151c57600080fd5b90975060208901359060ff8216821461153457600080fd5b9096506040890135908082111561154a57600080fd5b506115578a828b0161148c565b989b979a50986060810135976080820135975060a09091013595509350505050565b60006020828403121561158b57600080fd5b8135801515811461159b57600080fd5b9392505050565b600060208083528351808285015260005b818110156115cf578581018301518582016040015282016115b3565b818111156115e1576000604083870101525b50601f01601f1916929092016040019392505050565b60008282101561161757634e487b7160e01b600052601160045260246000fd5b500390565b634e487b7160e01b600052603260045260246000fd5b60008261164f57634e487b7160e01b600052601260045260246000fd5b500490565b6000823561011e1983360301811261166b57600080fd5b90910192915050565b803567ffffffffffffffff811681146113f757600080fd5b6000808335601e198436030181126116a357600080fd5b830160208101925035905067ffffffffffffffff8111156116c357600080fd5b8036038313156114ce57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600061012067ffffffffffffffff61171284611674565b1684526020830135602085015261172b60408401611674565b67ffffffffffffffff166040850152611746606084016113ec565b6001600160a01b031660608501526080838101359085015261176b60a084018461168c565b8260a087015261177e83870182846116d2565b9250505060c083013560c085015260e083013560e08501526101008084013581860152508091505092915050565b60808152600086356117bd816113d7565b6001600160a01b039081166080840152602088013560a0840152604088013560c08401526060880135906117f0826113d7565b1660e08301526118036080880188611654565b60e06101008401526118196101608401826116fb565b60a089013561012085015260c089013561014085015260ff88166020850152905085604084015282810360608401526118538185876116d2565b98975050505050505050565b60006020828403121561187157600080fd5b5051919050565b6001600160a01b0384811682528316602082015260608101600283106118a0576118a0611355565b82604083015294935050505056fea2646970667358221220ccb7efc97baac554457c80cecdacd0363693ddc1ffd8120b287494f2e6e7e88b64736f6c63430008090033a2646970667358221220b2320bae11ac531fb45a91ab63f86f0979c9923b7d16f7e7b868965375a21be764736f6c63430008090033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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.