ETH Price: $2,679.83 (-1.16%)

Contract

0xFA353402466F95fB7C705F78531ac9E831cf1B4E
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Submit Initial214510992024-12-21 13:11:4757 days ago1734786707IN
0xFA353402...831cf1B4E
0 ETH0.001032277.97985306
Submit Final214510982024-12-21 13:11:3557 days ago1734786695IN
0xFA353402...831cf1B4E
0 ETH0.00345738.27749261
Commit Prev Rand...214510962024-12-21 13:11:1157 days ago1734786671IN
0xFA353402...831cf1B4E
0 ETH0.000365047.81120927
Submit Initial214509662024-12-21 12:44:4757 days ago1734785087IN
0xFA353402...831cf1B4E
0 ETH0.001191859.21244209
Submit Final214509632024-12-21 12:44:1157 days ago1734785051IN
0xFA353402...831cf1B4E
0 ETH0.00424219.58143159
Commit Prev Rand...214509602024-12-21 12:43:3557 days ago1734785015IN
0xFA353402...831cf1B4E
0 ETH0.000454329.7192153
Submit Initial214508302024-12-21 12:17:2357 days ago1734783443IN
0xFA353402...831cf1B4E
0 ETH0.000851648.94781658
Submit Initial214508282024-12-21 12:16:5957 days ago1734783419IN
0xFA353402...831cf1B4E
0 ETH0.001060238.19479079
Submit Final214508262024-12-21 12:16:3557 days ago1734783395IN
0xFA353402...831cf1B4E
0 ETH0.003756928.91797775
Commit Prev Rand...214508232024-12-21 12:15:5957 days ago1734783359IN
0xFA353402...831cf1B4E
0 ETH0.000427299.14099714
Submit Initial214506932024-12-21 11:49:5957 days ago1734781799IN
0xFA353402...831cf1B4E
0 ETH0.000885196.84080253
Submit Final214506922024-12-21 11:49:4757 days ago1734781787IN
0xFA353402...831cf1B4E
0 ETH0.002863346.84314261
Commit Prev Rand...214506902024-12-21 11:49:2357 days ago1734781763IN
0xFA353402...831cf1B4E
0 ETH0.000305876.54354996
Submit Initial214505592024-12-21 11:22:5957 days ago1734780179IN
0xFA353402...831cf1B4E
0 ETH0.000887936.86255327
Submit Final214505562024-12-21 11:22:2357 days ago1734780143IN
0xFA353402...831cf1B4E
0 ETH0.002816776.71356275
Commit Prev Rand...214505542024-12-21 11:21:5957 days ago1734780119IN
0xFA353402...831cf1B4E
0 ETH0.000321746.8830034
Submit Initial214504242024-12-21 10:55:5957 days ago1734778559IN
0xFA353402...831cf1B4E
0 ETH0.000843986.52333282
Submit Final214504222024-12-21 10:55:3557 days ago1734778535IN
0xFA353402...831cf1B4E
0 ETH0.002773676.62857536
Commit Prev Rand...214504202024-12-21 10:55:1157 days ago1734778511IN
0xFA353402...831cf1B4E
0 ETH0.00030726.57194492
Submit Initial214502902024-12-21 10:29:1157 days ago1734776951IN
0xFA353402...831cf1B4E
0 ETH0.000954097.3753923
Submit Final214502892024-12-21 10:28:5957 days ago1734776939IN
0xFA353402...831cf1B4E
0 ETH0.003003637.17705271
Commit Prev Rand...214502872024-12-21 10:28:3557 days ago1734776915IN
0xFA353402...831cf1B4E
0 ETH0.000337.05975796
Submit Initial214501552024-12-21 10:02:1157 days ago1734775331IN
0xFA353402...831cf1B4E
0 ETH0.001013017.82938198
Submit Final214501542024-12-21 10:01:5957 days ago1734775319IN
0xFA353402...831cf1B4E
0 ETH0.003223417.61141536
Commit Prev Rand...214501522024-12-21 10:01:3557 days ago1734775295IN
0xFA353402...831cf1B4E
0 ETH0.000316656.77401071
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BeefyClient

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 999999 runs

Other Settings:
paris EvmVersion
File 1 of 12 : BeefyClient.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.25;

import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol";
import {SubstrateMerkleProof} from "./utils/SubstrateMerkleProof.sol";
import {Bitfield} from "./utils/Bitfield.sol";
import {Uint16Array, createUint16Array} from "./utils/Uint16Array.sol";
import {Math} from "./utils/Math.sol";
import {MMRProof} from "./utils/MMRProof.sol";
import {ScaleCodec} from "./utils/ScaleCodec.sol";

/**
 * @title BeefyClient
 *
 * High-level documentation at https://docs.snowbridge.network/architecture/verification/polkadot
 *
 * To submit new commitments, relayers must call the following methods sequentially:
 * 1. submitInitial: Setup the session for the interactive submission
 * 2. commitPrevRandao: Commit to a random seed for generating a validator subsampling
 * 3. createFinalBitfield: Generate the validator subsampling
 * 4. submitFinal: Complete submission after providing the request validator signatures
 *
 */
