Contract

0x6aba65dc38E6AE9ed0d95EB67Bf1B524C1e3036A

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

-

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MPTProofVerifier

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 9 : MPTProofVerifier.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

import {Lib_OVMCodec} from "@eth-optimism/contracts/libraries/codec/Lib_OVMCodec.sol";
import {Lib_SecureMerkleTrie} from "@eth-optimism/contracts/libraries/trie/Lib_SecureMerkleTrie.sol";
import {Lib_RLPReader} from "@eth-optimism/contracts/libraries/rlp/Lib_RLPReader.sol";
import {Lib_BytesUtils} from "@eth-optimism/contracts/libraries/utils/Lib_BytesUtils.sol";

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

/// MPT proof verifier allows to validate witness proof about a storage slot value on the other chain.
/// @custom:security-contact [email protected]
contract MPTProofVerifier is IProofVerifier {

    /// Verify proof generated by eth_getProof
    function verifyProof(address contractAddress, bytes32 slotIndex, bytes32 expectedValue, bytes32 stateRoot, bytes calldata proof) external pure {
        Lib_RLPReader.RLPItem[] memory proofItems = Lib_RLPReader.readList(proof);
        require(proofItems.length == 2, "Expected 2 proof parts");
        bytes memory accountProof = Lib_RLPReader.readBytes(proofItems[0]);
        bytes memory storageProof = Lib_RLPReader.readBytes(proofItems[1]);

        // MPT witness proof verification
        // https://github.com/ensdomains/arb-resolver/blob/master/packages/contracts/contracts/l1/ArbitrumResolverStub.sol#L166
        // https://github.com/ensdomains/op-resolver/blob/master/packages/contracts/contracts/l1/OptimismResolverStub.sol#L135
        (
            bool exists,
            bytes memory encodedAccount
        ) = Lib_SecureMerkleTrie.get(
                abi.encodePacked(contractAddress),
                accountProof,
                stateRoot
            );
        require(exists, "Proved account does not exist");
        Lib_OVMCodec.EVMAccount memory account = Lib_OVMCodec.decodeEVMAccount(
            encodedAccount
        );

        (bool storageExists, bytes memory retrievedValue) = Lib_SecureMerkleTrie
            .get(
                abi.encodePacked(slotIndex),
                storageProof,
                account.storageRoot
            );

        if (!storageExists) {
            require(expectedValue == 0, "Unexpected proved value");
        } else {
            bytes32 actualValue = toBytes32PadLeft(Lib_RLPReader.readBytes(retrievedValue));
            require(actualValue == expectedValue, "Unexpected proved value");
        }
    }

    /// Convert RLP bytes value to bytes32
    // Ported from Lib_BytesUtils.sol / OptimismResolverStub.sol
    function toBytes32PadLeft(bytes memory _bytes) private pure returns (bytes32) {
        bytes32 ret;
        uint256 len = _bytes.length <= 32 ? _bytes.length : 32;
        assembly {
            ret := shr(mul(sub(32, len), 8), mload(add(_bytes, 32)))
        }
        return ret;
    }
}

File 2 of 9 : Lib_OVMCodec.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

/**
 * @title Lib_OVMCodec
 */
library Lib_OVMCodec {
    /*********
     * Enums *
     *********/

    enum QueueOrigin {
        SEQUENCER_QUEUE,
        L1TOL2_QUEUE
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    uint256 internal constant MAX_LIST_LENGTH = 32;

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

    enum RLPItemType {
        DATA_ITEM,
        LIST_ITEM
    }

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

    struct RLPItem {
        uint256 length;
        uint256 ptr;
    }

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

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

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

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

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

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

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

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

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

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

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

        return out;
    }

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

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

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

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

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

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

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

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

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

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

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

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

        return out;
    }

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

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

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

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

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

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

        return out != 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            src += 32;
            dest += 32;
        }

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

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

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

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

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

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

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

        return encoded;
    }

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

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

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

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

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

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

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

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

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

        return encoded;
    }

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

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

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

        return res;
    }

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

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

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

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

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

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

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

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

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

        return flattened;
    }
}

File 5 of 9 : Lib_MerkleTrie.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

/**
 * @title Lib_MerkleTrie
 */
