Sepolia Testnet

Contract

0x3a3A8BF5454a5ad98a3ca6b095cD6929aD441D7f

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Handle Consensus64853472024-08-12 10:35:00123 days ago1723458900IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0202135119.80305316
Handle Consensus64850782024-08-12 9:35:00123 days ago1723455300IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0152628414.60128601
Handle Consensus64848022024-08-12 8:35:00123 days ago1723451700IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0435726542.05846997
Handle Consensus64845362024-08-12 7:35:00123 days ago1723448100IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0212952920.71287435
Handle Consensus64842772024-08-12 6:34:48123 days ago1723444488IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0240759123.32616538
Handle Consensus64840282024-08-12 5:35:00123 days ago1723440900IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0312875929.54327567
Handle Consensus64837832024-08-12 4:35:00123 days ago1723437300IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0167012316.44112018
Handle Consensus64835552024-08-12 3:40:12123 days ago1723434012IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0628161560.92929271
Handle Consensus64832932024-08-12 2:35:00123 days ago1723430100IN
0x3a3A8BF5...9aD441D7f
0 ETH0.003416213.2851092
Handle Consensus64830402024-08-12 1:34:48123 days ago1723426488IN
0x3a3A8BF5...9aD441D7f
0 ETH0.001282711.2552204
Handle Consensus64827772024-08-12 0:35:00123 days ago1723422900IN
0x3a3A8BF5...9aD441D7f
0 ETH0.002654642.59148596
Handle Consensus64825062024-08-11 23:35:00123 days ago1723419300IN
0x3a3A8BF5...9aD441D7f
0 ETH0.006588836.34866733
Handle Consensus64822332024-08-11 22:34:36123 days ago1723415676IN
0x3a3A8BF5...9aD441D7f
0 ETH0.007495517.31384397
Handle Consensus64819682024-08-11 21:34:12123 days ago1723412052IN
0x3a3A8BF5...9aD441D7f
0 ETH0.002250862.20304302
Handle Consensus64817202024-08-11 20:39:24123 days ago1723408764IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0128931112.37294762
Handle Consensus64814332024-08-11 19:34:36123 days ago1723404876IN
0x3a3A8BF5...9aD441D7f
0 ETH0.006596376.41069598
Handle Consensus64811592024-08-11 18:34:24123 days ago1723401264IN
0x3a3A8BF5...9aD441D7f
0 ETH0.003907933.77942457
Handle Consensus64808902024-08-11 17:34:12123 days ago1723397652IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0102789710.00402842
Handle Consensus64806312024-08-11 16:34:24123 days ago1723394064IN
0x3a3A8BF5...9aD441D7f
0 ETH0.008632918.35169505
Handle Consensus64803722024-08-11 15:34:24124 days ago1723390464IN
0x3a3A8BF5...9aD441D7f
0 ETH0.009251638.95751083
Handle Consensus64801002024-08-11 14:34:24124 days ago1723386864IN
0x3a3A8BF5...9aD441D7f
0 ETH0.006590636.40607704
Handle Consensus64798252024-08-11 13:34:12124 days ago1723383252IN
0x3a3A8BF5...9aD441D7f
0 ETH0.009563969.39177153
Handle Consensus64795622024-08-11 12:34:24124 days ago1723379664IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0321995631.20774732
Handle Consensus64793002024-08-11 11:34:12124 days ago1723376052IN
0x3a3A8BF5...9aD441D7f
0 ETH0.0144638614.11400602
Handle Consensus64790352024-08-11 10:34:24124 days ago1723372464IN
0x3a3A8BF5...9aD441D7f
0 ETH0.001562561.49241649
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
64295392024-08-03 14:33:00132 days ago1722695580  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
HandlerV1

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
File 1 of 25 : HandlerV1.sol
// Copyright (C) Polytope Labs Ltd.
// SPDX-License-Identifier: Apache-2.0

// 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.17;

import {MerkleMountainRange, MmrLeaf} from "@polytope-labs/solidity-merkle-trees/MerkleMountainRange.sol";
import {MerklePatricia, StorageValue} from "@polytope-labs/solidity-merkle-trees/MerklePatricia.sol";
import {Bytes} from "@polytope-labs/solidity-merkle-trees/trie/Bytes.sol";

import {IConsensusClient, IntermediateState, StateMachineHeight, StateCommitment} from "@polytope-labs/ismp-solidity/IConsensusClient.sol";
import {IIsmpHost, FeeMetadata, FrozenStatus} from "@polytope-labs/ismp-solidity/IIsmpHost.sol";
import {IHandler} from "@polytope-labs/ismp-solidity/IHandler.sol";
import {
    Message,
    PostResponse,
    PostRequest,
    GetRequest,
    GetResponse,
    PostRequestMessage,
    PostResponseMessage,
    GetResponseMessage,
    PostRequestTimeoutMessage,
    PostResponseTimeoutMessage,
    GetTimeoutMessage,
    PostRequestLeaf,
    PostResponseLeaf
} from "@polytope-labs/ismp-solidity/Message.sol";

import {Context} from "openzeppelin/utils/Context.sol";
import {ERC165} from "openzeppelin/utils/introspection/ERC165.sol";

// Storage prefix for request receipts in the pallet-ismp child trie
bytes constant REQUEST_RECEIPTS_STORAGE_PREFIX = hex"526571756573745265636569707473";

// Storage prefix for response receipts in the pallet-ismp child trie
bytes constant RESPONSE_RECEIPTS_STORAGE_PREFIX = hex"526573706f6e73655265636569707473";

/**
 * @title The ISMP Message Handler.
 * @author Polytope Labs ([email protected])
 *
 * @notice The Handler is responsible for verifying the cryptographic proofs needed
 * to confirm the validity of incoming requests/responses.
 * Refer to the official ISMP specification. https://docs.hyperbridge.network/protocol/ismp
 */
contract HandlerV1 is IHandler, ERC165, Context {
    using Bytes for bytes;
    using Message for PostResponse;
    using Message for PostRequest;
    using Message for GetRequest;

    // The cosensus client has now expired to mitigate
    // long fork attacks, this is unrecoverable.
    error ConsensusClientExpired();

    // The IsmpHost has been frozen by the admin
    error HostFrozen();

    // Challenge period has not yet elapsed
    error ChallengePeriodNotElapsed();

    // The requested state commitment does not exist
    error StateCommitmentNotFound();

    // The message destination is not intended for this host
    error InvalidMessageDestination();

    // The provided message has now timed-out
    error MessageTimedOut();

    // The provided message has not timed-out
    error MessageNotTimedOut();

    // The message has been previously processed
    error DuplicateMessage();

    // The provided message is unknown to the host
    error UnknownMessage();

    // The provided proof is invalid
    error InvalidProof();

    /**
     * @dev Checks if the host permits incoming datagrams
     */
    modifier notFrozen(IIsmpHost host) {
        FrozenStatus state = host.frozen();
        if (state == FrozenStatus.Incoming || state == FrozenStatus.All) revert HostFrozen();
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IHandler).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Handle incoming consensus messages. These message are accompanied with some cryptographic proof.
     * If the Host's internal consensus client verifies this proof successfully,
     * The `StateCommitment` enters the preconfigured challenge period.
     * @param host - `IsmpHost`
     * @param proof - consensus proof
     */
    function handleConsensus(IIsmpHost host, bytes calldata proof) external notFrozen(host) {
        uint256 delay = block.timestamp - host.consensusUpdateTime();

        if (delay >= host.unStakingPeriod()) revert ConsensusClientExpired();

        (bytes memory verifiedState, IntermediateState memory intermediate) = IConsensusClient(host.consensusClient())
            .verifyConsensus(host.consensusState(), proof);
        host.storeConsensusState(verifiedState);

        // check that we know this state machine and it's a new update
        uint256 latestHeight = host.latestStateMachineHeight(intermediate.stateMachineId);
        if (latestHeight != 0 && intermediate.height > latestHeight) {
            StateMachineHeight memory stateMachineHeight = StateMachineHeight({
                stateMachineId: intermediate.stateMachineId,
                height: intermediate.height
            });
            host.storeStateMachineCommitment(stateMachineHeight, intermediate.commitment);
        }
    }

    /**
     * @dev Checks the provided requests and their proofs, before dispatching them to their relevant destination modules
     * @param host - `IsmpHost`
     * @param request - batch post requests
     */
    function handlePostRequests(IIsmpHost host, PostRequestMessage calldata request) external notFrozen(host) {
        uint256 timestamp = block.timestamp;
        uint256 delay = timestamp - host.stateMachineCommitmentUpdateTime(request.proof.height);
        uint256 challengePeriod = host.challengePeriod();
        if (challengePeriod != 0 && challengePeriod > delay) revert ChallengePeriodNotElapsed();

        uint256 requestsLen = request.requests.length;
        MmrLeaf[] memory leaves = new MmrLeaf[](requestsLen);

        for (uint256 i = 0; i < requestsLen; ++i) {
            PostRequestLeaf memory leaf = request.requests[i];
            // check destination
            if (!leaf.request.dest.equals(host.host())) revert InvalidMessageDestination();
            // check time-out
            if (timestamp >= leaf.request.timeout()) revert MessageTimedOut();
            // duplicate request?
            bytes32 commitment = leaf.request.hash();
            if (host.requestReceipts(commitment) != address(0)) revert DuplicateMessage();

            leaves[i] = MmrLeaf(leaf.kIndex, leaf.index, commitment);
        }

        bytes32 root = host.stateMachineCommitment(request.proof.height).overlayRoot;
        if (root == bytes32(0)) revert StateCommitmentNotFound();
        if (!MerkleMountainRange.VerifyProof(root, request.proof.multiproof, leaves, request.proof.leafCount)) {
            revert InvalidProof();
        }

        for (uint256 i = 0; i < requestsLen; ++i) {
            PostRequestLeaf memory leaf = request.requests[i];
            host.dispatchIncoming(leaf.request, _msgSender());
        }
    }

    /**
     * @dev Checks the provided responses and their proofs, before dispatching them to their relevant destination modules
     * @param host - `IsmpHost`
     * @param response - batch post responses
     */
    function handlePostResponses(IIsmpHost host, PostResponseMessage calldata response) external notFrozen(host) {
        uint256 timestamp = block.timestamp;
        uint256 delay = timestamp - host.stateMachineCommitmentUpdateTime(response.proof.height);
        uint256 challengePeriod = host.challengePeriod();

        if (challengePeriod != 0 && challengePeriod > delay) revert ChallengePeriodNotElapsed();

        uint256 responsesLength = response.responses.length;
        MmrLeaf[] memory leaves = new MmrLeaf[](responsesLength);

        for (uint256 i = 0; i < responsesLength; ++i) {
            PostResponseLeaf memory leaf = response.responses[i];
            // check time-out
            if (timestamp >= leaf.response.timeout()) revert MessageTimedOut();
            // known request? also serves as a source check
            bytes32 requestCommitment = leaf.response.request.hash();
            FeeMetadata memory meta = host.requestCommitments(requestCommitment);
            if (meta.sender == address(0)) revert InvalidProof();

            // duplicate response?
            if (host.responseReceipts(leaf.response.hash()).relayer != address(0)) revert DuplicateMessage();
            leaves[i] = MmrLeaf(leaf.kIndex, leaf.index, leaf.response.hash());
        }

        bytes32 root = host.stateMachineCommitment(response.proof.height).overlayRoot;
        if (root == bytes32(0)) revert StateCommitmentNotFound();
        if (!MerkleMountainRange.VerifyProof(root, response.proof.multiproof, leaves, response.proof.leafCount)) {
            revert InvalidProof();
        }

        for (uint256 i = 0; i < responsesLength; ++i) {
            PostResponseLeaf memory leaf = response.responses[i];
            host.dispatchIncoming(leaf.response, _msgSender());
        }
    }

    /**
     * @dev Checks the provided timed-out requests and their proofs, before dispatching them to their relevant destination modules
     * @param host - IsmpHost
     * @param message - batch post request timeouts
     */
    function handlePostRequestTimeouts(
        IIsmpHost host,
        PostRequestTimeoutMessage calldata message
    ) external notFrozen(host) {
        uint256 delay = block.timestamp - host.stateMachineCommitmentUpdateTime(message.height);
        uint256 challengePeriod = host.challengePeriod();
        if (challengePeriod != 0 && challengePeriod > delay) revert ChallengePeriodNotElapsed();

        // fetch the state commitment
        StateCommitment memory state = host.stateMachineCommitment(message.height);
        if (state.stateRoot == bytes32(0)) revert StateCommitmentNotFound();
        uint256 timeoutsLength = message.timeouts.length;

        for (uint256 i = 0; i < timeoutsLength; ++i) {
            PostRequest memory request = message.timeouts[i];
            // timed-out?
            if (request.timeout() > state.timestamp) revert MessageNotTimedOut();

            // known request? also serves as source check
            bytes32 requestCommitment = request.hash();
            FeeMetadata memory meta = host.requestCommitments(requestCommitment);
            if (meta.sender == address(0)) revert UnknownMessage();

            bytes[] memory keys = new bytes[](1);
            keys[i] = bytes.concat(REQUEST_RECEIPTS_STORAGE_PREFIX, bytes.concat(requestCommitment));

            // verify state trie non-membership proofs
            StorageValue memory entry = MerklePatricia.VerifySubstrateProof(state.stateRoot, message.proof, keys)[0];
            if (entry.value.length != 0) revert InvalidProof();

            host.dispatchTimeOut(request, meta, requestCommitment);
        }
    }

    /**
     * @dev Check the provided timeouts and their proofs before dispatching them to their relevant modules
     * @param host - Ismp host
     * @param message - batch post response timeouts
     */
    function handlePostResponseTimeouts(
        IIsmpHost host,
        PostResponseTimeoutMessage calldata message
    ) external notFrozen(host) {
        uint256 delay = block.timestamp - host.stateMachineCommitmentUpdateTime(message.height);
        uint256 challengePeriod = host.challengePeriod();
        if (challengePeriod != 0 && challengePeriod > delay) revert ChallengePeriodNotElapsed();

        // fetch the state commitment
        StateCommitment memory state = host.stateMachineCommitment(message.height);
        if (state.stateRoot == bytes32(0)) revert StateCommitmentNotFound();
        uint256 timeoutsLength = message.timeouts.length;

        for (uint256 i = 0; i < timeoutsLength; ++i) {
            PostResponse memory response = message.timeouts[i];
            // timed-out?
            if (response.timeout() > state.timestamp) revert MessageNotTimedOut();

            // known response? also serves as source check
            bytes32 responseCommitment = response.hash();
            FeeMetadata memory meta = host.responseCommitments(responseCommitment);
            if (meta.sender == address(0)) revert UnknownMessage();

            bytes[] memory keys = new bytes[](1);
            keys[i] = bytes.concat(RESPONSE_RECEIPTS_STORAGE_PREFIX, bytes.concat(responseCommitment));

            // verify state trie non-membership proofs
            StorageValue memory entry = MerklePatricia.VerifySubstrateProof(state.stateRoot, message.proof, keys)[0];
            if (entry.value.length != 0) revert InvalidProof();

            host.dispatchTimeOut(response, meta, responseCommitment);
        }
    }

    /**
     * @dev check response proofs, message delay and timeouts, then dispatch get responses to modules
     * @param host - Ismp host
     * @param message - batch get responses
     */
    function handleGetResponses(IIsmpHost host, GetResponseMessage calldata message) external notFrozen(host) {
        uint256 timestamp = block.timestamp;
        uint256 delay = timestamp - host.stateMachineCommitmentUpdateTime(message.height);
        uint256 challengePeriod = host.challengePeriod();
        if (challengePeriod != 0 && challengePeriod > delay) revert ChallengePeriodNotElapsed();

        bytes32 root = host.stateMachineCommitment(message.height).stateRoot;
        if (root == bytes32(0)) revert StateCommitmentNotFound();

        uint256 responsesLength = message.requests.length;
        bytes[] memory proof = message.proof;

        for (uint256 i = 0; i < responsesLength; ++i) {
            GetRequest memory request = message.requests[i];
            // timed-out?
            if (timestamp >= request.timeout()) revert MessageTimedOut();

            // known request? also serves as source check
            bytes32 requestCommitment = request.hash();
            FeeMetadata memory meta = host.requestCommitments(requestCommitment);
            if (meta.sender == address(0)) revert UnknownMessage();

            // duplicate response?
            if (host.responseReceipts(requestCommitment).relayer != address(0)) revert DuplicateMessage();
            StorageValue[] memory values = MerklePatricia.ReadChildProofCheck(
                root,
                proof,
                request.keys,
                bytes.concat(requestCommitment)
            );
            GetResponse memory response = GetResponse({request: request, values: values});

            host.dispatchIncoming(response, _msgSender());
        }
    }

    /**
     * @dev Check the provided Get request timeouts, then dispatch to modules
     * @param host - Ismp host
     * @param message - batch get request timeouts
     */
    function handleGetRequestTimeouts(IIsmpHost host, GetTimeoutMessage calldata message) external notFrozen(host) {
        uint256 timeoutsLength = message.timeouts.length;
        uint256 timestamp = block.timestamp;

        for (uint256 i = 0; i < timeoutsLength; ++i) {
            GetRequest memory request = message.timeouts[i];
            bytes32 requestCommitment = request.hash();
            FeeMetadata memory meta = host.requestCommitments(requestCommitment);
            if (meta.sender == address(0)) revert InvalidProof();

            if (request.timeout() > timestamp) revert MessageNotTimedOut();
            host.dispatchTimeOut(request, meta, requestCommitment);
        }
    }
}

File 2 of 25 : MerkleMountainRange.sol
// SPDX-License-Identifier: Apache2
pragma solidity 0.8.17;

import "openzeppelin/utils/math/Math.sol";
import "./MerkleMultiProof.sol";
import "./Types.sol";

/**
 * @title A Merkle Mountain Range proof library
 * @author Polytope Labs
 * @notice Use this library to verify the leaves of a merkle mountain range tree
 * @dev refer to research for more info. https://research.polytope.technology/merkle-mountain-range-multi-proofs
 */
