ETH Price: $3,124.41 (-0.46%)
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Update Merkle Ro...382171842025-11-15 16:21:5519 days ago1763223715IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000210.00435413
Update Merkle Ro...379109212025-11-08 14:13:0926 days ago1762611189IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000150.00327326
Update Merkle Ro...378233822025-11-06 13:35:1128 days ago1762436111IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000940.01920933
Update Merkle Ro...372129542025-10-23 10:27:3542 days ago1761215255IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000140.00301562
Update Merkle Ro...371229202025-10-21 8:26:2744 days ago1761035187IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000170.00368414
Update Merkle Ro...360816432025-09-27 5:57:1368 days ago1758952633IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000090.00195841
Update Merkle Ro...353412062025-09-10 2:35:5985 days ago1757471759IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000260.00537192
Update Merkle Ro...350568592025-09-03 12:37:4592 days ago1756903065IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000060.00131914
Update Merkle Ro...349272562025-08-31 12:37:3995 days ago1756643859IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000050.00109954
Update Merkle Ro...345720952025-08-23 7:18:57103 days ago1755933537IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000060.00127459
Update Merkle Ro...342696902025-08-16 7:18:47110 days ago1755328727IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000070.0014516
Update Merkle Ro...341400872025-08-13 7:18:41113 days ago1755069521IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000460.00943624
Update Merkle Ro...340104852025-08-10 7:18:37116 days ago1754810317IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000130.0026586
Update Merkle Ro...337263842025-08-03 17:28:35123 days ago1754242115IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000090.00186163
Update Merkle Ro...336831832025-08-02 17:28:33124 days ago1754155713IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000260.00534755
Update Merkle Ro...335108472025-07-29 17:44:01128 days ago1753811041IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000770.01569979
Update Merkle Ro...330867672025-07-19 22:08:01137 days ago1752962881IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000140.00297
Update Merkle Ro...327715292025-07-12 15:00:05145 days ago1752332405IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000120.00251935
Update Merkle Ro...326985812025-07-10 22:28:29146 days ago1752186509IN
0x1CD9276f...7B8a3d55F
0 ETH0.00000050.00903237
Update Merkle Ro...323442972025-07-02 17:39:01155 days ago1751477941IN
0x1CD9276f...7B8a3d55F
0 ETH0.00000080.01627992
Update Merkle Ro...321599672025-06-28 11:14:41159 days ago1751109281IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000080.00164712
Update Merkle Ro...320211302025-06-25 6:06:47162 days ago1750831607IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000130.00210481
Update Merkle Ro...319770522025-06-24 5:37:31163 days ago1750743451IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000080.00177513
Update Merkle Ro...318661082025-06-21 15:59:23166 days ago1750521563IN
0x1CD9276f...7B8a3d55F
0 ETH0.000000430.00888412
Update Merkle Ro...318229042025-06-20 15:59:15167 days ago1750435155IN
0x1CD9276f...7B8a3d55F
0 ETH0.00000020.00400838
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AnteHashTreeIndexV2

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: GPL-3.0-only

// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

pragma solidity 0.8.23;

import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IAnteHashTreeIndexV2 } from "./interfaces/IAnteHashTreeIndexV2.sol";
import { MerklePatricia } from "./libraries/MerklePatricia.sol";