contract BeefyClient {
    using Math for uint16;
    using Math for uint256;

    /* Events */

    /**
     * @dev Emitted when the MMR root is updated
     * @param mmrRoot the updated MMR root
     * @param blockNumber the beefy block number of the updated MMR root
     */
    event NewMMRRoot(bytes32 mmrRoot, uint64 blockNumber);

    /**
     * @dev Emitted when a new ticket has been created
     * @param relayer The relayer who created the ticket
     * @param blockNumber the parent block number of the candidate MMR root
     */
    event NewTicket(address relayer, uint64 blockNumber);

    /* Types */

    /**
     * @dev The Commitment, with its payload, is the core thing we are trying to verify with
     * this contract. It contains an MMR root that commits to the polkadot history, including
     * past blocks and parachain blocks and can be used to verify both polkadot and parachain blocks.
     */
    struct Commitment {
        // Relay chain block number
        uint32 blockNumber;
        // ID of the validator set that signed the commitment
        uint64 validatorSetID;
        // The payload of the new commitment in beefy justifications (in
        // our case, this is a new MMR root for all past polkadot blocks)
        PayloadItem[] payload;
    }

    /**
     * @dev Each PayloadItem is a piece of data signed by validators at a particular block.
     */
    struct PayloadItem {
        // An ID that references a description of the data in the payload item.
        // Known payload ids can be found [upstream](https://github.com/paritytech/substrate/blob/fe1f8ba1c4f23931ae89c1ada35efb3d908b50f5/primitives/consensus/beefy/src/payload.rs#L27).
        bytes2 payloadID;
        // The contents of the payload item
        bytes data;
    }

    /**
     * @dev The ValidatorProof is a proof used to verify a commitment signature
     */
    struct ValidatorProof {
        // The parity bit to specify the intended solution
        uint8 v;
        // The x component on the secp256k1 curve
        bytes32 r;
        // The challenge solution
        bytes32 s;
        // Leaf index of the validator address in the merkle tree
        uint256 index;
        // Validator address
        address account;
        // Merkle proof for the validator
        bytes32[] proof;
    }

    /**
     * @dev A ticket tracks working state for the interactive submission of new commitments
     */
    struct Ticket {
        // The block number this ticket was issued
        uint64 blockNumber;
        // Length of the validator set that signed the commitment
        uint32 validatorSetLen;
        // The number of signatures required
        uint32 numRequiredSignatures;
        // The PREVRANDAO seed selected for this ticket session
        uint256 prevRandao;
        // Hash of a bitfield claiming which validators have signed
        bytes32 bitfieldHash;
    }

    /// @dev The MMRLeaf describes the leaf structure of the MMR
    struct MMRLeaf {
        // Version of the leaf type
        uint8 version;
        // Parent number of the block this leaf describes
        uint32 parentNumber;
        // Parent hash of the block this leaf describes
        bytes32 parentHash;
        // Validator set id that will be part of consensus for the next block
        uint64 nextAuthoritySetID;
        // Length of that validator set
        uint32 nextAuthoritySetLen;
        // Merkle root of all public keys in that validator set
        bytes32 nextAuthoritySetRoot;
        // Merkle root of all parachain headers in this block
        bytes32 parachainHeadsRoot;
    }

    /**
     * @dev The ValidatorSet describes a BEEFY validator set
     */
    struct ValidatorSet {
        // Identifier for the set
        uint128 id;
        // Number of validators in the set
        uint128 length;
        // Merkle root of BEEFY validator addresses
        bytes32 root;
    }

    /**
     * @dev The ValidatorSetState describes a BEEFY validator set along with signature usage counters
     */
    struct ValidatorSetState {
        // Identifier for the set
        uint128 id;
        // Number of validators in the set
        uint128 length;
        // Merkle root of BEEFY validator addresses
        bytes32 root;
        // Number of times a validator signature has been used
        Uint16Array usageCounters;
    }

    /* State */

    /// @dev The latest verified MMR root
    bytes32 public latestMMRRoot;

    /// @dev The block number in the relay chain in which the latest MMR root was emitted
    uint64 public latestBeefyBlock;

    /// @dev State of the current validator set
    ValidatorSetState public currentValidatorSet;

    /// @dev State of the next validator set
    ValidatorSetState public nextValidatorSet;

    /// @dev Pending tickets for commitment submission
    mapping(bytes32 ticketID => Ticket) public tickets;

    /* Constants */

    /**
     * @dev Beefy payload id for MMR Root payload items:
     * https://github.com/paritytech/substrate/blob/fe1f8ba1c4f23931ae89c1ada35efb3d908b50f5/primitives/consensus/beefy/src/payload.rs#L33
     */
    bytes2 public constant MMR_ROOT_ID = bytes2("mh");

    /**
     * @dev Minimum delay in number of blocks that a relayer must wait between calling
     * submitInitial and commitPrevRandao. In production this should be set to MAX_SEED_LOOKAHEAD:
     * https://eth2book.info/altair/part3/config/preset#max_seed_lookahead
     */
    uint256 public immutable randaoCommitDelay;

    /**
     * @dev after randaoCommitDelay is reached, relayer must
     * call commitPrevRandao within this number of blocks.
     * Without this expiration, relayers can roll the dice infinitely to get the subsampling
     * they desire.
     */
    uint256 public immutable randaoCommitExpiration;

    /**
     * @dev Minimum number of signatures required to validate a new commitment. This parameter
     * is calculated based on `randaoCommitExpiration`. See ~/scripts/beefy_signature_sampling.py
     * for the calculation.
     */
    uint256 public immutable minNumRequiredSignatures;

    /* Errors */
    error InvalidBitfield();
    error InvalidBitfieldLength();
    error InvalidCommitment();
    error InvalidMMRLeaf();
    error InvalidMMRLeafProof();
    error InvalidMMRRootLength();
    error InvalidSignature();
    error InvalidTicket();
    error InvalidValidatorProof();
    error InvalidValidatorProofLength();
    error CommitmentNotRelevant();
    error NotEnoughClaims();
    error PrevRandaoAlreadyCaptured();
    error PrevRandaoNotCaptured();
    error StaleCommitment();
    error TicketExpired();
    error WaitPeriodNotOver();

    constructor(
        uint256 _randaoCommitDelay,
        uint256 _randaoCommitExpiration,
        uint256 _minNumRequiredSignatures,
        uint64 _initialBeefyBlock,
        ValidatorSet memory _initialValidatorSet,
        ValidatorSet memory _nextValidatorSet
    ) {
        if (_nextValidatorSet.id != _initialValidatorSet.id + 1) {
            revert("invalid-constructor-params");
        }
        randaoCommitDelay = _randaoCommitDelay;
        randaoCommitExpiration = _randaoCommitExpiration;
        minNumRequiredSignatures = _minNumRequiredSignatures;
        latestBeefyBlock = _initialBeefyBlock;
        currentValidatorSet.id = _initialValidatorSet.id;
        currentValidatorSet.length = _initialValidatorSet.length;
        currentValidatorSet.root = _initialValidatorSet.root;
        currentValidatorSet.usageCounters = createUint16Array(currentValidatorSet.length);
        nextValidatorSet.id = _nextValidatorSet.id;
        nextValidatorSet.length = _nextValidatorSet.length;
        nextValidatorSet.root = _nextValidatorSet.root;
        nextValidatorSet.usageCounters = createUint16Array(nextValidatorSet.length);
    }

    /* External Functions */

    /**
     * @dev Begin submission of commitment
     * @param commitment contains the commitment signed by the validators
     * @param bitfield a bitfield claiming which validators have signed the commitment
     * @param proof a proof that a single validator from currentValidatorSet has signed the commitment
     */
    function submitInitial(Commitment calldata commitment, uint256[] calldata bitfield, ValidatorProof calldata proof)
        external
    {
        if (commitment.blockNumber <= latestBeefyBlock) {
            revert StaleCommitment();
        }

        ValidatorSetState storage vset;
        uint16 signatureUsageCount;
        if (commitment.validatorSetID == currentValidatorSet.id) {
            signatureUsageCount = currentValidatorSet.usageCounters.get(proof.index);
            currentValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1));
            vset = currentValidatorSet;
        } else if (commitment.validatorSetID == nextValidatorSet.id) {
            signatureUsageCount = nextValidatorSet.usageCounters.get(proof.index);
            nextValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1));
            vset = nextValidatorSet;
        } else {
            revert InvalidCommitment();
        }

        // Check if merkle proof is valid based on the validatorSetRoot and if proof is included in bitfield
        if (!isValidatorInSet(vset, proof.account, proof.index, proof.proof) || !Bitfield.isSet(bitfield, proof.index))
        {
            revert InvalidValidatorProof();
        }

        // Check if validatorSignature is correct, ie. check if it matches
        // the signature of senderPublicKey on the commitmentHash
        bytes32 commitmentHash = keccak256(encodeCommitment(commitment));
        if (ECDSA.recover(commitmentHash, proof.v, proof.r, proof.s) != proof.account) {
            revert InvalidSignature();
        }

        // For the initial submission, the supplied bitfield should claim that more than
        // two thirds of the validator set have sign the commitment
        if (Bitfield.countSetBits(bitfield) < computeQuorum(vset.length)) {
            revert NotEnoughClaims();
        }

        tickets[createTicketID(msg.sender, commitmentHash)] = Ticket({
            blockNumber: uint64(block.number),
            validatorSetLen: uint32(vset.length),
            numRequiredSignatures: uint32(
                computeNumRequiredSignatures(vset.length, signatureUsageCount, minNumRequiredSignatures)
                ),
            prevRandao: 0,
            bitfieldHash: keccak256(abi.encodePacked(bitfield))
        });

        emit NewTicket(msg.sender, commitment.blockNumber);
    }

    /**
     * @dev Capture PREVRANDAO
     * @param commitmentHash contains the commitmentHash signed by the validators
     */
    function commitPrevRandao(bytes32 commitmentHash) external {
        bytes32 ticketID = createTicketID(msg.sender, commitmentHash);
        Ticket storage ticket = tickets[ticketID];

        if (ticket.blockNumber == 0) {
            revert InvalidTicket();
        }

        if (ticket.prevRandao != 0) {
            revert PrevRandaoAlreadyCaptured();
        }

        // relayer must wait `randaoCommitDelay` blocks
        if (block.number < ticket.blockNumber + randaoCommitDelay) {
            revert WaitPeriodNotOver();
        }

        // relayer can capture within `randaoCommitExpiration` blocks
        if (block.number > ticket.blockNumber + randaoCommitDelay + randaoCommitExpiration) {
            delete tickets[ticketID];
            revert TicketExpired();
        }

        // Post-merge, the difficulty opcode now returns PREVRANDAO
        ticket.prevRandao = block.prevrandao;
    }

    /**
     * @dev Submit a commitment and leaf for final verification
     * @param commitment contains the full commitment that was used for the commitmentHash
     * @param bitfield claiming which validators have signed the commitment
     * @param proofs a struct containing the data needed to verify all validator signatures
     * @param leaf an MMR leaf provable using the MMR root in the commitment payload
     * @param leafProof an MMR leaf proof
     * @param leafProofOrder a bitfield describing the order of each item (left vs right)
     */
    function submitFinal(
        Commitment calldata commitment,
        uint256[] calldata bitfield,
        ValidatorProof[] calldata proofs,
        MMRLeaf calldata leaf,
        bytes32[] calldata leafProof,
        uint256 leafProofOrder
    ) external {
        bytes32 commitmentHash = keccak256(encodeCommitment(commitment));
        bytes32 ticketID = createTicketID(msg.sender, commitmentHash);
        validateTicket(ticketID, commitment, bitfield);

        bool is_next_session = false;
        ValidatorSetState storage vset;
        if (commitment.validatorSetID == nextValidatorSet.id) {
            is_next_session = true;
            vset = nextValidatorSet;
        } else if (commitment.validatorSetID == currentValidatorSet.id) {
            vset = currentValidatorSet;
        } else {
            revert InvalidCommitment();
        }

        verifyCommitment(commitmentHash, ticketID, bitfield, vset, proofs);

        bytes32 newMMRRoot = ensureProvidesMMRRoot(commitment);

        if (is_next_session) {
            if (leaf.nextAuthoritySetID != nextValidatorSet.id + 1) {
                revert InvalidMMRLeaf();
            }
            bool leafIsValid =
                MMRProof.verifyLeafProof(newMMRRoot, keccak256(encodeMMRLeaf(leaf)), leafProof, leafProofOrder);
            if (!leafIsValid) {
                revert InvalidMMRLeafProof();
            }
            currentValidatorSet = nextValidatorSet;
            nextValidatorSet.id = leaf.nextAuthoritySetID;
            nextValidatorSet.length = leaf.nextAuthoritySetLen;
            nextValidatorSet.root = leaf.nextAuthoritySetRoot;
            nextValidatorSet.usageCounters = createUint16Array(leaf.nextAuthoritySetLen);
        }

        latestMMRRoot = newMMRRoot;
        latestBeefyBlock = commitment.blockNumber;
        delete tickets[ticketID];

        emit NewMMRRoot(newMMRRoot, commitment.blockNumber);
    }

    /**
     * @dev Verify that the supplied MMR leaf is included in the latest verified MMR root.
     * @param leafHash contains the merkle leaf to be verified
     * @param proof contains simplified mmr proof
     * @param proofOrder a bitfield describing the order of each item (left vs right)
     */
    function verifyMMRLeafProof(bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder)
        external
        view
        returns (bool)
    {
        return MMRProof.verifyLeafProof(latestMMRRoot, leafHash, proof, proofOrder);
    }

    /**
     * @dev Helper to create an initial validator bitfield.
     * @param bitsToSet contains indexes of all signed validators, should be deduplicated
     * @param length of validator set
     */
    function createInitialBitfield(uint256[] calldata bitsToSet, uint256 length)
        external
        pure
        returns (uint256[] memory)
    {
        if (length < bitsToSet.length) {
            revert InvalidBitfieldLength();
        }
        return Bitfield.createBitfield(bitsToSet, length);
    }

    /**
     * @dev Helper to create a final bitfield, with subsampled validator selections
     * @param commitmentHash contains the commitmentHash signed by the validators
     * @param bitfield claiming which validators have signed the commitment
     */
    function createFinalBitfield(bytes32 commitmentHash, uint256[] calldata bitfield)
        external
        view
        returns (uint256[] memory)
    {
        Ticket storage ticket = tickets[createTicketID(msg.sender, commitmentHash)];
        if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) {
            revert InvalidBitfield();
        }
        return Bitfield.subsample(ticket.prevRandao, bitfield, ticket.numRequiredSignatures, ticket.validatorSetLen);
    }

    /* Internal Functions */

    // Creates a unique ticket ID for a new interactive prover-verifier session
    function createTicketID(address account, bytes32 commitmentHash) internal pure returns (bytes32 value) {
        assembly {
            mstore(0x00, account)
            mstore(0x20, commitmentHash)
            value := keccak256(0x0, 0x40)
        }
    }

    /**
     * @dev Calculates the number of required signatures for `submitFinal`.
     * @param validatorSetLen The length of the validator set
     * @param signatureUsageCount A counter of the number of times the validator signature was previously used in a call to `submitInitial` within the session.
     * @param minRequiredSignatures The minimum amount of signatures to verify
     */
    // For more details on the calculation, read the following:
    // 1. https://docs.snowbridge.network/architecture/verification/polkadot#signature-sampling
    // 2. https://hackmd.io/9OedC7icR5m-in_moUZ_WQ
    function computeNumRequiredSignatures(
        uint256 validatorSetLen,
        uint256 signatureUsageCount,
        uint256 minRequiredSignatures
    ) internal pure returns (uint256) {
        // Start with the minimum number of signatures.
        uint256 numRequiredSignatures = minRequiredSignatures;
        // Add signatures based on the number of validators in the validator set.
        numRequiredSignatures += Math.log2(validatorSetLen, Math.Rounding.Ceil);
        // Add signatures based on the signature usage count.
        numRequiredSignatures += 1 + (2 * Math.log2(signatureUsageCount, Math.Rounding.Ceil));
        // Never require more signatures than a 2/3 majority
        return Math.min(numRequiredSignatures, computeQuorum(validatorSetLen));
    }

    /**
     * @dev Calculates 2/3 majority required for quorum for a given number of validators.
     * @param numValidators The number of validators in the validator set.
     */
    function computeQuorum(uint256 numValidators) internal pure returns (uint256) {
        return numValidators - (numValidators - 1) / 3;
    }

    /**
     * @dev Verify commitment using the supplied signature proofs
     */
    function verifyCommitment(
        bytes32 commitmentHash,
        bytes32 ticketID,
        uint256[] calldata bitfield,
        ValidatorSetState storage vset,
        ValidatorProof[] calldata proofs
    ) internal view {
        Ticket storage ticket = tickets[ticketID];
        // Verify that enough signature proofs have been supplied
        uint256 numRequiredSignatures = ticket.numRequiredSignatures;
        if (proofs.length != numRequiredSignatures) {
            revert InvalidValidatorProofLength();
        }

        // Generate final bitfield indicating which validators need to be included in the proofs.
        uint256[] memory finalbitfield =
            Bitfield.subsample(ticket.prevRandao, bitfield, numRequiredSignatures, vset.length);

        for (uint256 i = 0; i < proofs.length; i++) {
            ValidatorProof calldata proof = proofs[i];

            // Check that validator is in bitfield
            if (!Bitfield.isSet(finalbitfield, proof.index)) {
                revert InvalidValidatorProof();
            }

            // Check that validator is actually in a validator set
            if (!isValidatorInSet(vset, proof.account, proof.index, proof.proof)) {
                revert InvalidValidatorProof();
            }

            // Check that validator signed the commitment
            if (ECDSA.recover(commitmentHash, proof.v, proof.r, proof.s) != proof.account) {
                revert InvalidSignature();
            }

            // Ensure no validator can appear more than once in bitfield
            Bitfield.unset(finalbitfield, proof.index);
        }
    }

    // Ensure that the commitment provides a new MMR root
    function ensureProvidesMMRRoot(Commitment calldata commitment) internal pure returns (bytes32) {
        for (uint256 i = 0; i < commitment.payload.length; i++) {
            if (commitment.payload[i].payloadID == MMR_ROOT_ID) {
                if (commitment.payload[i].data.length != 32) {
                    revert InvalidMMRRootLength();
                } else {
                    return bytes32(commitment.payload[i].data);
                }
            }
        }
        revert CommitmentNotRelevant();
    }

    function encodeCommitment(Commitment calldata commitment) internal pure returns (bytes memory) {
        return bytes.concat(
            encodeCommitmentPayload(commitment.payload),
            ScaleCodec.encodeU32(commitment.blockNumber),
            ScaleCodec.encodeU64(commitment.validatorSetID)
        );
    }

    function encodeCommitmentPayload(PayloadItem[] calldata items) internal pure returns (bytes memory) {
        bytes memory payload = ScaleCodec.checkedEncodeCompactU32(items.length);
        for (uint256 i = 0; i < items.length; i++) {
            payload = bytes.concat(
                payload, items[i].payloadID, ScaleCodec.checkedEncodeCompactU32(items[i].data.length), items[i].data
            );
        }

        return payload;
    }

    function encodeMMRLeaf(MMRLeaf calldata leaf) internal pure returns (bytes memory) {
        return bytes.concat(
            ScaleCodec.encodeU8(leaf.version),
            ScaleCodec.encodeU32(leaf.parentNumber),
            leaf.parentHash,
            ScaleCodec.encodeU64(leaf.nextAuthoritySetID),
            ScaleCodec.encodeU32(leaf.nextAuthoritySetLen),
            leaf.nextAuthoritySetRoot,
            leaf.parachainHeadsRoot
        );
    }

    /**
     * @dev Checks if a validators address is a member of the merkle tree
     * @param vset The validator set
     * @param account The address of the validator to check for inclusion in `vset`.
     * @param index The leaf index of the account in the merkle tree of validator set addresses.
     * @param proof Merkle proof required for validation of the address
     * @return true if the validator is in the set
     */
    function isValidatorInSet(ValidatorSetState storage vset, address account, uint256 index, bytes32[] calldata proof)
        internal
        view
        returns (bool)
    {
        bytes32 hashedLeaf = keccak256(abi.encodePacked(account));
        return SubstrateMerkleProof.verify(vset.root, hashedLeaf, index, vset.length, proof);
    }

    /**
     * @dev Basic validation of a ticket for submitFinal
     */
    function validateTicket(bytes32 ticketID, Commitment calldata commitment, uint256[] calldata bitfield)
        internal
        view
    {
        Ticket storage ticket = tickets[ticketID];

        if (ticket.blockNumber == 0) {
            // submitInitial hasn't been called yet
            revert InvalidTicket();
        }

        if (ticket.prevRandao == 0) {
            // commitPrevRandao hasn't been called yet
            revert PrevRandaoNotCaptured();
        }

        if (commitment.blockNumber <= latestBeefyBlock) {
            // ticket is obsolete
            revert StaleCommitment();
        }

        if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) {
            // The provided claims bitfield isn't the same one that was
            // passed to submitInitial
            revert InvalidBitfield();
        }
    }
}

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