library Lib_MerkleTrie {
    /*******************
     * Data Structures *
     *******************/

    enum NodeType {
        BranchNode,
        ExtensionNode,
        LeafNode
    }

    struct TrieNode {
        bytes encoded;
        Lib_RLPReader.RLPItem[] decoded;
    }

    /**********************
     * Contract Constants *
     **********************/

    // TREE_RADIX determines the number of elements per branch node.
    uint256 constant TREE_RADIX = 16;
    // Branch nodes have TREE_RADIX elements plus an additional `value` slot.
    uint256 constant BRANCH_NODE_LENGTH = TREE_RADIX + 1;
    // Leaf nodes and extension nodes always have two elements, a `path` and a `value`.
    uint256 constant LEAF_OR_EXTENSION_NODE_LENGTH = 2;

    // Prefixes are prepended to the `path` within a leaf or extension node and
    // allow us to differentiate between the two node types. `ODD` or `EVEN` is
    // determined by the number of nibbles within the unprefixed `path`. If the
    // number of nibbles if even, we need to insert an extra padding nibble so
    // the resulting prefixed `path` has an even number of nibbles.
    uint8 constant PREFIX_EXTENSION_EVEN = 0;
    uint8 constant PREFIX_EXTENSION_ODD = 1;
    uint8 constant PREFIX_LEAF_EVEN = 2;
    uint8 constant PREFIX_LEAF_ODD = 3;

    // Just a utility constant. RLP represents `NULL` as 0x80.
    bytes1 constant RLP_NULL = bytes1(0x80);

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

    /**
     * @notice Verifies a proof that a given key/value pair is present in the
     * Merkle trie.
     * @param _key Key of the node to search for, as a hex string.
     * @param _value Value of the node to search for, as a hex string.
     * @param _proof Merkle trie inclusion proof for the desired node. Unlike
     * traditional Merkle trees, this proof is executed top-down and consists
     * of a list of RLP-encoded nodes that make a path down to the target node.
     * @param _root Known root of the Merkle trie. Used to verify that the
     * included proof is correctly constructed.
     * @return _verified `true` if the k/v pair exists in the trie, `false` otherwise.
     */
    function verifyInclusionProof(
        bytes memory _key,
        bytes memory _value,
        bytes memory _proof,
        bytes32 _root
    ) internal pure returns (bool _verified) {
        (bool exists, bytes memory value) = get(_key, _proof, _root);

        return (exists && Lib_BytesUtils.equal(_value, value));
    }

    /**
     * @notice Retrieves the value associated with a given key.
     * @param _key Key to search for, as hex bytes.
     * @param _proof Merkle trie inclusion proof for the key.
     * @param _root Known root of the Merkle trie.
     * @return _exists Whether or not the key exists.
     * @return _value Value of the key if it exists.
     */
    function get(
        bytes memory _key,
        bytes memory _proof,
        bytes32 _root
    ) internal pure returns (bool _exists, bytes memory _value) {
        TrieNode[] memory proof = _parseProof(_proof);
        (uint256 pathLength, bytes memory keyRemainder, bool isFinalNode) = _walkNodePath(
            proof,
            _key,
            _root
        );

        bool exists = keyRemainder.length == 0;

        require(exists || isFinalNode, "Provided proof is invalid.");

        bytes memory value = exists ? _getNodeValue(proof[pathLength - 1]) : bytes("");

        return (exists, value);
    }

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

    /**
     * @notice Walks through a proof using a provided key.
     * @param _proof Inclusion proof to walk through.
     * @param _key Key to use for the walk.
     * @param _root Known root of the trie.
     * @return _pathLength Length of the final path
     * @return _keyRemainder Portion of the key remaining after the walk.
     * @return _isFinalNode Whether or not we've hit a dead end.
     */
    function _walkNodePath(
        TrieNode[] memory _proof,
        bytes memory _key,
        bytes32 _root
    )
        private
        pure
        returns (
            uint256 _pathLength,
            bytes memory _keyRemainder,
            bool _isFinalNode
        )
    {
        uint256 pathLength = 0;
        bytes memory key = Lib_BytesUtils.toNibbles(_key);

        bytes32 currentNodeID = _root;
        uint256 currentKeyIndex = 0;
        uint256 currentKeyIncrement = 0;
        TrieNode memory currentNode;

        // Proof is top-down, so we start at the first element (root).
        for (uint256 i = 0; i < _proof.length; i++) {
            currentNode = _proof[i];
            currentKeyIndex += currentKeyIncrement;

            // Keep track of the proof elements we actually need.
            // It's expensive to resize arrays, so this simply reduces gas costs.
            pathLength += 1;

            if (currentKeyIndex == 0) {
                // First proof element is always the root node.
                require(keccak256(currentNode.encoded) == currentNodeID, "Invalid root hash");
            } else if (currentNode.encoded.length >= 32) {
                // Nodes 32 bytes or larger are hashed inside branch nodes.
                require(
                    keccak256(currentNode.encoded) == currentNodeID,
                    "Invalid large internal hash"
                );
            } else {
                // Nodes smaller than 31 bytes aren't hashed.
                require(
                    Lib_BytesUtils.toBytes32(currentNode.encoded) == currentNodeID,
                    "Invalid internal node hash"
                );
            }

            if (currentNode.decoded.length == BRANCH_NODE_LENGTH) {
                if (currentKeyIndex == key.length) {
                    // We've hit the end of the key
                    // meaning the value should be within this branch node.
                    break;
                } else {
                    // We're not at the end of the key yet.
                    // Figure out what the next node ID should be and continue.
                    uint8 branchKey = uint8(key[currentKeyIndex]);
                    Lib_RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey];
                    currentNodeID = _getNodeID(nextNode);
                    currentKeyIncrement = 1;
                    continue;
                }
            } else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
                bytes memory path = _getNodePath(currentNode);
                uint8 prefix = uint8(path[0]);
                uint8 offset = 2 - (prefix % 2);
                bytes memory pathRemainder = Lib_BytesUtils.slice(path, offset);
                bytes memory keyRemainder = Lib_BytesUtils.slice(key, currentKeyIndex);
                uint256 sharedNibbleLength = _getSharedNibbleLength(pathRemainder, keyRemainder);

                if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
                    if (
                        pathRemainder.length == sharedNibbleLength &&
                        keyRemainder.length == sharedNibbleLength
                    ) {
                        // The key within this leaf matches our key exactly.
                        // Increment the key index to reflect that we have no remainder.
                        currentKeyIndex += sharedNibbleLength;
                    }

                    // We've hit a leaf node, so our next node should be NULL.
                    currentNodeID = bytes32(RLP_NULL);
                    break;
                } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
                    if (sharedNibbleLength != pathRemainder.length) {
                        // Our extension node is not identical to the remainder.
                        // We've hit the end of this path
                        // updates will need to modify this extension.
                        currentNodeID = bytes32(RLP_NULL);
                        break;
                    } else {
                        // Our extension shares some nibbles.
                        // Carry on to the next node.
                        currentNodeID = _getNodeID(currentNode.decoded[1]);
                        currentKeyIncrement = sharedNibbleLength;
                        continue;
                    }
                } else {
                    revert("Received a node with an unknown prefix");
                }
            } else {
                revert("Received an unparseable node.");
            }
        }

        // If our node ID is NULL, then we're at a dead end.
        bool isFinalNode = currentNodeID == bytes32(RLP_NULL);
        return (pathLength, Lib_BytesUtils.slice(key, currentKeyIndex), isFinalNode);
    }

    /**
     * @notice Parses an RLP-encoded proof into something more useful.
     * @param _proof RLP-encoded proof to parse.
     * @return _parsed Proof parsed into easily accessible structs.
     */
    function _parseProof(bytes memory _proof) private pure returns (TrieNode[] memory _parsed) {
        Lib_RLPReader.RLPItem[] memory nodes = Lib_RLPReader.readList(_proof);
        TrieNode[] memory proof = new TrieNode[](nodes.length);

        for (uint256 i = 0; i < nodes.length; i++) {
            bytes memory encoded = Lib_RLPReader.readBytes(nodes[i]);
            proof[i] = TrieNode({ encoded: encoded, decoded: Lib_RLPReader.readList(encoded) });
        }

        return proof;
    }

    /**
     * @notice Picks out the ID for a node. Node ID is referred to as the
     * "hash" within the specification, but nodes < 32 bytes are not actually
     * hashed.
     * @param _node Node to pull an ID for.
     * @return _nodeID ID for the node, depending on the size of its contents.
     */
    function _getNodeID(Lib_RLPReader.RLPItem memory _node) private pure returns (bytes32 _nodeID) {
        bytes memory nodeID;

        if (_node.length < 32) {
            // Nodes smaller than 32 bytes are RLP encoded.
            nodeID = Lib_RLPReader.readRawBytes(_node);
        } else {
            // Nodes 32 bytes or larger are hashed.
            nodeID = Lib_RLPReader.readBytes(_node);
        }

        return Lib_BytesUtils.toBytes32(nodeID);
    }

    /**
     * @notice Gets the path for a leaf or extension node.
     * @param _node Node to get a path for.
     * @return _path Node path, converted to an array of nibbles.
     */
    function _getNodePath(TrieNode memory _node) private pure returns (bytes memory _path) {
        return Lib_BytesUtils.toNibbles(Lib_RLPReader.readBytes(_node.decoded[0]));
    }

    /**
     * @notice Gets the path for a node.
     * @param _node Node to get a value for.
     * @return _value Node value, as hex bytes.
     */
    function _getNodeValue(TrieNode memory _node) private pure returns (bytes memory _value) {
        return Lib_RLPReader.readBytes(_node.decoded[_node.decoded.length - 1]);
    }

    /**
     * @notice Utility; determines the number of nibbles shared between two
     * nibble arrays.
     * @param _a First nibble array.
     * @param _b Second nibble array.
     * @return _shared Number of shared nibbles.
     */
    function _getSharedNibbleLength(bytes memory _a, bytes memory _b)
        private
        pure
        returns (uint256 _shared)
    {
        uint256 i = 0;
        while (_a.length > i && _b.length > i && _a[i] == _b[i]) {
            i++;
        }
        return i;
    }
}

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

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

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

    /**
     * @notice Verifies a proof that a given key/value pair is present in the
     * Merkle trie.
     * @param _key Key of the node to search for, as a hex string.
     * @param _value Value of the node to search for, as a hex string.
     * @param _proof Merkle trie inclusion proof for the desired node. Unlike
     * traditional Merkle trees, this proof is executed top-down and consists
     * of a list of RLP-encoded nodes that make a path down to the target node.
     * @param _root Known root of the Merkle trie. Used to verify that the
     * included proof is correctly constructed.
     * @return _verified `true` if the k/v pair exists in the trie, `false` otherwise.
     */
    function verifyInclusionProof(
        bytes memory _key,
        bytes memory _value,
        bytes memory _proof,
        bytes32 _root
    ) internal pure returns (bool _verified) {
        bytes memory key = _getSecureKey(_key);
        return Lib_MerkleTrie.verifyInclusionProof(key, _value, _proof, _root);
    }

    /**
     * @notice Retrieves the value associated with a given key.
     * @param _key Key to search for, as hex bytes.
     * @param _proof Merkle trie inclusion proof for the key.
     * @param _root Known root of the Merkle trie.
     * @return _exists Whether or not the key exists.
     * @return _value Value of the key if it exists.
     */
    function get(
        bytes memory _key,
        bytes memory _proof,
        bytes32 _root
    ) internal pure returns (bool _exists, bytes memory _value) {
        bytes memory key = _getSecureKey(_key);
        return Lib_MerkleTrie.get(key, _proof, _root);
    }

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

    /**
     * Computes the secure counterpart to a key.
     * @param _key Key to get a secure key from.
     * @return _secureKey Secure version of the key.
     */
    function _getSecureKey(bytes memory _key) private pure returns (bytes memory _secureKey) {
        return abi.encodePacked(keccak256(_key));
    }
}

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

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

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

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

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

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

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

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

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

        bytes memory tempBytes;

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

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

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

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

                mstore(tempBytes, _length)

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

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

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

        return tempBytes;
    }

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

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

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

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

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

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

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

        return nibbles;
    }

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

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

        return ret;
    }

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