/// @title Ante Hash Tree Index V2 smart contract
/// @notice Contract that stores the merkle roots of committed Antes
contract AnteHashTreeIndexV2 is IAnteHashTreeIndexV2, Ownable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @dev A set of unique relayer addresses that are allowed
    /// to update the merkle root.
    EnumerableSet.AddressSet private allowedRelayers;

    /// @notice The latest Merkle root of this batch
    bytes32 public latestMerkleRoot;
    /// @notice Data URI for IPFS or Arweave which contains the source
    ///         data used for constructing the Merkle root
    string public latestDataUri;

    /// @notice Address of the relayer that submitted the latest batch
    address public latestRelayer;

    constructor(address initialOwner) Ownable(initialOwner) { }

    /// @inheritdoc IAnteHashTreeIndexV2
    function addRelayer(address _relayerAddr) external override onlyOwner {
        bool success = allowedRelayers.add(_relayerAddr);
        if (!success) {
            revert AlreadyExists();
        }
        emit RelayerAddressAdded(_relayerAddr);
    }

    /// @inheritdoc IAnteHashTreeIndexV2
    function removeRelayer(address _relayerAddr) external override onlyOwner {
        bool success = allowedRelayers.remove(_relayerAddr);
        if (!success) {
            revert NotExists();
        }
        emit RelayerAddressRemoved(_relayerAddr);
    }

    /// @inheritdoc IAnteHashTreeIndexV2
    function updateMerkleRoot(bytes32 root, string calldata dataUri) external {
        if (!allowedRelayers.contains(msg.sender)) {
            revert NotAllowed();
        }

        bytes32 prevRoot = latestMerkleRoot;

        latestMerkleRoot = root;
        latestDataUri = dataUri;
        latestRelayer = msg.sender;

        emit MerkleRootUpdated(msg.sender, prevRoot, root, dataUri);
    }

    /// @inheritdoc IAnteHashTreeIndexV2
    function isRelayerAllowed(address _relayerAddr) external view override returns (bool) {
        return allowedRelayers.contains(_relayerAddr);
    }

    /// @inheritdoc IAnteHashTreeIndexV2
    function getAllowedRelayers() external view override returns (address[] memory) {
        address[] memory allowedRelayerAddresses = new address[](allowedRelayers.length());
        for (uint256 i = 0; i < allowedRelayers.length(); i++) {
            allowedRelayerAddresses[i] = allowedRelayers.at(i);
        }
        return allowedRelayerAddresses;
    }

    /// @inheritdoc IAnteHashTreeIndexV2
    function verifyMerkleProof(
        bytes32 merkleRoot,
        bytes[] memory merkleProof,
        bytes memory hash
    )
        external
        pure
        returns (bool)
    {
        return MerklePatricia.verifyInclusionProof(merkleRoot, merkleProof, hash);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

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

// SPDX-License-Identifier: GPL-3.0-only

// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

pragma solidity 0.8.23;

/// @title Ante Hash Tree Index V2 smart contract
/// @notice Contract that stores the merkle roots of committed antes
interface IAnteHashTreeIndexV2 {
    /// @notice Error thrown when entry already exists
    error AlreadyExists();

    /// @notice Error thrown when entry does not exist
    error NotExists();

    /// @notice Error thrown when caller is not allowed to perform the action
    error NotAllowed();

    /// @notice Emitted when a new merkle root is written to the contract
    /// @param sender The address of the relayer that sent the update
    /// @param previousRoot The merkle root before this update
    /// @param newRoot The merkle root added to the ante hash tree index
    /// @param dataUri The data URI for the new root
    event MerkleRootUpdated(address indexed sender, bytes32 previousRoot, bytes32 newRoot, string dataUri);

    /// @notice Emitted when a relayer address is added to the whitelist
    /// @param relayerAddr The relayer address that was added
    event RelayerAddressAdded(address indexed relayerAddr);

    /// @notice Emitted when a relayer address is removed from the whitelist
    /// @param relayerAddr The relayer address that was removed
    event RelayerAddressRemoved(address indexed relayerAddr);

    /// @notice Adds the provided relayer address to the whitelist
    /// @param _relayerAddr The address of the relayer to add
    function addRelayer(address _relayerAddr) external;

    /// @notice Removes the provided relayer address from the whitelist
    /// @param _relayerAddr The address of the relayer to remove
    function removeRelayer(address _relayerAddr) external;

    /// @notice Updates the latest stored root and data URI
    /// @param root The new root of the Merkle Trie
    /// @param dataUri The new data URI containing data used for generating
    ///                the Merkle Trie
    function updateMerkleRoot(bytes32 root, string calldata dataUri) external;

    /// @notice Check if the provided relayer address exists in the whitelist
    /// @param _relayerAddr The address of the relayer to check
    /// @return true if the provided address is in the whitelist
    function isRelayerAllowed(address _relayerAddr) external view returns (bool);

    /// @notice Retrieves an array of all allowed relayer addresses
    /// @return A list of relayer addresses that are allowed to be update the merkle root
    function getAllowedRelayers() external view returns (address[] memory);

    /// @notice Verifies MPT merkle proof for a given key hash
    /// @param merkleRoot hash of the merkle patricia trie
    /// @param merkleProof list of proof nodes
    /// @param hash hash of a key to verify
    /// @return bool `true` if a given proof is valid for a given hash, `false` otherwise
    function verifyMerkleProof(
        bytes32 merkleRoot,
        bytes[] memory merkleProof,
        bytes memory hash
    )
        external
        pure
        returns (bool);
}

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

import {
    NodeKind,
    NodeHandle,
    Extension,
    Branch,
    NibbledBranch,
    ValueOption,
    NodeHandleOption,
    Leaf,
    TrieNode
} from "./trie/Node.sol";
import { Option } from "./trie/Option.sol";
import { NibbleSlice, NibbleSliceOps } from "./trie/NibbleSlice.sol";
import { TrieDB } from "./trie/TrieDB.sol";
import { EthereumTrieDB } from "./trie/ethereum/EthereumTrieDB.sol";

/**
 * @title A Merkle Patricia library
 * @author Polytope Labs
 * @dev Use this library to verify merkle patricia inclusion proofs
 */
library MerklePatricia {
    /// @notice libraries in solidity can only have constant variables
    /// @dev MAX_TRIE_DEPTH, we don't explore deeply nested trie keys.
    uint256 internal constant MAX_TRIE_DEPTH = 50;

    /**
     * @notice Verifies ethereum specific merkle patricia trie inclusion proofs as described by EIP-1186.
     *
     * @param root hash of the merkle patricia trie
     * @param proof list of proof nodes
     * @param key a key to verify
     *
     * @return bool `true` if a given proof is valid for a given key, `false` otherwise
     */
    function verifyInclusionProof(bytes32 root, bytes[] memory proof, bytes memory key) internal pure returns (bool) {
        TrieNode[] memory nodes = new TrieNode[](proof.length);
        for (uint256 i = 0; i < proof.length; i++) {
            nodes[i] = TrieNode(keccak256(proof[i]), proof[i]);
        }

        NibbleSlice memory keyNibbles = NibbleSlice(key, 0);
        NodeKind memory node = EthereumTrieDB.decodeNodeKind(TrieDB.get(nodes, root));

        // worst case scenario, so we avoid unbounded loops
        for (uint256 j = 0; j < MAX_TRIE_DEPTH; j++) {
            NodeHandle memory nextNode;

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

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

        return false;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 7 of 14 : Node.sol
// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

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

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

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

struct Extension {
    NibbleSlice key;
    NodeHandle node;
}

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

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

struct ValueOption {
    bool isSome;
    bytes value;
}

struct NodeHandleOption {
    bool isSome;
    NodeHandle value;
}

struct Leaf {
    NibbleSlice key;
    NodeHandle value;
}

struct TrieNode {
    bytes32 hash;
    bytes node;
}

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

import "./Node.sol";

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

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

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

struct NibbleSlice {
    bytes data;
    uint256 offset;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

        bytes memory tempBytes;

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

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

                for { let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

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

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

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

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

import "./Node.sol";

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

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

        return bytes("");
    }

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

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

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

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

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

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

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

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

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

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

    bytes constant HASHED_NULL_NODE = hex"56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421";

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

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

        return leaf;
    }

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

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

        NodeHandleOption[16] memory childrens;

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

        return branch;
    }

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

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

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

struct ByteSlice {
    bytes data;
    uint256 offset;
}

library Bytes {
    uint256 internal constant BYTES_HEADER_SIZE = 32;

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

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

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

        return b;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

        return substr(data, startIndex);
    }

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

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

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

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

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

        return outbytes;
    }
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.23;

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

    struct RLPItem {
        uint256 len;
        uint256 memPtr;
    }

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

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

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

        return RLPItem(itemLength, ptr);
    }

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

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

        return RLPItem(item.length, memPtr);
    }

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

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

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

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

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

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

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

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

        return result;
    }

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

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

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

    /*
     * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
     * @return keccak256 hash of RLP encoded bytes.
     */
    function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        uint256 ptr = item.memPtr;
        uint256 len = item.len;
        bytes32 result;
        assembly {
            result := keccak256(ptr, len)
        }
        return result;
    }

    /*
     * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
     * @return keccak256 hash of the item payload.
     */
    function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        (uint256 memPtr, uint256 len) = payloadLocation(item);
        bytes32 result;
        assembly {
            result := keccak256(memPtr, len)
        }
        return result;
    }

    /**
     * RLPItem conversions into data types *
     */

    // @returns raw rlp encoding in bytes
    function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
        bytes memory result = new bytes(item.len);
        if (result.length == 0) return result;

        uint256 ptr;
        assembly {
            ptr := add(0x20, result)
        }

        copy(item.memPtr, ptr, item.len);
        return result;
    }

    // any non-zero byte except "0x80" is considered true
    function toBoolean(RLPItem memory item) internal pure returns (bool) {
        require(item.len == 1);
        uint256 result;
        uint256 memPtr = item.memPtr;
        assembly {
            result := byte(0, mload(memPtr))
        }

        // SEE Github Issue #5.
        // Summary: Most commonly used RLP libraries (i.e Geth) will encode
        // "0" as "0x80" instead of as "0". We handle this edge case explicitly
        // here.
        if (result == 0 || result == STRING_SHORT_START) {
            return false;
        } else {
            return true;
        }
    }

    function toAddress(RLPItem memory item) internal pure returns (address) {
        // 1 byte for the length prefix
        require(item.len == 21);

        return address(uint160(toUint(item)));
    }

    function toUint(RLPItem memory item) internal pure returns (uint256) {
        require(item.len > 0 && item.len <= 33);

        (uint256 memPtr, uint256 len) = payloadLocation(item);

        uint256 result;
        assembly {
            result := mload(memPtr)

            // shift to the correct location if neccesary
            if lt(len, 32) { result := div(result, exp(256, sub(32, len))) }
        }

        return result;
    }

    // enforces 32 byte length
    function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
        // one byte prefix
        require(item.len == 33);

        uint256 result;
        uint256 memPtr = item.memPtr + 1;
        assembly {
            result := mload(memPtr)
        }

        return result;
    }

    function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
        require(item.len > 0);

        (uint256 memPtr, uint256 len) = payloadLocation(item);
        bytes memory result = new bytes(len);

        uint256 destPtr;
        assembly {
            destPtr := add(0x20, result)
        }

        copy(memPtr, destPtr, len);
        return result;
    }

    /*
     * Private Helpers
     */

    // @return number of payload items inside an encoded list.
    function numItems(RLPItem memory item) private pure returns (uint256) {
        if (item.len == 0) return 0;

        uint256 count = 0;
        uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 endPtr = item.memPtr + item.len;
        while (currPtr < endPtr) {
            currPtr = currPtr + _itemLength(currPtr); // skip over an item
            count++;
        }

        return count;
    }

    // @return entire rlp item byte length
    function _itemLength(uint256 memPtr) private pure returns (uint256) {
        uint256 itemLen;
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) {
            itemLen = 1;
        } else if (byte0 < STRING_LONG_START) {
            itemLen = byte0 - STRING_SHORT_START + 1;
        } else if (byte0 < LIST_SHORT_START) {
            assembly {
                let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                memPtr := add(memPtr, 1) // skip over the first byte

                /* 32 byte word size */
                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                itemLen := add(dataLen, add(byteLen, 1))
            }
        } else if (byte0 < LIST_LONG_START) {
            itemLen = byte0 - LIST_SHORT_START + 1;
        } else {
            assembly {
                let byteLen := sub(byte0, 0xf7)
                memPtr := add(memPtr, 1)

                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                itemLen := add(dataLen, add(byteLen, 1))
            }
        }

        return itemLen;
    }

    // @return number of bytes until the data
    function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) {
            return 0;
        } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) {
            return 1;
        } else if (byte0 < LIST_SHORT_START) {
            // being explicit
            return byte0 - (STRING_LONG_START - 1) + 1;
        } else {
            return byte0 - (LIST_LONG_START - 1) + 1;
        }
    }

    /*
     * @param src Pointer to source
     * @param dest Pointer to destination
     * @param len Amount of memory to copy from the source
     */
    function copy(uint256 src, uint256 dest, uint256 len) private pure {
        if (len == 0) return;

        // copy as many word sizes as possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }

            src += WORD_SIZE;
            dest += WORD_SIZE;
        }

        if (len > 0) {
            // left over bytes. Mask is used to remove unwanted bytes from the word
            uint256 mask = 256 ** (WORD_SIZE - len) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask)) // zero out src
                let destpart := and(mload(dest), mask) // retrieve the bytes
                mstore(dest, or(destpart, srcpart))
            }
        }
    }
}