library MerkleMountainRange {
    /// @notice Verify that merkle proof is accurate
    /// @notice This calls CalculateRoot(...) under the hood
    /// @param root hash of the Merkle's root node
    /// @param proof a list of nodes required for the proof to be verified
    /// @param leaves a list of mmr leaves to prove
    /// @return boolean if the calculated root matches the provided root node
    function VerifyProof(bytes32 root, bytes32[] memory proof, MmrLeaf[] memory leaves, uint256 mmrSize)
        internal
        pure
        returns (bool)
    {
        return root == CalculateRoot(proof, leaves, mmrSize);
    }

    /// @notice Calculate merkle root
    /// @notice this method allows computing the root hash of a merkle tree using Merkle Mountain Range
    /// @param proof A list of the merkle nodes that are needed to re-calculate root node.
    /// @param leaves a list of mmr leaves to prove
    /// @param leafCount the size of the merkle tree
    /// @return bytes32 hash of the computed root node
    function CalculateRoot(bytes32[] memory proof, MmrLeaf[] memory leaves, uint256 leafCount)
        internal
        pure
        returns (bytes32)
    {
        // special handle the only 1 leaf MMR
        if (leafCount == 1 && leaves.length == 1 && leaves[0].leaf_index == 0) {
            return leaves[0].hash;
        }

        uint256[] memory subtrees = subtreeHeights(leafCount);
        uint256 length = subtrees.length;
        Iterator memory peakRoots = Iterator(0, new bytes32[](length));
        Iterator memory proofIter = Iterator(0, proof);

        uint256 current_subtree;
        for (uint256 p; p < length;) {
            uint256 height = subtrees[p];
            current_subtree += 2 ** height;

            MmrLeaf[] memory subtreeLeaves = new MmrLeaf[](0);
            if (leaves.length > 0) {
                (subtreeLeaves, leaves) = leavesForSubtree(leaves, current_subtree);
            }

            if (subtreeLeaves.length == 0) {
                if (proofIter.data.length == proofIter.offset) {
                    break;
                } else {
                    push(peakRoots, next(proofIter));
                }
            } else if (subtreeLeaves.length == 1 && height == 0) {
                push(peakRoots, subtreeLeaves[0].hash);
            } else {
                push(peakRoots, CalculateSubtreeRoot(subtreeLeaves, proofIter, height));
            }

            unchecked {
                ++p;
            }
        }

        unchecked {
            peakRoots.offset--;
        }

        while (peakRoots.offset != 0) {
            bytes32 right = previous(peakRoots);
            bytes32 left = previous(peakRoots);
            unchecked {
                ++peakRoots.offset;
            }
            peakRoots.data[peakRoots.offset] = keccak256(abi.encodePacked(right, left));
        }

        return peakRoots.data[0];
    }

    function subtreeHeights(uint256 leavesLength) internal pure returns (uint256[] memory) {
        uint256 maxSubtrees = 64;
        uint256[] memory indices = new uint256[](maxSubtrees);
        uint256 i;
        uint256 current = leavesLength;
        for (; i < maxSubtrees;) {
            if (current == 0) {
                break;
            }
            uint256 log = Math.log2(current);
            indices[i] = log;
            current = current - 2 ** log;

            unchecked {
                ++i;
            }
        }

        // resize array?, sigh solidity.
        uint256 excess = maxSubtrees - i;
        assembly {
            mstore(indices, sub(mload(indices), excess))
        }

        return indices;
    }

    /// @notice calculate root hash of a subtree of the merkle mountain
    /// @param peakLeaves  a list of nodes to provide proof for
    /// @param proofIter   a list of node hashes to traverse to compute the peak root hash
    /// @param height    Height of the subtree
    /// @return peakRoot a tuple containing the peak root hash, and the peak root position in the merkle
    function CalculateSubtreeRoot(MmrLeaf[] memory peakLeaves, Iterator memory proofIter, uint256 height)
        internal
        pure
        returns (bytes32)
    {
        uint256[] memory current_layer;
        Node[] memory leaves;
        (leaves, current_layer) = mmrLeafToNode(peakLeaves);

        Node[][] memory layers = new Node[][](height);
        for (uint256 i; i < height;) {
            uint256 nodelength = 2 ** (height - i);
            if (current_layer.length == nodelength) {
                break;
            }

            uint256[] memory siblings = siblingIndices(current_layer);
            uint256[] memory diff = difference(siblings, current_layer);

            uint256 length = diff.length;
            layers[i] = new Node[](length);
            for (uint256 j; j < length;) {
                layers[i][j] = Node(diff[j], next(proofIter));

                unchecked {
                    ++j;
                }
            }

            current_layer = parentIndices(siblings);

            unchecked {
                ++i;
            }
        }

        return MerkleMultiProof.CalculateRoot(layers, leaves);
    }

    /**
     * @notice difference ensures all nodes have a sibling.
     * @dev left and right are designed to be equal length array
     * @param left a list of hashes
     * @param right a list of hashes to compare
     * @return uint256[] a new array with difference
     */
    function difference(uint256[] memory left, uint256[] memory right) internal pure returns (uint256[] memory) {
        uint256 length = left.length;
        uint256 rightLength = right.length;

        uint256[] memory diff = new uint256[](length);
        uint256 d;
        for (uint256 i; i < length;) {
            bool found;
            for (uint256 j; j < rightLength;) {
                if (left[i] == right[j]) {
                    found = true;
                    break;
                }

                unchecked {
                    ++j;
                }
            }

            if (!found) {
                diff[d] = left[i];
                d++;
            }

            unchecked {
                ++i;
            }
        }

        // resize array?, sigh solidity.
        uint256 excess = length - d;
        assembly {
            mstore(diff, sub(mload(diff), excess))
        }

        return diff;
    }

    /**
     * @dev calculates the index of each sibling index of the proof nodes
     * @dev proof nodes are the nodes that will be traversed to estimate the root hash
     * @param indices a list of proof nodes indices
     * @return uint256[] a list of sibling indices
     */
    function siblingIndices(uint256[] memory indices) internal pure returns (uint256[] memory) {
        uint256 length = indices.length;
        uint256[] memory siblings = new uint256[](length);

        for (uint256 i; i < length;) {
            uint256 index = indices[i];
            if (index == 0) {
                siblings[i] = index + 1;
            } else if (index % 2 == 0) {
                siblings[i] = index + 1;
            } else {
                siblings[i] = index - 1;
            }

            unchecked {
                ++i;
            }
        }

        return siblings;
    }

    /**
     * @notice Compute Parent Indices
     * @dev Used internally to calculate the indices of the parent nodes of the provided proof nodes
     * @param indices a list of indices of proof nodes in a merkle mountain
     * @return uint256[] a list of parent indices for each index provided
     */
    function parentIndices(uint256[] memory indices) internal pure returns (uint256[] memory) {
        uint256 length = indices.length;
        uint256[] memory parents = new uint256[](length);
        uint256 k;

        for (uint256 i; i < length; i++) {
            uint256 index = indices[i] / 2;
            if (k > 0 && parents[k - 1] == index) {
                continue;
            }
            parents[k] = index;
            unchecked {
                ++k;
            }
        }

        // resize array?, sigh solidity.
        uint256 excess = length - k;

        assembly {
            mstore(parents, sub(mload(parents), excess))
        }

        return parents;
    }

    /**
     * @notice Convert Merkle mountain Leaf to a Merkle Node
     * @param leaves list of merkle mountain range leaf
     * @return A tuple with the list of merkle nodes and the list of nodes at 0 and 1 respectively
     */
    function mmrLeafToNode(MmrLeaf[] memory leaves) internal pure returns (Node[] memory, uint256[] memory) {
        uint256 i;
        uint256 length = leaves.length;
        Node[] memory nodes = new Node[](length);
        uint256[] memory indices = new uint256[](length);
        while (i < length) {
            nodes[i] = Node(leaves[i].k_index, leaves[i].hash);
            indices[i] = leaves[i].k_index;
            ++i;
        }

        return (nodes, indices);
    }

    /**
     * @notice Get a mountain peak's leaves
     * @notice this splits the leaves into either side of the peak [left & right]
     * @param leaves a list of mountain merkle leaves, for a subtree
     * @param leafIndex the index of the leaf of the next subtree
     * @return A tuple of 2 arrays of mountain merkle leaves. Index 1 and 2 represent left and right of the peak respectively
     */
    function leavesForSubtree(MmrLeaf[] memory leaves, uint256 leafIndex)
        internal
        pure
        returns (MmrLeaf[] memory, MmrLeaf[] memory)
    {
        uint256 p;
        uint256 length = leaves.length;
        for (; p < length; p++) {
            if (leafIndex <= leaves[p].leaf_index) {
                break;
            }
        }

        uint256 len = p == 0 ? 0 : p;
        MmrLeaf[] memory left = new MmrLeaf[](len);
        MmrLeaf[] memory right = new MmrLeaf[](length - len);

        uint256 i;
        uint256 leftLength = left.length;
        while (i < leftLength) {
            left[i] = leaves[i];
            ++i;
        }

        uint256 j;
        while (i < length) {
            right[j] = leaves[i];
            ++i;
            ++j;
        }

        return (left, right);
    }

    function push(Iterator memory iterator, bytes32 data) internal pure {
        iterator.data[iterator.offset] = data;
        unchecked {
            ++iterator.offset;
        }
    }

    function next(Iterator memory iterator) internal pure returns (bytes32) {
        bytes32 data = iterator.data[iterator.offset];
        unchecked {
            ++iterator.offset;
        }

        return data;
    }

    function previous(Iterator memory iterator) internal pure returns (bytes32) {
        bytes32 data = iterator.data[iterator.offset];
        unchecked {
            --iterator.offset;
        }

        return data;
    }
}

File 3 of 25 : MerklePatricia.sol
pragma solidity 0.8.17;


import "./trie/Node.sol";
import "./trie/Option.sol";
import "./trie/NibbleSlice.sol";
import "./trie/TrieDB.sol";

import "./trie/substrate/SubstrateTrieDB.sol";
import "./trie/ethereum/EthereumTrieDB.sol";
import "./Types.sol";

// SPDX-License-Identifier: Apache2

/**
 * @title A Merkle Patricia library
 * @author Polytope Labs
 * @dev Use this library to verify merkle patricia proofs
 * @dev refer to research for more info. https://research.polytope.technology/state-(machine)-proofs
 */
library MerklePatricia {
    /**
     * @notice Verifies substrate specific merkle patricia proofs.
     * @param root hash of the merkle patricia trie
     * @param proof a list of proof nodes
     * @param keys a list of keys to verify
     * @return bytes[] a list of values corresponding to the supplied keys.
     */
    function VerifySubstrateProof(bytes32 root, bytes[] memory proof, bytes[] memory keys)
        public
        pure
        returns (StorageValue[] memory)
    {
        StorageValue[] memory values = new StorageValue[](keys.length);
        TrieNode[] memory nodes = new TrieNode[](proof.length);

        for (uint256 i = 0; i < proof.length; i++) {
            nodes[i] = TrieNode(keccak256(proof[i]), proof[i]);
        }

        for (uint256 i = 0; i < keys.length; i++) {
            values[i].key = keys[i];
            NibbleSlice memory keyNibbles = NibbleSlice(keys[i], 0);
            NodeKind memory node = SubstrateTrieDB.decodeNodeKind(TrieDB.get(nodes, root));

            // This loop is unbounded so that an adversary cannot insert a deeply nested key in the trie
            // and successfully convince us of it's non-existence, if we consume the block gas limit while
            // traversing the trie, then the transaction should revert.
            for (uint256 j = 1; j > 0; j++) {
                NodeHandle memory nextNode;

                if (TrieDB.isLeaf(node)) {
                    Leaf memory leaf = SubstrateTrieDB.decodeLeaf(node);
                    if (NibbleSliceOps.eq(leaf.key, keyNibbles)) {
                        values[i].value = TrieDB.load(nodes, leaf.value);
                    }
                    break;
                } else if (TrieDB.isNibbledBranch(node)) {
                    NibbledBranch memory nibbled = SubstrateTrieDB.decodeNibbledBranch(node);
                    uint256 nibbledBranchKeyLength = NibbleSliceOps.len(nibbled.key);
                    if (!NibbleSliceOps.startsWith(keyNibbles, nibbled.key)) {
                        break;
                    }

                    if (NibbleSliceOps.len(keyNibbles) == nibbledBranchKeyLength) {
                        if (Option.isSome(nibbled.value)) {
                            values[i].value = TrieDB.load(nodes, nibbled.value.value);
                        }
                        break;
                    } else {
                        uint256 index = NibbleSliceOps.at(keyNibbles, nibbledBranchKeyLength);
                        NodeHandleOption memory handle = nibbled.children[index];
                        if (Option.isSome(handle)) {
                            keyNibbles = NibbleSliceOps.mid(keyNibbles, nibbledBranchKeyLength + 1);
                            nextNode = handle.value;
                        } else {
                            break;
                        }
                    }
                } else if (TrieDB.isEmpty(node)) {
                    break;
                }

                node = SubstrateTrieDB.decodeNodeKind(TrieDB.load(nodes, nextNode));
            }
        }

        return values;
    }

    /**
     * @notice Verify child trie keys
     * @dev substrate specific method in order to verify keys in the child trie.
     * @param root hash of the merkle root
     * @param proof a list of proof nodes
     * @param keys a list of keys to verify
     * @param childInfo data that can be used to compute the root of the child trie
     * @return bytes[], a list of values corresponding to the supplied keys.
     */
    function ReadChildProofCheck(bytes32 root, bytes[] memory proof, bytes[] memory keys, bytes memory childInfo)
        public
        pure
        returns (StorageValue[] memory)
    {
        // fetch the child trie root hash;
        bytes memory prefix = bytes(":child_storage:default:");
        bytes memory key = bytes.concat(prefix, childInfo);
        bytes[] memory _keys = new bytes[](1);
        _keys[0] = key;
        StorageValue[] memory values = VerifySubstrateProof(root, proof, _keys);

        bytes32 childRoot = bytes32(values[0].value);
        require(childRoot != bytes32(0), "Invalid child trie proof");

        return VerifySubstrateProof(childRoot, proof, keys);
    }

    /**
     * @notice Verifies ethereum specific merkle patricia proofs as described by EIP-1188.
     * @param root hash of the merkle patricia trie
     * @param proof a list of proof nodes
     * @param keys a list of keys to verify
     * @return bytes[] a list of values corresponding to the supplied keys.
     */
    function VerifyEthereumProof(bytes32 root, bytes[] memory proof, bytes[] memory keys)
        public
        pure
        returns (StorageValue[] memory)
    {
        StorageValue[] memory values = new StorageValue[](keys.length);
        TrieNode[] memory nodes = new TrieNode[](proof.length);

        for (uint256 i = 0; i < proof.length; i++) {
            nodes[i] = TrieNode(keccak256(proof[i]), proof[i]);
        }

        for (uint256 i = 0; i < keys.length; i++) {
            values[i].key = keys[i];
            NibbleSlice memory keyNibbles = NibbleSlice(keys[i], 0);
            NodeKind memory node = EthereumTrieDB.decodeNodeKind(TrieDB.get(nodes, root));

            // This loop is unbounded so that an adversary cannot insert a deeply nested key in the trie
            // and successfully convince us of it's non-existence, if we consume the block gas limit while
            // traversing the trie, then the transaction should revert.
            for (uint256 j = 1; j > 0; j++) {
                NodeHandle memory nextNode;

                if (TrieDB.isLeaf(node)) {
                    Leaf memory leaf = EthereumTrieDB.decodeLeaf(node);
                    // Let's retrieve the offset to be used
                    uint256 offset = keyNibbles.offset % 2 == 0 ? keyNibbles.offset / 2 : keyNibbles.offset / 2 + 1;
                    // Let's cut the key passed as input
                    keyNibbles = NibbleSlice(NibbleSliceOps.bytesSlice(keyNibbles.data, offset), 0);
                    if (NibbleSliceOps.eq(leaf.key, keyNibbles)) {
                        values[i].value = TrieDB.load(nodes, leaf.value);
                    }
                    break;
                } else if (TrieDB.isExtension(node)) {
                    Extension memory extension = EthereumTrieDB.decodeExtension(node);
                    if (NibbleSliceOps.startsWith(keyNibbles, extension.key)) {
                        // Let's cut the key passed as input
                        uint256 cutNibble = keyNibbles.offset + NibbleSliceOps.len(extension.key);
                        keyNibbles = NibbleSlice(
                            NibbleSliceOps.bytesSlice(keyNibbles.data, cutNibble / 2), cutNibble % 2
                        );
                        nextNode = extension.node;
                    } else {
                        break;
                    }
                } else if (TrieDB.isBranch(node)) {
                    Branch memory branch = EthereumTrieDB.decodeBranch(node);
                    if (NibbleSliceOps.isEmpty(keyNibbles)) {
                        if (Option.isSome(branch.value)) {
                            values[i].value = TrieDB.load(nodes, branch.value.value);
                        }
                        break;
                    } else {
                        NodeHandleOption memory handle = branch.children[NibbleSliceOps.at(keyNibbles, 0)];
                        if (Option.isSome(handle)) {
                            keyNibbles = NibbleSliceOps.mid(keyNibbles, 1);
                            nextNode = handle.value;
                        } else {
                            break;
                        }
                    }
                } else if (TrieDB.isEmpty(node)) {
                    break;
                }

                node = EthereumTrieDB.decodeNodeKind(TrieDB.load(nodes, nextNode));
            }
        }

        return values;
    }
}

File 4 of 25 : Bytes.sol
pragma solidity 0.8.17;

// SPDX-License-Identifier: Apache2

import {Memory} from "./Memory.sol";

struct ByteSlice {
    bytes data;
    uint256 offset;
}

library Bytes {
    uint256 internal constant BYTES_HEADER_SIZE = 32;

    // Checks if two `bytes memory` variables are equal. This is done using hashing,
    // which is much more gas efficient then comparing each byte individually.
    // Equality means that:
    //  - 'self.length == other.length'
    //  - For 'n' in '[0, self.length)', 'self[n] == other[n]'
    function equals(bytes memory self, bytes memory other) internal pure returns (bool equal) {
        if (self.length != other.length) {
            return false;
        }
        uint256 addr;
        uint256 addr2;
        assembly {
            addr := add(self, /*BYTES_HEADER_SIZE*/ 32)
            addr2 := add(other, /*BYTES_HEADER_SIZE*/ 32)
        }
        equal = Memory.equals(addr, addr2, self.length);
    }

    function readByte(ByteSlice memory self) internal pure returns (uint8) {
        if (self.offset + 1 > self.data.length) {
            revert("Out of range");
        }

        uint8 b = uint8(self.data[self.offset]);
        self.offset += 1;

        return b;
    }

    // Copies 'len' bytes from 'self' into a new array, starting at the provided 'startIndex'.
    // Returns the new copy.
    // Requires that:
    //  - 'startIndex + len <= self.length'
    // The length of the substring is: 'len'
    function read(ByteSlice memory self, uint256 len) internal pure returns (bytes memory) {
        require(self.offset + len <= self.data.length);
        if (len == 0) {
            return "";
        }
        uint256 addr = Memory.dataPtr(self.data);
        bytes memory slice = Memory.toBytes(addr + self.offset, len);
        self.offset += len;
        return slice;
    }

    // Copies a section of 'self' into a new array, starting at the provided 'startIndex'.
    // Returns the new copy.
    // Requires that 'startIndex <= self.length'
    // The length of the substring is: 'self.length - startIndex'
    function substr(bytes memory self, uint256 startIndex) internal pure returns (bytes memory) {
        require(startIndex <= self.length);
        uint256 len = self.length - startIndex;
        uint256 addr = Memory.dataPtr(self);
        return Memory.toBytes(addr + startIndex, len);
    }

    // Copies 'len' bytes from 'self' into a new array, starting at the provided 'startIndex'.
    // Returns the new copy.
    // Requires that:
    //  - 'startIndex + len <= self.length'
    // The length of the substring is: 'len'
    function substr(bytes memory self, uint256 startIndex, uint256 len) internal pure returns (bytes memory) {
        require(startIndex + len <= self.length);
        if (len == 0) {
            return "";
        }
        uint256 addr = Memory.dataPtr(self);
        return Memory.toBytes(addr + startIndex, len);
    }

    // Combines 'self' and 'other' into a single array.
    // Returns the concatenated arrays:
    //  [self[0], self[1], ... , self[self.length - 1], other[0], other[1], ... , other[other.length - 1]]
    // The length of the new array is 'self.length + other.length'
    function concat(bytes memory self, bytes memory other) internal pure returns (bytes memory) {
        bytes memory ret = new bytes(self.length + other.length);
        uint256 src;
        uint256 srcLen;
        (src, srcLen) = Memory.fromBytes(self);
        uint256 src2;
        uint256 src2Len;
        (src2, src2Len) = Memory.fromBytes(other);
        uint256 dest;
        (dest,) = Memory.fromBytes(ret);
        uint256 dest2 = dest + srcLen;
        Memory.copy(src, dest, srcLen);
        Memory.copy(src2, dest2, src2Len);
        return ret;
    }

    function toBytes32(bytes memory self) internal pure returns (bytes32 out) {
        require(self.length >= 32, "Bytes:: toBytes32: data is to short.");
        assembly {
            out := mload(add(self, 32))
        }
    }

    function toBytes16(bytes memory self, uint256 offset) internal pure returns (bytes16 out) {
        for (uint256 i = 0; i < 16; i++) {
            out |= bytes16(bytes1(self[offset + i]) & 0xFF) >> (i * 8);
        }
    }

    function toBytes8(bytes memory self, uint256 offset) internal pure returns (bytes8 out) {
        for (uint256 i = 0; i < 8; i++) {
            out |= bytes8(bytes1(self[offset + i]) & 0xFF) >> (i * 8);
        }
    }

    function toBytes4(bytes memory self, uint256 offset) internal pure returns (bytes4) {
        bytes4 out;

        for (uint256 i = 0; i < 4; i++) {
            out |= bytes4(self[offset + i] & 0xFF) >> (i * 8);
        }
        return out;
    }

    function toBytes2(bytes memory self, uint256 offset) internal pure returns (bytes2) {
        bytes2 out;

        for (uint256 i = 0; i < 2; i++) {
            out |= bytes2(self[offset + i] & 0xFF) >> (i * 8);
        }
        return out;
    }

    function removeLeadingZero(bytes memory data) internal pure returns (bytes memory) {
        uint256 length = data.length;

        uint256 startIndex = 0;
        for (uint256 i = 0; i < length; i++) {
            if (data[i] != 0) {
                startIndex = i;
                break;
            }
        }

        return substr(data, startIndex);
    }

    function removeEndingZero(bytes memory data) internal pure returns (bytes memory) {
        uint256 length = data.length;

        uint256 endIndex = 0;
        for (uint256 i = length - 1; i >= 0; i--) {
            if (data[i] != 0) {
                endIndex = i;
                break;
            }
        }

        return substr(data, 0, endIndex + 1);
    }

    function reverse(bytes memory inbytes) internal pure returns (bytes memory) {
        uint256 inlength = inbytes.length;
        bytes memory outbytes = new bytes(inlength);

        for (uint256 i = 0; i <= inlength - 1; i++) {
            outbytes[i] = inbytes[inlength - i - 1];
        }

        return outbytes;
    }
}

File 5 of 25 : IConsensusClient.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

// The state commiment identifies a commiment to some intermediate state in the state machine.
// This contains some metadata about the state machine like it's own timestamp at the time of this commitment.
struct StateCommitment {
	// This timestamp is useful for handling request timeouts.
	uint256 timestamp;
	// Overlay trie commitment to all ismp requests & response.
	bytes32 overlayRoot;
	// State trie commitment at the given block height
	bytes32 stateRoot;
}

// Identifies some state machine height. We allow for a state machine identifier here
// as some consensus clients may track multiple, concurrent state machines.
struct StateMachineHeight {
	// the state machine identifier
	uint256 stateMachineId;
	// height of this state machine
	uint256 height;
}

// An intermediate state in the series of state transitions undergone by a given state machine.
struct IntermediateState {
	// the state machine identifier
	uint256 stateMachineId;
	// height of this state machine
	uint256 height;
	// state commitment
	StateCommitment commitment;
}

/**
 * @title The Ismp ConsensusClient
 * @author Polytope Labs ([email protected])
 *
 * @notice The consensus client interface responsible for the verification of consensus datagrams.
 * It's internals are opaque to the ISMP framework allowing it to evolve as needed.
 */
interface IConsensusClient {
	// @dev Given some opaque consensus proof, produce the new consensus state and newly finalized intermediate states.
	function verifyConsensus(
		bytes memory trustedState,
		bytes memory proof
	) external returns (bytes memory, IntermediateState memory);
}

File 6 of 25 : IIsmpHost.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {StateCommitment, StateMachineHeight} from "./IConsensusClient.sol";
import {IDispatcher} from "./IDispatcher.sol";
import {PostRequest, PostResponse, GetResponse, PostTimeout, GetRequest} from "./Message.sol";

// Some metadata about the request fee
struct FeeMetadata {
	// the relayer fee
	uint256 fee;
	// user who initiated the request
	address sender;
}

struct ResponseReceipt {
	// commitment of the response object
	bytes32 responseCommitment;
	// address of the relayer responsible for this response delivery
	address relayer;
}

// Various frozen states of the IIsmpHost
enum FrozenStatus {
	// Host is operating normally
	None,
	// Host is currently disallowing incoming datagrams
	Incoming,
	// Host is currently disallowing outgoing messages
	Outgoing,
	// All actions have been frozen
	All
}