File 9 of 9 : IProofVerifier.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

/// Proof verifier allows to validate witness proof about a storage slot value on a different chain.
interface IProofVerifier {

    /// Verify witness proof - proof about storage slot value on a different chain.
    /// Reverts if the slot value does not match the expected value or if the proof is invalid.
    function verifyProof(address contractAddress, bytes32 slotIndex, bytes32 expectedValue, bytes32 stateRoot, bytes calldata proof) external view;

}

Settings
{
  "evmVersion": "cancun",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bytes32","name":"slotIndex","type":"bytes32"},{"internalType":"bytes32","name":"expectedValue","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"verifyProof","outputs":[],"stateMutability":"pure","type":"function"}]

6080604052348015600e575f5ffd5b5061177b8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80636a5310d01461002d575b5f5ffd5b61004061003b366004611581565b610042565b005b5f61008183838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506102a292505050565b905080516002146100d25760405162461bcd60e51b8152602060048201526016602482015275457870656374656420322070726f6f6620706172747360501b60448201526064015b60405180910390fd5b5f6100f5825f815181106100e8576100e8611628565b60200260200101516102da565b90505f61010e836001815181106100e8576100e8611628565b6040516bffffffffffffffffffffffff1960608c901b1660208201529091505f90819061014e90603401604051602081830303815290604052858a610367565b915091508161019f5760405162461bcd60e51b815260206004820152601d60248201527f50726f766564206163636f756e7420646f6573206e6f7420657869737400000060448201526064016100c9565b5f6101a98261038e565b90505f5f6101dd8d6040516020016101c391815260200190565b604051602081830303815290604052878560400151610367565b9150915081610233578b1561022e5760405162461bcd60e51b8152602060048201526017602482015276556e65787065637465642070726f7665642076616c756560481b60448201526064016100c9565b610292565b5f61024561024083610455565b610487565b90508c81146102905760405162461bcd60e51b8152602060048201526017602482015276556e65787065637465642070726f7665642076616c756560481b60448201526064016100c9565b505b5050505050505050505050505050565b6040805180820182525f808252602091820152815180830190925282518252808301908201526060906102d4906104b4565b92915050565b60605f5f5f6102e88561069a565b919450925090505f8160018111156103025761030261163c565b1461034f5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420524c502062797465732076616c75652e000000000000000060448201526064016100c9565b61035e856020015184846109d4565b95945050505050565b5f60605f61037486610aa0565b9050610381818686610ad2565b9250925050935093915050565b6103b560405180608001604052805f81526020015f81526020015f81526020015f81525090565b5f6103bf836102a2565b905060405180608001604052806103ee835f815181106103e1576103e1611628565b6020026020010151610ba7565b8152602001610409836001815181106103e1576103e1611628565b81526020016104318360028151811061042457610424611628565b6020026020010151610bad565b815260200161044c8360038151811061042457610424611628565b90529392505050565b6040805180820182525f808252602091820152815180830190925282518252808301908201526060906102d4906102da565b5f5f5f60208451111561049b57602061049e565b83515b60209485015194036008029390931c9392505050565b60605f5f6104c18461069a565b919350909150600190508160018111156104dd576104dd61163c565b1461052a5760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420524c50206c6973742076616c75652e00000000000000000060448201526064016100c9565b60408051602080825261042082019092525f91816020015b604080518082019091525f80825260208201528152602001906001900390816105425790505090505f835b865181101561068f57602082106105d95760405162461bcd60e51b815260206004820152602a60248201527f50726f766964656420524c50206c6973742065786365656473206d6178206c6960448201526939ba103632b733ba341760b11b60648201526084016100c9565b5f5f6106146040518060400160405280858c5f01516105f89190611678565b8152602001858c6020015161060d919061168b565b905261069a565b509150915060405180604001604052808383610630919061168b565b8152602001848b60200151610645919061168b565b81525085858151811061065a5761065a611628565b602090810291909101015261067060018561168b565b935061067c818361168b565b610686908461168b565b9250505061056d565b508152949350505050565b5f5f5f5f845f0151116106ef5760405162461bcd60e51b815260206004820152601860248201527f524c50206974656d2063616e6e6f74206265206e756c6c2e000000000000000060448201526064016100c9565b602084015180515f1a607f8111610711575f60015f94509450945050506109cd565b60b7811161078a575f610725608083611678565b905080875f0151116107795760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420524c502073686f727420737472696e672e0000000000000060448201526064016100c9565b6001955093505f92506109cd915050565b60bf8111610876575f61079e60b783611678565b905080875f0151116107f25760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420524c50206c6f6e6720737472696e67206c656e6774682e0060448201526064016100c9565b600183015160208290036101000a900461080c818361168b565b88511161085b5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420524c50206c6f6e6720737472696e672e000000000000000060448201526064016100c9565b61086682600161168b565b965094505f93506109cd92505050565b60f781116108ef575f61088a60c083611678565b905080875f0151116108de5760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420524c502073686f7274206c6973742e00000000000000000060448201526064016100c9565b6001955093508492506109cd915050565b5f6108fb60f783611678565b905080875f01511161094f5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420524c50206c6f6e67206c697374206c656e6774682e00000060448201526064016100c9565b600183015160208290036101000a9004610969818361168b565b8851116109b15760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b210292628103637b733903634b9ba1760511b60448201526064016100c9565b6109bc82600161168b565b96509450600193506109cd92505050565b9193909250565b60605f8267ffffffffffffffff8111156109f0576109f0611650565b6040519080825280601f01601f191660200182016040528015610a1a576020820181803683370190505b50905080515f03610a2c579050610a99565b5f610a37858761168b565b9050602082015f5b610a4a6020876116b2565b811015610a775782518252610a6060208461168b565b9250610a6d60208361168b565b9150600101610a3f565b5080519151601f86166020036101000a5f190192831692191691909117905290505b9392505050565b60608180519060200120604051602001610abc91815260200190565b6040516020818303038152906040529050919050565b5f60605f610adf85610caa565b90505f5f5f610aef848a89610d89565b81519295509093509150158080610b035750815b610b4f5760405162461bcd60e51b815260206004820152601a60248201527f50726f76696465642070726f6f6620697320696e76616c69642e00000000000060448201526064016100c9565b5f81610b695760405180602001604052805f815250610b95565b610b9586610b78600188611678565b81518110610b8857610b88611628565b6020026020010151611187565b919b919a509098505050505050505050565b5f6102d4825b5f6021825f01511115610c025760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420524c5020627974657333322076616c75652e00000000000060448201526064016100c9565b5f5f5f610c0e8561069a565b919450925090505f816001811115610c2857610c2861163c565b14610c755760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420524c5020627974657333322076616c75652e00000000000060448201526064016100c9565b5f838660200151610c86919061168b565b80519091506020841015610ca05760208490036101000a90045b9695505050505050565b60605f610cb6836102a2565b90505f815167ffffffffffffffff811115610cd357610cd3611650565b604051908082528060200260200182016040528015610d1857816020015b6040805180820190915260608082526020820152815260200190600190039081610cf15790505b5090505f5b8251811015610d81575f610d3c8483815181106100e8576100e8611628565b90506040518060400160405280828152602001610d58836102a2565b815250838381518110610d6d57610d6d611628565b602090810291909101015250600101610d1d565b509392505050565b5f6060818080610d98876111b1565b604080518082019091526060808252602082015290915086905f9081905f5b8c5181101561115f578c8181518110610dd257610dd2611628565b602002602001015191508284610de8919061168b565b9350610df560018861168b565b9650835f03610e4f57815180516020909101208514610e4a5760405162461bcd60e51b8152602060048201526011602482015270092dcecc2d8d2c840e4dedee840d0c2e6d607b1b60448201526064016100c9565b610f0b565b815151602011610eb157815180516020909101208514610e4a5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c6964206c6172676520696e7465726e616c2068617368000000000060448201526064016100c9565b84610ebe835f01516112de565b14610f0b5760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420696e7465726e616c206e6f6465206861736800000000000060448201526064016100c9565b610f176010600161168b565b82602001515103610f87578551841461115f575f868581518110610f3d57610f3d611628565b602001015160f81c60f81b60f81c90505f83602001518260ff1681518110610f6757610f67611628565b60200260200101519050610f7a81611305565b9650600194505050611157565b60028260200151510361110f575f610f9e83611339565b90505f815f81518110610fb357610fb3611628565b016020015160f81c90505f610fc96002836116c5565b610fd49060026116e6565b90505f610fe4848360ff1661135c565b90505f610ff18b8a61135c565b90505f610ffe8383611391565b905060ff851660021480611015575060ff85166003145b1561104f5780835114801561102a5750808251145b1561103c57611039818b61168b565b99505b50600160ff1b995061115f945050505050565b60ff85161580611062575060ff85166001145b156110b857825181146110825750600160ff1b995061115f945050505050565b6110a9886020015160018151811061109c5761109c611628565b6020026020010151611305565b9a509750611157945050505050565b60405162461bcd60e51b815260206004820152602660248201527f52656365697665642061206e6f6465207769746820616e20756e6b6e6f776e206044820152650e0e4caccd2f60d31b60648201526084016100c9565b60405162461bcd60e51b815260206004820152601d60248201527f526563656976656420616e20756e706172736561626c65206e6f64652e00000060448201526064016100c9565b600101610db7565b50600160ff1b841486611172878661135c565b909e909d50909b509950505050505050505050565b602081015180516060916102d4916111a190600190611678565b815181106100e8576100e8611628565b60605f825160026111c291906116ff565b67ffffffffffffffff8111156111da576111da611650565b6040519080825280601f01601f191660200182016040528015611204576020820181803683370190505b5090505f5b83518110156112d757600484828151811061122657611226611628565b01602001516001600160f81b031916901c826112438360026116ff565b8151811061125357611253611628565b60200101906001600160f81b03191690815f1a905350601084828151811061127d5761127d611628565b016020015161128f919060f81c6116c5565b60f81b8261129e8360026116ff565b6112a990600161168b565b815181106112b9576112b9611628565b60200101906001600160f81b03191690815f1a905350600101611209565b5092915050565b5f6020825110156112f157506020015190565b818060200190518101906102d49190611716565b5f60606020835f015110156113245761131d8361140c565b9050611330565b61132d836102da565b90505b610a99816112de565b60606102d461135783602001515f815181106100e8576100e8611628565b6111b1565b60608251821061137a575060408051602081019091525f81526102d4565b610a99838384865161138c9190611678565b611417565b5f805b8084511180156113a45750808351115b80156113f557508281815181106113bd576113bd611628565b602001015160f81c60f81b6001600160f81b0319168482815181106113e4576113e4611628565b01602001516001600160f81b031916145b15610a9957806114048161172d565b915050611394565b60606102d48261156d565b60608161142581601f61168b565b10156114645760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016100c9565b8261146f838261168b565b10156114ae5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016100c9565b6114b8828461168b565b845110156114fc5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b60448201526064016100c9565b60608215801561151a5760405191505f825260208201604052611564565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561155357805183526020928301920161153b565b5050858452601f01601f1916604052505b50949350505050565b60606102d482602001515f845f01516109d4565b5f5f5f5f5f5f60a08789031215611596575f5ffd5b86356001600160a01b03811681146115ac575f5ffd5b9550602087013594506040870135935060608701359250608087013567ffffffffffffffff8111156115dc575f5ffd5b8701601f810189136115ec575f5ffd5b803567ffffffffffffffff811115611602575f5ffd5b896020828401011115611613575f5ffd5b60208201935080925050509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156102d4576102d4611664565b808201808211156102d4576102d4611664565b634e487b7160e01b5f52601260045260245ffd5b5f826116c0576116c061169e565b500490565b5f60ff8316806116d7576116d761169e565b8060ff84160691505092915050565b60ff82811682821603908111156102d4576102d4611664565b80820281158282048414176102d4576102d4611664565b5f60208284031215611726575f5ffd5b5051919050565b5f6001820161173e5761173e611664565b506001019056fea26469706673582212202670d568dd4eabd4a95c29c77cb2e2bfc39146cca8a3475eace57ceab4ea241064736f6c634300081b0033