// SPDX-License-Identifier: Apache2

pragma solidity 0.8.23;

library Memory {
    uint256 internal constant WORD_SIZE = 32;

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

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

    function equals(uint256 addr, uint256 len, bytes memory bts) internal pure returns (bool equal) {
        require(bts.length >= len);
        uint256 addr2;
        assembly {
            addr2 := add(bts, /*BYTES_HEADER_SIZE*/ 32)
        }
        return equals(addr, addr2, len);
    }

    // Returns a memory pointer to the data portion of the provided bytes array.
    function dataPtr(bytes memory bts) internal pure returns (uint256 addr) {
        assembly {
            addr := add(bts, /*BYTES_HEADER_SIZE*/ 32)
        }
    }

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

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

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

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

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

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "hardhat/=node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyExists","type":"error"},{"inputs":[],"name":"NotAllowed","type":"error"},{"inputs":[],"name":"NotExists","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bytes32","name":"previousRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"newRoot","type":"bytes32"},{"indexed":false,"internalType":"string","name":"dataUri","type":"string"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayerAddr","type":"address"}],"name":"RelayerAddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayerAddr","type":"address"}],"name":"RelayerAddressRemoved","type":"event"},{"inputs":[{"internalType":"address","name":"_relayerAddr","type":"address"}],"name":"addRelayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllowedRelayers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_relayerAddr","type":"address"}],"name":"isRelayerAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestDataUri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRelayer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_relayerAddr","type":"address"}],"name":"removeRelayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"string","name":"dataUri","type":"string"}],"name":"updateMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"bytes[]","name":"merkleProof","type":"bytes[]"},{"internalType":"bytes","name":"hash","type":"bytes"}],"name":"verifyMerkleProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]