pragma solidity ^0.8.0;

import "../Strings.sol";

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

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

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

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

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

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

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

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

        return (signer, RecoverError.NoError);
    }

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 6 of 12 : Bitfield.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.25;

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

library Bitfield {
    using Bits for uint256;

    /**
     * @dev Constants used to efficiently calculate the hamming weight of a bitfield. See
     * https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation for an explanation of those constants.
     */
    uint256 internal constant M1 = 0x5555555555555555555555555555555555555555555555555555555555555555;
    uint256 internal constant M2 = 0x3333333333333333333333333333333333333333333333333333333333333333;
    uint256 internal constant M4 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
    uint256 internal constant M8 = 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff;
    uint256 internal constant M16 = 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff;
    uint256 internal constant M32 = 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff;
    uint256 internal constant M64 = 0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff;
    uint256 internal constant M128 = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;

    uint256 internal constant ONE = uint256(1);

    /**
     * @notice Core subsampling algorithm. Draws a random number, derives an index in the bitfield, and sets the bit if it is in the `prior` and not
     * yet set. Repeats that `n` times.
     * @param seed Source of randomness for selecting validator signatures.
     * @param prior Bitfield indicating which validators claim to have signed the commitment.
     * @param n Number of unique bits in prior that must be set in the result. Must be <= number of set bits in `prior`.
     * @param length Length of the bitfield prior to draw bits from. Must be <= prior.length * 256.
     */
    function subsample(uint256 seed, uint256[] memory prior, uint256 n, uint256 length)
        internal
        pure
        returns (uint256[] memory bitfield)
    {
        bitfield = new uint256[](prior.length);
        uint256 found = 0;

        for (uint256 i = 0; found < n;) {
            uint256 index = makeIndex(seed, i, length);

            // require randomly selected bit to be set in prior and not yet set in bitfield
            if (!isSet(prior, index) || isSet(bitfield, index)) {
                unchecked {
                    i++;
                }
                continue;
            }

            set(bitfield, index);

            unchecked {
                found++;
                i++;
            }
        }

        return bitfield;
    }

    /**
     * @dev Helper to create a bitfield.
     */
    function createBitfield(uint256[] calldata bitsToSet, uint256 length)
        internal
        pure
        returns (uint256[] memory bitfield)
    {
        // Calculate length of uint256 array based on rounding up to number of uint256 needed
        uint256 arrayLength = (length + 255) / 256;

        bitfield = new uint256[](arrayLength);

        for (uint256 i = 0; i < bitsToSet.length; i++) {
            set(bitfield, bitsToSet[i]);
        }

        return bitfield;
    }

    /**
     * @notice Calculates the number of set bits by using the hamming weight of the bitfield.
     * The algorithm below is implemented after https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation.
     * Further improvements are possible, see the article above.
     */
    function countSetBits(uint256[] memory self) internal pure returns (uint256) {
        unchecked {
            uint256 count = 0;
            for (uint256 i = 0; i < self.length; i++) {
                uint256 x = self[i];
                x = (x & M1) + ((x >> 1) & M1); //put count of each  2 bits into those  2 bits
                x = (x & M2) + ((x >> 2) & M2); //put count of each  4 bits into those  4 bits
                x = (x & M4) + ((x >> 4) & M4); //put count of each  8 bits into those  8 bits
                x = (x & M8) + ((x >> 8) & M8); //put count of each 16 bits into those 16 bits
                x = (x & M16) + ((x >> 16) & M16); //put count of each 32 bits into those 32 bits
                x = (x & M32) + ((x >> 32) & M32); //put count of each 64 bits into those 64 bits
                x = (x & M64) + ((x >> 64) & M64); //put count of each 128 bits into those 128 bits
                x = (x & M128) + ((x >> 128) & M128); //put count of each 256 bits into those 256 bits
                count += x;
            }
            return count;
        }
    }

    function isSet(uint256[] memory self, uint256 index) internal pure returns (bool) {
        uint256 element = index >> 8;
        return self[element].bit(uint8(index)) == 1;
    }

    function set(uint256[] memory self, uint256 index) internal pure {
        uint256 element = index >> 8;
        self[element] = self[element].setBit(uint8(index));
    }

    function unset(uint256[] memory self, uint256 index) internal pure {
        uint256 element = index >> 8;
        self[element] = self[element].clearBit(uint8(index));
    }

    function makeIndex(uint256 seed, uint256 iteration, uint256 length) internal pure returns (uint256 index) {
        assembly {
            mstore(0x00, seed)
            mstore(0x20, iteration)
            index := mod(keccak256(0x00, 0x40), length)
        }
    }
}

File 7 of 12 : Bits.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
// Code from https://github.com/ethereum/solidity-examples
pragma solidity 0.8.25;

library Bits {
    uint256 internal constant ONE = uint256(1);
    uint256 internal constant ONES = type(uint256).max;

    // Sets the bit at the given 'index' in 'self' to '1'.
    // Returns the modified value.
    function setBit(uint256 self, uint8 index) internal pure returns (uint256) {
        return self | (ONE << index);
    }

    // Sets the bit at the given 'index' in 'self' to '0'.
    // Returns the modified value.
    function clearBit(uint256 self, uint8 index) internal pure returns (uint256) {
        return self & ~(ONE << index);
    }

    // Sets the bit at the given 'index' in 'self' to:
    //  '1' - if the bit is '0'
    //  '0' - if the bit is '1'
    // Returns the modified value.
    function toggleBit(uint256 self, uint8 index) internal pure returns (uint256) {
        return self ^ (ONE << index);
    }

    // Get the value of the bit at the given 'index' in 'self'.
    function bit(uint256 self, uint8 index) internal pure returns (uint8) {
        return uint8((self >> index) & 1);
    }

    // Check if the bit at the given 'index' in 'self' is set.
    // Returns:
    //  'true' - if the value of the bit is '1'
    //  'false' - if the value of the bit is '0'
    function bitSet(uint256 self, uint8 index) internal pure returns (bool) {
        return (self >> index) & 1 == 1;
    }

    // Checks if the bit at the given 'index' in 'self' is equal to the corresponding
    // bit in 'other'.
    // Returns:
    //  'true' - if both bits are '0' or both bits are '1'
    //  'false' - otherwise
    function bitEqual(uint256 self, uint256 other, uint8 index) internal pure returns (bool) {
        return ((self ^ other) >> index) & 1 == 0;
    }

    // Get the bitwise NOT of the bit at the given 'index' in 'self'.
    function bitNot(uint256 self, uint8 index) internal pure returns (uint8) {
        return uint8(1 - ((self >> index) & 1));
    }

    // Computes the bitwise AND of the bit at the given 'index' in 'self', and the
    // corresponding bit in 'other', and returns the value.
    function bitAnd(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) {
        return uint8(((self & other) >> index) & 1);
    }

    // Computes the bitwise OR of the bit at the given 'index' in 'self', and the
    // corresponding bit in 'other', and returns the value.
    function bitOr(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) {
        return uint8(((self | other) >> index) & 1);
    }

    // Computes the bitwise XOR of the bit at the given 'index' in 'self', and the
    // corresponding bit in 'other', and returns the value.
    function bitXor(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) {
        return uint8(((self ^ other) >> index) & 1);
    }

    // Gets 'numBits' consecutive bits from 'self', starting from the bit at 'startIndex'.
    // Returns the bits as a 'uint'.
    // Requires that:
    //  - '0 < numBits <= 256'
    //  - 'startIndex < 256'
    //  - 'numBits + startIndex <= 256'
    function bits(uint256 self, uint8 startIndex, uint16 numBits) internal pure returns (uint256) {
        require(0 < numBits && startIndex < 256 && startIndex + numBits <= 256, "out of bounds");
        return (self >> startIndex) & (ONES >> (256 - numBits));
    }

    // Computes the index of the highest bit set in 'self'.
    // Returns the highest bit set as an 'uint8'.
    // Requires that 'self != 0'.
    function highestBitSet(uint256 self) internal pure returns (uint8 highest) {
        require(self != 0, "should not be zero");
        uint256 val = self;
        for (uint8 i = 128; i >= 1; i >>= 1) {
            if (val & (((ONE << i) - 1) << i) != 0) {
                highest += i;
                val >>= i;
            }
        }
    }

    // Computes the index of the lowest bit set in 'self'.
    // Returns the lowest bit set as an 'uint8'.
    // Requires that 'self != 0'.
    function lowestBitSet(uint256 self) internal pure returns (uint8 lowest) {
        require(self != 0, "should not be zero");
        uint256 val = self;
        for (uint8 i = 128; i >= 1; i >>= 1) {
            if (val & ((ONE << i) - 1) == 0) {
                lowest += i;
                val >>= i;
            }
        }
    }
}

File 8 of 12 : Math.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 OpenZeppelin
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
// Code from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol
pragma solidity 0.8.25;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from 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 Return the log in base 2 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }

    /**
     * @dev Safely adds two unsigned 16-bit integers, preventing overflow by saturating to max uint16.
     */
    function saturatingAdd(uint16 a, uint16 b) internal pure returns (uint16) {
        unchecked {
            uint16 c = a + b;
            if (c < a) {
                return 0xFFFF;
            }
            return c;
        }
    }

    /**
     * @dev Safely subtracts two unsigned 256-bit integers, preventing overflow by saturating to min uint256.
     */
    function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            if (b >= a) {
                return 0;
            }
            return a - b;
        }
    }
}

File 9 of 12 : MMRProof.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.25;