Deployed Bytecode

0x608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80636a5310d01461002d575b5f5ffd5b61004061003b366004611581565b610042565b005b5f61008183838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506102a292505050565b905080516002146100d25760405162461bcd60e51b8152602060048201526016602482015275457870656374656420322070726f6f6620706172747360501b60448201526064015b60405180910390fd5b5f6100f5825f815181106100e8576100e8611628565b60200260200101516102da565b90505f61010e836001815181106100e8576100e8611628565b6040516bffffffffffffffffffffffff1960608c901b1660208201529091505f90819061014e90603401604051602081830303815290604052858a610367565b915091508161019f5760405162461bcd60e51b815260206004820152601d60248201527f50726f766564206163636f756e7420646f6573206e6f7420657869737400000060448201526064016100c9565b5f6101a98261038e565b90505f5f6101dd8d6040516020016101c391815260200190565b604051602081830303815290604052878560400151610367565b9150915081610233578b1561022e5760405162461bcd60e51b8152602060048201526017602482015276556e65787065637465642070726f7665642076616c756560481b60448201526064016100c9565b610292565b5f61024561024083610455565b610487565b90508c81146102905760405162461bcd60e51b8152602060048201526017602482015276556e65787065637465642070726f7665642076616c756560481b60448201526064016100c9565b505b5050505050505050505050505050565b6040805180820182525f808252602091820152815180830190925282518252808301908201526060906102d4906104b4565b92915050565b60605f5f5f6102e88561069a565b919450925090505f8160018111156103025761030261163c565b1461034f5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420524c502062797465732076616c75652e000000000000000060448201526064016100c9565b61035e856020015184846109d4565b95945050505050565b5f60605f61037486610aa0565b9050610381818686610ad2565b9250925050935093915050565b6103b560405180608001604052805f81526020015f81526020015f81526020015f81525090565b5f6103bf836102a2565b905060405180608001604052806103ee835f815181106103e1576103e1611628565b6020026020010151610ba7565b8152602001610409836001815181106103e1576103e1611628565b81526020016104318360028151811061042457610424611628565b6020026020010151610bad565b815260200161044c8360038151811061042457610424611628565b90529392505050565b6040805180820182525f808252602091820152815180830190925282518252808301908201526060906102d4906102da565b5f5f5f60208451111561049b57602061049e565b83515b60209485015194036008029390931c9392505050565b60605f5f6104c18461069a565b919350909150600190508160018111156104dd576104dd61163c565b1461052a5760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420524c50206c6973742076616c75652e00000000000000000060448201526064016100c9565b60408051602080825261042082019092525f91816020015b604080518082019091525f80825260208201528152602001906001900390816105425790505090505f835b865181101561068f57602082106105d95760405162461bcd60e51b815260206004820152602a60248201527f50726f766964656420524c50206c6973742065786365656473206d6178206c6960448201526939ba103632b733ba341760b11b60648201526084016100c9565b5f5f6106146040518060400160405280858c5f01516105f89190611678565b8152602001858c6020015161060d919061168b565b905261069a565b509150915060405180604001604052808383610630919061168b565b8152602001848b60200151610645919061168b565b81525085858151811061065a5761065a611628565b602090810291909101015261067060018561168b565b935061067c818361168b565b610686908461168b565b9250505061056d565b508152949350505050565b5f5f5f5f845f0151116106ef5760405162461bcd60e51b815260206004820152601860248201527f524c50206974656d2063616e6e6f74206265206e756c6c2e000000000000000060448201526064016100c9565b602084015180515f1a607f8111610711575f60015f94509450945050506109cd565b60b7811161078a575f610725608083611678565b905080875f0151116107795760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420524c502073686f727420737472696e672e0000000000000060448201526064016100c9565b6001955093505f92506109cd915050565b60bf8111610876575f61079e60b783611678565b905080875f0151116107f25760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420524c50206c6f6e6720737472696e67206c656e6774682e0060448201526064016100c9565b600183015160208290036101000a900461080c818361168b565b88511161085b5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420524c50206c6f6e6720737472696e672e000000000000000060448201526064016100c9565b61086682600161168b565b965094505f93506109cd92505050565b60f781116108ef575f61088a60c083611678565b905080875f0151116108de5760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420524c502073686f7274206c6973742e00000000000000000060448201526064016100c9565b6001955093508492506109cd915050565b5f6108fb60f783611678565b905080875f01511161094f5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420524c50206c6f6e67206c697374206c656e6774682e00000060448201526064016100c9565b600183015160208290036101000a9004610969818361168b565b8851116109b15760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b210292628103637b733903634b9ba1760511b60448201526064016100c9565b6109bc82600161168b565b96509450600193506109cd92505050565b9193909250565b60605f8267ffffffffffffffff8111156109f0576109f0611650565b6040519080825280601f01601f191660200182016040528015610a1a576020820181803683370190505b50905080515f03610a2c579050610a99565b5f610a37858761168b565b9050602082015f5b610a4a6020876116b2565b811015610a775782518252610a6060208461168b565b9250610a6d60208361168b565b9150600101610a3f565b5080519151601f86166020036101000a5f190192831692191691909117905290505b9392505050565b60608180519060200120604051602001610abc91815260200190565b6040516020818303038152906040529050919050565b5f60605f610adf85610caa565b90505f5f5f610aef848a89610d89565b81519295509093509150158080610b035750815b610b4f5760405162461bcd60e51b815260206004820152601a60248201527f50726f76696465642070726f6f6620697320696e76616c69642e00000000000060448201526064016100c9565b5f81610b695760405180602001604052805f815250610b95565b610b9586610b78600188611678565b81518110610b8857610b88611628565b6020026020010151611187565b919b919a509098505050505050505050565b5f6102d4825b5f6021825f01511115610c025760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420524c5020627974657333322076616c75652e00000000000060448201526064016100c9565b5f5f5f610c0e8561069a565b919450925090505f816001811115610c2857610c2861163c565b14610c755760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420524c5020627974657333322076616c75652e00000000000060448201526064016100c9565b5f838660200151610c86919061168b565b80519091506020841015610ca05760208490036101000a90045b9695505050505050565b60605f610cb6836102a2565b90505f815167ffffffffffffffff811115610cd357610cd3611650565b604051908082528060200260200182016040528015610d1857816020015b6040805180820190915260608082526020820152815260200190600190039081610cf15790505b5090505f5b8251811015610d81575f610d3c8483815181106100e8576100e8611628565b90506040518060400160405280828152602001610d58836102a2565b815250838381518110610d6d57610d6d611628565b602090810291909101015250600101610d1d565b509392505050565b5f6060818080610d98876111b1565b604080518082019091526060808252602082015290915086905f9081905f5b8c5181101561115f578c8181518110610dd257610dd2611628565b602002602001015191508284610de8919061168b565b9350610df560018861168b565b9650835f03610e4f57815180516020909101208514610e4a5760405162461bcd60e51b8152602060048201526011602482015270092dcecc2d8d2c840e4dedee840d0c2e6d607b1b60448201526064016100c9565b610f0b565b815151602011610eb157815180516020909101208514610e4a5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c6964206c6172676520696e7465726e616c2068617368000000000060448201526064016100c9565b84610ebe835f01516112de565b14610f0b5760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420696e7465726e616c206e6f6465206861736800000000000060448201526064016100c9565b610f176010600161168b565b82602001515103610f87578551841461115f575f868581518110610f3d57610f3d611628565b602001015160f81c60f81b60f81c90505f83602001518260ff1681518110610f6757610f67611628565b60200260200101519050610f7a81611305565b9650600194505050611157565b60028260200151510361110f575f610f9e83611339565b90505f815f81518110610fb357610fb3611628565b016020015160f81c90505f610fc96002836116c5565b610fd49060026116e6565b90505f610fe4848360ff1661135c565b90505f610ff18b8a61135c565b90505f610ffe8383611391565b905060ff851660021480611015575060ff85166003145b1561104f5780835114801561102a5750808251145b1561103c57611039818b61168b565b99505b50600160ff1b995061115f945050505050565b60ff85161580611062575060ff85166001145b156110b857825181146110825750600160ff1b995061115f945050505050565b6110a9886020015160018151811061109c5761109c611628565b6020026020010151611305565b9a509750611157945050505050565b60405162461bcd60e51b815260206004820152602660248201527f52656365697665642061206e6f6465207769746820616e20756e6b6e6f776e206044820152650e0e4caccd2f60d31b60648201526084016100c9565b60405162461bcd60e51b815260206004820152601d60248201527f526563656976656420616e20756e706172736561626c65206e6f64652e00000060448201526064016100c9565b600101610db7565b50600160ff1b841486611172878661135c565b909e909d50909b509950505050505050505050565b602081015180516060916102d4916111a190600190611678565b815181106100e8576100e8611628565b60605f825160026111c291906116ff565b67ffffffffffffffff8111156111da576111da611650565b6040519080825280601f01601f191660200182016040528015611204576020820181803683370190505b5090505f5b83518110156112d757600484828151811061122657611226611628565b01602001516001600160f81b031916901c826112438360026116ff565b8151811061125357611253611628565b60200101906001600160f81b03191690815f1a905350601084828151811061127d5761127d611628565b016020015161128f919060f81c6116c5565b60f81b8261129e8360026116ff565b6112a990600161168b565b815181106112b9576112b9611628565b60200101906001600160f81b03191690815f1a905350600101611209565b5092915050565b5f6020825110156112f157506020015190565b818060200190518101906102d49190611716565b5f60606020835f015110156113245761131d8361140c565b9050611330565b61132d836102da565b90505b610a99816112de565b60606102d461135783602001515f815181106100e8576100e8611628565b6111b1565b60608251821061137a575060408051602081019091525f81526102d4565b610a99838384865161138c9190611678565b611417565b5f805b8084511180156113a45750808351115b80156113f557508281815181106113bd576113bd611628565b602001015160f81c60f81b6001600160f81b0319168482815181106113e4576113e4611628565b01602001516001600160f81b031916145b15610a9957806114048161172d565b915050611394565b60606102d48261156d565b60608161142581601f61168b565b10156114645760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016100c9565b8261146f838261168b565b10156114ae5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016100c9565b6114b8828461168b565b845110156114fc5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b60448201526064016100c9565b60608215801561151a5760405191505f825260208201604052611564565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561155357805183526020928301920161153b565b5050858452601f01601f1916604052505b50949350505050565b60606102d482602001515f845f01516109d4565b5f5f5f5f5f5f60a08789031215611596575f5ffd5b86356001600160a01b03811681146115ac575f5ffd5b9550602087013594506040870135935060608701359250608087013567ffffffffffffffff8111156115dc575f5ffd5b8701601f810189136115ec575f5ffd5b803567ffffffffffffffff811115611602575f5ffd5b896020828401011115611613575f5ffd5b60208201935080925050509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156102d4576102d4611664565b808201808211156102d4576102d4611664565b634e487b7160e01b5f52601260045260245ffd5b5f826116c0576116c061169e565b500490565b5f60ff8316806116d7576116d761169e565b8060ff84160691505092915050565b60ff82811682821603908111156102d4576102d4611664565b80820281158282048414176102d4576102d4611664565b5f60208284031215611726575f5ffd5b5051919050565b5f6001820161173e5761173e611664565b506001019056fea26469706673582212202670d568dd4eabd4a95c29c77cb2e2bfc39146cca8a3475eace57ceab4ea241064736f6c634300081b0033

Block Transaction 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

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.