/**
 * @title The Ismp Host Interface
 * @author Polytope Labs ([email protected])
 *
 * @notice The Ismp Host interface sits at the core of the interoperable state machine protocol,
 * It which encapsulates the interfaces required for ISMP datagram handlers and modules.
 *
 * @dev The IsmpHost provides the necessary storage interface for the ISMP handlers to process
 * ISMP messages, the IsmpDispatcher provides the interfaces applications use for dispatching requests
 * and responses. This host implementation delegates all verification logic to the IHandler contract.
 * It is only responsible for dispatching incoming & outgoing messages as well as managing
 * the state of the ISMP protocol.
 */
interface IIsmpHost is IDispatcher {
	/**
	 * @return the host admin
	 */
	function admin() external returns (address);

	/**
	 * @return the address of the fee token ERC-20 contract on this state machine
	 */
	function feeToken() external view returns (address);

	/**
	 * @return the per-byte fee for outgoing messages.
	 */
	function perByteFee() external view returns (uint256);

	/**
	 * @return the host state machine id
	 */
	function host() external view returns (bytes memory);

	/**
	 * @return the state machine identifier for the connected hyperbridge instance
	 */
	function hyperbridge() external view returns (bytes memory);

	/**
	 * @return the host timestamp
	 */
	function timestamp() external view returns (uint256);

	/**
	 * @dev Returns the nonce immediately available for requests
	 * @return the `nonce`
	 */
	function nonce() external view returns (uint256);

	/**
	 * @dev Returns the fisherman responsible for vetoing the given state machine height.
	 * @return the `fisherman` address
	 */
	function vetoes(uint256 paraId, uint256 height) external view returns (address);

	/**
	 * @return the `frozen` status
	 */
	function frozen() external view returns (FrozenStatus);

	/**
	 * @dev Returns the address for the Uniswap V2 Router implementation used for swaps
	 * @return routerAddress - The address to the in-use RouterV02 implementation
	 */
	function uniswapV2Router() external view returns (address);

	/**
	 * @dev Returns the fee required for 3rd party applications to access hyperbridge state commitments.
	 * @return the `stateCommitmentFee`
	 */
	function stateCommitmentFee() external view returns (uint256);

	/**
	 * @notice Charges the stateCommitmentFee to 3rd party applications.
	 * If native tokens are provided, will attempt to swap them for the stateCommitmentFee.
	 * If not enough native tokens are supplied, will revert.
	 *
	 * If no native tokens are provided then it will try to collect payment from the calling contract in
	 * the IIsmpHost.feeToken.
	 *
	 * @param height - state machine height
	 * @return the state commitment at `height`
	 */
	function stateMachineCommitment(StateMachineHeight memory height) external payable returns (StateCommitment memory);

	/**
	 * @param height - state machine height
	 * @return the state machine commitment update time at `height`
	 */
	function stateMachineCommitmentUpdateTime(StateMachineHeight memory height) external returns (uint256);

	/**
	 * @return the consensus client contract
	 */
	function consensusClient() external view returns (address);

	/**
	 * @return the last updated time of the consensus client
	 */
	function consensusUpdateTime() external view returns (uint256);

	/**
	 * @return the latest state machine height for the given stateMachineId. If it returns 0, the state machine is unsupported.
	 */
	function latestStateMachineHeight(uint256 stateMachineId) external view returns (uint256);

	/**
	 * @return the state of the consensus client
	 */
	function consensusState() external view returns (bytes memory);

	/**
	 * @dev Check the response status for a given request.
	 * @return `response` status
	 */
	function responded(bytes32 commitment) external view returns (bool);

	/**
	 * @param commitment - commitment to the request
	 * @return relayer address
	 */
	function requestReceipts(bytes32 commitment) external view returns (address);

	/**
	 * @param commitment - commitment to the request of the response
	 * @return response receipt
	 */
	function responseReceipts(bytes32 commitment) external view returns (ResponseReceipt memory);

	/**
	 * @param commitment - commitment to the request
	 * @return existence status of an outgoing request commitment
	 */
	function requestCommitments(bytes32 commitment) external view returns (FeeMetadata memory);

	/**
	 * @param commitment - commitment to the response
	 * @return existence status of an outgoing response commitment
	 */
	function responseCommitments(bytes32 commitment) external view returns (FeeMetadata memory);

	/**
	 * @return the challenge period
	 */
	function challengePeriod() external view returns (uint256);

	/**
	 * @return the unstaking period
	 */
	function unStakingPeriod() external view returns (uint256);

	/**
	 * @dev set the new frozen state of the host, only the admin or handler can call this.
	 * @param newState - the new frozen state
	 */
	function setFrozenState(FrozenStatus newState) external;

	/**
	 * @dev Store an encoded consensus state
	 * @param state new consensus state
	 */
	function storeConsensusState(bytes memory state) external;

	/**
	 * @dev Store the commitment at `state height`
	 * @param height state machine height
	 * @param commitment state commitment
	 */
	function storeStateMachineCommitment(StateMachineHeight memory height, StateCommitment memory commitment) external;

	/**
	 * @dev Delete the state commitment at given state height.
	 */
	function deleteStateMachineCommitment(StateMachineHeight memory height, address fisherman) external;

	/**
	 * @dev Dispatch an incoming request to destination module
	 * @param request - post request
	 */
	function dispatchIncoming(PostRequest memory request, address relayer) external;

	/**
	 * @dev Dispatch an incoming post response to source module
	 * @param response - post response
	 */
	function dispatchIncoming(PostResponse memory response, address relayer) external;

	/**
	 * @dev Dispatch an incoming get response to source module
	 * @param response - get response
	 */
	function dispatchIncoming(GetResponse memory response, address relayer) external;

	/**
	 * @dev Dispatch an incoming get timeout to source module
	 * @param timeout - timed-out get request
	 */
	function dispatchTimeOut(GetRequest memory timeout, FeeMetadata memory meta, bytes32 commitment) external;

	/**
	 * @dev Dispatch an incoming post timeout to source module
	 * @param timeout - timed-out post request
	 */
	function dispatchTimeOut(PostRequest memory timeout, FeeMetadata memory meta, bytes32 commitment) external;

	/**
	 * @dev Dispatch an incoming post response timeout to source module
	 * @param timeout - timed-out post response
	 */
	function dispatchTimeOut(PostResponse memory timeout, FeeMetadata memory meta, bytes32 commitment) external;
}

File 7 of 25 : IHandler.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {IIsmpHost} from "./IIsmpHost.sol";
import {PostRequestMessage, PostResponseMessage, GetResponseMessage, PostRequestTimeoutMessage, PostResponseTimeoutMessage, GetTimeoutMessage} from "./Message.sol";

/*
 * @title The Ismp Handler
 * @author Polytope Labs ([email protected])
 *
 * @notice The IHandler interface serves as the entry point for ISMP datagrams, i.e consensus, requests & response messages.
 */
interface IHandler {
	/**
	 * @dev Handle an incoming consensus message. This uses the IConsensusClient contract registered on the host to perform the consensus message verification.
	 * @param host - Ismp host
	 * @param proof - consensus proof
	 */
	function handleConsensus(IIsmpHost host, bytes memory proof) external;

	/**
	 * @dev Handles incoming POST requests, check request proofs, message delay and timeouts, then dispatch POST requests to the apropriate contracts.
	 * @param host - Ismp host
	 * @param request - batch post requests
	 */
	function handlePostRequests(IIsmpHost host, PostRequestMessage memory request) external;

	/**
	 * @dev Handles incoming POST responses, check response proofs, message delay and timeouts, then dispatch POST responses to the apropriate contracts.
	 * @param host - Ismp host
	 * @param response - batch post responses
	 */
	function handlePostResponses(IIsmpHost host, PostResponseMessage memory response) external;

	/**
	 * @dev check response proofs, message delay and timeouts, then dispatch get responses to modules
	 * @param host - Ismp host
	 * @param message - batch get responses
	 */
	function handleGetResponses(IIsmpHost host, GetResponseMessage memory message) external;

	/**
	 * @dev check timeout proofs then dispatch to modules
	 * @param host - Ismp host
	 * @param message - batch post request timeouts
	 */
	function handlePostRequestTimeouts(IIsmpHost host, PostRequestTimeoutMessage memory message) external;

	/**
	 * @dev check timeout proofs then dispatch to modules
	 * @param host - Ismp host
	 * @param message - batch post response timeouts
	 */
	function handlePostResponseTimeouts(IIsmpHost host, PostResponseTimeoutMessage memory message) external;

	/**
	 * @dev dispatch to modules
	 * @param host - Ismp host
	 * @param message - batch get request timeouts
	 */
	function handleGetRequestTimeouts(IIsmpHost host, GetTimeoutMessage memory message) external;
}

File 8 of 25 : Message.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {StateMachineHeight} from "./IConsensusClient.sol";
import {StorageValue} from "@polytope-labs/solidity-merkle-trees/Types.sol";

struct PostRequest {
	// the source state machine of this request
	bytes source;
	// the destination state machine of this request
	bytes dest;
	// request nonce
	uint64 nonce;
	// Module Id of this request origin
	bytes from;
	// destination module id
	bytes to;
	// timestamp by which this request times out.
	uint64 timeoutTimestamp;
	// request body
	bytes body;
}

struct GetRequest {
	// the source state machine of this request
	bytes source;
	// the destination state machine of this request
	bytes dest;
	// request nonce
	uint64 nonce;
	// Module Id of this request origin
	address from;
	// timestamp by which this request times out.
	uint64 timeoutTimestamp;
	// Storage keys to read.
	bytes[] keys;
	// height at which to read destination state machine
	uint64 height;
}

struct GetResponse {
	// The request that initiated this response
	GetRequest request;
	// storage values for get response
	StorageValue[] values;
}

struct PostResponse {
	// The request that initiated this response
	PostRequest request;
	// bytes for post response
	bytes response;
	// timestamp by which this response times out.
	uint64 timeoutTimestamp;
}

// A post request as a leaf in a merkle tree
struct PostRequestLeaf {
	// The request
	PostRequest request;
	// It's index in the mmr leaves
	uint256 index;
	// it's k-index
	uint256 kIndex;
}

// A post response as a leaf in a merkle tree
struct PostResponseLeaf {
	// The response
	PostResponse response;
	// It's index in the mmr leaves
	uint256 index;
	// it's k-index
	uint256 kIndex;
}

// A merkle mountain range proof.
struct Proof {
	// height of the state machine
	StateMachineHeight height;
	// the multi-proof
	bytes32[] multiproof;
	// The total number of leaves in the mmr for this proof.
	uint256 leafCount;
}

// A message for handling incoming requests
struct PostRequestMessage {
	// proof for the requests
	Proof proof;
	// the requests, contained in a merkle tree leaf
	PostRequestLeaf[] requests;
}

// A message for handling incoming GET responses
struct GetResponseMessage {
	// the state (merkle-patricia) proof of the get request keys
	bytes[] proof;
	// the height of the state machine proof
	StateMachineHeight height;
	// The requests that initiated this response
	GetRequest[] requests;
}

struct GetTimeoutMessage {
	// requests which have timed-out
	GetRequest[] timeouts;
}

struct PostTimeout {
	PostRequest request;
}

struct PostRequestTimeoutMessage {
	// requests which have timed-out
	PostRequest[] timeouts;
	// the height of the state machine proof
	StateMachineHeight height;
	// non-membership proof of the requests
	bytes[] proof;
}

struct PostResponseTimeoutMessage {
	// responses which have timed-out
	PostResponse[] timeouts;
	// the height of the state machine proof
	StateMachineHeight height;
	// non-membership proof of the requests
	bytes[] proof;
}

// A message for handling incoming responses
struct PostResponseMessage {
	// proof for the responses
	Proof proof;
	// the responses, contained in a merkle tree leaf
	PostResponseLeaf[] responses;
}

library Message {
	function timeout(PostRequest memory req) internal pure returns (uint64) {
		if (req.timeoutTimestamp == 0) {
			return type(uint64).max;
		} else {
			return req.timeoutTimestamp;
		}
	}

	function timeout(GetRequest memory req) internal pure returns (uint64) {
		if (req.timeoutTimestamp == 0) {
			return type(uint64).max;
		} else {
			return req.timeoutTimestamp;
		}
	}

	function timeout(PostResponse memory res) internal pure returns (uint64) {
		if (res.timeoutTimestamp == 0) {
			return type(uint64).max;
		} else {
			return res.timeoutTimestamp;
		}
	}

	function encodeRequest(PostRequest memory req) internal pure returns (bytes memory) {
		return abi.encodePacked(req.source, req.dest, req.nonce, req.timeoutTimestamp, req.from, req.to, req.body);
	}

	function hash(PostResponse memory res) internal pure returns (bytes32) {
		return
			keccak256(bytes.concat(encodeRequest(res.request), abi.encodePacked(res.response, res.timeoutTimestamp)));
	}

	function hash(PostRequest memory req) internal pure returns (bytes32) {
		return keccak256(encodeRequest(req));
	}

	function hash(GetRequest memory req) internal pure returns (bytes32) {
		bytes memory keysEncoding = bytes("");
		uint256 len = req.keys.length;
		for (uint256 i = 0; i < len; i++) {
			keysEncoding = bytes.concat(keysEncoding, req.keys[i]);
		}

		return
			keccak256(
				abi.encodePacked(
					req.source,
					req.dest,
					req.nonce,
					req.height,
					req.timeoutTimestamp,
					abi.encodePacked(req.from),
					keysEncoding
				)
			);
	}

	function hash(GetResponse memory res) internal pure returns (bytes32) {
		return hash(res.request);
	}
}

File 9 of 25 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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 10 of 25 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 11 of 25 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 12 of 25 : MerkleMultiProof.sol
// SPDX-License-Identifier: Apache2
pragma solidity 0.8.17;

import "openzeppelin/utils/math/Math.sol";
import "./Types.sol";

/**
 * @title A Merkle Multi proof library
 * @author Polytope Labs
 * @dev Use this library to verify merkle tree leaves using merkle multi proofs
 * @dev refer to research for more info. https://research.polytope.technology/merkle-multi-proofs
 */
library MerkleMultiProof {
    /**
     * @notice Verify a Merkle Multi Proof
     * @param root hash of the root node of the merkle tree
     * @param proof A list of the merkle nodes along with their k-indices that are needed to re-calculate root node.
     * @param leaves A list of the leaves along with their k-indices to prove
     * @return boolean if the calculated root matches the provides root node
     */
    function VerifyProof(bytes32 root, Node[][] memory proof, Node[] memory leaves) internal pure returns (bool) {
        return root == CalculateRoot(proof, leaves);
    }

    /**
     * @notice Verify a Merkle Multi Proof whose internal nodes are sorted
     * @param root hash of the root node of the merkle tree
     * @param proof A list of the merkle nodes along with their k-indices that are needed to re-calculate root node.
     * @param leaves A list of the leaves along with their k-indices to prove
     * @return boolean if the calculated root matches the provides root node
     */
    function VerifyProofSorted(bytes32 root, Node[][] memory proof, Node[] memory leaves)
        internal
        pure
        returns (bool)
    {
        return root == CalculateRootSorted(proof, leaves);
    }

    /// @notice Calculate the hash of the root node
    /// @dev Use this function to calculate the hash of the root node
    /// @param proof A list of the merkle nodes along with their k-indices that are needed to re-calculate root node.
    /// @param leaves A list of the leaves along with their k-indices to prove
    /// @return Hash of root node, value is a bytes32 type
    function CalculateRoot(Node[][] memory proof, Node[] memory leaves) internal pure returns (bytes32) {
        // holds the output from hashing a previous layer
        Node[] memory next_layer = new Node[](0);

        // merge leaves
        proof[0] = mergeSort(leaves, proof[0]);

        uint256 proof_length = proof.length;
        for (uint256 height = 0; height < proof_length; height++) {
            Node[] memory current_layer = new Node[](0);

            if (next_layer.length == 0) {
                current_layer = proof[height];
            } else {
                current_layer = mergeSort(proof[height], next_layer);
            }

            next_layer = new Node[](div_ceil(current_layer.length, 2));

            uint256 p = 0;
            uint256 current_layer_length = current_layer.length;
            for (uint256 index = 0; index < current_layer_length; index += 2) {
                if (index + 1 >= current_layer_length) {
                    Node memory node = current_layer[index];
                    node.k_index = div_floor(current_layer[index].k_index, 2);
                    next_layer[p] = node;
                } else {
                    Node memory node;
                    node.k_index = div_floor(current_layer[index].k_index, 2);
                    node.node = _optimizedHash(current_layer[index].node, current_layer[index + 1].node);
                    next_layer[p] = node;
                    unchecked {
                        p++;
                    }
                }
            }
        }

        // we should have arrived at the root node
        require(next_layer.length == 1);

        return next_layer[0].node;
    }

    /// @notice Calculate the hash of the root node using a sorted node approach.
    /// @dev Use this function to calculate the hash of the root node
    /// @param proof A list of the merkle nodes that are needed to re-calculate root node.
    /// @param leaves A list of the leaves to prove
    /// @return Hash of root node, value is a bytes32 type
    function CalculateRootSorted(Node[][] memory proof, Node[] memory leaves) internal pure returns (bytes32) {
        // holds the output from hashing a previous layer
        Node[] memory next_layer = new Node[](0);

        // merge leaves
        proof[0] = mergeSort(leaves, proof[0]);

        uint256 proof_length = proof.length;
        for (uint256 height = 0; height < proof_length; height++) {
            Node[] memory current_layer = new Node[](0);

            if (next_layer.length == 0) {
                current_layer = proof[height];
            } else {
                current_layer = mergeSort(proof[height], next_layer);
            }
            uint256 current_layer_length = current_layer.length;
            uint256 p = 0;

            next_layer = new Node[](div_ceil(current_layer_length, 2));
            for (uint256 index = 0; index < current_layer_length; index += 2) {
                if (index + 1 >= current_layer_length) {
                    Node memory node = current_layer[index];
                    node.k_index = div_floor(current_layer[index].k_index, 2);
                    next_layer[p] = node;
                } else {
                    Node memory node;
                    bytes32 a = current_layer[index].node;
                    bytes32 b = current_layer[index + 1].node;
                    if (a < b) {
                        node.node = _optimizedHash(a, b);
                    } else {
                        node.node = _optimizedHash(b, a);
                    }
                    node.k_index = div_floor(current_layer[index].k_index, 2);
                    next_layer[p] = node;
                    unchecked {
                        p++;
                    }
                }
            }
        }

        // we should have arrived at the root node
        require(next_layer.length == 1);

        return next_layer[0].node;
    }

    function div_floor(uint256 x, uint256 y) internal pure returns (uint256) {
        return x / y;
    }

    function div_ceil(uint256 x, uint256 y) internal pure returns (uint256) {
        uint256 result = x / y;
        if (x % y != 0) {
            unchecked {
                result += 1;
            }
        }

        return result;
    }

    /// @notice an internal function to merge two arrays and sort them at the same time.
    /// @dev compares the k-index of each node and sort in increasing order
    /// @param arr1 leftmost index in arr
    /// @param arr2 highest index in arr
    function mergeSort(Node[] memory arr1, Node[] memory arr2) internal pure returns (Node[] memory) {
        // merge the two arrays
        uint256 i = 0;
        uint256 j = 0;
        uint256 k = 0;
        uint256 arr1_length = arr1.length;
        uint256 arr2_length = arr2.length;
        uint256 out_len = arr1_length + arr2_length;
        Node[] memory out = new Node[](out_len);

        while (i < arr1_length && j < arr2_length) {
            if (arr1[i].k_index < arr2[j].k_index) {
                out[k] = arr1[i];
                unchecked {
                    i++;
                    k++;
                }
            } else {
                out[k] = arr2[j];
                unchecked {
                    j++;
                    k++;
                }
            }
        }

        while (i < arr1_length) {
            out[k] = arr1[i];
            unchecked {
                i++;
                k++;
            }
        }

        while (j < arr2_length) {
            out[k] = arr2[j];
            unchecked {
                j++;
                k++;
            }
        }

        return out;
    }

    /// @notice compute the keccak256 hash of two nodes
    /// @param node1 hash of one of the two nodes
    /// @param node2 hash of the other of the two nodes
    function _optimizedHash(bytes32 node1, bytes32 node2) internal pure returns (bytes32 hash) {
        assembly {
            // use EVM scratch space, its memory safe
            mstore(0x0, node1)
            mstore(0x20, node2)
            hash := keccak256(0x0, 0x40)
        }
    }

    /// @notice compute the height of the tree whose total number of leaves is given, it accounts for unbalanced trees.
    /// @param leavesCount number of leaves in the tree
    /// @return height of the tree
    function TreeHeight(uint256 leavesCount) internal pure returns (uint256) {
        uint256 height = Math.log2(leavesCount, Math.Rounding.Up);
        if (!isPowerOfTwo(leavesCount)) {
            unchecked {
                height++;
            }
        }

        return height;
    }

    function isPowerOfTwo(uint256 x) internal pure returns (bool) {
        if (x == 0) {
            return false;
        }

        return (x & (x - 1)) == 0;
    }
}

File 13 of 25 : Types.sol
pragma solidity 0.8.17;

// SPDX-License-Identifier: Apache2

// Outcome of a successfully verified merkle-patricia proof
struct StorageValue {
    // the storage key
    bytes key;
    // the encoded value
    bytes value;
}

/// @title A representation of a Merkle tree node
struct Node {
    // Distance of the node to the leftmost node
    uint256 k_index;
    // A hash of the node itself
    bytes32 node;
}


/// @title A representation of a MerkleMountainRange leaf
struct MmrLeaf {
    // the leftmost index of a node
    uint256 k_index;
    // The position in the tree
    uint256 leaf_index;
    // The hash of the position in the tree
    bytes32 hash;
}

struct Iterator {
    uint256 offset;
    bytes32[] data;
}

File 14 of 25 : Node.sol
pragma solidity 0.8.17;

// SPDX-License-Identifier: Apache2

import "./NibbleSlice.sol";
import "./Bytes.sol";