library MMRProof {
    error ProofSizeExceeded();

    uint256 internal constant MAXIMUM_PROOF_SIZE = 256;

    /**
     * @dev Verify inclusion of a leaf in an MMR
     * @param root MMR root hash
     * @param leafHash leaf hash
     * @param proof an array of hashes
     * @param proofOrder a bitfield describing the order of each item (left vs right)
     */
    function verifyLeafProof(bytes32 root, bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder)
        internal
        pure
        returns (bool)
    {
        // Size of the proof is bounded, since `proofOrder` can only contain `MAXIMUM_PROOF_SIZE` orderings.
        if (proof.length > MAXIMUM_PROOF_SIZE) {
            revert ProofSizeExceeded();
        }

        bytes32 acc = leafHash;
        for (uint256 i = 0; i < proof.length; i++) {
            acc = hashPairs(acc, proof[i], (proofOrder >> i) & 1);
        }
        return root == acc;
    }

    function hashPairs(bytes32 x, bytes32 y, uint256 order) internal pure returns (bytes32 value) {
        assembly {
            switch order
            case 0 {
                mstore(0x00, x)
                mstore(0x20, y)
            }
            default {
                mstore(0x00, y)
                mstore(0x20, x)
            }
            value := keccak256(0x0, 0x40)
        }
    }
}

File 10 of 12 : ScaleCodec.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.25;

library ScaleCodec {
    error UnsupportedCompactEncoding();

    uint256 internal constant MAX_COMPACT_ENCODABLE_UINT = 2 ** 30 - 1;

    // 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 encodeU256(uint256 input) internal pure returns (bytes32) {
        return bytes32(reverse256(input));
    }

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

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

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

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

    function encodeU8(uint8 input) internal pure returns (bytes1) {
        return bytes1(input);
    }

    // Supports compact encoding of integers in [0, uint32.MAX]
    function encodeCompactU32(uint32 value) internal pure returns (bytes memory) {
        if (value <= 2 ** 6 - 1) {
            // add single byte flag
            return abi.encodePacked(uint8(value << 2));
        } else if (value <= 2 ** 14 - 1) {
            // add two byte flag and create little endian encoding
            return abi.encodePacked(ScaleCodec.reverse16(uint16(((value << 2) + 1))));
        } else if (value <= 2 ** 30 - 1) {
            // add four byte flag and create little endian encoding
            return abi.encodePacked(ScaleCodec.reverse32(uint32((value << 2)) + 2));
        } else {
            return abi.encodePacked(uint8(3), ScaleCodec.reverse32(value));
        }
    }

    function checkedEncodeCompactU32(uint256 value) internal pure returns (bytes memory) {
        if (value > type(uint32).max) {
            revert UnsupportedCompactEncoding();
        }
        return encodeCompactU32(uint32(value));
    }
}

File 11 of 12 : SubstrateMerkleProof.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.25;