60806040523480156200001157600080fd5b506040516200293f3803806200293f8339810160408190526200003491620000c7565b806001600160a01b0381166200006457604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006f8162000077565b5050620000f9565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215620000da57600080fd5b81516001600160a01b0381168114620000f257600080fd5b9392505050565b61283680620001096000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063dd39f00d1161005b578063dd39f00d146101d5578063e48034b5146101e8578063f2fde38b146101fb57600080fd5b80638da5cb5b1461018f578063ccae7f6b146101ad578063d98cbc8b146101c257600080fd5b806360f0a5ac116100b257806360f0a5ac1461015d5780636ac11d3b14610172578063715018a61461018757600080fd5b80632f850855146100d957806348ddb3f7146100f55780635e1c792a14610118575b600080fd5b6100e260035481565b6040519081526020015b60405180910390f35b610108610103366004612168565b61020e565b60405190151581526020016100ec565b6005546101389073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ec565b61017061016b366004612259565b610225565b005b61017a6102b8565b6040516100ec919061228f565b61017061036e565b60005473ffffffffffffffffffffffffffffffffffffffff16610138565b6101b5610382565b6040516100ec91906122e9565b6101086101d0366004612259565b610410565b6101706101e3366004612259565b610423565b6101706101f6366004612338565b6104b6565b610170610209366004612259565b61057e565b600061021b8484846105e7565b90505b9392505050565b61022d610914565b600061023a600183610967565b905080610273576040517f5861b41d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8316907ff0729ff1e41c7b30ab700244ba4e6e5cc18209a09f842e8bf1bab7762d0ef3dc90600090a25050565b606060006102c66001610989565b67ffffffffffffffff8111156102de576102de612098565b604051908082528060200260200182016040528015610307578160200160208202803683370190505b50905060005b6103176001610989565b81101561036857610329600182610993565b82828151811061033b5761033b6123b4565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015260010161030d565b50919050565b610376610914565b610380600061099f565b565b6004805461038f906123e3565b80601f01602080910402602001604051908101604052809291908181526020018280546103bb906123e3565b80156104085780601f106103dd57610100808354040283529160200191610408565b820191906000526020600020905b8154815290600101906020018083116103eb57829003601f168201915b505050505081565b600061041d600183610a14565b92915050565b61042b610914565b6000610438600183610a43565b905080610471576040517f23369fa600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8316907f0bb142cb3e51924e21a59a9e18d61a54e960bce50ef144e74111318e2920c36e90600090a25050565b6104c1600133610a14565b6104f7576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805490849055600461050c838583612480565b50600580547fffffffffffffffffffffffff000000000000000000000000000000000000000016339081179091556040517fa73fd016054ff5ed7be8ea41f468626decc3f4b3181ec243120b6cfc393a4fb19061057090849088908890889061257d565b60405180910390a250505050565b610586610914565b73ffffffffffffffffffffffffffffffffffffffff81166105db576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b6105e48161099f565b50565b600080835167ffffffffffffffff81111561060457610604612098565b60405190808252806020026020018201604052801561064a57816020015b6040805180820190915260008152606060208201528152602001906001900390816106225790505b50905060005b84518110156106cc576040518060400160405280868381518110610676576106766123b4565b602002602001015180519060200120815260200186838151811061069c5761069c6123b4565b60200260200101518152508282815181106106b9576106b96123b4565b6020908102919091010152600101610650565b50604080518082019091528381526000602082018190526106f56106f08489610a65565b610b28565b905060005b603281101561090657604080516080810182526000808252602082018190529181019190915260608082015261072f83610d9a565b156107dd57600061073f84610daf565b905060006002866020015161075491906125e9565b1561077a576002866020015161076a919061262c565b610775906001612640565b61078b565b6002866020015161078b919061262c565b905060405180604001604052806107a6886000015184610e7b565b8152602001600081525095506107c0826000015187610f6b565b156107d557600197505050505050505061021e565b505050610906565b60c0830151156108515760006107f284610f91565b905061080285826000015161106a565b1561084457604051806040016040528061082c87600001516108278560000151611087565b610e7b565b8152602001600081525094508060200151915061084b565b5050610906565b506108ee565b60e0830151156108e1576000610866846110aa565b9050610871856112ca565b15610891578051511515600103610844576001965050505050505061021e565b600081602001516108a38760006112dc565b601081106108b3576108b36123b4565b6020020151805190915015156001036107d5576108d1866001611366565b95508060200151925050506108ee565b8251156108ee5750610906565b6108fb6106f086836113a9565b9250506001016106fa565b506000979650505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610380576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016105d2565b600061021e8373ffffffffffffffffffffffffffffffffffffffff84166113f2565b600061041d825490565b600061021e83836114ec565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561021e565b600061021e8373ffffffffffffffffffffffffffffffffffffffff8416611516565b606060005b8351811015610ac55782848281518110610a8657610a866123b4565b60200260200101516000015103610abd57838181518110610aa957610aa96123b4565b60200260200101516020015191505061041d565b600101610a6a565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e636f6d706c6574652050726f6f662100000000000000000000000000000060448201526064016105d2565b610b8c60408051610140810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905260c0840183905260e084018390526101008401839052845180860190955284528301529061012082015290565b610bf060408051610140810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905260c0840183905260e084018390526101008401839052845180860190955284528301529061012082015290565b6000604051806040016040528085815260200160008152509050610c49846040518060400160405280602081526020017f56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421815250611565565b15610c5957506001815292915050565b6000610c94610c8f8660408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b611595565b80519091506000819003610caf575050600182525092915050565b80600203610d16576000610cdc83600081518110610ccf57610ccf6123b4565b60200260200101516116a1565b602081015190915060fc1c6002811480610cf65750806003145b15610d075760016020870152610d0f565b600160c08701525b5050610d8c565b80601103610d2a57600160e0850152610d8c565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f496e76616c69642064617461000000000000000000000000000000000000000060448201526064016105d2565b505061012082015292915050565b600081602001518061041d5750506040015190565b610db7611fa9565b610dbf611fa9565b610120830151516040805180820182526000808252602091820181905282518084019093528351835292810190820152610df890611595565b90506000610e1282600181518110610ccf57610ccf6123b4565b90506040518060400160405280610e3f610e3885600081518110610ccf57610ccf6123b4565b600161171f565b81526000602091820181905291855260408051608081018252838152808301939093526001908301526060820192909252908301525092915050565b81516060906000610e8c8483612653565b905083821015610ef8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016105d2565b606081158015610f175760405191506000825260208201604052610f61565b6040519150601f8316801560200281840101848101888315602002848c0101015b81831015610f50578051835260209283019201610f38565b5050848452601f01601f1916604052505b5095945050505050565b6000610f7682611087565b610f7f84611087565b14801561021e575061021e838361106a565b610f99611fa9565b610fa1611fa9565b610120830151516040805180820182526000808252602091820181905282518084019093528351835292810190820152610fda90611595565b90506000610ff482600181518110610ccf57610ccf6123b4565b9050604051806040016040528061101a610e3885600081518110610ccf57610ccf6123b4565b8152600060209182015290845260408051608081019091526001815290810161104283611757565b8152600060208083018290526040805192835282820181529092015284015250909392505050565b600061107582611087565b61107f84846117f2565b149392505050565b6000816020015160028360000151516110a09190612666565b61041d9190612653565b6110b2611ffa565b6110ba611ffa565b6101208301515160408051808201825260008082526020918201819052825180840190935283518352928101908201526110f390611595565b90506110fd612039565b60005b60108110156111fb576000611120848381518110610ccf57610ccf6123b4565b9050805160201461118857604080518082018252600080825282516080810184528181526020808201839052818501839052845192835282810190945260608101919091529181019190915283836010811061117e5761117e6123b4565b60200201526111f2565b600061119382611757565b604080518082018252600180825282516080810184529081526020808201859052600082850181905284519081528082019094526060820193909352918101919091529091508484601081106111eb576111eb6123b4565b6020020152505b50600101611100565b5061121a61121583601081518110610ccf57610ccf6123b4565b6119a8565b1561126757604080518082018252600080825282516080810184528181526020808201839052818501839052845192835282810190945260608101919091529181019190915283526112be565b604051806040016040528060011515815260200160405180608001604052806000151581526020016000801b81526020016001151581526020016112b786601081518110610ccf57610ccf6123b4565b9052905283525b60208301525092915050565b60006112d582611087565b1592915050565b60008060028385602001516112f19190612640565b6112fb919061262c565b9050600060028486602001516113119190612640565b61131b91906125e9565b9050600085600001518381518110611335576113356123b4565b016020015160f81c9050600182146113545760048160ff16901c611359565b80600f165b60ff169695505050505050565b6040805180820190915260608152600060208201526040518060400160405280846000015181526020018385602001516113a09190612640565b90529392505050565b60608160400151156113c05750606081015161041d565b8151156113dc576113d5838360200151610a65565b905061041d565b5060408051602081019091526000815292915050565b600081815260018301602052604081205480156114db576000611416600183612653565b855490915060009061142a90600190612653565b905080821461148f57600086600001828154811061144a5761144a6123b4565b906000526020600020015490508087600001848154811061146d5761146d6123b4565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806114a0576114a061267d565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061041d565b600091505061041d565b5092915050565b6000826000018281548110611503576115036123b4565b9060005260206000200154905092915050565b600081815260018301602052604081205461155d5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561041d565b50600061041d565b600081518351146115785750600061041d565b825160208381018281209186019283209091145b95945050505050565b60606115a082611a7b565b6115a957600080fd5b60006115b483611ab6565b905060008167ffffffffffffffff8111156115d1576115d1612098565b60405190808252806020026020018201604052801561161657816020015b60408051808201909152600080825260208201528152602001906001900390816115ef5790505b50905060006116288560200151611b3b565b85602001516116379190612640565b90506000805b848110156116965761164e83611bb6565b9150604051806040016040528083815260200184815250848281518110611677576116776123b4565b602090810291909101015261168c8284612640565b925060010161163d565b509195945050505050565b80516060906116af57600080fd5b6000806116bb84611c78565b9150915060008167ffffffffffffffff8111156116da576116da612098565b6040519080825280601f01601f191660200182016040528015611704576020820181803683370190505b50905060208101611716848285611cbf565b50949350505050565b6060825182111561172f57600080fd5b600082845161173e9190612653565b90506020840161158c6117518583612640565b83611d42565b60006020825110156117ea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f42797465733a3a20746f427974657333323a206461746120697320746f20736860448201527f6f72742e0000000000000000000000000000000000000000000000000000000060648201526084016105d2565b506020015190565b6000806002846020015161180691906125e9565b905060006002846020015161181b91906125e9565b905080820361194b57600060028660200151611837919061262c565b905060006002866020015161184c919061262c565b90506000841561190457865180518390811061186a5761186a6123b4565b0160200151885180517f0f0000000000000000000000000000000000000000000000000000000000000090921691859081106118a8576118a86123b4565b01602001517f0f0000000000000000000000000000000000000000000000000000000000000016146118e25760009550505050505061041d565b6118eb836126ac565b92506118f6826126ac565b9150611901816126ac565b90505b6000611914896000015185610e7b565b90506000611926896000015185610e7b565b9050826119338383611d99565b61193d9190612640565b97505050505050505061041d565b600061196761195987611087565b61196287611087565b611ec9565b905060005b8181101561199d5761197e86826112dc565b61198888836112dc565b0361199d57611996816126ac565b905061196c565b935061041d92505050565b600080825111801561041d5750816000815181106119c8576119c86123b4565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167fc000000000000000000000000000000000000000000000000000000000000000148061041d575081600081518110611a2b57611a2b6123b4565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f80000000000000000000000000000000000000000000000000000000000000001492915050565b80516000908103611a8e57506000919050565b6020820151805160001a9060c0821015611aac575060009392505050565b5060019392505050565b80516000908103611ac957506000919050565b600080611ad98460200151611b3b565b8460200151611ae89190612640565b9050600084600001518560200151611b009190612640565b90505b80821015611b3257611b1482611bb6565b611b1e9083612640565b915082611b2a816126ac565b935050611b03565b50909392505050565b8051600090811a6080811015611b545750600092915050565b60b8811080611b6f575060c08110801590611b6f575060f881105b15611b7d5750600192915050565b60c0811015611baa57611b92600160b86126e4565b611b9f9060ff1682612653565b61021e906001612640565b611b92600160f86126e4565b80516000908190811a6080811015611bd157600191506114e5565b60b8811015611bf757611be5608082612653565b611bf0906001612640565b91506114e5565b60c0811015611c245760b78103600185019450806020036101000a855104600182018101935050506114e5565b60f8811015611c3857611be560c082612653565b60019390930151602084900360f7016101000a90049092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a0192915050565b6000806000611c8a8460200151611b3b565b90506000818560200151611c9e9190612640565b90506000828660000151611cb29190612653565b9196919550909350505050565b80600003611ccc57505050565b60208110611d045782518252611ce3602084612640565b9250611cf0602083612640565b9150611cfd602082612653565b9050611ccc565b8015611d3d5760006001611d19836020612653565b611d259061010061281d565b611d2f9190612653565b845184518216911916178352505b505050565b60608167ffffffffffffffff811115611d5d57611d5d612098565b6040519080825280601f01601f191660200182016040528015611d87576020820181803683370190505b509050602081016114e5848285611edf565b600080611da884518451611ec9565b905060005b81811015611ebe57838181518110611dc757611dc76123b4565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916858281518110611e0657611e066123b4565b01602001517fff000000000000000000000000000000000000000000000000000000000000001614611eae57611e90858281518110611e4757611e476123b4565b602001015160f81c60f81b858381518110611e6457611e646123b4565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016611f4d565b611e9b600283612666565b611ea59190612640565b9250505061041d565b611eb7816126ac565b9050611dad565b61158c600282612666565b6000818310611ed8578161021e565b5090919050565b60208110611f175782518252611ef6602083612640565b9150611f03602084612640565b9250611f10602082612653565b9050611edf565b60008115611f47576001611f2c836020612653565b611f389061010061281d565b611f429190612653565b611d2f565b50505050565b60007fff0000000000000000000000000000000000000000000000000000000000000080831690841603611f835750600261041d565b8160f81c60f01660ff168360f81c60f01660ff160361155d5750600161041d565b905290565b6040518060400160405280611fd1604051806040016040528060608152602001600081525090565b815260408051608081018252600080825260208281018290529282015260608082015291015290565b6040805160808082018352600082840181815284519283018552818352602083018290529382015260608082018190528201529081908152602001611fa45b6040518061020001604052806010905b61208260408051808201825260008082528251608081018452818152602081810183905293810191909152606080820152909182015290565b8152602001906001900390816120495790505090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156120f0576120f0612098565b604052919050565b600082601f83011261210957600080fd5b813567ffffffffffffffff81111561212357612123612098565b6121366020601f19601f840116016120c7565b81815284602083860101111561214b57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060006060848603121561217d57600080fd5b8335925060208085013567ffffffffffffffff8082111561219d57600080fd5b818701915087601f8301126121b157600080fd5b8135818111156121c3576121c3612098565b8060051b6121d28582016120c7565b918252838101850191858101908b8411156121ec57600080fd5b86860192505b838310156122285782358581111561220a5760008081fd5b6122188d89838a01016120f8565b83525091860191908601906121f2565b9750505050604087013592508083111561224157600080fd5b505061224f868287016120f8565b9150509250925092565b60006020828403121561226b57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461021e57600080fd5b6020808252825182820181905260009190848201906040850190845b818110156122dd57835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016122ab565b50909695505050505050565b60006020808352835180602085015260005b81811015612317578581018301518582016040015282016122fb565b506000604082860101526040601f19601f8301168501019250505092915050565b60008060006040848603121561234d57600080fd5b83359250602084013567ffffffffffffffff8082111561236c57600080fd5b818601915086601f83011261238057600080fd5b81358181111561238f57600080fd5b8760208285010111156123a157600080fd5b6020830194508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600181811c908216806123f757607f821691505b602082108103610368577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b601f821115611d3d576000816000526020600020601f850160051c810160208610156124595750805b601f850160051c820191505b8181101561247857828155600101612465565b505050505050565b67ffffffffffffffff83111561249857612498612098565b6124ac836124a683546123e3565b83612430565b6000601f8411600181146124fe57600085156124c85750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355612576565b600083815260209020601f19861690835b8281101561252f578685013582556020948501946001909201910161250f565b508682101561256a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b84815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826125f8576125f86125ba565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008261263b5761263b6125ba565b500490565b8082018082111561041d5761041d6125fd565b8181038181111561041d5761041d6125fd565b808202811582820484141761041d5761041d6125fd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036126dd576126dd6125fd565b5060010190565b60ff828116828216039081111561041d5761041d6125fd565b600181815b8085111561275657817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561273c5761273c6125fd565b8085161561274957918102915b93841c9390800290612702565b509250929050565b60008261276d5750600161041d565b8161277a5750600061041d565b8160018114612790576002811461279a576127b6565b600191505061041d565b60ff8411156127ab576127ab6125fd565b50506001821b61041d565b5060208310610133831016604e8410600b84101617156127d9575081810a61041d565b6127e383836126fd565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612815576128156125fd565b029392505050565b600061021e838361275e56fea164736f6c6343000817000a0000000000000000000000007df0808cfd89ea3995af99cb1374d2907c2399b6

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100d45760003560e01c80638da5cb5b11610081578063dd39f00d1161005b578063dd39f00d146101d5578063e48034b5146101e8578063f2fde38b146101fb57600080fd5b80638da5cb5b1461018f578063ccae7f6b146101ad578063d98cbc8b146101c257600080fd5b806360f0a5ac116100b257806360f0a5ac1461015d5780636ac11d3b14610172578063715018a61461018757600080fd5b80632f850855146100d957806348ddb3f7146100f55780635e1c792a14610118575b600080fd5b6100e260035481565b6040519081526020015b60405180910390f35b610108610103366004612168565b61020e565b60405190151581526020016100ec565b6005546101389073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ec565b61017061016b366004612259565b610225565b005b61017a6102b8565b6040516100ec919061228f565b61017061036e565b60005473ffffffffffffffffffffffffffffffffffffffff16610138565b6101b5610382565b6040516100ec91906122e9565b6101086101d0366004612259565b610410565b6101706101e3366004612259565b610423565b6101706101f6366004612338565b6104b6565b610170610209366004612259565b61057e565b600061021b8484846105e7565b90505b9392505050565b61022d610914565b600061023a600183610967565b905080610273576040517f5861b41d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8316907ff0729ff1e41c7b30ab700244ba4e6e5cc18209a09f842e8bf1bab7762d0ef3dc90600090a25050565b606060006102c66001610989565b67ffffffffffffffff8111156102de576102de612098565b604051908082528060200260200182016040528015610307578160200160208202803683370190505b50905060005b6103176001610989565b81101561036857610329600182610993565b82828151811061033b5761033b6123b4565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015260010161030d565b50919050565b610376610914565b610380600061099f565b565b6004805461038f906123e3565b80601f01602080910402602001604051908101604052809291908181526020018280546103bb906123e3565b80156104085780601f106103dd57610100808354040283529160200191610408565b820191906000526020600020905b8154815290600101906020018083116103eb57829003601f168201915b505050505081565b600061041d600183610a14565b92915050565b61042b610914565b6000610438600183610a43565b905080610471576040517f23369fa600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8316907f0bb142cb3e51924e21a59a9e18d61a54e960bce50ef144e74111318e2920c36e90600090a25050565b6104c1600133610a14565b6104f7576040517f3d693ada00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805490849055600461050c838583612480565b50600580547fffffffffffffffffffffffff000000000000000000000000000000000000000016339081179091556040517fa73fd016054ff5ed7be8ea41f468626decc3f4b3181ec243120b6cfc393a4fb19061057090849088908890889061257d565b60405180910390a250505050565b610586610914565b73ffffffffffffffffffffffffffffffffffffffff81166105db576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b6105e48161099f565b50565b600080835167ffffffffffffffff81111561060457610604612098565b60405190808252806020026020018201604052801561064a57816020015b6040805180820190915260008152606060208201528152602001906001900390816106225790505b50905060005b84518110156106cc576040518060400160405280868381518110610676576106766123b4565b602002602001015180519060200120815260200186838151811061069c5761069c6123b4565b60200260200101518152508282815181106106b9576106b96123b4565b6020908102919091010152600101610650565b50604080518082019091528381526000602082018190526106f56106f08489610a65565b610b28565b905060005b603281101561090657604080516080810182526000808252602082018190529181019190915260608082015261072f83610d9a565b156107dd57600061073f84610daf565b905060006002866020015161075491906125e9565b1561077a576002866020015161076a919061262c565b610775906001612640565b61078b565b6002866020015161078b919061262c565b905060405180604001604052806107a6886000015184610e7b565b8152602001600081525095506107c0826000015187610f6b565b156107d557600197505050505050505061021e565b505050610906565b60c0830151156108515760006107f284610f91565b905061080285826000015161106a565b1561084457604051806040016040528061082c87600001516108278560000151611087565b610e7b565b8152602001600081525094508060200151915061084b565b5050610906565b506108ee565b60e0830151156108e1576000610866846110aa565b9050610871856112ca565b15610891578051511515600103610844576001965050505050505061021e565b600081602001516108a38760006112dc565b601081106108b3576108b36123b4565b6020020151805190915015156001036107d5576108d1866001611366565b95508060200151925050506108ee565b8251156108ee5750610906565b6108fb6106f086836113a9565b9250506001016106fa565b506000979650505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610380576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016105d2565b600061021e8373ffffffffffffffffffffffffffffffffffffffff84166113f2565b600061041d825490565b600061021e83836114ec565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561021e565b600061021e8373ffffffffffffffffffffffffffffffffffffffff8416611516565b606060005b8351811015610ac55782848281518110610a8657610a866123b4565b60200260200101516000015103610abd57838181518110610aa957610aa96123b4565b60200260200101516020015191505061041d565b600101610a6a565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e636f6d706c6574652050726f6f662100000000000000000000000000000060448201526064016105d2565b610b8c60408051610140810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905260c0840183905260e084018390526101008401839052845180860190955284528301529061012082015290565b610bf060408051610140810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905260c0840183905260e084018390526101008401839052845180860190955284528301529061012082015290565b6000604051806040016040528085815260200160008152509050610c49846040518060400160405280602081526020017f56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421815250611565565b15610c5957506001815292915050565b6000610c94610c8f8660408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b611595565b80519091506000819003610caf575050600182525092915050565b80600203610d16576000610cdc83600081518110610ccf57610ccf6123b4565b60200260200101516116a1565b602081015190915060fc1c6002811480610cf65750806003145b15610d075760016020870152610d0f565b600160c08701525b5050610d8c565b80601103610d2a57600160e0850152610d8c565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f496e76616c69642064617461000000000000000000000000000000000000000060448201526064016105d2565b505061012082015292915050565b600081602001518061041d5750506040015190565b610db7611fa9565b610dbf611fa9565b610120830151516040805180820182526000808252602091820181905282518084019093528351835292810190820152610df890611595565b90506000610e1282600181518110610ccf57610ccf6123b4565b90506040518060400160405280610e3f610e3885600081518110610ccf57610ccf6123b4565b600161171f565b81526000602091820181905291855260408051608081018252838152808301939093526001908301526060820192909252908301525092915050565b81516060906000610e8c8483612653565b905083821015610ef8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016105d2565b606081158015610f175760405191506000825260208201604052610f61565b6040519150601f8316801560200281840101848101888315602002848c0101015b81831015610f50578051835260209283019201610f38565b5050848452601f01601f1916604052505b5095945050505050565b6000610f7682611087565b610f7f84611087565b14801561021e575061021e838361106a565b610f99611fa9565b610fa1611fa9565b610120830151516040805180820182526000808252602091820181905282518084019093528351835292810190820152610fda90611595565b90506000610ff482600181518110610ccf57610ccf6123b4565b9050604051806040016040528061101a610e3885600081518110610ccf57610ccf6123b4565b8152600060209182015290845260408051608081019091526001815290810161104283611757565b8152600060208083018290526040805192835282820181529092015284015250909392505050565b600061107582611087565b61107f84846117f2565b149392505050565b6000816020015160028360000151516110a09190612666565b61041d9190612653565b6110b2611ffa565b6110ba611ffa565b6101208301515160408051808201825260008082526020918201819052825180840190935283518352928101908201526110f390611595565b90506110fd612039565b60005b60108110156111fb576000611120848381518110610ccf57610ccf6123b4565b9050805160201461118857604080518082018252600080825282516080810184528181526020808201839052818501839052845192835282810190945260608101919091529181019190915283836010811061117e5761117e6123b4565b60200201526111f2565b600061119382611757565b604080518082018252600180825282516080810184529081526020808201859052600082850181905284519081528082019094526060820193909352918101919091529091508484601081106111eb576111eb6123b4565b6020020152505b50600101611100565b5061121a61121583601081518110610ccf57610ccf6123b4565b6119a8565b1561126757604080518082018252600080825282516080810184528181526020808201839052818501839052845192835282810190945260608101919091529181019190915283526112be565b604051806040016040528060011515815260200160405180608001604052806000151581526020016000801b81526020016001151581526020016112b786601081518110610ccf57610ccf6123b4565b9052905283525b60208301525092915050565b60006112d582611087565b1592915050565b60008060028385602001516112f19190612640565b6112fb919061262c565b9050600060028486602001516113119190612640565b61131b91906125e9565b9050600085600001518381518110611335576113356123b4565b016020015160f81c9050600182146113545760048160ff16901c611359565b80600f165b60ff169695505050505050565b6040805180820190915260608152600060208201526040518060400160405280846000015181526020018385602001516113a09190612640565b90529392505050565b60608160400151156113c05750606081015161041d565b8151156113dc576113d5838360200151610a65565b905061041d565b5060408051602081019091526000815292915050565b600081815260018301602052604081205480156114db576000611416600183612653565b855490915060009061142a90600190612653565b905080821461148f57600086600001828154811061144a5761144a6123b4565b906000526020600020015490508087600001848154811061146d5761146d6123b4565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806114a0576114a061267d565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061041d565b600091505061041d565b5092915050565b6000826000018281548110611503576115036123b4565b9060005260206000200154905092915050565b600081815260018301602052604081205461155d5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561041d565b50600061041d565b600081518351146115785750600061041d565b825160208381018281209186019283209091145b95945050505050565b60606115a082611a7b565b6115a957600080fd5b60006115b483611ab6565b905060008167ffffffffffffffff8111156115d1576115d1612098565b60405190808252806020026020018201604052801561161657816020015b60408051808201909152600080825260208201528152602001906001900390816115ef5790505b50905060006116288560200151611b3b565b85602001516116379190612640565b90506000805b848110156116965761164e83611bb6565b9150604051806040016040528083815260200184815250848281518110611677576116776123b4565b602090810291909101015261168c8284612640565b925060010161163d565b509195945050505050565b80516060906116af57600080fd5b6000806116bb84611c78565b9150915060008167ffffffffffffffff8111156116da576116da612098565b6040519080825280601f01601f191660200182016040528015611704576020820181803683370190505b50905060208101611716848285611cbf565b50949350505050565b6060825182111561172f57600080fd5b600082845161173e9190612653565b90506020840161158c6117518583612640565b83611d42565b60006020825110156117ea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f42797465733a3a20746f427974657333323a206461746120697320746f20736860448201527f6f72742e0000000000000000000000000000000000000000000000000000000060648201526084016105d2565b506020015190565b6000806002846020015161180691906125e9565b905060006002846020015161181b91906125e9565b905080820361194b57600060028660200151611837919061262c565b905060006002866020015161184c919061262c565b90506000841561190457865180518390811061186a5761186a6123b4565b0160200151885180517f0f0000000000000000000000000000000000000000000000000000000000000090921691859081106118a8576118a86123b4565b01602001517f0f0000000000000000000000000000000000000000000000000000000000000016146118e25760009550505050505061041d565b6118eb836126ac565b92506118f6826126ac565b9150611901816126ac565b90505b6000611914896000015185610e7b565b90506000611926896000015185610e7b565b9050826119338383611d99565b61193d9190612640565b97505050505050505061041d565b600061196761195987611087565b61196287611087565b611ec9565b905060005b8181101561199d5761197e86826112dc565b61198888836112dc565b0361199d57611996816126ac565b905061196c565b935061041d92505050565b600080825111801561041d5750816000815181106119c8576119c86123b4565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167fc000000000000000000000000000000000000000000000000000000000000000148061041d575081600081518110611a2b57611a2b6123b4565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f80000000000000000000000000000000000000000000000000000000000000001492915050565b80516000908103611a8e57506000919050565b6020820151805160001a9060c0821015611aac575060009392505050565b5060019392505050565b80516000908103611ac957506000919050565b600080611ad98460200151611b3b565b8460200151611ae89190612640565b9050600084600001518560200151611b009190612640565b90505b80821015611b3257611b1482611bb6565b611b1e9083612640565b915082611b2a816126ac565b935050611b03565b50909392505050565b8051600090811a6080811015611b545750600092915050565b60b8811080611b6f575060c08110801590611b6f575060f881105b15611b7d5750600192915050565b60c0811015611baa57611b92600160b86126e4565b611b9f9060ff1682612653565b61021e906001612640565b611b92600160f86126e4565b80516000908190811a6080811015611bd157600191506114e5565b60b8811015611bf757611be5608082612653565b611bf0906001612640565b91506114e5565b60c0811015611c245760b78103600185019450806020036101000a855104600182018101935050506114e5565b60f8811015611c3857611be560c082612653565b60019390930151602084900360f7016101000a90049092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a0192915050565b6000806000611c8a8460200151611b3b565b90506000818560200151611c9e9190612640565b90506000828660000151611cb29190612653565b9196919550909350505050565b80600003611ccc57505050565b60208110611d045782518252611ce3602084612640565b9250611cf0602083612640565b9150611cfd602082612653565b9050611ccc565b8015611d3d5760006001611d19836020612653565b611d259061010061281d565b611d2f9190612653565b845184518216911916178352505b505050565b60608167ffffffffffffffff811115611d5d57611d5d612098565b6040519080825280601f01601f191660200182016040528015611d87576020820181803683370190505b509050602081016114e5848285611edf565b600080611da884518451611ec9565b905060005b81811015611ebe57838181518110611dc757611dc76123b4565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916858281518110611e0657611e066123b4565b01602001517fff000000000000000000000000000000000000000000000000000000000000001614611eae57611e90858281518110611e4757611e476123b4565b602001015160f81c60f81b858381518110611e6457611e646123b4565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016611f4d565b611e9b600283612666565b611ea59190612640565b9250505061041d565b611eb7816126ac565b9050611dad565b61158c600282612666565b6000818310611ed8578161021e565b5090919050565b60208110611f175782518252611ef6602083612640565b9150611f03602084612640565b9250611f10602082612653565b9050611edf565b60008115611f47576001611f2c836020612653565b611f389061010061281d565b611f429190612653565b611d2f565b50505050565b60007fff0000000000000000000000000000000000000000000000000000000000000080831690841603611f835750600261041d565b8160f81c60f01660ff168360f81c60f01660ff160361155d5750600161041d565b905290565b6040518060400160405280611fd1604051806040016040528060608152602001600081525090565b815260408051608081018252600080825260208281018290529282015260608082015291015290565b6040805160808082018352600082840181815284519283018552818352602083018290529382015260608082018190528201529081908152602001611fa45b6040518061020001604052806010905b61208260408051808201825260008082528251608081018452818152602081810183905293810191909152606080820152909182015290565b8152602001906001900390816120495790505090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156120f0576120f0612098565b604052919050565b600082601f83011261210957600080fd5b813567ffffffffffffffff81111561212357612123612098565b6121366020601f19601f840116016120c7565b81815284602083860101111561214b57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060006060848603121561217d57600080fd5b8335925060208085013567ffffffffffffffff8082111561219d57600080fd5b818701915087601f8301126121b157600080fd5b8135818111156121c3576121c3612098565b8060051b6121d28582016120c7565b918252838101850191858101908b8411156121ec57600080fd5b86860192505b838310156122285782358581111561220a5760008081fd5b6122188d89838a01016120f8565b83525091860191908601906121f2565b9750505050604087013592508083111561224157600080fd5b505061224f868287016120f8565b9150509250925092565b60006020828403121561226b57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461021e57600080fd5b6020808252825182820181905260009190848201906040850190845b818110156122dd57835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016122ab565b50909695505050505050565b60006020808352835180602085015260005b81811015612317578581018301518582016040015282016122fb565b506000604082860101526040601f19601f8301168501019250505092915050565b60008060006040848603121561234d57600080fd5b83359250602084013567ffffffffffffffff8082111561236c57600080fd5b818601915086601f83011261238057600080fd5b81358181111561238f57600080fd5b8760208285010111156123a157600080fd5b6020830194508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600181811c908216806123f757607f821691505b602082108103610368577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b601f821115611d3d576000816000526020600020601f850160051c810160208610156124595750805b601f850160051c820191505b8181101561247857828155600101612465565b505050505050565b67ffffffffffffffff83111561249857612498612098565b6124ac836124a683546123e3565b83612430565b6000601f8411600181146124fe57600085156124c85750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355612576565b600083815260209020601f19861690835b8281101561252f578685013582556020948501946001909201910161250f565b508682101561256a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b84815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826125f8576125f86125ba565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008261263b5761263b6125ba565b500490565b8082018082111561041d5761041d6125fd565b8181038181111561041d5761041d6125fd565b808202811582820484141761041d5761041d6125fd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036126dd576126dd6125fd565b5060010190565b60ff828116828216039081111561041d5761041d6125fd565b600181815b8085111561275657817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561273c5761273c6125fd565b8085161561274957918102915b93841c9390800290612702565b509250929050565b60008261276d5750600161041d565b8161277a5750600061041d565b8160018114612790576002811461279a576127b6565b600191505061041d565b60ff8411156127ab576127ab6125fd565b50506001821b61041d565b5060208310610133831016604e8410600b84101617156127d9575081810a61041d565b6127e383836126fd565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612815576128156125fd565b029392505050565b600061021e838361275e56fea164736f6c6343000817000a

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

0000000000000000000000007df0808cfd89ea3995af99cb1374d2907c2399b6

-----Decoded View---------------
Arg [0] : initialOwner (address): 0x7DF0808CFD89Ea3995af99CB1374D2907C2399b6

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000007df0808cfd89ea3995af99cb1374d2907c2399b6


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
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.