/// This is an enum for the different node types.
struct NodeKind {
    bool isEmpty;
    bool isLeaf;
    bool isHashedLeaf;
    bool isNibbledValueBranch;
    bool isNibbledHashedValueBranch;
    bool isNibbledBranch;
    bool isExtension;
    bool isBranch;
    uint256 nibbleSize;
    ByteSlice data;
}

struct NodeHandle {
    bool isHash;
    bytes32 hash;
    bool isInline;
    bytes inLine;
}

struct Extension {
    NibbleSlice key;
    NodeHandle node;
}

struct Branch {
    NodeHandleOption value;
    NodeHandleOption[16] children;
}

struct NibbledBranch {
    NibbleSlice key;
    NodeHandleOption value;
    NodeHandleOption[16] children;
}

struct ValueOption {
    bool isSome;
    bytes value;
}

struct NodeHandleOption {
    bool isSome;
    NodeHandle value;
}

struct Leaf {
    NibbleSlice key;
    NodeHandle value;
}

struct TrieNode {
    bytes32 hash;
    bytes node;
}

File 15 of 25 : Option.sol
pragma solidity 0.8.17;

import "./Node.sol";

// SPDX-License-Identifier: Apache2

library Option {
    function isSome(ValueOption memory val) internal pure returns (bool) {
        return val.isSome == true;
    }

    function isSome(NodeHandleOption memory val) internal pure returns (bool) {
        return val.isSome == true;
    }
}

File 16 of 25 : NibbleSlice.sol
pragma solidity 0.8.17;

// SPDX-License-Identifier: Apache2

struct NibbleSlice {
    bytes data;
    uint256 offset;
}

library NibbleSliceOps {
    uint256 internal constant NIBBLE_PER_BYTE = 2;
    uint256 internal constant BITS_PER_NIBBLE = 4;

    function len(NibbleSlice memory nibble) internal pure returns (uint256) {
        return nibble.data.length * NIBBLE_PER_BYTE - nibble.offset;
    }

    function mid(NibbleSlice memory self, uint256 i) internal pure returns (NibbleSlice memory) {
        return NibbleSlice(self.data, self.offset + i);
    }

    function isEmpty(NibbleSlice memory self) internal pure returns (bool) {
        return len(self) == 0;
    }

    function eq(NibbleSlice memory self, NibbleSlice memory other) internal pure returns (bool) {
        return len(self) == len(other) && startsWith(self, other);
    }

    function at(NibbleSlice memory self, uint256 i) internal pure returns (uint256) {
        uint256 ix = (self.offset + i) / NIBBLE_PER_BYTE;
        uint256 pad = (self.offset + i) % NIBBLE_PER_BYTE;
        uint8 data = uint8(self.data[ix]);
        return (pad == 1) ? data & 0x0F : data >> BITS_PER_NIBBLE;
    }

    function startsWith(NibbleSlice memory self, NibbleSlice memory other) internal pure returns (bool) {
        return commonPrefix(self, other) == len(other);
    }

    function commonPrefix(NibbleSlice memory self, NibbleSlice memory other) internal pure returns (uint256) {
        uint256 self_align = self.offset % NIBBLE_PER_BYTE;
        uint256 other_align = other.offset % NIBBLE_PER_BYTE;

        if (self_align == other_align) {
            uint256 self_start = self.offset / NIBBLE_PER_BYTE;
            uint256 other_start = other.offset / NIBBLE_PER_BYTE;
            uint256 first = 0;

            if (self_align != 0) {
                if ((self.data[self_start] & 0x0F) != (other.data[other_start] & 0x0F)) {
                    return 0;
                }
                ++self_start;
                ++other_start;
                ++first;
            }
            bytes memory selfSlice = bytesSlice(self.data, self_start);
            bytes memory otherSlice = bytesSlice(other.data, other_start);
            return biggestDepth(selfSlice, otherSlice) + first;
        } else {
            uint256 s = min(len(self), len(other));
            uint256 i = 0;
            while (i < s) {
                if (at(self, i) != at(other, i)) {
                    break;
                }
                ++i;
            }
            return i;
        }
    }

    function biggestDepth(bytes memory a, bytes memory b) internal pure returns (uint256) {
        uint256 upperBound = min(a.length, b.length);
        uint256 i = 0;
        while (i < upperBound) {
            if (a[i] != b[i]) {
                return i * NIBBLE_PER_BYTE + leftCommon(a[i], b[i]);
            }
            ++i;
        }
        return i * NIBBLE_PER_BYTE;
    }

    function leftCommon(bytes1 a, bytes1 b) internal pure returns (uint256) {
        if (a == b) {
            return 2;
        } else if (uint8(a) & 0xF0 == uint8(b) & 0xF0) {
            return 1;
        } else {
            return 0;
        }
    }

    function bytesSlice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) {
        uint256 bytesLength = _bytes.length;
        uint256 _length = bytesLength - _start;
        require(bytesLength >= _start, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                tempBytes := mload(0x40) // load free memory pointer
                let lengthmod := and(_length, 31)

                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for { 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)

                mstore(0x40, and(add(mc, 31), not(31)))
            }
            default {
                tempBytes := mload(0x40)
                mstore(tempBytes, 0)

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

    function min(uint256 a, uint256 b) private pure returns (uint256) {
        return (a < b) ? a : b;
    }
}

File 17 of 25 : TrieDB.sol
// SPDX-License-Identifier: Apache2
pragma solidity 0.8.17;

import "./Node.sol";

library TrieDB {
    function get(TrieNode[] memory nodes, bytes32 hash) internal pure returns (bytes memory) {
        for (uint256 i = 0; i < nodes.length; i++) {
            if (nodes[i].hash == hash) {
                return nodes[i].node;
            }
        }
        revert("Incomplete Proof!");
    }

    function load(TrieNode[] memory nodes, NodeHandle memory node) internal pure returns (bytes memory) {
        if (node.isInline) {
            return node.inLine;
        } else if (node.isHash) {
            return get(nodes, node.hash);
        }

        return bytes("");
    }

    function isNibbledBranch(NodeKind memory node) internal pure returns (bool) {
        return (node.isNibbledBranch || node.isNibbledHashedValueBranch || node.isNibbledValueBranch);
    }

    function isExtension(NodeKind memory node) internal pure returns (bool) {
        return node.isExtension;
    }

    function isBranch(NodeKind memory node) internal pure returns (bool) {
        return node.isBranch;
    }

    function isLeaf(NodeKind memory node) internal pure returns (bool) {
        return (node.isLeaf || node.isHashedLeaf);
    }

    function isEmpty(NodeKind memory node) internal pure returns (bool) {
        return node.isEmpty;
    }

    function isHash(NodeHandle memory node) internal pure returns (bool) {
        return node.isHash;
    }

    function isInline(NodeHandle memory node) internal pure returns (bool) {
        return node.isInline;
    }
}

File 18 of 25 : SubstrateTrieDB.sol
pragma solidity 0.8.17;

import "../Node.sol";
import "../Bytes.sol";
import {NibbleSliceOps} from "../NibbleSlice.sol";

import {ScaleCodec} from "./ScaleCodec.sol";
import "openzeppelin/utils/Strings.sol";

// SPDX-License-Identifier: Apache2

library SubstrateTrieDB {
    uint8 public constant FIRST_PREFIX = 0x00 << 6;
    uint8 public constant PADDING_BITMASK = 0x0F;
    uint8 public constant EMPTY_TRIE = FIRST_PREFIX | (0x00 << 4);
    uint8 public constant LEAF_PREFIX_MASK = 0x01 << 6;
    uint8 public constant BRANCH_WITH_MASK = 0x03 << 6;
    uint8 public constant BRANCH_WITHOUT_MASK = 0x02 << 6;
    uint8 public constant ALT_HASHING_LEAF_PREFIX_MASK = FIRST_PREFIX | (0x01 << 5);
    uint8 public constant ALT_HASHING_BRANCH_WITH_MASK = FIRST_PREFIX | (0x01 << 4);
    uint8 public constant NIBBLE_PER_BYTE = 2;
    uint256 public constant NIBBLE_SIZE_BOUND = uint256(type(uint16).max);
    uint256 public constant BITMAP_LENGTH = 2;
    uint256 public constant HASH_lENGTH = 32;

    function decodeNodeKind(bytes memory encoded) internal pure returns (NodeKind memory) {
        NodeKind memory node;
        ByteSlice memory input = ByteSlice(encoded, 0);
        uint8 i = Bytes.readByte(input);

        if (i == EMPTY_TRIE) {
            node.isEmpty = true;
            return node;
        }

        uint8 mask = i & (0x03 << 6);

        if (mask == LEAF_PREFIX_MASK) {
            node.nibbleSize = decodeSize(i, input, 2);
            node.isLeaf = true;
        } else if (mask == BRANCH_WITH_MASK) {
            node.nibbleSize = decodeSize(i, input, 2);
            node.isNibbledValueBranch = true;
        } else if (mask == BRANCH_WITHOUT_MASK) {
            node.nibbleSize = decodeSize(i, input, 2);
            node.isNibbledBranch = true;
        } else if (mask == EMPTY_TRIE) {
            if (i & (0x07 << 5) == ALT_HASHING_LEAF_PREFIX_MASK) {
                node.nibbleSize = decodeSize(i, input, 3);
                node.isHashedLeaf = true;
            } else if (i & (0x0F << 4) == ALT_HASHING_BRANCH_WITH_MASK) {
                node.nibbleSize = decodeSize(i, input, 4);
                node.isNibbledHashedValueBranch = true;
            } else {
                // do not allow any special encoding
                revert("Unallowed encoding");
            }
        }
        node.data = input;

        return node;
    }

    function decodeNibbledBranch(NodeKind memory node) internal pure returns (NibbledBranch memory) {
        NibbledBranch memory nibbledBranch;
        ByteSlice memory input = node.data;

        bool padding = node.nibbleSize % NIBBLE_PER_BYTE != 0;
        if (padding && (padLeft(uint8(input.data[input.offset])) != 0)) {
            revert("Bad Format!");
        }
        uint256 nibbleLen = ((node.nibbleSize + (NibbleSliceOps.NIBBLE_PER_BYTE - 1)) / NibbleSliceOps.NIBBLE_PER_BYTE);
        nibbledBranch.key = NibbleSlice(Bytes.read(input, nibbleLen), node.nibbleSize % NIBBLE_PER_BYTE);

        bytes memory bitmapBytes = Bytes.read(input, BITMAP_LENGTH);
        uint16 bitmap = uint16(ScaleCodec.decodeUint256(bitmapBytes));

        NodeHandleOption memory valueHandle;
        if (node.isNibbledHashedValueBranch) {
            valueHandle.isSome = true;
            valueHandle.value.isHash = true;
            valueHandle.value.hash = Bytes.toBytes32(Bytes.read(input, HASH_lENGTH));
        } else if (node.isNibbledValueBranch) {
            uint256 len = ScaleCodec.decodeUintCompact(input);
            valueHandle.isSome = true;
            valueHandle.value.isInline = true;
            valueHandle.value.inLine = Bytes.read(input, len);
        }
        nibbledBranch.value = valueHandle;

        for (uint256 i = 0; i < 16; i++) {
            NodeHandleOption memory childHandle;
            if (valueAt(bitmap, i)) {
                childHandle.isSome = true;
                uint256 len = ScaleCodec.decodeUintCompact(input);
                //                revert(string.concat("node index: ", Strings.toString(len)));
                if (len == HASH_lENGTH) {
                    childHandle.value.isHash = true;
                    childHandle.value.hash = Bytes.toBytes32(Bytes.read(input, HASH_lENGTH));
                } else {
                    childHandle.value.isInline = true;
                    childHandle.value.inLine = Bytes.read(input, len);
                }
            }
            nibbledBranch.children[i] = childHandle;
        }

        return nibbledBranch;
    }

    function decodeLeaf(NodeKind memory node) internal pure returns (Leaf memory) {
        Leaf memory leaf;
        ByteSlice memory input = node.data;

        bool padding = node.nibbleSize % NIBBLE_PER_BYTE != 0;
        if (padding && padLeft(uint8(input.data[input.offset])) != 0) {
            revert("Bad Format!");
        }
        uint256 nibbleLen = (node.nibbleSize + (NibbleSliceOps.NIBBLE_PER_BYTE - 1)) / NibbleSliceOps.NIBBLE_PER_BYTE;
        bytes memory nibbleBytes = Bytes.read(input, nibbleLen);
        leaf.key = NibbleSlice(nibbleBytes, node.nibbleSize % NIBBLE_PER_BYTE);

        NodeHandle memory handle;
        if (node.isHashedLeaf) {
            handle.isHash = true;
            handle.hash = Bytes.toBytes32(Bytes.read(input, HASH_lENGTH));
        } else {
            uint256 len = ScaleCodec.decodeUintCompact(input);
            handle.isInline = true;
            handle.inLine = Bytes.read(input, len);
        }
        leaf.value = handle;

        return leaf;
    }

    function decodeSize(uint8 first, ByteSlice memory encoded, uint8 prefixMask) internal pure returns (uint256) {
        uint8 maxValue = uint8(255 >> prefixMask);
        uint256 result = uint256(first & maxValue);

        if (result < maxValue) {
            return result;
        }

        result -= 1;

        while (result <= NIBBLE_SIZE_BOUND) {
            uint256 n = uint256(Bytes.readByte(encoded));
            if (n < 255) {
                return result + n + 1;
            }
            result += 255;
        }

        return NIBBLE_SIZE_BOUND;
    }

    function padLeft(uint8 b) internal pure returns (uint8) {
        return b & ~PADDING_BITMASK;
    }

    function valueAt(uint16 bitmap, uint256 i) internal pure returns (bool) {
        return bitmap & (uint16(1) << uint16(i)) != 0;
    }
}

File 19 of 25 : EthereumTrieDB.sol
pragma solidity 0.8.17;

import "../Node.sol";
import "../Bytes.sol";
import {NibbleSliceOps} from "../NibbleSlice.sol";
import "./RLPReader.sol";

// SPDX-License-Identifier: Apache2

library EthereumTrieDB {
    using RLPReader for bytes;
    using RLPReader for RLPReader.RLPItem;
    using RLPReader for RLPReader.Iterator;

    bytes constant HASHED_NULL_NODE = hex"56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421";

    function decodeNodeKind(bytes memory encoded) external pure returns (NodeKind memory) {
        NodeKind memory node;
        ByteSlice memory input = ByteSlice(encoded, 0);
        if (Bytes.equals(encoded, HASHED_NULL_NODE)) {
            node.isEmpty = true;
            return node;
        }
        RLPReader.RLPItem[] memory itemList = encoded.toRlpItem().toList();
        uint256 numItems = itemList.length;
        if (numItems == 0) {
            node.isEmpty = true;
            return node;
        } else if (numItems == 2) {
            //It may be a leaf or extension
            bytes memory key = itemList[0].toBytes();
            uint256 prefix;
            assembly {
                let first := shr(248, mload(add(key, 32)))
                prefix := shr(4, first)
            }
            if (prefix == 2 || prefix == 3) {
                node.isLeaf = true;
            } else {
                node.isExtension = true;
            }
        } else if (numItems == 17) {
            node.isBranch = true;
        } else {
            revert("Invalid data");
        }
        node.data = input;
        return node;
    }

    function decodeLeaf(NodeKind memory node) external pure returns (Leaf memory) {
        Leaf memory leaf;
        RLPReader.RLPItem[] memory decoded = node.data.data.toRlpItem().toList();
        bytes memory data = decoded[1].toBytes();
        //Remove the first byte, which is the prefix and not present in the user provided key
        leaf.key = NibbleSlice(Bytes.substr(decoded[0].toBytes(), 1), 0);
        leaf.value = NodeHandle(false, bytes32(0), true, data);

        return leaf;
    }

    function decodeExtension(NodeKind memory node) external pure returns (Extension memory) {
        Extension memory extension;
        RLPReader.RLPItem[] memory decoded = node.data.data.toRlpItem().toList();
        bytes memory data = decoded[1].toBytes();
        uint8 isOdd = uint8(decoded[0].toBytes()[0] >> 4) & 0x01;
        //Remove the first byte, which is the prefix and not present in the user provided key
        extension.key = NibbleSlice(Bytes.substr(decoded[0].toBytes(), (isOdd + 1) % 2), isOdd);
        extension.node = NodeHandle(true, Bytes.toBytes32(data), false, new bytes(0));
        return extension;
    }

    function decodeBranch(NodeKind memory node) external pure returns (Branch memory) {
        Branch memory branch;
        RLPReader.RLPItem[] memory decoded = node.data.data.toRlpItem().toList();

        NodeHandleOption[16] memory childrens;

        for (uint256 i = 0; i < 16; i++) {
            bytes memory dataAsBytes = decoded[i].toBytes();
            if (dataAsBytes.length != 32) {
                childrens[i] = NodeHandleOption(false, NodeHandle(false, bytes32(0), false, new bytes(0)));
            } else {
                bytes32 data = Bytes.toBytes32(dataAsBytes);
                childrens[i] = NodeHandleOption(true, NodeHandle(true, data, false, new bytes(0)));
            }
        }
        if (isEmpty(decoded[16].toBytes())) {
            branch.value = NodeHandleOption(false, NodeHandle(false, bytes32(0), false, new bytes(0)));
        } else {
            branch.value = NodeHandleOption(true, NodeHandle(false, bytes32(0), true, decoded[16].toBytes()));
        }
        branch.children = childrens;

        return branch;
    }

    function isEmpty(bytes memory item) internal pure returns (bool) {
        return item.length > 0 && (item[0] == 0xc0 || item[0] == 0x80);
    }
}

File 20 of 25 : Memory.sol
pragma solidity 0.8.17;

// SPDX-License-Identifier: Apache2

library Memory {
    uint256 internal constant WORD_SIZE = 32;

    // Compares the 'len' bytes starting at address 'addr' in memory with the 'len'
    // bytes starting at 'addr2'.
    // Returns 'true' if the bytes are the same, otherwise 'false'.
    function equals(uint256 addr, uint256 addr2, uint256 len) internal pure returns (bool equal) {
        assembly {
            equal := eq(keccak256(addr, len), keccak256(addr2, len))
        }
    }

    // Compares the 'len' bytes starting at address 'addr' in memory with the bytes stored in
    // 'bts'. It is allowed to set 'len' to a lower value then 'bts.length', in which case only
    // the first 'len' bytes will be compared.
    // Requires that 'bts.length >= len'

    function equals(uint256 addr, uint256 len, bytes memory bts) internal pure returns (bool equal) {
        require(bts.length >= len);
        uint256 addr2;
        assembly {
            addr2 := add(bts, /*BYTES_HEADER_SIZE*/ 32)
        }
        return equals(addr, addr2, len);
    }
    // Returns a memory pointer to the data portion of the provided bytes array.

    function dataPtr(bytes memory bts) internal pure returns (uint256 addr) {
        assembly {
            addr := add(bts, /*BYTES_HEADER_SIZE*/ 32)
        }
    }

    // Creates a 'bytes memory' variable from the memory address 'addr', with the
    // length 'len'. The function will allocate new memory for the bytes array, and
    // the 'len bytes starting at 'addr' will be copied into that new memory.
    function toBytes(uint256 addr, uint256 len) internal pure returns (bytes memory bts) {
        bts = new bytes(len);
        uint256 btsptr;
        assembly {
            btsptr := add(bts, /*BYTES_HEADER_SIZE*/ 32)
        }
        copy(addr, btsptr, len);
    }

    // Copies 'self' into a new 'bytes memory'.
    // Returns the newly created 'bytes memory'
    // The returned bytes will be of length '32'.
    function toBytes(bytes32 self) internal pure returns (bytes memory bts) {
        bts = new bytes(32);
        assembly {
            mstore(add(bts, /*BYTES_HEADER_SIZE*/ 32), self)
        }
    }

    // Copy 'len' bytes from memory address 'src', to address 'dest'.
    // This function does not check the or destination, it only copies
    // the bytes.
    function copy(uint256 src, uint256 dest, uint256 len) internal pure {
        // Copy word-length chunks while possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += WORD_SIZE;
            src += WORD_SIZE;
        }

        // Copy remaining bytes
        uint256 mask =
            len == 0 ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff : 256 ** (WORD_SIZE - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    // This function does the same as 'dataPtr(bytes memory)', but will also return the
    // length of the provided bytes array.
    function fromBytes(bytes memory bts) internal pure returns (uint256 addr, uint256 len) {
        len = bts.length;
        assembly {
            addr := add(bts, /*BYTES_HEADER_SIZE*/ 32)
        }
    }
}

File 21 of 25 : IDispatcher.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {StateMachineHeight} from "./IConsensusClient.sol";
import {PostRequest} from "./Message.sol";

// @notice An object for dispatching post requests to the Hyperbridge
struct DispatchPost {
	// bytes representation of the destination state machine
	bytes dest;
	// the destination module
	bytes to;
	// the request body
	bytes body;
	// timeout for this request in seconds
	uint64 timeout;
	// the amount put up to be paid to the relayer,
	// this is charged in `IIsmpHost.feeToken` to `msg.sender`
	uint256 fee;
	// who pays for this request?
	address payer;
}

// @notice An object for dispatching get requests to the Hyperbridge
struct DispatchGet {
	// bytes representation of the destination state machine
	bytes dest;
	// height at which to read the state machine
	uint64 height;
	// storage keys to read
	bytes[] keys;
	// timeout for this request in seconds
	uint64 timeout;
	// Hyperbridge protocol fees for processing this request.
	uint256 fee;
}

struct DispatchPostResponse {
	// The request that initiated this response
	PostRequest request;
	// bytes for post response
	bytes response;
	// timeout for this response in seconds
	uint64 timeout;
	// the amount put up to be paid to the relayer,
	// this is charged in `IIsmpHost.feeToken` to `msg.sender`
	uint256 fee;
	// who pays for this request?
	address payer;
}

/*
 * @title The Ismp Dispatcher
 * @author Polytope Labs ([email protected])
 *
 * @notice The IHandler interface serves as the entry point for ISMP datagrams, i.e consensus, requests & response messages.
 */
interface IDispatcher {
	/**
	 * @dev Dispatch a POST request to Hyperbridge
	 *
	 * @notice Payment for the request can be made with either the native token or the IIsmpHost.feeToken.
	 * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router.
	 * Will revert if enough native tokens are not provided.
	 *
	 * If no native tokens are provided then it will try to collect payment from the calling contract in
	 * the IIsmpHost.feeToken.
	 *
	 * @param request - post request
	 * @return commitment - the request commitment
	 */
	function dispatch(DispatchPost memory request) external payable returns (bytes32 commitment);

	/**
	 * @dev Dispatch a GET request to Hyperbridge
	 *
	 * @notice Payment for the request can be made with either the native token or the IIsmpHost.feeToken.
	 * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router.
	 * Will revert if enough native tokens are not provided.
	 *
	 * If no native tokens are provided then it will try to collect payment from the calling contract in
	 * the IIsmpHost.feeToken.
	 *
	 * @param request - get request
	 * @return commitment - the request commitment
	 */
	function dispatch(DispatchGet memory request) external payable returns (bytes32 commitment);

	/**
	 * @dev Dispatch a POST response to Hyperbridge
	 *
	 * @notice Payment for the request can be made with either the native token or the IIsmpHost.feeToken.
	 * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router.
	 * Will revert if enough native tokens are not provided.
	 *
	 * If no native tokens are provided then it will try to collect payment from the calling contract in
	 * the IIsmpHost.feeToken.
	 *
	 * @param response - post response
	 * @return commitment - the request commitment
	 */
	function dispatch(DispatchPostResponse memory response) external payable returns (bytes32 commitment);

	/**
	 * @dev Increase the relayer fee for a previously dispatched request.
	 * This is provided for use only on pending requests, such that when they timeout,
	 * the user can recover the entire relayer fee.
	 *
	 * @notice Payment can be made with either the native token or the IIsmpHost.feeToken.
	 * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router.
	 * Will revert if enough native tokens are not provided.
	 *
	 * If no native tokens are provided then it will try to collect payment from the calling contract in
	 * the IIsmpHost.feeToken.
	 *
	 * If called on an already delivered request, these funds will be seen as a donation to the hyperbridge protocol.
	 * @param commitment - The request commitment
	 * @param amount - The amount provided in `IIsmpHost.feeToken()`
	 */
	function fundRequest(bytes32 commitment, uint256 amount) external payable;

	/**
	 * @dev Increase the relayer fee for a previously dispatched response.
	 * This is provided for use only on pending responses, such that when they timeout,
	 * the user can recover the entire relayer fee.
	 *
	 * @notice Payment can be made with either the native token or the IIsmpHost.feeToken.
	 * If native tokens are supplied, it will perform a swap under the hood using the local uniswap router.
	 * Will revert if enough native tokens are not provided.
	 *
	 * If no native tokens are provided then it will try to collect payment from the calling contract in
	 * the IIsmpHost.feeToken.
	 *
	 * If called on an already delivered response, these funds will be seen as a donation to the hyperbridge protocol.
	 * @param commitment - The response commitment
	 * @param amount - The amount to be provided in `IIsmpHost.feeToken()`
	 */
	function fundResponse(bytes32 commitment, uint256 amount) external payable;
}

File 22 of 25 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 23 of 25 : ScaleCodec.sol
pragma solidity 0.8.17;

// SPDX-License-Identifier: Apache2

import {Bytes, ByteSlice} from "../Bytes.sol";

library ScaleCodec {
    // Decodes a SCALE encoded uint256 by converting bytes (bid endian) to little endian format
    function decodeUint256(bytes memory data) internal pure returns (uint256) {
        uint256 number;
        for (uint256 i = data.length; i > 0; i--) {
            number = number + uint256(uint8(data[i - 1])) * (2 ** (8 * (i - 1)));
        }
        return number;
    }

    // Decodes a SCALE encoded compact unsigned integer
    function decodeUintCompact(ByteSlice memory data) internal pure returns (uint256 v) {
        uint8 b = Bytes.readByte(data); // read the first byte
        uint8 mode = b % 4; // bitwise operation

        uint256 value;
        if (mode == 0) {
            // [0, 63]
            value = b >> 2; // right shift to remove mode bits
        } else if (mode == 1) {
            // [64, 16383]
            uint8 bb = Bytes.readByte(data); // read the second byte
            uint64 r = bb; // convert to uint64
            r <<= 6; // multiply by * 2^6
            r += b >> 2; // right shift to remove mode bits
            value = r;
        } else if (mode == 2) {
            // [16384, 1073741823]
            uint8 b2 = Bytes.readByte(data); // read the next 3 bytes
            uint8 b3 = Bytes.readByte(data);
            uint8 b4 = Bytes.readByte(data);

            uint32 x1 = uint32(b) | (uint32(b2) << 8); // convert to little endian
            uint32 x2 = x1 | (uint32(b3) << 16);
            uint32 x3 = x2 | (uint32(b4) << 24);

            x3 >>= 2; // remove the last 2 mode bits
            value = uint256(x3);
        } else if (mode == 3) {
            // [1073741824, 4503599627370496]
            uint8 l = (b >> 2) + 4; // remove mode bits
            require(l <= 8, "unexpected prefix decoding Compact<Uint>");
            return decodeUint256(Bytes.read(data, l));
        } else {
            revert("Code should be unreachable");
        }
        return value;
    }

    // Decodes a SCALE encoded compact unsigned integer
    function decodeUintCompact(bytes memory data) internal pure returns (uint256 v, uint8 m) {
        uint8 b = readByteAtIndex(data, 0); // read the first byte
        uint8 mode = b & 3; // bitwise operation

        uint256 value;
        if (mode == 0) {
            // [0, 63]
            value = b >> 2; // right shift to remove mode bits
        } else if (mode == 1) {
            // [64, 16383]
            uint8 bb = readByteAtIndex(data, 1); // read the second byte
            uint64 r = bb; // convert to uint64
            r <<= 6; // multiply by * 2^6
            r += b >> 2; // right shift to remove mode bits
            value = r;
        } else if (mode == 2) {
            // [16384, 1073741823]
            uint8 b2 = readByteAtIndex(data, 1); // read the next 3 bytes
            uint8 b3 = readByteAtIndex(data, 2);
            uint8 b4 = readByteAtIndex(data, 3);

            uint32 x1 = uint32(b) | (uint32(b2) << 8); // convert to little endian
            uint32 x2 = x1 | (uint32(b3) << 16);
            uint32 x3 = x2 | (uint32(b4) << 24);

            x3 >>= 2; // remove the last 2 mode bits
            value = uint256(x3);
        } else if (mode == 3) {
            // [1073741824, 4503599627370496]
            uint8 l = b >> 2; // remove mode bits
            require(l > 32, "Not supported: number cannot be greater than 32 bytes");
        } else {
            revert("Code should be unreachable");
        }
        return (value, mode);
    }

    // The biggest compact supported uint is 2 ** 536 - 1.
    // But the biggest value supported by this method is 2 ** 256 - 1(max of uint256)
    function encodeUintCompact(uint256 v) internal pure returns (bytes memory) {
        if (v < 64) {
            return abi.encodePacked(uint8(v << 2));
        } else if (v < 2 ** 14) {
            return abi.encodePacked(reverse16(uint16(((v << 2) + 1))));
        } else if (v < 2 ** 30) {
            return abi.encodePacked(reverse32(uint32(((v << 2) + 2))));
        } else {
            bytes memory valueBytes = Bytes.removeEndingZero(abi.encodePacked(reverse256(v)));

            uint256 length = valueBytes.length;
            uint8 prefix = uint8(((length - 4) << 2) + 3);

            return abi.encodePacked(prefix, valueBytes);
        }
    }

    // Read a byte at a specific index and return it as type uint8
    function readByteAtIndex(bytes memory data, uint8 index) internal pure returns (uint8) {
        return uint8(data[index]);
    }

    // Sources:
    //   * https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity/50528
    //   * https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel

    function reverse256(uint256 input) internal pure returns (uint256 v) {
        v = input;

        // swap bytes
        v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8)
            | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);

        // swap 2-byte long pairs
        v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16)
            | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);

        // swap 4-byte long pairs
        v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32)
            | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);

        // swap 8-byte long pairs
        v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64)
            | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);

        // swap 16-byte long pairs
        v = (v >> 128) | (v << 128);
    }

    function reverse128(uint128 input) internal pure returns (uint128 v) {
        v = input;

        // swap bytes
        v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8);

        // swap 2-byte long pairs
        v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16);

        // swap 4-byte long pairs
        v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32);

        // swap 8-byte long pairs
        v = (v >> 64) | (v << 64);
    }

    function reverse64(uint64 input) internal pure returns (uint64 v) {
        v = input;

        // swap bytes
        v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8);

        // swap 2-byte long pairs
        v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);

        // swap 4-byte long pairs
        v = (v >> 32) | (v << 32);
    }

    function reverse32(uint32 input) internal pure returns (uint32 v) {
        v = input;

        // swap bytes
        v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);

        // swap 2-byte long pairs
        v = (v >> 16) | (v << 16);
    }

    function reverse16(uint16 input) internal pure returns (uint16 v) {
        v = input;

        // swap bytes
        v = (v >> 8) | (v << 8);
    }

    function encode256(uint256 input) internal pure returns (bytes32) {
        return bytes32(reverse256(input));
    }

    function encode128(uint128 input) internal pure returns (bytes16) {
        return bytes16(reverse128(input));
    }

    function encode64(uint64 input) internal pure returns (bytes8) {
        return bytes8(reverse64(input));
    }

    function encode32(uint32 input) internal pure returns (bytes4) {
        return bytes4(reverse32(input));
    }

    function encode16(uint16 input) internal pure returns (bytes2) {
        return bytes2(reverse16(input));
    }

    function encodeBytes(bytes memory input) internal pure returns (bytes memory) {
        return abi.encodePacked(encodeUintCompact(input.length), input);
    }
}