// Used to verify merkle proofs generated by https://github.com/paritytech/substrate/tree/master/utils/binary-merkle-tree
library SubstrateMerkleProof {
    /**
     * @notice Verify that a specific leaf element is part of the Merkle Tree at a specific position in the tree
     *
     * The tree would have been constructed using
     * https://paritytech.github.io/substrate/master/binary_merkle_tree/fn.merkle_root.html
     *
     * This implementation adapted from
     * https://paritytech.github.io/substrate/master/binary_merkle_tree/fn.verify_proof.html
     *
     * @param root the root of the merkle tree
     * @param leaf the leaf which needs to be proven
     * @param position the position of the leaf, index starting with 0
     * @param width the width or number of leaves in the tree
     * @param proof the array of proofs to help verify the leaf's membership, ordered from leaf to root
     * @return a boolean value representing the success or failure of the operation
     */
    function verify(bytes32 root, bytes32 leaf, uint256 position, uint256 width, bytes32[] calldata proof)
        internal
        pure
        returns (bool)
    {
        if (position >= width) {
            return false;
        }
        return root == computeRoot(leaf, position, width, proof);
    }

    function computeRoot(bytes32 leaf, uint256 position, uint256 width, bytes32[] calldata proof)
        internal
        pure
        returns (bytes32)
    {
        bytes32 node = leaf;
        unchecked {
            for (uint256 i = 0; i < proof.length; i++) {
                if (position & 1 == 1 || position + 1 == width) {
                    node = efficientHash(proof[i], node);
                } else {
                    node = efficientHash(node, proof[i]);
                }
                position = position >> 1;
                width = ((width - 1) >> 1) + 1;
            }
            return node;
        }
    }

    function efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 12 of 12 : Uint16Array.sol
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.25;

/**
 * @title A utility library for 16 bit counters packed in 256 bit array.
 * @dev The BeefyClient needs to store a count of how many times a validators signature is used. In solidity
 * a uint16 would take up as much space as a uin256 in storage, making storing counters for 1000 validators
 * expensive in terms of gas. The BeefyClient only needs 16 bits per counter. This library allows us to pack
 * 16 uint16 into a single uint256 and save 16x storage.
 *
 * Layout of 32 counters (2 uint256)
 * We store all counts in a single large uint256 array and convert from index from the logical uint16 array
 * to the physical uint256 array.
 *
 *           0                                               1                                               2
 * uint256[] |-- -- -- -- -- -- -- -- -- -- -- -- YY -- -- --|-- -- -- -- -- -- XX -- -- -- -- -- -- -- -- --|
 * uint16[]  |--|--|--|--|--|--|--|--|--|--|--|--|YY|--|--|--|--|--|--|--|--|--|XX|--|--|--|--|--|--|--|--|--|
 *           0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Logical Index Layout
 * We use the first 4
 * |-------...---------|----|
 * 256                 4    0
 *        ^index          ^bit-index
 *
 * In the above table counter YY is at logical index 12 in the uint16 array. It will convert to a physical
 * index of 0 in the physical uint256 array and then to bit-index of 192 to 207 of that uint256. In the
 * above table counter XX is at logical index 22. It will convert to a physical index of 1 in the array and
 * then to bit-index 96 to 111 of uint256[1].
 */
using {get, set} for Uint16Array global;

error IndexOutOfBounds();

/**
 * @dev stores the backing array and the length.
 */
struct Uint16Array {
    uint256[] data;
    uint256 length;
}

/**
 * @dev Creates a new counter which can store at least `length` counters.
 * @param length The amount of counters.
 */
function createUint16Array(uint256 length) pure returns (Uint16Array memory) {
    // create space for `length` elements and round up if needed.
    uint256 bufferLength = length / 16 + (length % 16 == 0 ? 0 : 1);
    return Uint16Array({data: new uint256[](bufferLength), length: length});
}

/**
 * @dev Gets the counter at the logical index
 * @param self The array.
 * @param index The logical index.
 */
function get(Uint16Array storage self, uint256 index) view returns (uint16) {
    if (index >= self.length) {
        revert IndexOutOfBounds();
    }
    // Right-shift the index by 4. This truncates the first 4 bits (bit-index) leaving us with the index
    // into the array.
    uint256 element = index >> 4;
    // Mask out the first 4 bits of the logical index to give us the bit-index.
    uint8 inside = uint8(index) & 0x0F;
    // find the element in the array, shift until its bit index and mask to only take the first 16 bits.
    return uint16((self.data[element] >> (16 * inside)) & 0xFFFF);
}

/**
 * @dev Sets the counter at the logical index.
 * @param self The array.
 * @param index The logical index of the counter in the array.
 * @param value The value to set the counter to.
 */
function set(Uint16Array storage self, uint256 index, uint16 value) {
    if (index >= self.length) {
        revert IndexOutOfBounds();
    }
    // Right-shift the index by 4. This truncates the first 4 bits (bit-index) leaving us with the index
    // into the array.
    uint256 element = index >> 4;
    // Mask out the first 4 bytes of the logical index to give us the bit-index.
    uint8 inside = uint8(index) & 0x0F;
    // Create a zero mask which will clear the existing value at the bit-index.
    uint256 zero = ~(uint256(0xFFFF) << (16 * inside));
    // Shift the value to the bit index.
    uint256 shiftedValue = uint256(value) << (16 * inside);
    // Take the element, apply the zero mask to clear the existing value, and then apply the shifted value with bitwise or.
    self.data[element] = self.data[element] & zero | shiftedValue;
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_randaoCommitDelay","type":"uint256"},{"internalType":"uint256","name":"_randaoCommitExpiration","type":"uint256"},{"internalType":"uint256","name":"_minNumRequiredSignatures","type":"uint256"},{"internalType":"uint64","name":"_initialBeefyBlock","type":"uint64"},{"components":[{"internalType":"uint128","name":"id","type":"uint128"},{"internalType":"uint128","name":"length","type":"uint128"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct BeefyClient.ValidatorSet","name":"_initialValidatorSet","type":"tuple"},{"components":[{"internalType":"uint128","name":"id","type":"uint128"},{"internalType":"uint128","name":"length","type":"uint128"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct BeefyClient.ValidatorSet","name":"_nextValidatorSet","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CommitmentNotRelevant","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[],"name":"InvalidBitfield","type":"error"},{"inputs":[],"name":"InvalidBitfieldLength","type":"error"},{"inputs":[],"name":"InvalidCommitment","type":"error"},{"inputs":[],"name":"InvalidMMRLeaf","type":"error"},{"inputs":[],"name":"InvalidMMRLeafProof","type":"error"},{"inputs":[],"name":"InvalidMMRRootLength","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidTicket","type":"error"},{"inputs":[],"name":"InvalidValidatorProof","type":"error"},{"inputs":[],"name":"InvalidValidatorProofLength","type":"error"},{"inputs":[],"name":"NotEnoughClaims","type":"error"},{"inputs":[],"name":"PrevRandaoAlreadyCaptured","type":"error"},{"inputs":[],"name":"PrevRandaoNotCaptured","type":"error"},{"inputs":[],"name":"ProofSizeExceeded","type":"error"},{"inputs":[],"name":"StaleCommitment","type":"error"},{"inputs":[],"name":"TicketExpired","type":"error"},{"inputs":[],"name":"UnsupportedCompactEncoding","type":"error"},{"inputs":[],"name":"WaitPeriodNotOver","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"mmrRoot","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"blockNumber","type":"uint64"}],"name":"NewMMRRoot","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"uint64","name":"blockNumber","type":"uint64"}],"name":"NewTicket","type":"event"},{"inputs":[],"name":"MMR_ROOT_ID","outputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commitmentHash","type":"bytes32"}],"name":"commitPrevRandao","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commitmentHash","type":"bytes32"},{"internalType":"uint256[]","name":"bitfield","type":"uint256[]"}],"name":"createFinalBitfield","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"bitsToSet","type":"uint256[]"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"createInitialBitfield","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"currentValidatorSet","outputs":[{"internalType":"uint128","name":"id","type":"uint128"},{"internalType":"uint128","name":"length","type":"uint128"},{"internalType":"bytes32","name":"root","type":"bytes32"},{"components":[{"internalType":"uint256[]","name":"data","type":"uint256[]"},{"internalType":"uint256","name":"length","type":"uint256"}],"internalType":"struct Uint16Array","name":"usageCounters","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestBeefyBlock","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestMMRRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minNumRequiredSignatures","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextValidatorSet","outputs":[{"internalType":"uint128","name":"id","type":"uint128"},{"internalType":"uint128","name":"length","type":"uint128"},{"internalType":"bytes32","name":"root","type":"bytes32"},{"components":[{"internalType":"uint256[]","name":"data","type":"uint256[]"},{"internalType":"uint256","name":"length","type":"uint256"}],"internalType":"struct Uint16Array","name":"usageCounters","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randaoCommitDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randaoCommitExpiration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"validatorSetID","type":"uint64"},{"components":[{"internalType":"bytes2","name":"payloadID","type":"bytes2"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BeefyClient.PayloadItem[]","name":"payload","type":"tuple[]"}],"internalType":"struct BeefyClient.Commitment","name":"commitment","type":"tuple"},{"internalType":"uint256[]","name":"bitfield","type":"uint256[]"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct BeefyClient.ValidatorProof[]","name":"proofs","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint32","name":"parentNumber","type":"uint32"},{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"uint64","name":"nextAuthoritySetID","type":"uint64"},{"internalType":"uint32","name":"nextAuthoritySetLen","type":"uint32"},{"internalType":"bytes32","name":"nextAuthoritySetRoot","type":"bytes32"},{"internalType":"bytes32","name":"parachainHeadsRoot","type":"bytes32"}],"internalType":"struct BeefyClient.MMRLeaf","name":"leaf","type":"tuple"},{"internalType":"bytes32[]","name":"leafProof","type":"bytes32[]"},{"internalType":"uint256","name":"leafProofOrder","type":"uint256"}],"name":"submitFinal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint64","name":"validatorSetID","type":"uint64"},{"components":[{"internalType":"bytes2","name":"payloadID","type":"bytes2"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BeefyClient.PayloadItem[]","name":"payload","type":"tuple[]"}],"internalType":"struct BeefyClient.Commitment","name":"commitment","type":"tuple"},{"internalType":"uint256[]","name":"bitfield","type":"uint256[]"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"internalType":"struct BeefyClient.ValidatorProof","name":"proof","type":"tuple"}],"name":"submitInitial","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"ticketID","type":"bytes32"}],"name":"tickets","outputs":[{"internalType":"uint64","name":"blockNumber","type":"uint64"},{"internalType":"uint32","name":"validatorSetLen","type":"uint32"},{"internalType":"uint32","name":"numRequiredSignatures","type":"uint32"},{"internalType":"uint256","name":"prevRandao","type":"uint256"},{"internalType":"bytes32","name":"bitfieldHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"leafHash","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"uint256","name":"proofOrder","type":"uint256"}],"name":"verifyMMRLeafProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60e060405234801561001057600080fd5b506040516137fc3803806137fc83398101604081905261002f9161033d565b815161003c9060016103c5565b6001600160801b031681600001516001600160801b0316146100a45760405162461bcd60e51b815260206004820152601a60248201527f696e76616c69642d636f6e7374727563746f722d706172616d73000000000000604482015260640160405180910390fd5b608086905260a085905260c0849052600180546001600160401b0319166001600160401b038516179055815160208301516001600160801b03918216600160801b91831682021760028190556040850151600355610105929190041661018e565b8051805160049161011b91839160200190610236565b50602091820151600191909101558151908201516001600160801b03918216600160801b9183168202176006819055604084015160075561015f929190041661018e565b8051805160089161017591839160200190610236565b5060208201518160010155905050505050505050610443565b60408051808201909152606081526000602082015260006101b0601084610402565b156101bc5760016101bf565b60005b60ff166101cd601085610416565b6101d7919061042a565b90506040518060400160405280826001600160401b038111156101fc576101fc610296565b604051908082528060200260200182016040528015610225578160200160208202803683370190505b508152602001939093525090919050565b828054828255906000526020600020908101928215610271579160200282015b82811115610271578251825591602001919060010190610256565b5061027d929150610281565b5090565b5b8082111561027d5760008155600101610282565b634e487b7160e01b600052604160045260246000fd5b80516001600160801b03811681146102c357600080fd5b919050565b6000606082840312156102da57600080fd5b604051606081016001600160401b038111828210171561030a57634e487b7160e01b600052604160045260246000fd5b604052905080610319836102ac565b8152610327602084016102ac565b6020820152604083015160408201525092915050565b600080600080600080610140878903121561035757600080fd5b86516020880151604089015160608a015192985090965094506001600160401b038116811461038557600080fd5b925061039488608089016102c8565b91506103a38860e089016102c8565b90509295509295509295565b634e487b7160e01b600052601160045260246000fd5b6001600160801b038181168382160190808211156103e5576103e56103af565b5092915050565b634e487b7160e01b600052601260045260246000fd5b600082610411576104116103ec565b500690565b600082610425576104256103ec565b500490565b8082018082111561043d5761043d6103af565b92915050565b60805160a05160c05161336e61048e6000396000818161021c0152610fe501526000818161028c0152610b7901526000818161019301528181610b0d0152610b9f015261336e6000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c806366ae69a011610097578063a77cf3d211610066578063a77cf3d214610274578063ad209a9b14610287578063bb51f1eb146102ae578063df0dd0d5146102c157600080fd5b806366ae69a0146101ea5780636f55bd32146102175780638ab81d131461023e578063a401662b1461025157600080fd5b806341c9634e116100d357806341c9634e14610177578063591d99ee1461018e5780635da57fe9146101b5578063623b223d146101d557600080fd5b80630a7c8faa146100fa5780632cdea71714610157578063366675131461016f575b600080fd5b6101217f6d6800000000000000000000000000000000000000000000000000000000000081565b6040517fffff00000000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b61015f61035c565b60405161014e9493929190612a13565b61015f6103f5565b61018060005481565b60405190815260200161014e565b6101807f000000000000000000000000000000000000000000000000000000000000000081565b6101c86101c3366004612aef565b61048c565b60405161014e9190612b3b565b6101e86101e3366004612ba9565b6104db565b005b6001546101fe9067ffffffffffffffff1681565b60405167ffffffffffffffff909116815260200161014e565b6101807f000000000000000000000000000000000000000000000000000000000000000081565b6101c861024c366004612c87565b610950565b61026461025f366004612cd3565b610a53565b604051901515815260200161014e565b6101e8610282366004612d26565b610a64565b6101807f000000000000000000000000000000000000000000000000000000000000000081565b6101e86102bc366004612d3f565b610c5e565b61031f6102cf366004612d26565b600a6020526000908152604090208054600182015460029092015467ffffffffffffffff82169263ffffffff6801000000000000000084048116936c010000000000000000000000009004169185565b6040805167ffffffffffffffff96909616865263ffffffff948516602087015292909316918401919091526060830152608082015260a00161014e565b6002805460035460408051600480546060602082028401810185529383018181526fffffffffffffffffffffffffffffffff80881698700100000000000000000000000000000000909804169694849284918401828280156103dd57602002820191906000526020600020905b8154815260200190600101908083116103c9575b50505050508152602001600182015481525050905084565b6006805460075460408051600880546060602082028401810185529383018181526fffffffffffffffffffffffffffffffff80881698700100000000000000000000000000000000909804169694849284918401828280156103dd57602002820191906000526020600020908154815260200190600101908083116103c95750505050508152602001600182015481525050905084565b6060828210156104c8576040517f5c85a0e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104d3848484611187565b949350505050565b60006104e68a611228565b8051906020012090506000610505338360009182526020526040902090565b9050610513818c8c8c61131b565b600080600660000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168d602001602081019061055d9190612dd2565b67ffffffffffffffff16036105785750600190506006610608565b600260000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168d60200160208101906105bf9190612dd2565b67ffffffffffffffff16036105d657506002610608565b6040517fc06789fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61061784848e8e858f8f611472565b60006106228e6116c9565b9050821561083f5760065461064a906fffffffffffffffffffffffffffffffff166001612e2b565b6fffffffffffffffffffffffffffffffff1661066c60808b0160608c01612dd2565b67ffffffffffffffff16146106ad576040517fc72c820000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006106cb826106bc8c611868565b805190602001208b8b8b611964565b905080610704576040517f128597bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547001000000000000000000000000000000008082046fffffffffffffffffffffffffffffffff9081169091029116176002908155600754600355600880546004906107569082908490612973565b5060019182015491015550610773905060808b0160608c01612dd2565b600680547fffffffffffffffffffffffffffffffff000000000000000000000000000000001667ffffffffffffffff929092169190911790556107bc60a08b0160808c01612e54565b600680546fffffffffffffffffffffffffffffffff1663ffffffff929092167001000000000000000000000000000000000291909117905560a08a0180356007556108199061080e9060808d01612e54565b63ffffffff166119ee565b8051805160089161082f918391602001906129c3565b5060208201518160010155905050505b600081905561085160208f018f612e54565b63ffffffff16600160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550600a6000858152602001908152602001600020600080820160006101000a81549067ffffffffffffffff02191690556000820160086101000a81549063ffffffff021916905560008201600c6101000a81549063ffffffff02191690556001820160009055600282016000905550507fd95fe1258d152dc91c81b09380498adc76ed36a6079bcb2ed31eff622ae2d0f1818f60000160208101906109239190612e54565b6040805192835263ffffffff90911660208301520160405180910390a15050505050505050505050505050565b60606000600a600061096c338860009182526020526040902090565b81526020019081526020016000209050838360405160200161098f929190612e7a565b604051602081830303815290604052805190602001208160020154146109e1576040517f6768c0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a4a816001015485858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050855463ffffffff6c0100000000000000000000000082048116935068010000000000000000909104169050611a97565b95945050505050565b6000610a4a60005486868686611964565b3360009081526020829052604081206000818152600a6020526040812080549293509167ffffffffffffffff169003610ac9576040517f6686db6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600181015415610b05576040517fe31d900500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610b3c907f00000000000000000000000000000000000000000000000000000000000000009067ffffffffffffffff16612ebc565b431015610b75576040517fc77c194900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547f000000000000000000000000000000000000000000000000000000000000000090610bce907f00000000000000000000000000000000000000000000000000000000000000009067ffffffffffffffff16612ebc565b610bd89190612ebc565b431115610c53576000828152600a602052604080822080547fffffffffffffffffffffffffffffffff00000000000000000000000000000000168155600181018390556002019190915580517f40d3544700000000000000000000000000000000000000000000000000000000815290519081900360040190fd5b446001909101555050565b60015467ffffffffffffffff16610c786020860186612e54565b63ffffffff1611610cb5576040517f3d618e5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460009081906fffffffffffffffffffffffffffffffff16610cdf6040880160208901612dd2565b67ffffffffffffffff1603610d2857610cfd60046060850135611b46565b9050610d1f6060840135610d1661ffff84166001611bcd565b60049190611bec565b60029150610d92565b6006546fffffffffffffffffffffffffffffffff16610d4d6040880160208901612dd2565b67ffffffffffffffff16036105d657610d6b60086060850135611b46565b9050610d8d6060840135610d8461ffff84166001611bcd565b60089190611bec565b600691505b610dbd82610da660a0860160808701612ecf565b6060860135610db860a0880188612f05565b611cb3565b1580610e065750610e048585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050506060850135611d6a565b155b15610e3d576040517fe00153fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610e4887611228565b80516020909101209050610e6260a0850160808601612ecf565b73ffffffffffffffffffffffffffffffffffffffff16610e9882610e896020880188612f6d565b87602001358860400135611daf565b73ffffffffffffffffffffffffffffffffffffffff1614610ee5576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8254610f169070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16611dd7565b610f52878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250611dfa92505050565b1015610f8a576040517fee3e74af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160a0810182524367ffffffffffffffff1681528454700100000000000000000000000000000000900463ffffffff811660208301529091820190611009906fffffffffffffffffffffffffffffffff1661ffff86167f000000000000000000000000000000000000000000000000000000000000000061206a565b63ffffffff16815260200160008152602001878760405160200161102e929190612e7a565b60405160208183030381529060405280519060200120815250600a600061105f338560009182526020526040902090565b8152602080820192909252604090810160002083518154858501519386015163ffffffff9081166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9190951668010000000000000000027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090921667ffffffffffffffff909316929092171716919091178155606083015160018201556080909201516002909201919091557fbee983fc706c692efb9b0240bddc5666c010a53af55ed5fb42d226e7e429386990339061114a908a018a612e54565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835263ffffffff90911660208301520160405180910390a150505050505050565b606060006101006111998460ff612ebc565b6111a39190612fbf565b90508067ffffffffffffffff8111156111be576111be612fd3565b6040519080825280602002602001820160405280156111e7578160200160208202803683370190505b50915060005b8481101561121f576112178387878481811061120b5761120b613002565b905060200201356120c3565b6001016111ed565b50509392505050565b606061123f61123a6040840184612f05565b61211b565b61127661124f6020850185612e54565b600881811c62ff00ff1663ff00ff009290911b9190911617601081811c91901b1760e01b90565b6112f36112896040860160208701612dd2565b600065ff000000ff00600883811b91821664ff000000ff9185901c91821617601090811b67ff000000ff0000009390931666ff000000ff00009290921691909117901c17602081811b6bffffffffffffffff000000001691901c63ffffffff161760c01b92915050565b60405160200161130593929190613061565b6040516020818303038152906040529050919050565b6000848152600a602052604081208054909167ffffffffffffffff9091169003611371576040517f6686db6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600101546000036113af576040517f78ef3a4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015467ffffffffffffffff166113c96020860186612e54565b63ffffffff1611611406576040517f3d618e5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282604051602001611419929190612e7a565b6040516020818303038152906040528051906020012081600201541461146b576040517f6768c0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6000868152600a6020526040902080546c01000000000000000000000000900463ffffffff168281146114d1576040517f1f1711da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061153e8360010154898980806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250508a5487925070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169050611a97565b905060005b848110156116bc573686868381811061155e5761155e613002565b905060200281019061157091906130c5565b9050611580838260600135611d6a565b6115b6576040517fe00153fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115dc886115ca60a0840160808501612ecf565b6060840135610db860a0860186612f05565b611612576040517fe00153fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61162260a0820160808301612ecf565b73ffffffffffffffffffffffffffffffffffffffff166116588d6116496020850185612f6d565b84602001358560400135611daf565b73ffffffffffffffffffffffffffffffffffffffff16146116a5576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116b383826060013561222a565b50600101611543565b5050505050505050505050565b6000805b6116da6040840184612f05565b9050811015611835577f6d680000000000000000000000000000000000000000000000000000000000006117116040850185612f05565b8381811061172157611721613002565b90506020028101906117339190613103565b611741906020810190613137565b7fffff000000000000000000000000000000000000000000000000000000000000160361182d576117756040840184612f05565b8281811061178557611785613002565b90506020028101906117979190613103565b6117a5906020810190613179565b90506020146117e0576040517f7df9c48600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117ed6040840184612f05565b828181106117fd576117fd613002565b905060200281019061180f9190613103565b61181d906020810190613179565b611826916131de565b9392505050565b6001016116cd565b506040517f484ab7df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606061188061187a6020840184612f6d565b60f81b90565b61189361124f6040850160208601612e54565b60408401356118ab6112896080870160608801612dd2565b6118be61124f60a0880160808901612e54565b6040517fff0000000000000000000000000000000000000000000000000000000000000090951660208601527fffffffff00000000000000000000000000000000000000000000000000000000938416602186015260258501929092527fffffffffffffffff00000000000000000000000000000000000000000000000016604584015216604d82015260a0830135605182015260c08301356071820152609101611305565b60006101008311156119a2576040517f5e862a8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b848110156119e1576119d7828787848181106119c4576119c4613002565b905060200201358387901c60011661225f565b91506001016119a6565b5090951495945050505050565b6040805180820190915260608152600060208201526000611a1060108461321a565b15611a1c576001611a1f565b60005b60ff16611a2d601085612fbf565b611a379190612ebc565b905060405180604001604052808267ffffffffffffffff811115611a5d57611a5d612fd3565b604051908082528060200260200182016040528015611a86578160200160208202803683370190505b508152602001939093525090919050565b6060835167ffffffffffffffff811115611ab357611ab3612fd3565b604051908082528060200260200182016040528015611adc578160200160208202803683370190505b5090506000805b84821015611b3c576000878152602082905260409020849006611b068782611d6a565b1580611b175750611b178482611d6a565b15611b255750600101611ae3565b611b2f84826120c3565b5060019182019101611ae3565b5050949350505050565b600082600101548210611b85576040517f4e23d03500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600482901c600f8316611b9981601061322e565b60ff16856000018381548110611bb157611bb1613002565b9060005260206000200154901c61ffff16925050505b92915050565b600082820161ffff80851690821610156118265761ffff915050611bc7565b82600101548210611c29576040517f4e23d03500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600482901c600f83166000611c3f82601061322e565b60ff1661ffff901b1990506000826010611c59919061322e565b60ff168561ffff16901b90508082886000018681548110611c7c57611c7c613002565b90600052602060002001541617876000018581548110611c9e57611c9e613002565b60009182526020909120015550505050505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660208201526000908190603401604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052805160209091012060018801548854919250611d5f918390889070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16888861228c565b979650505050505050565b600080600883901c9050611da083858381518110611d8a57611d8a613002565b60200260200101516122b990919063ffffffff16565b60ff1660011491505092915050565b6000806000611dc0878787876122c3565b91509150611dcd816123b2565b5095945050505050565b60006003611de660018461324a565b611df09190612fbf565b611bc7908361324a565b600080805b8351811015612063576000848281518110611e1c57611e1c613002565b602002602001015190507f5555555555555555555555555555555555555555555555555555555555555555600182901c167f555555555555555555555555555555555555555555555555555555555555555582160190507f3333333333333333333333333333333333333333333333333333333333333333600282901c167f333333333333333333333333333333333333333333333333333333333333333382160190507f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f600482901c167f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82160190507eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff600882901c167eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82160190507dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff601082901c167dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82160190507bffffffff00000000ffffffff00000000ffffffff00000000ffffffff602082901c167bffffffff00000000ffffffff00000000ffffffff00000000ffffffff821601905077ffffffffffffffff0000000000000000ffffffffffffffff604082901c1677ffffffffffffffff0000000000000000ffffffffffffffff82160190506fffffffffffffffffffffffffffffffff608082901c166fffffffffffffffffffffffffffffffff82160190508083019250508080600101915050611dff565b5092915050565b60008161207885600161256d565b6120829082612ebc565b905061208f84600161256d565b61209a90600261325d565b6120a5906001612ebc565b6120af9082612ebc565b9050610a4a816120be87611dd7565b6125ac565b6000600882901c90506120f8828483815181106120e2576120e2613002565b60200260200101516125c290919063ffffffff16565b83828151811061210a5761210a613002565b602002602001018181525050505050565b60606000612128836125cf565b905060005b83811015612222578185858381811061214857612148613002565b905060200281019061215a9190613103565b612168906020810190613137565b6121a487878581811061217d5761217d613002565b905060200281019061218f9190613103565b61219d906020810190613179565b90506125cf565b8787858181106121b6576121b6613002565b90506020028101906121c89190613103565b6121d6906020810190613179565b6040516020016121ea959493929190613274565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052915060010161212d565b509392505050565b6000600882901c90506120f88284838151811061224957612249613002565b602002602001015161261890919063ffffffff16565b600081801561227557836000528460205261227e565b84600052836020525b505060406000209392505050565b600083851061229d575060006122af565b6122aa8686868686612626565b871490505b9695505050505050565b60ff161c60011690565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156122fa57506000905060036123a9565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561234e573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166123a2576000600192509250506123a9565b9150600090505b94509492505050565b60008160048111156123c6576123c66132ca565b036123ce5750565b60018160048111156123e2576123e26132ca565b0361244e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064015b60405180910390fd5b6002816004811115612462576124626132ca565b036124c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401612445565b60038160048111156124dd576124dd6132ca565b0361256a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401612445565b50565b600080612579846126ee565b905061258483612782565b8015612593575083816001901b105b61259e5760006125a1565b60015b60ff16019392505050565b60008183106125bb5781611826565b5090919050565b600160ff919091161b1790565b606063ffffffff82111561260f576040517fe809999a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bc7826127af565b600160ff919091161b191690565b600085815b838110156126e357866001166001148061264757508587600101145b1561267f5761267885858381811061266157612661613002565b905060200201358360009182526020526040902090565b91506126ae565b6126ab8286868481811061269557612695613002565b9050602002013560009182526020526040902090565b91505b600196871c967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909601861c8601950161262b565b509695505050505050565b600080608083901c1561270357608092831c92015b604083901c1561271557604092831c92015b602083901c1561272757602092831c92015b601083901c1561273957601092831c92015b600883901c1561274b57600892831c92015b600483901c1561275d57600492831c92015b600283901c1561276f57600292831c92015b600183901c15611bc75760010192915050565b60006002826003811115612798576127986132ca565b6127a291906132f9565b60ff166001149050919050565b6060603f8263ffffffff16116127f6576040517ffc0000000000000000000000000000000000000000000000000000000000000060fa84901b166020820152602101611305565b613fff8263ffffffff161161286d5761283261281e6403fffffffc600285901b16600161331b565b600881811b62ffff001691901c60ff161790565b604051602001611305919060f09190911b7fffff00000000000000000000000000000000000000000000000000000000000016815260020190565b633fffffff8263ffffffff16116128f7576128bc60028363ffffffff16901b6002612898919061331b565b600881811c62ff00ff1663ff00ff009290911b9190911617601081811c91901b1790565b604051602001611305919060e09190911b7fffffffff0000000000000000000000000000000000000000000000000000000016815260040190565b6040517f030000000000000000000000000000000000000000000000000000000000000060208201527fffffffff00000000000000000000000000000000000000000000000000000000600884811c62ff00ff1663ff00ff009186901b9190911617601081811c91901b1760e01b166021820152602501611305565b8280548282559060005260206000209081019282156129b35760005260206000209182015b828111156129b3578254825591600101919060010190612998565b506129bf9291506129fe565b5090565b8280548282559060005260206000209081019282156129b3579160200282015b828111156129b35782518255916020019190600101906129e3565b5b808211156129bf57600081556001016129ff565b60006fffffffffffffffffffffffffffffffff8087168352602081871660208501528560408501526080606085015260c08401915084516040608086015282815180855260e087019150602083019450600092505b80831015612a885784518252938301936001929092019190830190612a68565b50602087015160a08701528094505050505095945050505050565b60008083601f840112612ab557600080fd5b50813567ffffffffffffffff811115612acd57600080fd5b6020830191508360208260051b8501011115612ae857600080fd5b9250929050565b600080600060408486031215612b0457600080fd5b833567ffffffffffffffff811115612b1b57600080fd5b612b2786828701612aa3565b909790965060209590950135949350505050565b6020808252825182820181905260009190848201906040850190845b81811015612b7357835183529284019291840191600101612b57565b50909695505050505050565b600060608284031215612b9157600080fd5b50919050565b600060e08284031215612b9157600080fd5b60008060008060008060008060006101808a8c031215612bc857600080fd5b893567ffffffffffffffff80821115612be057600080fd5b612bec8d838e01612b7f565b9a5060208c0135915080821115612c0257600080fd5b612c0e8d838e01612aa3565b909a50985060408c0135915080821115612c2757600080fd5b612c338d838e01612aa3565b9098509650869150612c488d60608e01612b97565b95506101408c0135915080821115612c5f57600080fd5b50612c6c8c828d01612aa3565b9a9d999c50979a969995989497966101600135949350505050565b600080600060408486031215612c9c57600080fd5b83359250602084013567ffffffffffffffff811115612cba57600080fd5b612cc686828701612aa3565b9497909650939450505050565b60008060008060608587031215612ce957600080fd5b84359350602085013567ffffffffffffffff811115612d0757600080fd5b612d1387828801612aa3565b9598909750949560400135949350505050565b600060208284031215612d3857600080fd5b5035919050565b60008060008060608587031215612d5557600080fd5b843567ffffffffffffffff80821115612d6d57600080fd5b612d7988838901612b7f565b95506020870135915080821115612d8f57600080fd5b612d9b88838901612aa3565b90955093506040870135915080821115612db457600080fd5b50850160c08188031215612dc757600080fd5b939692955090935050565b600060208284031215612de457600080fd5b813567ffffffffffffffff8116811461182657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6fffffffffffffffffffffffffffffffff81811683821601908082111561206357612063612dfc565b600060208284031215612e6657600080fd5b813563ffffffff8116811461182657600080fd5b60007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612ea957600080fd5b8260051b80858437919091019392505050565b80820180821115611bc757611bc7612dfc565b600060208284031215612ee157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461182657600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612f3a57600080fd5b83018035915067ffffffffffffffff821115612f5557600080fd5b6020019150600581901b3603821315612ae857600080fd5b600060208284031215612f7f57600080fd5b813560ff8116811461182657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082612fce57612fce612f90565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000815160005b818110156130525760208185018101518683015201613038565b50600093019283525090919050565b600061306d8286613031565b7fffffffff0000000000000000000000000000000000000000000000000000000094909416845250507fffffffffffffffff000000000000000000000000000000000000000000000000166004820152600c01919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff418336030181126130f957600080fd5b9190910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126130f957600080fd5b60006020828403121561314957600080fd5b81357fffff0000000000000000000000000000000000000000000000000000000000008116811461182657600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126131ae57600080fd5b83018035915067ffffffffffffffff8211156131c957600080fd5b602001915036819003821315612ae857600080fd5b80356020831015611bc7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b60008261322957613229612f90565b500690565b60ff818116838216029081169081811461206357612063612dfc565b81810381811115611bc757611bc7612dfc565b8082028115828204841417611bc757611bc7612dfc565b60006132808288613031565b7fffff000000000000000000000000000000000000000000000000000000000000871681526132b26002820187613031565b90508385823760009301928352509095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600060ff83168061330c5761330c612f90565b8060ff84160691505092915050565b63ffffffff81811683821601908082111561206357612063612dfc56fea2646970667358221220d200b88b1fbd76940c01aeea4316095882bf04d47f9383da64dca480e6080f3a64736f6c6343000819003300000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000019591900000000000000000000000000000000000000000000000000000000000002de0000000000000000000000000000000000000000000000000000000000000095fa59503298e8233afb850f7dd29653d1f96d1fcbc5a8693f2aabeff99db2216200000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000000942b13c7e8b09f9135f3a8f26b5cd66107c2d65de8765ca685d144c37f3661bc55

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100f55760003560e01c806366ae69a011610097578063a77cf3d211610066578063a77cf3d214610274578063ad209a9b14610287578063bb51f1eb146102ae578063df0dd0d5146102c157600080fd5b806366ae69a0146101ea5780636f55bd32146102175780638ab81d131461023e578063a401662b1461025157600080fd5b806341c9634e116100d357806341c9634e14610177578063591d99ee1461018e5780635da57fe9146101b5578063623b223d146101d557600080fd5b80630a7c8faa146100fa5780632cdea71714610157578063366675131461016f575b600080fd5b6101217f6d6800000000000000000000000000000000000000000000000000000000000081565b6040517fffff00000000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b61015f61035c565b60405161014e9493929190612a13565b61015f6103f5565b61018060005481565b60405190815260200161014e565b6101807f000000000000000000000000000000000000000000000000000000000000008081565b6101c86101c3366004612aef565b61048c565b60405161014e9190612b3b565b6101e86101e3366004612ba9565b6104db565b005b6001546101fe9067ffffffffffffffff1681565b60405167ffffffffffffffff909116815260200161014e565b6101807f000000000000000000000000000000000000000000000000000000000000000a81565b6101c861024c366004612c87565b610950565b61026461025f366004612cd3565b610a53565b604051901515815260200161014e565b6101e8610282366004612d26565b610a64565b6101807f000000000000000000000000000000000000000000000000000000000000001881565b6101e86102bc366004612d3f565b610c5e565b61031f6102cf366004612d26565b600a6020526000908152604090208054600182015460029092015467ffffffffffffffff82169263ffffffff6801000000000000000084048116936c010000000000000000000000009004169185565b6040805167ffffffffffffffff96909616865263ffffffff948516602087015292909316918401919091526060830152608082015260a00161014e565b6002805460035460408051600480546060602082028401810185529383018181526fffffffffffffffffffffffffffffffff80881698700100000000000000000000000000000000909804169694849284918401828280156103dd57602002820191906000526020600020905b8154815260200190600101908083116103c9575b50505050508152602001600182015481525050905084565b6006805460075460408051600880546060602082028401810185529383018181526fffffffffffffffffffffffffffffffff80881698700100000000000000000000000000000000909804169694849284918401828280156103dd57602002820191906000526020600020908154815260200190600101908083116103c95750505050508152602001600182015481525050905084565b6060828210156104c8576040517f5c85a0e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104d3848484611187565b949350505050565b60006104e68a611228565b8051906020012090506000610505338360009182526020526040902090565b9050610513818c8c8c61131b565b600080600660000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168d602001602081019061055d9190612dd2565b67ffffffffffffffff16036105785750600190506006610608565b600260000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168d60200160208101906105bf9190612dd2565b67ffffffffffffffff16036105d657506002610608565b6040517fc06789fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61061784848e8e858f8f611472565b60006106228e6116c9565b9050821561083f5760065461064a906fffffffffffffffffffffffffffffffff166001612e2b565b6fffffffffffffffffffffffffffffffff1661066c60808b0160608c01612dd2565b67ffffffffffffffff16146106ad576040517fc72c820000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006106cb826106bc8c611868565b805190602001208b8b8b611964565b905080610704576040517f128597bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547001000000000000000000000000000000008082046fffffffffffffffffffffffffffffffff9081169091029116176002908155600754600355600880546004906107569082908490612973565b5060019182015491015550610773905060808b0160608c01612dd2565b600680547fffffffffffffffffffffffffffffffff000000000000000000000000000000001667ffffffffffffffff929092169190911790556107bc60a08b0160808c01612e54565b600680546fffffffffffffffffffffffffffffffff1663ffffffff929092167001000000000000000000000000000000000291909117905560a08a0180356007556108199061080e9060808d01612e54565b63ffffffff166119ee565b8051805160089161082f918391602001906129c3565b5060208201518160010155905050505b600081905561085160208f018f612e54565b63ffffffff16600160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550600a6000858152602001908152602001600020600080820160006101000a81549067ffffffffffffffff02191690556000820160086101000a81549063ffffffff021916905560008201600c6101000a81549063ffffffff02191690556001820160009055600282016000905550507fd95fe1258d152dc91c81b09380498adc76ed36a6079bcb2ed31eff622ae2d0f1818f60000160208101906109239190612e54565b6040805192835263ffffffff90911660208301520160405180910390a15050505050505050505050505050565b60606000600a600061096c338860009182526020526040902090565b81526020019081526020016000209050838360405160200161098f929190612e7a565b604051602081830303815290604052805190602001208160020154146109e1576040517f6768c0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a4a816001015485858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050855463ffffffff6c0100000000000000000000000082048116935068010000000000000000909104169050611a97565b95945050505050565b6000610a4a60005486868686611964565b3360009081526020829052604081206000818152600a6020526040812080549293509167ffffffffffffffff169003610ac9576040517f6686db6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600181015415610b05576040517fe31d900500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610b3c907f00000000000000000000000000000000000000000000000000000000000000809067ffffffffffffffff16612ebc565b431015610b75576040517fc77c194900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547f000000000000000000000000000000000000000000000000000000000000001890610bce907f00000000000000000000000000000000000000000000000000000000000000809067ffffffffffffffff16612ebc565b610bd89190612ebc565b431115610c53576000828152600a602052604080822080547fffffffffffffffffffffffffffffffff00000000000000000000000000000000168155600181018390556002019190915580517f40d3544700000000000000000000000000000000000000000000000000000000815290519081900360040190fd5b446001909101555050565b60015467ffffffffffffffff16610c786020860186612e54565b63ffffffff1611610cb5576040517f3d618e5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460009081906fffffffffffffffffffffffffffffffff16610cdf6040880160208901612dd2565b67ffffffffffffffff1603610d2857610cfd60046060850135611b46565b9050610d1f6060840135610d1661ffff84166001611bcd565b60049190611bec565b60029150610d92565b6006546fffffffffffffffffffffffffffffffff16610d4d6040880160208901612dd2565b67ffffffffffffffff16036105d657610d6b60086060850135611b46565b9050610d8d6060840135610d8461ffff84166001611bcd565b60089190611bec565b600691505b610dbd82610da660a0860160808701612ecf565b6060860135610db860a0880188612f05565b611cb3565b1580610e065750610e048585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050506060850135611d6a565b155b15610e3d576040517fe00153fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610e4887611228565b80516020909101209050610e6260a0850160808601612ecf565b73ffffffffffffffffffffffffffffffffffffffff16610e9882610e896020880188612f6d565b87602001358860400135611daf565b73ffffffffffffffffffffffffffffffffffffffff1614610ee5576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8254610f169070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16611dd7565b610f52878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250611dfa92505050565b1015610f8a576040517fee3e74af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160a0810182524367ffffffffffffffff1681528454700100000000000000000000000000000000900463ffffffff811660208301529091820190611009906fffffffffffffffffffffffffffffffff1661ffff86167f000000000000000000000000000000000000000000000000000000000000000a61206a565b63ffffffff16815260200160008152602001878760405160200161102e929190612e7a565b60405160208183030381529060405280519060200120815250600a600061105f338560009182526020526040902090565b8152602080820192909252604090810160002083518154858501519386015163ffffffff9081166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9190951668010000000000000000027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090921667ffffffffffffffff909316929092171716919091178155606083015160018201556080909201516002909201919091557fbee983fc706c692efb9b0240bddc5666c010a53af55ed5fb42d226e7e429386990339061114a908a018a612e54565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835263ffffffff90911660208301520160405180910390a150505050505050565b606060006101006111998460ff612ebc565b6111a39190612fbf565b90508067ffffffffffffffff8111156111be576111be612fd3565b6040519080825280602002602001820160405280156111e7578160200160208202803683370190505b50915060005b8481101561121f576112178387878481811061120b5761120b613002565b905060200201356120c3565b6001016111ed565b50509392505050565b606061123f61123a6040840184612f05565b61211b565b61127661124f6020850185612e54565b600881811c62ff00ff1663ff00ff009290911b9190911617601081811c91901b1760e01b90565b6112f36112896040860160208701612dd2565b600065ff000000ff00600883811b91821664ff000000ff9185901c91821617601090811b67ff000000ff0000009390931666ff000000ff00009290921691909117901c17602081811b6bffffffffffffffff000000001691901c63ffffffff161760c01b92915050565b60405160200161130593929190613061565b6040516020818303038152906040529050919050565b6000848152600a602052604081208054909167ffffffffffffffff9091169003611371576040517f6686db6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600101546000036113af576040517f78ef3a4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015467ffffffffffffffff166113c96020860186612e54565b63ffffffff1611611406576040517f3d618e5000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282604051602001611419929190612e7a565b6040516020818303038152906040528051906020012081600201541461146b576040517f6768c0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b6000868152600a6020526040902080546c01000000000000000000000000900463ffffffff168281146114d1576040517f1f1711da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061153e8360010154898980806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250508a5487925070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169050611a97565b905060005b848110156116bc573686868381811061155e5761155e613002565b905060200281019061157091906130c5565b9050611580838260600135611d6a565b6115b6576040517fe00153fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115dc886115ca60a0840160808501612ecf565b6060840135610db860a0860186612f05565b611612576040517fe00153fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61162260a0820160808301612ecf565b73ffffffffffffffffffffffffffffffffffffffff166116588d6116496020850185612f6d565b84602001358560400135611daf565b73ffffffffffffffffffffffffffffffffffffffff16146116a5576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116b383826060013561222a565b50600101611543565b5050505050505050505050565b6000805b6116da6040840184612f05565b9050811015611835577f6d680000000000000000000000000000000000000000000000000000000000006117116040850185612f05565b8381811061172157611721613002565b90506020028101906117339190613103565b611741906020810190613137565b7fffff000000000000000000000000000000000000000000000000000000000000160361182d576117756040840184612f05565b8281811061178557611785613002565b90506020028101906117979190613103565b6117a5906020810190613179565b90506020146117e0576040517f7df9c48600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117ed6040840184612f05565b828181106117fd576117fd613002565b905060200281019061180f9190613103565b61181d906020810190613179565b611826916131de565b9392505050565b6001016116cd565b506040517f484ab7df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606061188061187a6020840184612f6d565b60f81b90565b61189361124f6040850160208601612e54565b60408401356118ab6112896080870160608801612dd2565b6118be61124f60a0880160808901612e54565b6040517fff0000000000000000000000000000000000000000000000000000000000000090951660208601527fffffffff00000000000000000000000000000000000000000000000000000000938416602186015260258501929092527fffffffffffffffff00000000000000000000000000000000000000000000000016604584015216604d82015260a0830135605182015260c08301356071820152609101611305565b60006101008311156119a2576040517f5e862a8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b848110156119e1576119d7828787848181106119c4576119c4613002565b905060200201358387901c60011661225f565b91506001016119a6565b5090951495945050505050565b6040805180820190915260608152600060208201526000611a1060108461321a565b15611a1c576001611a1f565b60005b60ff16611a2d601085612fbf565b611a379190612ebc565b905060405180604001604052808267ffffffffffffffff811115611a5d57611a5d612fd3565b604051908082528060200260200182016040528015611a86578160200160208202803683370190505b508152602001939093525090919050565b6060835167ffffffffffffffff811115611ab357611ab3612fd3565b604051908082528060200260200182016040528015611adc578160200160208202803683370190505b5090506000805b84821015611b3c576000878152602082905260409020849006611b068782611d6a565b1580611b175750611b178482611d6a565b15611b255750600101611ae3565b611b2f84826120c3565b5060019182019101611ae3565b5050949350505050565b600082600101548210611b85576040517f4e23d03500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600482901c600f8316611b9981601061322e565b60ff16856000018381548110611bb157611bb1613002565b9060005260206000200154901c61ffff16925050505b92915050565b600082820161ffff80851690821610156118265761ffff915050611bc7565b82600101548210611c29576040517f4e23d03500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600482901c600f83166000611c3f82601061322e565b60ff1661ffff901b1990506000826010611c59919061322e565b60ff168561ffff16901b90508082886000018681548110611c7c57611c7c613002565b90600052602060002001541617876000018581548110611c9e57611c9e613002565b60009182526020909120015550505050505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660208201526000908190603401604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052805160209091012060018801548854919250611d5f918390889070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16888861228c565b979650505050505050565b600080600883901c9050611da083858381518110611d8a57611d8a613002565b60200260200101516122b990919063ffffffff16565b60ff1660011491505092915050565b6000806000611dc0878787876122c3565b91509150611dcd816123b2565b5095945050505050565b60006003611de660018461324a565b611df09190612fbf565b611bc7908361324a565b600080805b8351811015612063576000848281518110611e1c57611e1c613002565b602002602001015190507f5555555555555555555555555555555555555555555555555555555555555555600182901c167f555555555555555555555555555555555555555555555555555555555555555582160190507f3333333333333333333333333333333333333333333333333333333333333333600282901c167f333333333333333333333333333333333333333333333333333333333333333382160190507f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f600482901c167f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f82160190507eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff600882901c167eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff82160190507dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff601082901c167dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff82160190507bffffffff00000000ffffffff00000000ffffffff00000000ffffffff602082901c167bffffffff00000000ffffffff00000000ffffffff00000000ffffffff821601905077ffffffffffffffff0000000000000000ffffffffffffffff604082901c1677ffffffffffffffff0000000000000000ffffffffffffffff82160190506fffffffffffffffffffffffffffffffff608082901c166fffffffffffffffffffffffffffffffff82160190508083019250508080600101915050611dff565b5092915050565b60008161207885600161256d565b6120829082612ebc565b905061208f84600161256d565b61209a90600261325d565b6120a5906001612ebc565b6120af9082612ebc565b9050610a4a816120be87611dd7565b6125ac565b6000600882901c90506120f8828483815181106120e2576120e2613002565b60200260200101516125c290919063ffffffff16565b83828151811061210a5761210a613002565b602002602001018181525050505050565b60606000612128836125cf565b905060005b83811015612222578185858381811061214857612148613002565b905060200281019061215a9190613103565b612168906020810190613137565b6121a487878581811061217d5761217d613002565b905060200281019061218f9190613103565b61219d906020810190613179565b90506125cf565b8787858181106121b6576121b6613002565b90506020028101906121c89190613103565b6121d6906020810190613179565b6040516020016121ea959493929190613274565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052915060010161212d565b509392505050565b6000600882901c90506120f88284838151811061224957612249613002565b602002602001015161261890919063ffffffff16565b600081801561227557836000528460205261227e565b84600052836020525b505060406000209392505050565b600083851061229d575060006122af565b6122aa8686868686612626565b871490505b9695505050505050565b60ff161c60011690565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156122fa57506000905060036123a9565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561234e573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166123a2576000600192509250506123a9565b9150600090505b94509492505050565b60008160048111156123c6576123c66132ca565b036123ce5750565b60018160048111156123e2576123e26132ca565b0361244e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064015b60405180910390fd5b6002816004811115612462576124626132ca565b036124c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401612445565b60038160048111156124dd576124dd6132ca565b0361256a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401612445565b50565b600080612579846126ee565b905061258483612782565b8015612593575083816001901b105b61259e5760006125a1565b60015b60ff16019392505050565b60008183106125bb5781611826565b5090919050565b600160ff919091161b1790565b606063ffffffff82111561260f576040517fe809999a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bc7826127af565b600160ff919091161b191690565b600085815b838110156126e357866001166001148061264757508587600101145b1561267f5761267885858381811061266157612661613002565b905060200201358360009182526020526040902090565b91506126ae565b6126ab8286868481811061269557612695613002565b9050602002013560009182526020526040902090565b91505b600196871c967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909601861c8601950161262b565b509695505050505050565b600080608083901c1561270357608092831c92015b604083901c1561271557604092831c92015b602083901c1561272757602092831c92015b601083901c1561273957601092831c92015b600883901c1561274b57600892831c92015b600483901c1561275d57600492831c92015b600283901c1561276f57600292831c92015b600183901c15611bc75760010192915050565b60006002826003811115612798576127986132ca565b6127a291906132f9565b60ff166001149050919050565b6060603f8263ffffffff16116127f6576040517ffc0000000000000000000000000000000000000000000000000000000000000060fa84901b166020820152602101611305565b613fff8263ffffffff161161286d5761283261281e6403fffffffc600285901b16600161331b565b600881811b62ffff001691901c60ff161790565b604051602001611305919060f09190911b7fffff00000000000000000000000000000000000000000000000000000000000016815260020190565b633fffffff8263ffffffff16116128f7576128bc60028363ffffffff16901b6002612898919061331b565b600881811c62ff00ff1663ff00ff009290911b9190911617601081811c91901b1790565b604051602001611305919060e09190911b7fffffffff0000000000000000000000000000000000000000000000000000000016815260040190565b6040517f030000000000000000000000000000000000000000000000000000000000000060208201527fffffffff00000000000000000000000000000000000000000000000000000000600884811c62ff00ff1663ff00ff009186901b9190911617601081811c91901b1760e01b166021820152602501611305565b8280548282559060005260206000209081019282156129b35760005260206000209182015b828111156129b3578254825591600101919060010190612998565b506129bf9291506129fe565b5090565b8280548282559060005260206000209081019282156129b3579160200282015b828111156129b35782518255916020019190600101906129e3565b5b808211156129bf57600081556001016129ff565b60006fffffffffffffffffffffffffffffffff8087168352602081871660208501528560408501526080606085015260c08401915084516040608086015282815180855260e087019150602083019450600092505b80831015612a885784518252938301936001929092019190830190612a68565b50602087015160a08701528094505050505095945050505050565b60008083601f840112612ab557600080fd5b50813567ffffffffffffffff811115612acd57600080fd5b6020830191508360208260051b8501011115612ae857600080fd5b9250929050565b600080600060408486031215612b0457600080fd5b833567ffffffffffffffff811115612b1b57600080fd5b612b2786828701612aa3565b909790965060209590950135949350505050565b6020808252825182820181905260009190848201906040850190845b81811015612b7357835183529284019291840191600101612b57565b50909695505050505050565b600060608284031215612b9157600080fd5b50919050565b600060e08284031215612b9157600080fd5b60008060008060008060008060006101808a8c031215612bc857600080fd5b893567ffffffffffffffff80821115612be057600080fd5b612bec8d838e01612b7f565b9a5060208c0135915080821115612c0257600080fd5b612c0e8d838e01612aa3565b909a50985060408c0135915080821115612c2757600080fd5b612c338d838e01612aa3565b9098509650869150612c488d60608e01612b97565b95506101408c0135915080821115612c5f57600080fd5b50612c6c8c828d01612aa3565b9a9d999c50979a969995989497966101600135949350505050565b600080600060408486031215612c9c57600080fd5b83359250602084013567ffffffffffffffff811115612cba57600080fd5b612cc686828701612aa3565b9497909650939450505050565b60008060008060608587031215612ce957600080fd5b84359350602085013567ffffffffffffffff811115612d0757600080fd5b612d1387828801612aa3565b9598909750949560400135949350505050565b600060208284031215612d3857600080fd5b5035919050565b60008060008060608587031215612d5557600080fd5b843567ffffffffffffffff80821115612d6d57600080fd5b612d7988838901612b7f565b95506020870135915080821115612d8f57600080fd5b612d9b88838901612aa3565b90955093506040870135915080821115612db457600080fd5b50850160c08188031215612dc757600080fd5b939692955090935050565b600060208284031215612de457600080fd5b813567ffffffffffffffff8116811461182657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6fffffffffffffffffffffffffffffffff81811683821601908082111561206357612063612dfc565b600060208284031215612e6657600080fd5b813563ffffffff8116811461182657600080fd5b60007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612ea957600080fd5b8260051b80858437919091019392505050565b80820180821115611bc757611bc7612dfc565b600060208284031215612ee157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461182657600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612f3a57600080fd5b83018035915067ffffffffffffffff821115612f5557600080fd5b6020019150600581901b3603821315612ae857600080fd5b600060208284031215612f7f57600080fd5b813560ff8116811461182657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082612fce57612fce612f90565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000815160005b818110156130525760208185018101518683015201613038565b50600093019283525090919050565b600061306d8286613031565b7fffffffff0000000000000000000000000000000000000000000000000000000094909416845250507fffffffffffffffff000000000000000000000000000000000000000000000000166004820152600c01919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff418336030181126130f957600080fd5b9190910192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126130f957600080fd5b60006020828403121561314957600080fd5b81357fffff0000000000000000000000000000000000000000000000000000000000008116811461182657600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126131ae57600080fd5b83018035915067ffffffffffffffff8211156131c957600080fd5b602001915036819003821315612ae857600080fd5b80356020831015611bc7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b60008261322957613229612f90565b500690565b60ff818116838216029081169081811461206357612063612dfc565b81810381811115611bc757611bc7612dfc565b8082028115828204841417611bc757611bc7612dfc565b60006132808288613031565b7fffff000000000000000000000000000000000000000000000000000000000000871681526132b26002820187613031565b90508385823760009301928352509095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600060ff83168061330c5761330c612f90565b8060ff84160691505092915050565b63ffffffff81811683821601908082111561206357612063612dfc56fea2646970667358221220d200b88b1fbd76940c01aeea4316095882bf04d47f9383da64dca480e6080f3a64736f6c63430008190033

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

00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000019591900000000000000000000000000000000000000000000000000000000000002de0000000000000000000000000000000000000000000000000000000000000095fa59503298e8233afb850f7dd29653d1f96d1fcbc5a8693f2aabeff99db2216200000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000000942b13c7e8b09f9135f3a8f26b5cd66107c2d65de8765ca685d144c37f3661bc55

-----Decoded View---------------
Arg [0] : _randaoCommitDelay (uint256): 128
Arg [1] : _randaoCommitExpiration (uint256): 24
Arg [2] : _minNumRequiredSignatures (uint256): 10
Arg [3] : _initialBeefyBlock (uint64): 1661209
Arg [4] : _initialValidatorSet (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : _nextValidatorSet (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000018
Arg [2] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [3] : 0000000000000000000000000000000000000000000000000000000000195919
Arg [4] : 00000000000000000000000000000000000000000000000000000000000002de
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000095
Arg [6] : fa59503298e8233afb850f7dd29653d1f96d1fcbc5a8693f2aabeff99db22162
Arg [7] : 00000000000000000000000000000000000000000000000000000000000002df
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000094
Arg [9] : 2b13c7e8b09f9135f3a8f26b5cd66107c2d65de8765ca685d144c37f3661bc55


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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