File 24 of 25 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

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

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

/*
 * @author Hamdi Allam [email protected]
 * Please reach out with any questions or concerns
 */
pragma solidity >=0.5.10 <0.9.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.
     */
    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)

            // shift 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))
            }
        }
    }
}

Settings
{
  "remappings": [
    "@polytope-labs/ismp-solidity/=node_modules/@polytope-labs/ismp-solidity/interfaces/",
    "openzeppelin/=node_modules/openzeppelin-solidity/contracts/",
    "@polytope-labs/solidity-merkle-trees/=node_modules/@polytope-labs/solidity-merkle-trees/src/",
    "@polytope-labs/erc6160/=node_modules/@polytope-labs/erc6160/src/",
    "@uniswap/v2-periphery/=node_modules/@uniswap/v2-periphery/",
    "stringutils/=lib/solidity-stringutils/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solidity-stringutils/=lib/solidity-stringutils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {
    "node_modules/@polytope-labs/solidity-merkle-trees/src/MerklePatricia.sol": {
      "MerklePatricia": "0xd8e040255903d5534b41cc7b0b256f76dcdb343c"
    },
    "node_modules/@polytope-labs/solidity-merkle-trees/src/trie/ethereum/EthereumTrieDB.sol": {
      "EthereumTrieDB": "0x669bc248b74ad0657fdfc59b557f21d3c415be31"
    },
    "src/consensus/Header.sol": {
      "HeaderImpl": "0xe27eecad5fbc5b4c2a77cbbd5700df11cdb89ed8"
    }
  }
}

Contract ABI

[{"inputs":[],"name":"ChallengePeriodNotElapsed","type":"error"},{"inputs":[],"name":"ConsensusClientExpired","type":"error"},{"inputs":[],"name":"DuplicateMessage","type":"error"},{"inputs":[],"name":"HostFrozen","type":"error"},{"inputs":[],"name":"InvalidMessageDestination","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"MessageNotTimedOut","type":"error"},{"inputs":[],"name":"MessageTimedOut","type":"error"},{"inputs":[],"name":"StateCommitmentNotFound","type":"error"},{"inputs":[],"name":"UnknownMessage","type":"error"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"handleConsensus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"components":[{"components":[{"internalType":"bytes","name":"source","type":"bytes"},{"internalType":"bytes","name":"dest","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"},{"internalType":"bytes[]","name":"keys","type":"bytes[]"},{"internalType":"uint64","name":"height","type":"uint64"}],"internalType":"struct GetRequest[]","name":"timeouts","type":"tuple[]"}],"internalType":"struct GetTimeoutMessage","name":"message","type":"tuple"}],"name":"handleGetRequestTimeouts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"components":[{"internalType":"bytes[]","name":"proof","type":"bytes[]"},{"components":[{"internalType":"uint256","name":"stateMachineId","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct StateMachineHeight","name":"height","type":"tuple"},{"components":[{"internalType":"bytes","name":"source","type":"bytes"},{"internalType":"bytes","name":"dest","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"},{"internalType":"bytes[]","name":"keys","type":"bytes[]"},{"internalType":"uint64","name":"height","type":"uint64"}],"internalType":"struct GetRequest[]","name":"requests","type":"tuple[]"}],"internalType":"struct GetResponseMessage","name":"message","type":"tuple"}],"name":"handleGetResponses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"components":[{"components":[{"internalType":"bytes","name":"source","type":"bytes"},{"internalType":"bytes","name":"dest","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"from","type":"bytes"},{"internalType":"bytes","name":"to","type":"bytes"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"},{"internalType":"bytes","name":"body","type":"bytes"}],"internalType":"struct PostRequest[]","name":"timeouts","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"stateMachineId","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct StateMachineHeight","name":"height","type":"tuple"},{"internalType":"bytes[]","name":"proof","type":"bytes[]"}],"internalType":"struct PostRequestTimeoutMessage","name":"message","type":"tuple"}],"name":"handlePostRequestTimeouts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"components":[{"components":[{"components":[{"internalType":"uint256","name":"stateMachineId","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct StateMachineHeight","name":"height","type":"tuple"},{"internalType":"bytes32[]","name":"multiproof","type":"bytes32[]"},{"internalType":"uint256","name":"leafCount","type":"uint256"}],"internalType":"struct Proof","name":"proof","type":"tuple"},{"components":[{"components":[{"internalType":"bytes","name":"source","type":"bytes"},{"internalType":"bytes","name":"dest","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"from","type":"bytes"},{"internalType":"bytes","name":"to","type":"bytes"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"},{"internalType":"bytes","name":"body","type":"bytes"}],"internalType":"struct PostRequest","name":"request","type":"tuple"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"kIndex","type":"uint256"}],"internalType":"struct PostRequestLeaf[]","name":"requests","type":"tuple[]"}],"internalType":"struct PostRequestMessage","name":"request","type":"tuple"}],"name":"handlePostRequests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"components":[{"components":[{"components":[{"internalType":"bytes","name":"source","type":"bytes"},{"internalType":"bytes","name":"dest","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"from","type":"bytes"},{"internalType":"bytes","name":"to","type":"bytes"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"},{"internalType":"bytes","name":"body","type":"bytes"}],"internalType":"struct PostRequest","name":"request","type":"tuple"},{"internalType":"bytes","name":"response","type":"bytes"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"}],"internalType":"struct PostResponse[]","name":"timeouts","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"stateMachineId","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct StateMachineHeight","name":"height","type":"tuple"},{"internalType":"bytes[]","name":"proof","type":"bytes[]"}],"internalType":"struct PostResponseTimeoutMessage","name":"message","type":"tuple"}],"name":"handlePostResponseTimeouts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IIsmpHost","name":"host","type":"address"},{"components":[{"components":[{"components":[{"internalType":"uint256","name":"stateMachineId","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct StateMachineHeight","name":"height","type":"tuple"},{"internalType":"bytes32[]","name":"multiproof","type":"bytes32[]"},{"internalType":"uint256","name":"leafCount","type":"uint256"}],"internalType":"struct Proof","name":"proof","type":"tuple"},{"components":[{"components":[{"components":[{"internalType":"bytes","name":"source","type":"bytes"},{"internalType":"bytes","name":"dest","type":"bytes"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"bytes","name":"from","type":"bytes"},{"internalType":"bytes","name":"to","type":"bytes"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"},{"internalType":"bytes","name":"body","type":"bytes"}],"internalType":"struct PostRequest","name":"request","type":"tuple"},{"internalType":"bytes","name":"response","type":"bytes"},{"internalType":"uint64","name":"timeoutTimestamp","type":"uint64"}],"internalType":"struct PostResponse","name":"response","type":"tuple"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"kIndex","type":"uint256"}],"internalType":"struct PostResponseLeaf[]","name":"responses","type":"tuple[]"}],"internalType":"struct PostResponseMessage","name":"response","type":"tuple"}],"name":"handlePostResponses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50614dcc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80639d38eb351161005b5780639d38eb35146100ef578063bb1689be14610102578063e407f86b14610115578063ee241c0d1461012857600080fd5b806301ffc9a71461008d578063089b174c146100b45780632e8f454b146100c957806372becccd146100dc575b600080fd5b6100a061009b366004613b02565b61013b565b604051901515815260200160405180910390f35b6100c76100c2366004613b5c565b610172565b005b6100c76100d7366004613b5c565b61071e565b6100c76100ea366004613bbd565b610cc8565b6100c76100fd366004613bbd565b611346565b6100c7610110366004613c02565b61193b565b6100c7610123366004613b5c565b611db4565b6100c7610136366004613c86565b612356565b60006001600160e01b031982166378a7182760e01b148061016c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d79190613cdc565b905060018160038111156101ed576101ed613cfd565b148061020a5750600381600381111561020857610208613cfd565b145b1561022857604051631c6d5f7760e31b815260040160405180910390fd5b60408051631a880a9360e01b8152602085013560048201529084013560248201526000906001600160a01b03861690631a880a93906044016020604051808303816000875af115801561027f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102a39190613d13565b6102ad9042613d42565b90506000856001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103139190613d13565b9050801580159061032357508181115b156103415760405163048c969960e01b815260040160405180910390fd5b6040805163a70a8c4760e01b8152602087013560048201529086013560248201526000906001600160a01b0388169063a70a8c47906044016060604051808303816000875af1158015610398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103bc9190613e41565b60408101519091506103e1576040516353ae552b60e11b815260040160405180910390fd5b60006103ed8780613e5d565b9050905060005b818110156107135760006104088980613e5d565b8381811061041857610418613ea6565b905060200281019061042a9190613ebc565b6104339061406f565b8451909150610441826125b9565b6001600160401b03161115610469576040516348e8dd2f60e11b815260040160405180910390fd5b6000610474826125eb565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038d169063368bf464906024016040805180830381865afa1580156104be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e2919061407b565b60208101519091506001600160a01b03166105105760405163f058bfd960e01b815260040160405180910390fd5b604080516001808252818301909252600091816020015b60608152602001906001900390816105275790505090506040518060400160405280600f81526020016e52657175657374526563656970747360881b8152508360405160200161057991815260200190565b60408051601f198184030181529082905261059792916020016140d7565b6040516020818303038152906040528186815181106105b8576105b8613ea6565b6020026020010181905250600073d8e040255903d5534b41cc7b0b256f76dcdb343c631475ff4589604001518f80606001906105f49190613e5d565b866040518563ffffffff1660e01b815260040161061494939291906141b0565b600060405180830381865af4158015610631573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261065991908101906142cf565b60008151811061066b5761066b613ea6565b6020026020010151905080602001515160001461069b576040516309bde33960e01b815260040160405180910390fd5b6040516325a377d560e11b81526001600160a01b038f1690634b46efaa906106cb9088908790899060040161447b565b600060405180830381600087803b1580156106e557600080fd5b505af11580156106f9573d6000803e3d6000fd5b5050505050505050508061070c906144be565b90506103f4565b505050505050505050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107839190613cdc565b9050600181600381111561079957610799613cfd565b14806107b6575060038160038111156107b4576107b4613cfd565b145b156107d457604051631c6d5f7760e31b815260040160405180910390fd5b60408051631a880a9360e01b81526020850135600482015290840135602482015242906000906001600160a01b03871690631a880a93906044016020604051808303816000875af115801561082d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108519190613d13565b61085b9083613d42565b90506000866001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561089d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c19190613d13565b905080158015906108d157508181115b156108ef5760405163048c969960e01b815260040160405180910390fd5b6040805163a70a8c4760e01b8152602088013560048201529087013560248201526000906001600160a01b0389169063a70a8c47906044016060604051808303816000875af1158015610946573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061096a9190613e41565b6040015190508061098e576040516353ae552b60e11b815260040160405180910390fd5b600061099d6060890189613e5d565b9150600090506109ad8980613e5d565b6109b69161454a565b905060005b82811015610cbb5760006109d260608c018c613e5d565b838181106109e2576109e2613ea6565b90506020028101906109f49190613ebc565b6109fd90614582565b9050610a0881612604565b6001600160401b03168810610a3057604051631676f4b360e01b815260040160405180910390fd5b6000610a3b82612631565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038f169063368bf464906024016040805180830381865afa158015610a85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa9919061407b565b60208101519091506001600160a01b0316610ad75760405163f058bfd960e01b815260040160405180910390fd5b60006001600160a01b03168e6001600160a01b0316638856337e846040518263ffffffff1660e01b8152600401610b1091815260200190565b6040805180830381865afa158015610b2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b50919061407b565b602001516001600160a01b031614610b7b5760405163156a571760e11b815260040160405180910390fd5b600073d8e040255903d5534b41cc7b0b256f76dcdb343c6355028f6f89888760a0015187604051602001610bb191815260200190565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401610bdf9493929190614657565b600060405180830381865af4158015610bfc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c2491908101906142cf565b9050600060405180604001604052808681526020018381525090508f6001600160a01b031663ff279b9882610c563390565b6040518363ffffffff1660e01b8152600401610c7392919061472d565b600060405180830381600087803b158015610c8d57600080fd5b505af1158015610ca1573d6000803e3d6000fd5b50505050505050505080610cb4906144be565b90506109bb565b5050505050505050505050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2d9190613cdc565b90506001816003811115610d4357610d43613cfd565b1480610d6057506003816003811115610d5e57610d5e613cfd565b145b15610d7e57604051631c6d5f7760e31b815260040160405180910390fd5b4260006001600160a01b038616631a880a93610d9a87806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016020604051808303816000875af1158015610de2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e069190613d13565b610e109083613d42565b90506000866001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e769190613d13565b90508015801590610e8657508181115b15610ea45760405163048c969960e01b815260040160405180910390fd5b6000610eb36020880188613e5d565b905090506000816001600160401b03811115610ed157610ed1613d55565b604051908082528060200260200182016040528015610f1c57816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181610eef5790505b50905060005b82811015611156576000610f3960208b018b613e5d565b83818110610f4957610f49613ea6565b9050602002810190610f5b9190614801565b610f6490614896565b9050610f738160000151612732565b6001600160401b03168710610f9b57604051631676f4b360e01b815260040160405180910390fd5b805151600090610faa906125eb565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038e169063368bf464906024016040805180830381865afa158015610ff4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611018919061407b565b60208101519091506001600160a01b0316611046576040516309bde33960e01b815260040160405180910390fd5b60006001600160a01b03168d6001600160a01b0316638856337e61106d866000015161275f565b6040518263ffffffff1660e01b815260040161108b91815260200190565b6040805180830381865afa1580156110a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110cb919061407b565b602001516001600160a01b0316146110f65760405163156a571760e11b815260040160405180910390fd5b60405180606001604052808460400151815260200184602001518152602001611122856000015161275f565b81525085858151811061113757611137613ea6565b60200260200101819052505050508061114f906144be565b9050610f22565b5060006001600160a01b038a1663a70a8c476111728b806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016060604051808303816000875af11580156111ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111de9190613e41565b60200151905080611202576040516353ae552b60e11b815260040160405180910390fd5b611266816112108b806147eb565b61121e906040810190613e5d565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525087925061125d91508e9050806147eb565b606001356127c1565b611283576040516309bde33960e01b815260040160405180910390fd5b60005b83811015610cbb57600061129d60208c018c613e5d565b838181106112ad576112ad613ea6565b90506020028101906112bf9190614801565b6112c890614896565b90508b6001600160a01b031663ab013de182600001516112e53390565b6040518363ffffffff1660e01b8152600401611302929190614940565b600060405180830381600087803b15801561131c57600080fd5b505af1158015611330573d6000803e3d6000fd5b50505050508061133f906144be565b9050611286565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611387573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ab9190613cdc565b905060018160038111156113c1576113c1613cfd565b14806113de575060038160038111156113dc576113dc613cfd565b145b156113fc57604051631c6d5f7760e31b815260040160405180910390fd5b4260006001600160a01b038616631a880a9361141887806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016020604051808303816000875af1158015611460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114849190613d13565b61148e9083613d42565b90506000866001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f49190613d13565b9050801580159061150457508181115b156115225760405163048c969960e01b815260040160405180910390fd5b60006115316020880188613e5d565b905090506000816001600160401b0381111561154f5761154f613d55565b60405190808252806020026020018201604052801561159a57816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161156d5790505b50905060005b828110156117a15760006115b760208b018b613e5d565b838181106115c7576115c7613ea6565b90506020028101906115d99190614801565b6115e29061496a565b90506116598b6001600160a01b031663f437bc596040518163ffffffff1660e01b8152600401600060405180830381865afa158015611625573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261164d91908101906149a6565b825160200151906127d9565b611676576040516390d4c20960e01b815260040160405180910390fd5b8051611681906125b9565b6001600160401b031687106116a957604051631676f4b360e01b815260040160405180910390fd5b60006116b882600001516125eb565b604051630cb33d1f60e11b8152600481018290529091506000906001600160a01b038e16906319667a3e90602401602060405180830381865afa158015611703573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172791906149da565b6001600160a01b03161461174e5760405163156a571760e11b815260040160405180910390fd5b604051806060016040528083604001518152602001836020015181526020018281525084848151811061178357611783613ea6565b602002602001018190525050508061179a906144be565b90506115a0565b5060006001600160a01b038a1663a70a8c476117bd8b806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016060604051808303816000875af1158015611805573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118299190613e41565b6020015190508061184d576040516353ae552b60e11b815260040160405180910390fd5b61185b816112108b806147eb565b611878576040516309bde33960e01b815260040160405180910390fd5b60005b83811015610cbb57600061189260208c018c613e5d565b838181106118a2576118a2613ea6565b90506020028101906118b49190614801565b6118bd9061496a565b90508b6001600160a01b031663b85e6fbb82600001516118da3390565b6040518363ffffffff1660e01b81526004016118f79291906149f7565b600060405180830381600087803b15801561191157600080fd5b505af1158015611925573d6000803e3d6000fd5b505050505080611934906144be565b905061187b565b826000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561197c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a09190613cdc565b905060018160038111156119b6576119b6613cfd565b14806119d3575060038160038111156119d1576119d1613cfd565b145b156119f157604051631c6d5f7760e31b815260040160405180910390fd5b6000856001600160a01b0316639a8425bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a559190613d13565b611a5f9042613d42565b9050856001600160a01b031663d40784c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac39190613d13565b8110611ae25760405163040dc5c360e41b815260040160405180910390fd5b600080876001600160a01b0316632476132b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b4791906149da565b6001600160a01b0316637d755598896001600160a01b031663bbad99d46040518163ffffffff1660e01b8152600401600060405180830381865afa158015611b93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611bbb91908101906149a6565b89896040518463ffffffff1660e01b8152600401611bdb93929190614a0a565b6000604051808303816000875af1158015611bfa573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611c229190810190614a3a565b604051630b4974cf60e41b815291935091506001600160a01b0389169063b4974cf090611c53908590600401614abc565b600060405180830381600087803b158015611c6d57600080fd5b505af1158015611c81573d6000803e3d6000fd5b50508251604051634e04afc360e11b8152600093506001600160a01b038c169250639c095f8691611cb89160040190815260200190565b602060405180830381865afa158015611cd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cf99190613d13565b90508015801590611d0d5750808260200151115b156107135760408051808201825283518152602080850151818301908152858401518451632acf7f4f60e11b81528451600482015291516024830152805160448301529182015160648201529201516084830152906001600160a01b038b169063559efe9e9060a401600060405180830381600087803b158015611d9057600080fd5b505af1158015611da4573d6000803e3d6000fd5b5050505050505050505050505050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e199190613cdc565b90506001816003811115611e2f57611e2f613cfd565b1480611e4c57506003816003811115611e4a57611e4a613cfd565b145b15611e6a57604051631c6d5f7760e31b815260040160405180910390fd5b60408051631a880a9360e01b8152602085013560048201529084013560248201526000906001600160a01b03861690631a880a93906044016020604051808303816000875af1158015611ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee59190613d13565b611eef9042613d42565b90506000856001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f559190613d13565b90508015801590611f6557508181115b15611f835760405163048c969960e01b815260040160405180910390fd5b6040805163a70a8c4760e01b8152602087013560048201529086013560248201526000906001600160a01b0388169063a70a8c47906044016060604051808303816000875af1158015611fda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffe9190613e41565b6040810151909150612023576040516353ae552b60e11b815260040160405180910390fd5b600061202f8780613e5d565b9050905060005b8181101561071357600061204a8980613e5d565b8381811061205a5761205a613ea6565b905060200281019061206c9190614801565b61207590614acf565b845190915061208382612732565b6001600160401b031611156120ab576040516348e8dd2f60e11b815260040160405180910390fd5b60006120b68261275f565b604051632211f1dd60e01b8152600481018290529091506000906001600160a01b038d1690632211f1dd906024016040805180830381865afa158015612100573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612124919061407b565b60208101519091506001600160a01b03166121525760405163f058bfd960e01b815260040160405180910390fd5b604080516001808252818301909252600091816020015b60608152602001906001900390816121695790505090506040518060400160405280601081526020016f526573706f6e7365526563656970747360801b815250836040516020016121bc91815260200190565b60408051601f19818403018152908290526121da92916020016140d7565b6040516020818303038152906040528186815181106121fb576121fb613ea6565b6020026020010181905250600073d8e040255903d5534b41cc7b0b256f76dcdb343c631475ff4589604001518f80606001906122379190613e5d565b866040518563ffffffff1660e01b815260040161225794939291906141b0565b600060405180830381865af4158015612274573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261229c91908101906142cf565b6000815181106122ae576122ae613ea6565b602002602001015190508060200151516000146122de576040516309bde33960e01b815260040160405180910390fd5b604051630446fc4760e01b81526001600160a01b038f1690630446fc479061230e90889087908990600401614adb565b600060405180830381600087803b15801561232857600080fd5b505af115801561233c573d6000803e3d6000fd5b5050505050505050508061234f906144be565b9050612036565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bb9190613cdc565b905060018160038111156123d1576123d1613cfd565b14806123ee575060038160038111156123ec576123ec613cfd565b145b1561240c57604051631c6d5f7760e31b815260040160405180910390fd5b60006124188480613e5d565b915042905060005b828110156125b05760006124348780613e5d565b8381811061244457612444613ea6565b90506020028101906124569190613ebc565b61245f90614582565b9050600061246c82612631565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038b169063368bf464906024016040805180830381865afa1580156124b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124da919061407b565b60208101519091506001600160a01b0316612508576040516309bde33960e01b815260040160405180910390fd5b8461251284612604565b6001600160401b0316111561253a576040516348e8dd2f60e11b815260040160405180910390fd5b60405163017d055b60e11b81526001600160a01b038b16906302fa0ab69061256a90869085908790600401614aee565b600060405180830381600087803b15801561258457600080fd5b505af1158015612598573d6000803e3d6000fd5b50505050505050806125a9906144be565b9050612420565b50505050505050565b60008160a001516001600160401b03166000036125de57506001600160401b03919050565b5060a0015190565b919050565b60006125f682612809565b805190602001209050919050565b600081608001516001600160401b031660000361262957506001600160401b03919050565b506080015190565b6040805160208101909152600080825260a083015151909190825b818110156126a857828560a00151828151811061266b5761266b613ea6565b60200260200101516040516020016126849291906140d7565b604051602081830303815290604052925080806126a0906144be565b91505061264c565b508360000151846020015185604001518660c00151876080015188606001516040516020016126ef919060609190911b6bffffffffffffffffffffffff1916815260140190565b60408051601f19818403018152908290526127139695949392918990602001614b01565b6040516020818303038152906040528051906020012092505050919050565b600081604001516001600160401b031660000361275757506001600160401b03919050565b506040015190565b600061276e8260000151612809565b60208084015160408086015190516127869301614b8c565b60408051601f19818403018152908290526127a492916020016140d7565b604051602081830303815290604052805190602001209050919050565b60006127ce84848461285a565b909414949350505050565b600081518351146127ec5750600061016c565b825160208381018281209186019283209091145b95945050505050565b60608160000151826020015183604001518460a00151856060015186608001518760c001516040516020016128449796959493929190614bbe565b6040516020818303038152906040529050919050565b600081600114801561286d575082516001145b801561289757508260008151811061288757612887613ea6565b6020026020010151602001516000145b156128c257826000815181106128af576128af613ea6565b6020026020010151604001519050612b21565b60006128cd83612b28565b90506000815190506000604051806040016040528060008152602001836001600160401b0381111561290157612901613d55565b60405190808252806020026020018201604052801561292a578160200160208202803683370190505b509052604080518082019091526000808252602082018a905291925090805b84811015612a7057600086828151811061296557612965613ea6565b6020026020010151905080600261297c9190614d39565b6129869084614d45565b6040805160008082526020820190925291945090816129cd565b60408051606081018252600080825260208083018290529282015282526000199092019101816129a05790505b508b51909150156129e7576129e28b85612bcb565b9b5090505b8051600003612a1b57845160208601515103612a04575050612a70565b612a1686612a1187612dca565b612e01565b612a66565b80516001148015612a2a575081155b15612a5757612a168682600081518110612a4657612a46613ea6565b602002602001015160400151612e01565b612a6686612a11838886612e31565b5050600101612949565b5082516000190183525b825115612afa576000612a8c8461300e565b90506000612a998561300e565b855160010186526040805160208101859052908101829052909150606001604051602081830303815290604052805190602001208560200151866000015181518110612ae757612ae7613ea6565b6020026020010181815250505050612a7a565b8260200151600081518110612b1157612b11613ea6565b6020026020010151955050505050505b9392505050565b6040805181815261082081018252606091906000908260208201610800803683370190505090506000845b83821015612baf578015612baf576000612b6c82613046565b905080848481518110612b8157612b81613ea6565b6020908102919091010152612b97816002614d39565b612ba19083613d42565b915082600101925050612b53565b6000612bbb8386613d42565b8451038452509195945050505050565b606080600080855190505b80821015612c1557858281518110612bf057612bf0613ea6565b602002602001015160200151851115612c155781612c0d816144be565b925050612bd6565b60008215612c235782612c26565b60005b90506000816001600160401b03811115612c4257612c42613d55565b604051908082528060200260200182016040528015612c8d57816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181612c605790505b5090506000612c9c8385613d42565b6001600160401b03811115612cb357612cb3613d55565b604051908082528060200260200182016040528015612cfe57816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181612cd15790505b5082519091506000905b80821015612d58578a8281518110612d2257612d22613ea6565b6020026020010151848381518110612d3c57612d3c613ea6565b602002602001018190525081612d51906144be565b9150612d08565b60005b86831015612db6578b8381518110612d7557612d75613ea6565b6020026020010151848281518110612d8f57612d8f613ea6565b602002602001018190525082612da4906144be565b9250612daf816144be565b9050612d5b565b5092975090955050505050505b9250929050565b6000808260200151836000015181518110612de757612de7613ea6565b602090810291909101015183516001019093525090919050565b808260200151836000015181518110612e1c57612e1c613ea6565b60209081029190910101525080516001019052565b6000606080612e3f866130da565b925090506000846001600160401b03811115612e5d57612e5d613d55565b604051908082528060200260200182016040528015612e9057816020015b6060815260200190600190039081612e7b5790505b50905060005b85811015612ff8576000612eaa8288613d42565b612eb5906002614d39565b905080855103612ec55750612ff8565b6000612ed086613258565b90506000612ede828861335a565b8051909150806001600160401b03811115612efb57612efb613d55565b604051908082528060200260200182016040528015612f4057816020015b6040805180820190915260008082526020820152815260200190600190039081612f195790505b50868681518110612f5357612f53613ea6565b602002602001018190525060005b81811015612fdd576040518060400160405280848381518110612f8657612f86613ea6565b60200260200101518152602001612f9c8e612dca565b815250878781518110612fb157612fb1613ea6565b60200260200101518281518110612fca57612fca613ea6565b6020908102919091010152600101612f61565b50612fe78361347c565b975084600101945050505050612e96565b50613003818361358f565b979650505050505050565b600080826020015183600001518151811061302b5761302b613ea6565b60209081029190910101518351600019019093525090919050565b600080608083901c1561305b57608092831c92015b604083901c1561306d57604092831c92015b602083901c1561307f57602092831c92015b601083901c1561309157601092831c92015b600883901c156130a357600892831c92015b600483901c156130b557600492831c92015b600283901c156130c757600292831c92015b600183901c1561016c5760010192915050565b606080600080845190506000816001600160401b038111156130fe576130fe613d55565b60405190808252806020026020018201604052801561314357816020015b604080518082019091526000808252602082015281526020019060019003908161311c5790505b5090506000826001600160401b0381111561316057613160613d55565b604051908082528060200260200182016040528015613189578160200160208202803683370190505b5090505b8284101561324c5760405180604001604052808886815181106131b2576131b2613ea6565b60200260200101516000015181526020018886815181106131d5576131d5613ea6565b6020026020010151604001518152508285815181106131f6576131f6613ea6565b602002602001018190525086848151811061321357613213613ea6565b60200260200101516000015181858151811061323157613231613ea6565b6020908102919091010152613245846144be565b935061318d565b90969095509350505050565b80516060906000816001600160401b0381111561327757613277613d55565b6040519080825280602002602001820160405280156132a0578160200160208202803683370190505b50905060005b828110156133525760008582815181106132c2576132c2613ea6565b6020026020010151905080600003613302576132df816001614d45565b8383815181106132f1576132f1613ea6565b602002602001018181525050613349565b61330d600282614d6e565b60000361331f576132df816001614d45565b61332a600182613d42565b83838151811061333c5761333c613ea6565b6020026020010181815250505b506001016132a6565b509392505050565b81518151606091906000826001600160401b0381111561337c5761337c613d55565b6040519080825280602002602001820160405280156133a5578160200160208202803683370190505b5090506000805b8481101561345e576000805b8581101561340a578881815181106133d2576133d2613ea6565b60200260200101518a84815181106133ec576133ec613ea6565b602002602001015103613402576001915061340a565b6001016133b8565b50806134555788828151811061342257613422613ea6565b602002602001015184848151811061343c5761343c613ea6565b602090810291909101015282613451816144be565b9350505b506001016133ac565b50600061346b8286613d42565b835103835250909695505050505050565b80516060906000816001600160401b0381111561349b5761349b613d55565b6040519080825280602002602001820160405280156134c4578160200160208202803683370190505b5090506000805b8381101561357357600060028783815181106134e9576134e9613ea6565b60200260200101516134fb9190614d82565b905060008311801561352f57508084613515600186613d42565b8151811061352557613525613ea6565b6020026020010151145b1561353a5750613561565b8084848151811061354d5761354d613ea6565b602002602001018181525050826001019250505b8061356b816144be565b9150506134cb565b5060006135808285613d42565b83510383525090949350505050565b604080516000808252602082019092528190816135ce565b60408051808201909152600080825260208201528152602001906001900390816135a75790505b5090506135f583856000815181106135e8576135e8613ea6565b60200260200101516138c2565b8460008151811061360857613608613ea6565b6020908102919091010152835160005b8181101561388a57604080516000808252602082019092528161365d565b60408051808201909152600080825260208201528152602001906001900390816136365790505b509050835160000361368a5786828151811061367b5761367b613ea6565b602002602001015190506136b0565b6136ad87838151811061369f5761369f613ea6565b6020026020010151856138c2565b90505b6136bc81516002613ace565b6001600160401b038111156136d3576136d3613d55565b60405190808252806020026020018201604052801561371857816020015b60408051808201909152600080825260208201528152602001906001900390816136f15790505b508151909450600090815b818110156138735781613737826001614d45565b106137ab57600084828151811061375057613750613ea6565b6020026020010151905061378285838151811061376f5761376f613ea6565b6020026020010151600001516002613af6565b81528751819089908690811061379a5761379a613ea6565b602002602001018190525050613861565b60408051808201909152600080825260208201526137d485838151811061376f5761376f613ea6565b81528451613833908690849081106137ee576137ee613ea6565b602002602001015160200151868460016138089190614d45565b8151811061381857613818613ea6565b60200260200101516020015160009182526020526040902090565b60208201528751819089908690811061384e5761384e613ea6565b6020908102919091010152506001909201915b61386c600282614d45565b9050613723565b505050508080613882906144be565b915050613618565b50815160011461389957600080fd5b816000815181106138ac576138ac613ea6565b6020026020010151602001519250505092915050565b8151815160609160009182918291826138db8284614d45565b90506000816001600160401b038111156138f7576138f7613d55565b60405190808252806020026020018201604052801561393c57816020015b60408051808201909152600080825260208201528152602001906001900390816139155790505b5090505b838710801561394e57508286105b15613a235788868151811061396557613965613ea6565b6020026020010151600001518a888151811061398357613983613ea6565b60200260200101516000015110156139dc578987815181106139a7576139a7613ea6565b60200260200101518186815181106139c1576139c1613ea6565b60209081029190910101526001968701969490940193613940565b8886815181106139ee576139ee613ea6565b6020026020010151818681518110613a0857613a08613ea6565b60209081029190910101526001958601959490940193613940565b83871015613a7257898781518110613a3d57613a3d613ea6565b6020026020010151818681518110613a5757613a57613ea6565b60209081029190910101526001968701969490940193613a23565b82861015613ac157888681518110613a8c57613a8c613ea6565b6020026020010151818681518110613aa657613aa6613ea6565b60209081029190910101526001958601959490940193613a72565b9998505050505050505050565b600080613adb8385614d82565b9050613ae78385614d6e565b15612b21576001019392505050565b6000612b218284614d82565b600060208284031215613b1457600080fd5b81356001600160e01b031981168114612b2157600080fd5b6001600160a01b0381168114613b4157600080fd5b50565b600060808284031215613b5657600080fd5b50919050565b60008060408385031215613b6f57600080fd5b8235613b7a81613b2c565b915060208301356001600160401b03811115613b9557600080fd5b613ba185828601613b44565b9150509250929050565b600060408284031215613b5657600080fd5b60008060408385031215613bd057600080fd5b8235613bdb81613b2c565b915060208301356001600160401b03811115613bf657600080fd5b613ba185828601613bab565b600080600060408486031215613c1757600080fd5b8335613c2281613b2c565b925060208401356001600160401b0380821115613c3e57600080fd5b818601915086601f830112613c5257600080fd5b813581811115613c6157600080fd5b876020828501011115613c7357600080fd5b6020830194508093505050509250925092565b60008060408385031215613c9957600080fd5b8235613ca481613b2c565b915060208301356001600160401b03811115613cbf57600080fd5b830160208186031215613cd157600080fd5b809150509250929050565b600060208284031215613cee57600080fd5b815160048110612b2157600080fd5b634e487b7160e01b600052602160045260246000fd5b600060208284031215613d2557600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561016c5761016c613d2c565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715613d8d57613d8d613d55565b60405290565b60405160e081016001600160401b0381118282101715613d8d57613d8d613d55565b604080519081016001600160401b0381118282101715613d8d57613d8d613d55565b604051601f8201601f191681016001600160401b0381118282101715613dff57613dff613d55565b604052919050565b600060608284031215613e1957600080fd5b613e21613d6b565b905081518152602082015160208201526040820151604082015292915050565b600060608284031215613e5357600080fd5b612b218383613e07565b6000808335601e19843603018112613e7457600080fd5b8301803591506001600160401b03821115613e8e57600080fd5b6020019150600581901b3603821315612dc357600080fd5b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112613ed257600080fd5b9190910192915050565b60006001600160401b03821115613ef557613ef5613d55565b50601f01601f191660200190565b600082601f830112613f1457600080fd5b8135613f27613f2282613edc565b613dd7565b818152846020838601011115613f3c57600080fd5b816020850160208301376000918101602001919091529392505050565b80356001600160401b03811681146125e657600080fd5b600060e08284031215613f8257600080fd5b613f8a613d93565b905081356001600160401b0380821115613fa357600080fd5b613faf85838601613f03565b83526020840135915080821115613fc557600080fd5b613fd185838601613f03565b6020840152613fe260408501613f59565b60408401526060840135915080821115613ffb57600080fd5b61400785838601613f03565b6060840152608084013591508082111561402057600080fd5b61402c85838601613f03565b608084015261403d60a08501613f59565b60a084015260c084013591508082111561405657600080fd5b5061406384828501613f03565b60c08301525092915050565b600061016c3683613f70565b60006040828403121561408d57600080fd5b614095613db5565b8251815260208301516140a781613b2c565b60208201529392505050565b60005b838110156140ce5781810151838201526020016140b6565b50506000910152565b600083516140e98184602088016140b3565b8351908301906140fd8183602088016140b3565b01949350505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600081518084526141478160208601602086016140b3565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b858110156141a357828403895261419184835161412f565b98850198935090840190600101614179565b5091979650505050505050565b60006060820186835260206060818501528186835260808501905060808760051b86010192508760005b8881101561424957868503607f190183528135368b9003601e1901811261420057600080fd5b8a0184810190356001600160401b0381111561421b57600080fd5b80360382131561422a57600080fd5b614235878284614106565b9650505091830191908301906001016141da565b505050508281036040840152613003818561415b565b60006001600160401b0382111561427857614278613d55565b5060051b60200190565b600082601f83011261429357600080fd5b81516142a1613f2282613edc565b8181528460208386010111156142b657600080fd5b6142c78260208301602087016140b3565b949350505050565b600060208083850312156142e257600080fd5b82516001600160401b03808211156142f957600080fd5b818501915085601f83011261430d57600080fd5b815161431b613f228261425f565b81815260059190911b8301840190848101908883111561433a57600080fd5b8585015b838110156143cd578051858111156143565760008081fd5b86016040818c03601f190181131561436e5760008081fd5b614376613db5565b89830151888111156143885760008081fd5b6143968e8c83870101614282565b8252509082015190878211156143ac5760008081fd5b6143ba8d8b84860101614282565b818b01528552505091860191860161433e565b5098975050505050505050565b6000815160e084526143ef60e085018261412f565b905060208301518482036020860152614408828261412f565b91505060408301516001600160401b038082166040870152606085015191508583036060870152614439838361412f565b9250608085015191508583036080870152614454838361412f565b92508060a08601511660a0870152505060c083015184820360c0860152612800828261412f565b60808152600061448e60808301866143da565b90506144b06020830185805182526020908101516001600160a01b0316910152565b826060830152949350505050565b6000600182016144d0576144d0613d2c565b5060010190565b60006144e5613f228461425f565b8381529050602080820190600585901b84018681111561450457600080fd5b845b8181101561453f5780356001600160401b038111156145255760008081fd5b61453189828901613f03565b855250928201928201614506565b505050509392505050565b6000612b213684846144d7565b80356125e681613b2c565b600082601f83011261457357600080fd5b612b21838335602085016144d7565b600060e0823603121561459457600080fd5b61459c613d93565b82356001600160401b03808211156145b357600080fd5b6145bf36838701613f03565b835260208501359150808211156145d557600080fd5b6145e136838701613f03565b60208401526145f260408601613f59565b604084015261460360608601614557565b606084015261461460808601613f59565b608084015260a085013591508082111561462d57600080fd5b5061463a36828601614562565b60a08301525061464c60c08401613f59565b60c082015292915050565b848152608060208201526000614670608083018661415b565b8281036040840152614682818661415b565b90508281036060840152613003818561412f565b6000815160e084526146ab60e085018261412f565b9050602083015184820360208601526146c4828261412f565b91505060408301516001600160401b03808216604087015260018060a01b03606086015116606087015280608086015116608087015260a0850151915085830360a0870152614713838361415b565b92508060c08601511660c087015250508091505092915050565b60006040808352845181828501526147486080850182614696565b9050602080870151603f198684030160608701528281518085528385019150838160051b860101848401935060005b828110156147c757868203601f190184528451805189845261479b8a85018261412f565b91880151848303858a01529190506147b3818361412f565b968801969588019593505050600101614777565b506001600160a01b038a16858a015296506147e192505050565b5050509392505050565b60008235607e19833603018112613ed257600080fd5b60008235605e19833603018112613ed257600080fd5b60006060828403121561482957600080fd5b614831613d6b565b905081356001600160401b038082111561484a57600080fd5b61485685838601613f70565b8352602084013591508082111561486c57600080fd5b5061487984828501613f03565b60208301525061488b60408301613f59565b604082015292915050565b6000606082360312156148a857600080fd5b6148b0613d6b565b82356001600160401b038111156148c657600080fd5b6148d236828601614817565b825250602083013560208201526040830135604082015280915050919050565b600081516060845261490760608501826143da565b905060208301518482036020860152614920828261412f565b9150506001600160401b0360408401511660408501528091505092915050565b60408152600061495360408301856148f2565b905060018060a01b03831660208301529392505050565b60006060823603121561497c57600080fd5b614984613d6b565b82356001600160401b0381111561499a57600080fd5b6148d236828601613f70565b6000602082840312156149b857600080fd5b81516001600160401b038111156149ce57600080fd5b6142c784828501614282565b6000602082840312156149ec57600080fd5b8151612b2181613b2c565b60408152600061495360408301856143da565b604081526000614a1d604083018661412f565b8281036020840152614a30818587614106565b9695505050505050565b60008082840360c0811215614a4e57600080fd5b83516001600160401b03811115614a6457600080fd5b614a7086828701614282565b93505060a0601f1982011215614a8557600080fd5b50614a8e613d6b565b6020840151815260408401516020820152614aac8560608601613e07565b6040820152809150509250929050565b602081526000612b21602083018461412f565b600061016c3683614817565b60808152600061448e60808301866148f2565b60808152600061448e6080830186614696565b60008851614b13818460208d016140b3565b885190830190614b27818360208d016140b3565b60c089811b6001600160c01b03199081169390920192835288811b8216600884015287901b1660108201528451614b658160188401602089016140b3565b8451910190614b7b8160188401602088016140b3565b016018019998505050505050505050565b60008351614b9e8184602088016140b3565b60c09390931b6001600160c01b0319169190920190815260080192915050565b60008851614bd0818460208d016140b3565b885190830190614be4818360208d016140b3565b60c089811b6001600160c01b03199081169390920192835288901b1660088201528551614c18816010840160208a016140b3565b8551910190614c2e8160108401602089016140b3565b8451910190614c448160108401602088016140b3565b016010019998505050505050505050565b600181815b80851115614c90578160001904821115614c7657614c76613d2c565b80851615614c8357918102915b93841c9390800290614c5a565b509250929050565b600082614ca75750600161016c565b81614cb45750600061016c565b8160018114614cca5760028114614cd457614cf0565b600191505061016c565b60ff841115614ce557614ce5613d2c565b50506001821b61016c565b5060208310610133831016604e8410600b8410161715614d13575081810a61016c565b614d1d8383614c55565b8060001904821115614d3157614d31613d2c565b029392505050565b6000612b218383614c98565b8082018082111561016c5761016c613d2c565b634e487b7160e01b600052601260045260246000fd5b600082614d7d57614d7d614d58565b500690565b600082614d9157614d91614d58565b50049056fea26469706673582212209cfacc48ea27d6e669c54fa6bd03b3260804fd2212e74b33613a4f918f8fb13a64736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100885760003560e01c80639d38eb351161005b5780639d38eb35146100ef578063bb1689be14610102578063e407f86b14610115578063ee241c0d1461012857600080fd5b806301ffc9a71461008d578063089b174c146100b45780632e8f454b146100c957806372becccd146100dc575b600080fd5b6100a061009b366004613b02565b61013b565b604051901515815260200160405180910390f35b6100c76100c2366004613b5c565b610172565b005b6100c76100d7366004613b5c565b61071e565b6100c76100ea366004613bbd565b610cc8565b6100c76100fd366004613bbd565b611346565b6100c7610110366004613c02565b61193b565b6100c7610123366004613b5c565b611db4565b6100c7610136366004613c86565b612356565b60006001600160e01b031982166378a7182760e01b148061016c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d79190613cdc565b905060018160038111156101ed576101ed613cfd565b148061020a5750600381600381111561020857610208613cfd565b145b1561022857604051631c6d5f7760e31b815260040160405180910390fd5b60408051631a880a9360e01b8152602085013560048201529084013560248201526000906001600160a01b03861690631a880a93906044016020604051808303816000875af115801561027f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102a39190613d13565b6102ad9042613d42565b90506000856001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103139190613d13565b9050801580159061032357508181115b156103415760405163048c969960e01b815260040160405180910390fd5b6040805163a70a8c4760e01b8152602087013560048201529086013560248201526000906001600160a01b0388169063a70a8c47906044016060604051808303816000875af1158015610398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103bc9190613e41565b60408101519091506103e1576040516353ae552b60e11b815260040160405180910390fd5b60006103ed8780613e5d565b9050905060005b818110156107135760006104088980613e5d565b8381811061041857610418613ea6565b905060200281019061042a9190613ebc565b6104339061406f565b8451909150610441826125b9565b6001600160401b03161115610469576040516348e8dd2f60e11b815260040160405180910390fd5b6000610474826125eb565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038d169063368bf464906024016040805180830381865afa1580156104be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e2919061407b565b60208101519091506001600160a01b03166105105760405163f058bfd960e01b815260040160405180910390fd5b604080516001808252818301909252600091816020015b60608152602001906001900390816105275790505090506040518060400160405280600f81526020016e52657175657374526563656970747360881b8152508360405160200161057991815260200190565b60408051601f198184030181529082905261059792916020016140d7565b6040516020818303038152906040528186815181106105b8576105b8613ea6565b6020026020010181905250600073d8e040255903d5534b41cc7b0b256f76dcdb343c631475ff4589604001518f80606001906105f49190613e5d565b866040518563ffffffff1660e01b815260040161061494939291906141b0565b600060405180830381865af4158015610631573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261065991908101906142cf565b60008151811061066b5761066b613ea6565b6020026020010151905080602001515160001461069b576040516309bde33960e01b815260040160405180910390fd5b6040516325a377d560e11b81526001600160a01b038f1690634b46efaa906106cb9088908790899060040161447b565b600060405180830381600087803b1580156106e557600080fd5b505af11580156106f9573d6000803e3d6000fd5b5050505050505050508061070c906144be565b90506103f4565b505050505050505050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107839190613cdc565b9050600181600381111561079957610799613cfd565b14806107b6575060038160038111156107b4576107b4613cfd565b145b156107d457604051631c6d5f7760e31b815260040160405180910390fd5b60408051631a880a9360e01b81526020850135600482015290840135602482015242906000906001600160a01b03871690631a880a93906044016020604051808303816000875af115801561082d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108519190613d13565b61085b9083613d42565b90506000866001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561089d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c19190613d13565b905080158015906108d157508181115b156108ef5760405163048c969960e01b815260040160405180910390fd5b6040805163a70a8c4760e01b8152602088013560048201529087013560248201526000906001600160a01b0389169063a70a8c47906044016060604051808303816000875af1158015610946573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061096a9190613e41565b6040015190508061098e576040516353ae552b60e11b815260040160405180910390fd5b600061099d6060890189613e5d565b9150600090506109ad8980613e5d565b6109b69161454a565b905060005b82811015610cbb5760006109d260608c018c613e5d565b838181106109e2576109e2613ea6565b90506020028101906109f49190613ebc565b6109fd90614582565b9050610a0881612604565b6001600160401b03168810610a3057604051631676f4b360e01b815260040160405180910390fd5b6000610a3b82612631565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038f169063368bf464906024016040805180830381865afa158015610a85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa9919061407b565b60208101519091506001600160a01b0316610ad75760405163f058bfd960e01b815260040160405180910390fd5b60006001600160a01b03168e6001600160a01b0316638856337e846040518263ffffffff1660e01b8152600401610b1091815260200190565b6040805180830381865afa158015610b2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b50919061407b565b602001516001600160a01b031614610b7b5760405163156a571760e11b815260040160405180910390fd5b600073d8e040255903d5534b41cc7b0b256f76dcdb343c6355028f6f89888760a0015187604051602001610bb191815260200190565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401610bdf9493929190614657565b600060405180830381865af4158015610bfc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c2491908101906142cf565b9050600060405180604001604052808681526020018381525090508f6001600160a01b031663ff279b9882610c563390565b6040518363ffffffff1660e01b8152600401610c7392919061472d565b600060405180830381600087803b158015610c8d57600080fd5b505af1158015610ca1573d6000803e3d6000fd5b50505050505050505080610cb4906144be565b90506109bb565b5050505050505050505050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2d9190613cdc565b90506001816003811115610d4357610d43613cfd565b1480610d6057506003816003811115610d5e57610d5e613cfd565b145b15610d7e57604051631c6d5f7760e31b815260040160405180910390fd5b4260006001600160a01b038616631a880a93610d9a87806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016020604051808303816000875af1158015610de2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e069190613d13565b610e109083613d42565b90506000866001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e769190613d13565b90508015801590610e8657508181115b15610ea45760405163048c969960e01b815260040160405180910390fd5b6000610eb36020880188613e5d565b905090506000816001600160401b03811115610ed157610ed1613d55565b604051908082528060200260200182016040528015610f1c57816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181610eef5790505b50905060005b82811015611156576000610f3960208b018b613e5d565b83818110610f4957610f49613ea6565b9050602002810190610f5b9190614801565b610f6490614896565b9050610f738160000151612732565b6001600160401b03168710610f9b57604051631676f4b360e01b815260040160405180910390fd5b805151600090610faa906125eb565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038e169063368bf464906024016040805180830381865afa158015610ff4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611018919061407b565b60208101519091506001600160a01b0316611046576040516309bde33960e01b815260040160405180910390fd5b60006001600160a01b03168d6001600160a01b0316638856337e61106d866000015161275f565b6040518263ffffffff1660e01b815260040161108b91815260200190565b6040805180830381865afa1580156110a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110cb919061407b565b602001516001600160a01b0316146110f65760405163156a571760e11b815260040160405180910390fd5b60405180606001604052808460400151815260200184602001518152602001611122856000015161275f565b81525085858151811061113757611137613ea6565b60200260200101819052505050508061114f906144be565b9050610f22565b5060006001600160a01b038a1663a70a8c476111728b806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016060604051808303816000875af11580156111ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111de9190613e41565b60200151905080611202576040516353ae552b60e11b815260040160405180910390fd5b611266816112108b806147eb565b61121e906040810190613e5d565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525087925061125d91508e9050806147eb565b606001356127c1565b611283576040516309bde33960e01b815260040160405180910390fd5b60005b83811015610cbb57600061129d60208c018c613e5d565b838181106112ad576112ad613ea6565b90506020028101906112bf9190614801565b6112c890614896565b90508b6001600160a01b031663ab013de182600001516112e53390565b6040518363ffffffff1660e01b8152600401611302929190614940565b600060405180830381600087803b15801561131c57600080fd5b505af1158015611330573d6000803e3d6000fd5b50505050508061133f906144be565b9050611286565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611387573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ab9190613cdc565b905060018160038111156113c1576113c1613cfd565b14806113de575060038160038111156113dc576113dc613cfd565b145b156113fc57604051631c6d5f7760e31b815260040160405180910390fd5b4260006001600160a01b038616631a880a9361141887806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016020604051808303816000875af1158015611460573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114849190613d13565b61148e9083613d42565b90506000866001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f49190613d13565b9050801580159061150457508181115b156115225760405163048c969960e01b815260040160405180910390fd5b60006115316020880188613e5d565b905090506000816001600160401b0381111561154f5761154f613d55565b60405190808252806020026020018201604052801561159a57816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161156d5790505b50905060005b828110156117a15760006115b760208b018b613e5d565b838181106115c7576115c7613ea6565b90506020028101906115d99190614801565b6115e29061496a565b90506116598b6001600160a01b031663f437bc596040518163ffffffff1660e01b8152600401600060405180830381865afa158015611625573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261164d91908101906149a6565b825160200151906127d9565b611676576040516390d4c20960e01b815260040160405180910390fd5b8051611681906125b9565b6001600160401b031687106116a957604051631676f4b360e01b815260040160405180910390fd5b60006116b882600001516125eb565b604051630cb33d1f60e11b8152600481018290529091506000906001600160a01b038e16906319667a3e90602401602060405180830381865afa158015611703573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061172791906149da565b6001600160a01b03161461174e5760405163156a571760e11b815260040160405180910390fd5b604051806060016040528083604001518152602001836020015181526020018281525084848151811061178357611783613ea6565b602002602001018190525050508061179a906144be565b90506115a0565b5060006001600160a01b038a1663a70a8c476117bd8b806147eb565b60405160e083901b6001600160e01b03191681528135600482015260209091013560248201526044016060604051808303816000875af1158015611805573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118299190613e41565b6020015190508061184d576040516353ae552b60e11b815260040160405180910390fd5b61185b816112108b806147eb565b611878576040516309bde33960e01b815260040160405180910390fd5b60005b83811015610cbb57600061189260208c018c613e5d565b838181106118a2576118a2613ea6565b90506020028101906118b49190614801565b6118bd9061496a565b90508b6001600160a01b031663b85e6fbb82600001516118da3390565b6040518363ffffffff1660e01b81526004016118f79291906149f7565b600060405180830381600087803b15801561191157600080fd5b505af1158015611925573d6000803e3d6000fd5b505050505080611934906144be565b905061187b565b826000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561197c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a09190613cdc565b905060018160038111156119b6576119b6613cfd565b14806119d3575060038160038111156119d1576119d1613cfd565b145b156119f157604051631c6d5f7760e31b815260040160405180910390fd5b6000856001600160a01b0316639a8425bc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a559190613d13565b611a5f9042613d42565b9050856001600160a01b031663d40784c76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac39190613d13565b8110611ae25760405163040dc5c360e41b815260040160405180910390fd5b600080876001600160a01b0316632476132b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b4791906149da565b6001600160a01b0316637d755598896001600160a01b031663bbad99d46040518163ffffffff1660e01b8152600401600060405180830381865afa158015611b93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611bbb91908101906149a6565b89896040518463ffffffff1660e01b8152600401611bdb93929190614a0a565b6000604051808303816000875af1158015611bfa573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611c229190810190614a3a565b604051630b4974cf60e41b815291935091506001600160a01b0389169063b4974cf090611c53908590600401614abc565b600060405180830381600087803b158015611c6d57600080fd5b505af1158015611c81573d6000803e3d6000fd5b50508251604051634e04afc360e11b8152600093506001600160a01b038c169250639c095f8691611cb89160040190815260200190565b602060405180830381865afa158015611cd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cf99190613d13565b90508015801590611d0d5750808260200151115b156107135760408051808201825283518152602080850151818301908152858401518451632acf7f4f60e11b81528451600482015291516024830152805160448301529182015160648201529201516084830152906001600160a01b038b169063559efe9e9060a401600060405180830381600087803b158015611d9057600080fd5b505af1158015611da4573d6000803e3d6000fd5b5050505050505050505050505050565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e199190613cdc565b90506001816003811115611e2f57611e2f613cfd565b1480611e4c57506003816003811115611e4a57611e4a613cfd565b145b15611e6a57604051631c6d5f7760e31b815260040160405180910390fd5b60408051631a880a9360e01b8152602085013560048201529084013560248201526000906001600160a01b03861690631a880a93906044016020604051808303816000875af1158015611ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee59190613d13565b611eef9042613d42565b90506000856001600160a01b031663f3f480d96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f559190613d13565b90508015801590611f6557508181115b15611f835760405163048c969960e01b815260040160405180910390fd5b6040805163a70a8c4760e01b8152602087013560048201529086013560248201526000906001600160a01b0388169063a70a8c47906044016060604051808303816000875af1158015611fda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffe9190613e41565b6040810151909150612023576040516353ae552b60e11b815260040160405180910390fd5b600061202f8780613e5d565b9050905060005b8181101561071357600061204a8980613e5d565b8381811061205a5761205a613ea6565b905060200281019061206c9190614801565b61207590614acf565b845190915061208382612732565b6001600160401b031611156120ab576040516348e8dd2f60e11b815260040160405180910390fd5b60006120b68261275f565b604051632211f1dd60e01b8152600481018290529091506000906001600160a01b038d1690632211f1dd906024016040805180830381865afa158015612100573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612124919061407b565b60208101519091506001600160a01b03166121525760405163f058bfd960e01b815260040160405180910390fd5b604080516001808252818301909252600091816020015b60608152602001906001900390816121695790505090506040518060400160405280601081526020016f526573706f6e7365526563656970747360801b815250836040516020016121bc91815260200190565b60408051601f19818403018152908290526121da92916020016140d7565b6040516020818303038152906040528186815181106121fb576121fb613ea6565b6020026020010181905250600073d8e040255903d5534b41cc7b0b256f76dcdb343c631475ff4589604001518f80606001906122379190613e5d565b866040518563ffffffff1660e01b815260040161225794939291906141b0565b600060405180830381865af4158015612274573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261229c91908101906142cf565b6000815181106122ae576122ae613ea6565b602002602001015190508060200151516000146122de576040516309bde33960e01b815260040160405180910390fd5b604051630446fc4760e01b81526001600160a01b038f1690630446fc479061230e90889087908990600401614adb565b600060405180830381600087803b15801561232857600080fd5b505af115801561233c573d6000803e3d6000fd5b5050505050505050508061234f906144be565b9050612036565b816000816001600160a01b031663054f7d9c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bb9190613cdc565b905060018160038111156123d1576123d1613cfd565b14806123ee575060038160038111156123ec576123ec613cfd565b145b1561240c57604051631c6d5f7760e31b815260040160405180910390fd5b60006124188480613e5d565b915042905060005b828110156125b05760006124348780613e5d565b8381811061244457612444613ea6565b90506020028101906124569190613ebc565b61245f90614582565b9050600061246c82612631565b604051630da2fd1960e21b8152600481018290529091506000906001600160a01b038b169063368bf464906024016040805180830381865afa1580156124b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124da919061407b565b60208101519091506001600160a01b0316612508576040516309bde33960e01b815260040160405180910390fd5b8461251284612604565b6001600160401b0316111561253a576040516348e8dd2f60e11b815260040160405180910390fd5b60405163017d055b60e11b81526001600160a01b038b16906302fa0ab69061256a90869085908790600401614aee565b600060405180830381600087803b15801561258457600080fd5b505af1158015612598573d6000803e3d6000fd5b50505050505050806125a9906144be565b9050612420565b50505050505050565b60008160a001516001600160401b03166000036125de57506001600160401b03919050565b5060a0015190565b919050565b60006125f682612809565b805190602001209050919050565b600081608001516001600160401b031660000361262957506001600160401b03919050565b506080015190565b6040805160208101909152600080825260a083015151909190825b818110156126a857828560a00151828151811061266b5761266b613ea6565b60200260200101516040516020016126849291906140d7565b604051602081830303815290604052925080806126a0906144be565b91505061264c565b508360000151846020015185604001518660c00151876080015188606001516040516020016126ef919060609190911b6bffffffffffffffffffffffff1916815260140190565b60408051601f19818403018152908290526127139695949392918990602001614b01565b6040516020818303038152906040528051906020012092505050919050565b600081604001516001600160401b031660000361275757506001600160401b03919050565b506040015190565b600061276e8260000151612809565b60208084015160408086015190516127869301614b8c565b60408051601f19818403018152908290526127a492916020016140d7565b604051602081830303815290604052805190602001209050919050565b60006127ce84848461285a565b909414949350505050565b600081518351146127ec5750600061016c565b825160208381018281209186019283209091145b95945050505050565b60608160000151826020015183604001518460a00151856060015186608001518760c001516040516020016128449796959493929190614bbe565b6040516020818303038152906040529050919050565b600081600114801561286d575082516001145b801561289757508260008151811061288757612887613ea6565b6020026020010151602001516000145b156128c257826000815181106128af576128af613ea6565b6020026020010151604001519050612b21565b60006128cd83612b28565b90506000815190506000604051806040016040528060008152602001836001600160401b0381111561290157612901613d55565b60405190808252806020026020018201604052801561292a578160200160208202803683370190505b509052604080518082019091526000808252602082018a905291925090805b84811015612a7057600086828151811061296557612965613ea6565b6020026020010151905080600261297c9190614d39565b6129869084614d45565b6040805160008082526020820190925291945090816129cd565b60408051606081018252600080825260208083018290529282015282526000199092019101816129a05790505b508b51909150156129e7576129e28b85612bcb565b9b5090505b8051600003612a1b57845160208601515103612a04575050612a70565b612a1686612a1187612dca565b612e01565b612a66565b80516001148015612a2a575081155b15612a5757612a168682600081518110612a4657612a46613ea6565b602002602001015160400151612e01565b612a6686612a11838886612e31565b5050600101612949565b5082516000190183525b825115612afa576000612a8c8461300e565b90506000612a998561300e565b855160010186526040805160208101859052908101829052909150606001604051602081830303815290604052805190602001208560200151866000015181518110612ae757612ae7613ea6565b6020026020010181815250505050612a7a565b8260200151600081518110612b1157612b11613ea6565b6020026020010151955050505050505b9392505050565b6040805181815261082081018252606091906000908260208201610800803683370190505090506000845b83821015612baf578015612baf576000612b6c82613046565b905080848481518110612b8157612b81613ea6565b6020908102919091010152612b97816002614d39565b612ba19083613d42565b915082600101925050612b53565b6000612bbb8386613d42565b8451038452509195945050505050565b606080600080855190505b80821015612c1557858281518110612bf057612bf0613ea6565b602002602001015160200151851115612c155781612c0d816144be565b925050612bd6565b60008215612c235782612c26565b60005b90506000816001600160401b03811115612c4257612c42613d55565b604051908082528060200260200182016040528015612c8d57816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181612c605790505b5090506000612c9c8385613d42565b6001600160401b03811115612cb357612cb3613d55565b604051908082528060200260200182016040528015612cfe57816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181612cd15790505b5082519091506000905b80821015612d58578a8281518110612d2257612d22613ea6565b6020026020010151848381518110612d3c57612d3c613ea6565b602002602001018190525081612d51906144be565b9150612d08565b60005b86831015612db6578b8381518110612d7557612d75613ea6565b6020026020010151848281518110612d8f57612d8f613ea6565b602002602001018190525082612da4906144be565b9250612daf816144be565b9050612d5b565b5092975090955050505050505b9250929050565b6000808260200151836000015181518110612de757612de7613ea6565b602090810291909101015183516001019093525090919050565b808260200151836000015181518110612e1c57612e1c613ea6565b60209081029190910101525080516001019052565b6000606080612e3f866130da565b925090506000846001600160401b03811115612e5d57612e5d613d55565b604051908082528060200260200182016040528015612e9057816020015b6060815260200190600190039081612e7b5790505b50905060005b85811015612ff8576000612eaa8288613d42565b612eb5906002614d39565b905080855103612ec55750612ff8565b6000612ed086613258565b90506000612ede828861335a565b8051909150806001600160401b03811115612efb57612efb613d55565b604051908082528060200260200182016040528015612f4057816020015b6040805180820190915260008082526020820152815260200190600190039081612f195790505b50868681518110612f5357612f53613ea6565b602002602001018190525060005b81811015612fdd576040518060400160405280848381518110612f8657612f86613ea6565b60200260200101518152602001612f9c8e612dca565b815250878781518110612fb157612fb1613ea6565b60200260200101518281518110612fca57612fca613ea6565b6020908102919091010152600101612f61565b50612fe78361347c565b975084600101945050505050612e96565b50613003818361358f565b979650505050505050565b600080826020015183600001518151811061302b5761302b613ea6565b60209081029190910101518351600019019093525090919050565b600080608083901c1561305b57608092831c92015b604083901c1561306d57604092831c92015b602083901c1561307f57602092831c92015b601083901c1561309157601092831c92015b600883901c156130a357600892831c92015b600483901c156130b557600492831c92015b600283901c156130c757600292831c92015b600183901c1561016c5760010192915050565b606080600080845190506000816001600160401b038111156130fe576130fe613d55565b60405190808252806020026020018201604052801561314357816020015b604080518082019091526000808252602082015281526020019060019003908161311c5790505b5090506000826001600160401b0381111561316057613160613d55565b604051908082528060200260200182016040528015613189578160200160208202803683370190505b5090505b8284101561324c5760405180604001604052808886815181106131b2576131b2613ea6565b60200260200101516000015181526020018886815181106131d5576131d5613ea6565b6020026020010151604001518152508285815181106131f6576131f6613ea6565b602002602001018190525086848151811061321357613213613ea6565b60200260200101516000015181858151811061323157613231613ea6565b6020908102919091010152613245846144be565b935061318d565b90969095509350505050565b80516060906000816001600160401b0381111561327757613277613d55565b6040519080825280602002602001820160405280156132a0578160200160208202803683370190505b50905060005b828110156133525760008582815181106132c2576132c2613ea6565b6020026020010151905080600003613302576132df816001614d45565b8383815181106132f1576132f1613ea6565b602002602001018181525050613349565b61330d600282614d6e565b60000361331f576132df816001614d45565b61332a600182613d42565b83838151811061333c5761333c613ea6565b6020026020010181815250505b506001016132a6565b509392505050565b81518151606091906000826001600160401b0381111561337c5761337c613d55565b6040519080825280602002602001820160405280156133a5578160200160208202803683370190505b5090506000805b8481101561345e576000805b8581101561340a578881815181106133d2576133d2613ea6565b60200260200101518a84815181106133ec576133ec613ea6565b602002602001015103613402576001915061340a565b6001016133b8565b50806134555788828151811061342257613422613ea6565b602002602001015184848151811061343c5761343c613ea6565b602090810291909101015282613451816144be565b9350505b506001016133ac565b50600061346b8286613d42565b835103835250909695505050505050565b80516060906000816001600160401b0381111561349b5761349b613d55565b6040519080825280602002602001820160405280156134c4578160200160208202803683370190505b5090506000805b8381101561357357600060028783815181106134e9576134e9613ea6565b60200260200101516134fb9190614d82565b905060008311801561352f57508084613515600186613d42565b8151811061352557613525613ea6565b6020026020010151145b1561353a5750613561565b8084848151811061354d5761354d613ea6565b602002602001018181525050826001019250505b8061356b816144be565b9150506134cb565b5060006135808285613d42565b83510383525090949350505050565b604080516000808252602082019092528190816135ce565b60408051808201909152600080825260208201528152602001906001900390816135a75790505b5090506135f583856000815181106135e8576135e8613ea6565b60200260200101516138c2565b8460008151811061360857613608613ea6565b6020908102919091010152835160005b8181101561388a57604080516000808252602082019092528161365d565b60408051808201909152600080825260208201528152602001906001900390816136365790505b509050835160000361368a5786828151811061367b5761367b613ea6565b602002602001015190506136b0565b6136ad87838151811061369f5761369f613ea6565b6020026020010151856138c2565b90505b6136bc81516002613ace565b6001600160401b038111156136d3576136d3613d55565b60405190808252806020026020018201604052801561371857816020015b60408051808201909152600080825260208201528152602001906001900390816136f15790505b508151909450600090815b818110156138735781613737826001614d45565b106137ab57600084828151811061375057613750613ea6565b6020026020010151905061378285838151811061376f5761376f613ea6565b6020026020010151600001516002613af6565b81528751819089908690811061379a5761379a613ea6565b602002602001018190525050613861565b60408051808201909152600080825260208201526137d485838151811061376f5761376f613ea6565b81528451613833908690849081106137ee576137ee613ea6565b602002602001015160200151868460016138089190614d45565b8151811061381857613818613ea6565b60200260200101516020015160009182526020526040902090565b60208201528751819089908690811061384e5761384e613ea6565b6020908102919091010152506001909201915b61386c600282614d45565b9050613723565b505050508080613882906144be565b915050613618565b50815160011461389957600080fd5b816000815181106138ac576138ac613ea6565b6020026020010151602001519250505092915050565b8151815160609160009182918291826138db8284614d45565b90506000816001600160401b038111156138f7576138f7613d55565b60405190808252806020026020018201604052801561393c57816020015b60408051808201909152600080825260208201528152602001906001900390816139155790505b5090505b838710801561394e57508286105b15613a235788868151811061396557613965613ea6565b6020026020010151600001518a888151811061398357613983613ea6565b60200260200101516000015110156139dc578987815181106139a7576139a7613ea6565b60200260200101518186815181106139c1576139c1613ea6565b60209081029190910101526001968701969490940193613940565b8886815181106139ee576139ee613ea6565b6020026020010151818681518110613a0857613a08613ea6565b60209081029190910101526001958601959490940193613940565b83871015613a7257898781518110613a3d57613a3d613ea6565b6020026020010151818681518110613a5757613a57613ea6565b60209081029190910101526001968701969490940193613a23565b82861015613ac157888681518110613a8c57613a8c613ea6565b6020026020010151818681518110613aa657613aa6613ea6565b60209081029190910101526001958601959490940193613a72565b9998505050505050505050565b600080613adb8385614d82565b9050613ae78385614d6e565b15612b21576001019392505050565b6000612b218284614d82565b600060208284031215613b1457600080fd5b81356001600160e01b031981168114612b2157600080fd5b6001600160a01b0381168114613b4157600080fd5b50565b600060808284031215613b5657600080fd5b50919050565b60008060408385031215613b6f57600080fd5b8235613b7a81613b2c565b915060208301356001600160401b03811115613b9557600080fd5b613ba185828601613b44565b9150509250929050565b600060408284031215613b5657600080fd5b60008060408385031215613bd057600080fd5b8235613bdb81613b2c565b915060208301356001600160401b03811115613bf657600080fd5b613ba185828601613bab565b600080600060408486031215613c1757600080fd5b8335613c2281613b2c565b925060208401356001600160401b0380821115613c3e57600080fd5b818601915086601f830112613c5257600080fd5b813581811115613c6157600080fd5b876020828501011115613c7357600080fd5b6020830194508093505050509250925092565b60008060408385031215613c9957600080fd5b8235613ca481613b2c565b915060208301356001600160401b03811115613cbf57600080fd5b830160208186031215613cd157600080fd5b809150509250929050565b600060208284031215613cee57600080fd5b815160048110612b2157600080fd5b634e487b7160e01b600052602160045260246000fd5b600060208284031215613d2557600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561016c5761016c613d2c565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715613d8d57613d8d613d55565b60405290565b60405160e081016001600160401b0381118282101715613d8d57613d8d613d55565b604080519081016001600160401b0381118282101715613d8d57613d8d613d55565b604051601f8201601f191681016001600160401b0381118282101715613dff57613dff613d55565b604052919050565b600060608284031215613e1957600080fd5b613e21613d6b565b905081518152602082015160208201526040820151604082015292915050565b600060608284031215613e5357600080fd5b612b218383613e07565b6000808335601e19843603018112613e7457600080fd5b8301803591506001600160401b03821115613e8e57600080fd5b6020019150600581901b3603821315612dc357600080fd5b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112613ed257600080fd5b9190910192915050565b60006001600160401b03821115613ef557613ef5613d55565b50601f01601f191660200190565b600082601f830112613f1457600080fd5b8135613f27613f2282613edc565b613dd7565b818152846020838601011115613f3c57600080fd5b816020850160208301376000918101602001919091529392505050565b80356001600160401b03811681146125e657600080fd5b600060e08284031215613f8257600080fd5b613f8a613d93565b905081356001600160401b0380821115613fa357600080fd5b613faf85838601613f03565b83526020840135915080821115613fc557600080fd5b613fd185838601613f03565b6020840152613fe260408501613f59565b60408401526060840135915080821115613ffb57600080fd5b61400785838601613f03565b6060840152608084013591508082111561402057600080fd5b61402c85838601613f03565b608084015261403d60a08501613f59565b60a084015260c084013591508082111561405657600080fd5b5061406384828501613f03565b60c08301525092915050565b600061016c3683613f70565b60006040828403121561408d57600080fd5b614095613db5565b8251815260208301516140a781613b2c565b60208201529392505050565b60005b838110156140ce5781810151838201526020016140b6565b50506000910152565b600083516140e98184602088016140b3565b8351908301906140fd8183602088016140b3565b01949350505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b600081518084526141478160208601602086016140b3565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b858110156141a357828403895261419184835161412f565b98850198935090840190600101614179565b5091979650505050505050565b60006060820186835260206060818501528186835260808501905060808760051b86010192508760005b8881101561424957868503607f190183528135368b9003601e1901811261420057600080fd5b8a0184810190356001600160401b0381111561421b57600080fd5b80360382131561422a57600080fd5b614235878284614106565b9650505091830191908301906001016141da565b505050508281036040840152613003818561415b565b60006001600160401b0382111561427857614278613d55565b5060051b60200190565b600082601f83011261429357600080fd5b81516142a1613f2282613edc565b8181528460208386010111156142b657600080fd5b6142c78260208301602087016140b3565b949350505050565b600060208083850312156142e257600080fd5b82516001600160401b03808211156142f957600080fd5b818501915085601f83011261430d57600080fd5b815161431b613f228261425f565b81815260059190911b8301840190848101908883111561433a57600080fd5b8585015b838110156143cd578051858111156143565760008081fd5b86016040818c03601f190181131561436e5760008081fd5b614376613db5565b89830151888111156143885760008081fd5b6143968e8c83870101614282565b8252509082015190878211156143ac5760008081fd5b6143ba8d8b84860101614282565b818b01528552505091860191860161433e565b5098975050505050505050565b6000815160e084526143ef60e085018261412f565b905060208301518482036020860152614408828261412f565b91505060408301516001600160401b038082166040870152606085015191508583036060870152614439838361412f565b9250608085015191508583036080870152614454838361412f565b92508060a08601511660a0870152505060c083015184820360c0860152612800828261412f565b60808152600061448e60808301866143da565b90506144b06020830185805182526020908101516001600160a01b0316910152565b826060830152949350505050565b6000600182016144d0576144d0613d2c565b5060010190565b60006144e5613f228461425f565b8381529050602080820190600585901b84018681111561450457600080fd5b845b8181101561453f5780356001600160401b038111156145255760008081fd5b61453189828901613f03565b855250928201928201614506565b505050509392505050565b6000612b213684846144d7565b80356125e681613b2c565b600082601f83011261457357600080fd5b612b21838335602085016144d7565b600060e0823603121561459457600080fd5b61459c613d93565b82356001600160401b03808211156145b357600080fd5b6145bf36838701613f03565b835260208501359150808211156145d557600080fd5b6145e136838701613f03565b60208401526145f260408601613f59565b604084015261460360608601614557565b606084015261461460808601613f59565b608084015260a085013591508082111561462d57600080fd5b5061463a36828601614562565b60a08301525061464c60c08401613f59565b60c082015292915050565b848152608060208201526000614670608083018661415b565b8281036040840152614682818661415b565b90508281036060840152613003818561412f565b6000815160e084526146ab60e085018261412f565b9050602083015184820360208601526146c4828261412f565b91505060408301516001600160401b03808216604087015260018060a01b03606086015116606087015280608086015116608087015260a0850151915085830360a0870152614713838361415b565b92508060c08601511660c087015250508091505092915050565b60006040808352845181828501526147486080850182614696565b9050602080870151603f198684030160608701528281518085528385019150838160051b860101848401935060005b828110156147c757868203601f190184528451805189845261479b8a85018261412f565b91880151848303858a01529190506147b3818361412f565b968801969588019593505050600101614777565b506001600160a01b038a16858a015296506147e192505050565b5050509392505050565b60008235607e19833603018112613ed257600080fd5b60008235605e19833603018112613ed257600080fd5b60006060828403121561482957600080fd5b614831613d6b565b905081356001600160401b038082111561484a57600080fd5b61485685838601613f70565b8352602084013591508082111561486c57600080fd5b5061487984828501613f03565b60208301525061488b60408301613f59565b604082015292915050565b6000606082360312156148a857600080fd5b6148b0613d6b565b82356001600160401b038111156148c657600080fd5b6148d236828601614817565b825250602083013560208201526040830135604082015280915050919050565b600081516060845261490760608501826143da565b905060208301518482036020860152614920828261412f565b9150506001600160401b0360408401511660408501528091505092915050565b60408152600061495360408301856148f2565b905060018060a01b03831660208301529392505050565b60006060823603121561497c57600080fd5b614984613d6b565b82356001600160401b0381111561499a57600080fd5b6148d236828601613f70565b6000602082840312156149b857600080fd5b81516001600160401b038111156149ce57600080fd5b6142c784828501614282565b6000602082840312156149ec57600080fd5b8151612b2181613b2c565b60408152600061495360408301856143da565b604081526000614a1d604083018661412f565b8281036020840152614a30818587614106565b9695505050505050565b60008082840360c0811215614a4e57600080fd5b83516001600160401b03811115614a6457600080fd5b614a7086828701614282565b93505060a0601f1982011215614a8557600080fd5b50614a8e613d6b565b6020840151815260408401516020820152614aac8560608601613e07565b6040820152809150509250929050565b602081526000612b21602083018461412f565b600061016c3683614817565b60808152600061448e60808301866148f2565b60808152600061448e6080830186614696565b60008851614b13818460208d016140b3565b885190830190614b27818360208d016140b3565b60c089811b6001600160c01b03199081169390920192835288811b8216600884015287901b1660108201528451614b658160188401602089016140b3565b8451910190614b7b8160188401602088016140b3565b016018019998505050505050505050565b60008351614b9e8184602088016140b3565b60c09390931b6001600160c01b0319169190920190815260080192915050565b60008851614bd0818460208d016140b3565b885190830190614be4818360208d016140b3565b60c089811b6001600160c01b03199081169390920192835288901b1660088201528551614c18816010840160208a016140b3565b8551910190614c2e8160108401602089016140b3565b8451910190614c448160108401602088016140b3565b016010019998505050505050505050565b600181815b80851115614c90578160001904821115614c7657614c76613d2c565b80851615614c8357918102915b93841c9390800290614c5a565b509250929050565b600082614ca75750600161016c565b81614cb45750600061016c565b8160018114614cca5760028114614cd457614cf0565b600191505061016c565b60ff841115614ce557614ce5613d2c565b50506001821b61016c565b5060208310610133831016604e8410600b8410161715614d13575081810a61016c565b614d1d8383614c55565b8060001904821115614d3157614d31613d2c565b029392505050565b6000612b218383614c98565b8082018082111561016c5761016c613d2c565b634e487b7160e01b600052601260045260246000fd5b600082614d7d57614d7d614d58565b500690565b600082614d9157614d91614d58565b50049056fea26469706673582212209cfacc48ea27d6e669c54fa6bd03b3260804fd2212e74b33613a4f918f8fb13a64736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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