Holesky Testnet

Contract

0xE8EC2D7FE265c6e5c1850D43f1b1d2D03567E216

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Deposit27453272024-11-15 13:18:24124 days ago1731676704IN
0xE8EC2D7F...03567E216
0.5 ETH0.000003070.05480183
Activate27451862024-11-15 12:47:48124 days ago1731674868IN
0xE8EC2D7F...03567E216
0.01 ETH0.000006580.05592023
Set Tunnel Id27451862024-11-15 12:47:48124 days ago1731674868IN
0xE8EC2D7F...03567E216
0 ETH0.000002560.05592023

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer*27453272024-11-15 13:18:24124 days ago1731676704
0xE8EC2D7F...03567E216
0.5 ETH
Transfer*27451862024-11-15 12:47:48124 days ago1731674868
0xE8EC2D7F...03567E216
0.01 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PacketConsumer

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 12 : PacketConsumer.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

import "@openzeppelin/contracts/access/Ownable2Step.sol";

import "./interfaces/IDataConsumer.sol";
import "./interfaces/ITunnelRouter.sol";
import "./interfaces/IVault.sol";

import "./libraries/PacketDecoder.sol";
import "./libraries/Address.sol";
import "./TssVerifier.sol";

contract PacketConsumer is IDataConsumer, Ownable2Step {
    // An object that contains the price of a signal Id.
    struct Price {
        uint64 price;
        int64 timestamp;
    }

    // The tunnel router contract.
    address public immutable tunnelRouter;
    // The hashed source chain Id that this contract is consuming from.
    bytes32 public immutable hashedSourceChainId;
    // The hashed target chain Id that this contract is at.
    bytes32 public immutable hashedTargetChainId;

    // The tunnel Id that this contract is consuming; cannot be immutable or else the create2
    // will result in different address.
    uint64 public tunnelId;
    // Mapping between a signal Id and its corresponding latest price object.
    mapping(bytes32 => Price) public prices;

    modifier onlyTunnelRouter() {
        if (msg.sender != tunnelRouter) {
            revert UnauthorizedTunnelRouter();
        }
        _;
    }

    constructor(
        address tunnelRouter_,
        bytes32 hashedSourceChainId_,
        bytes32 hashedTargetChainId_,
        address initialOwner
    ) Ownable(initialOwner) {
        hashedSourceChainId = hashedSourceChainId_;
        hashedTargetChainId = hashedTargetChainId_;
        tunnelRouter = tunnelRouter_;
    }

    /**
     * @dev See {IDataConsumer-process}.
     */
    function process(
        PacketDecoder.TssMessage memory data
    ) external onlyTunnelRouter {
        if (data.hashOriginator != hashOriginator()) {
            revert InvalidHashOriginator();
        }

        PacketDecoder.Packet memory packet = data.packet;
        for (uint256 i = 0; i < packet.signals.length; i++) {
            prices[packet.signals[i].signal] = Price({
                price: packet.signals[i].price,
                timestamp: packet.timestamp
            });

            emit SignalPriceUpdated(
                packet.signals[i].signal,
                packet.signals[i].price,
                packet.timestamp
            );
        }
    }

    /**
     * @dev See {IDataConsumer-activate}.
     */
    function activate(uint64 latestSeq) external payable onlyOwner {
        ITunnelRouter(tunnelRouter).activate{value: msg.value}(
            tunnelId,
            latestSeq
        );
    }

    /**
     * @dev See {IDataConsumer-deactivate}.
     */
    function deactivate() external onlyOwner {
        ITunnelRouter(tunnelRouter).deactivate(tunnelId);
    }

    /**
     * @dev See {IDataConsumer-deposit}.
     */
    function deposit() external payable {
        IVault vault = ITunnelRouter(tunnelRouter).vault();

        vault.deposit{value: msg.value}(tunnelId, address(this));
    }

    /**
     * @dev See {IDataConsumer-withdraw}.
     */
    function withdraw(uint256 amount) external onlyOwner {
        IVault vault = ITunnelRouter(tunnelRouter).vault();

        vault.withdraw(tunnelId, amount);

        // send the remaining balance to the caller.
        uint256 balance = address(this).balance;
        (bool ok, ) = payable(msg.sender).call{value: balance}("");
        if (!ok) {
            revert TokenTransferFailed(msg.sender);
        }
    }

    /**
     * @dev See {IDataConsumer-withdrawAll}.
     */
    function withdrawAll() external onlyOwner {
        IVault vault = ITunnelRouter(tunnelRouter).vault();

        vault.withdrawAll(tunnelId);

        // send the remaining balance to the caller.
        uint256 balance = address(this).balance;
        (bool ok, ) = payable(msg.sender).call{value: balance}("");
        if (!ok) {
            revert TokenTransferFailed(msg.sender);
        }
    }

    ///@dev Sets the tunnel Id of the contract.
    function setTunnelId(uint64 tunnelId_) external onlyOwner {
        tunnelId = tunnelId_;
    }

    ///@dev Returns the hash of the originator of the packet.
    function hashOriginator() public view returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    bytes4(0xa466d313), // keccak("tunnelOriginatorPrefix")[:4]
                    hashedSourceChainId,
                    tunnelId,
                    keccak256(bytes(Address.toChecksumString(address(this)))),
                    hashedTargetChainId
                )
            );
    }

    ///@dev The contract receive eth from the vault contract when user call withdraw.
    receive() external payable {}
}

File 2 of 12 : Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

File 3 of 12 : IDataConsumer.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

import "../libraries/PacketDecoder.sol";

interface IDataConsumer {
    // ========================================
    // Events
    // ========================================

    /**
     * @notice Emitted when the signal price is updated.
     *
     * @param signalId The Id of the signal whose price is updated.
     * @param price The new price of the signal.
     * @param timestamp The timestamp of the updated prices.
     */
    event SignalPriceUpdated(
        bytes32 indexed signalId,
        uint64 price,
        int64 timestamp
    );

    // ========================================
    // Custom Errors
    // ========================================

    /**
     * @notice Reverts if the caller is not the tunnelRouter contract.
     */
    error UnauthorizedTunnelRouter();

    /**
     * @notice Reverts if the hashed originator is not matched.
     */
    error InvalidHashOriginator();

    /**
     * @notice Reverts if the program fails to send tokens to the specified address.
     *
     * @param addr The address to which the token transfer failed.
     */
    error TokenTransferFailed(address addr);

    /**
     * @notice Reverts if the caller is not the factory contract.
     */
    error UnauthorizedFactory(address addr);

    // ========================================
    // Functions
    // ========================================

    /**
     * @dev Processes the relayed message.
     *
     * The relayed message must be evaluated from the tunnelRouter contract and
     * verified by the tssVerifier contract before forwarding to the target contract.
     *
     * @param data The decoded tss message that is relayed from the tunnelRouter contract.
     */
    function process(PacketDecoder.TssMessage memory data) external;

    /**
     * @dev Activates the tunnel and set the sequence on tunnelRouter contract.
     *
     * This function deposits tokens into the vault and sets the latest sequence on the
     * tunnelRouter contract if the current deposit in the vault contract exceeds a threshold.
     * The transaction is reverted if the threshold is not met.
     *
     * This function should be called by the contract owner.
     *
     * @param latestSeq The new sequence of the tunnel.
     */
    function activate(uint64 latestSeq) external payable;

    /**
     * @dev Deactivates the tunnel on tunnelRouter contract.
     *
     * This function should be called by the contract owner.
     */
    function deactivate() external;

    /**
     * @dev Deposits the native tokens into the vault on behalf of the given account and tunnelId.
     * The amount of tokens to be deposited is provided as msg.value in the transaction.
     *
     * The contract calls the vault to deposit the tokens.
     */
    function deposit() external payable;

    /**
     * @dev Withdraws the native tokens from the vault contract with specific amount.
     *
     * This function should be called by the contract owner.
     */
    function withdraw(uint256 amount) external;

    /**
     * @dev Withdraws all native tokens from the vault contract.
     *
     * This function should be called by the contract owner.
     */
    function withdrawAll() external;

    /**
     * @dev Returns The tunnelRouter contract address.
     */
    function tunnelRouter() external view returns (address);

    /**
     * @dev Returns The tunnelId of the contract address.
     */
    function tunnelId() external view returns (uint64);
}

File 4 of 12 : ITunnelRouter.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

import "./IVault.sol";

interface ITunnelRouter {
    // ========================================
    // Events
    // ========================================

    /**
     * @notice Emitted when the maximum allowable callback gas limit is set.
     *
     * @param maxAllowableCallbackGasLimit The maximum gas limit to be used
     * when calling the target contract.
     */
    event MaxAllowableCallbackGasLimitSet(uint256 maxAllowableCallbackGasLimit);

    /**
     * @notice Emitted when the additional gas used is set.
     *
     * @param additionalGasUsed The additional gas estimated for relaying the message;
     * does not include the gas cost for executing the target contract.
     */
    event AdditionalGasUsedSet(uint256 additionalGasUsed);

    /**
     * @notice Emitted after the message is relayed to the target contract
     * to indicate the result of the process.
     *
     * @param tunnelId The tunnel ID that the message is relayed.
     * @param targetAddr The target address that the message is relayed.
     * @param sequence The sequence of the message.
     * @param isReverted The flag indicating whether the message is reverted.
     */
    event MessageProcessed(
        uint64 indexed tunnelId,
        address indexed targetAddr,
        uint64 indexed sequence,
        bool isReverted
    );

    /**
     * @notice Emitted when the target address is activated.
     *
     * @param tunnelId The tunnel ID that the sender is activating.
     * @param targetAddr The target address that the sender is activating.
     * @param latestNonce The latest nonce of the sender.
     */
    event Activated(
        uint64 indexed tunnelId,
        address indexed targetAddr,
        uint64 latestNonce
    );

    /**
     * @notice Emitted when the target address is deactivated.
     *
     * @param tunnelId The tunnel ID that the sender is deactivating.
     * @param targetAddr The target address that the sender is deactivating.
     * @param latestNonce The latest nonce of the sender.
     */
    event Deactivated(
        uint64 indexed tunnelId,
        address indexed targetAddr,
        uint64 latestNonce
    );

    // ========================================
    // Custom Errors
    // ========================================

    /**
     * @notice Reverts if the target contract is inactive.
     *
     * @param targetAddr The target address that is inactive.
     */
    error InactiveTargetContract(address targetAddr);

    /**
     * @notice Reverts if the target contract is active.
     *
     * @param targetAddr The target address that is active.
     */
    error ActiveTargetContract(address targetAddr);

    /**
     * @notice Reverts if the sequence is incorrect.
     *
     * @param expected The expected sequence of the tunnel.
     * @param input The input sequence of the tunnel.
     */
    error InvalidSequence(uint64 expected, uint64 input);

    /**
     * @notice Reverts if the chain ID is incorrect.
     *
     * @param chainId The chain ID of the tunnel.
     */
    error InvalidChain(string chainId);

    /**
     * @notice Reverts if the message and its signature doesn't match.
     */
    error InvalidSignature();

    /**
     * @notice Reverts if the contract cannot send fee to the specific address.
     */
    error TokenTransferFailed(address addr);

    /**
     * @notice Reverts if the remaining balance is insufficient to withdraw.
     *
     * @param tunnelId The tunnel ID that the sender is withdrawing tokens.
     * @param addr The account from which the sender is withdrawing tokens.
     */
    error InsufficientRemainingBalance(uint64 tunnelId, address addr);

    // ========================================
    // Functions
    // ========================================

    ///@dev Tunnel information
    struct TunnelInfo {
        bool isActive; // whether the tunnel is active or not
        uint64 latestSequence; // the latest sequence of the tunnel
        uint256 balance; // the remaining balance of the tunnel
    }

    /**
     * @dev Relays the message to the target contract.
     *
     * Verifies the message's sequence and signature before forwarding it to
     * the data consumer contract. The sender is entitled to a reward from the
     * vault contract, even if the data consumer contract fails to process the
     * message. The reward is based on the gas consumed during processing plus
     * a predefined additional gas estimate.
     *
     * @param message The message to be relayed.
     * @param randomAddr The random address used in signature.
     * @param signature The signature of the message.
     */
    function relay(
        bytes calldata message,
        address randomAddr,
        uint256 signature
    ) external;

    /**
     * @dev Activates the sender and associated tunnel ID.
     *
     * @param tunnelId The tunnel ID that the sender contract is activating.
     * @param latestSeq The new sequence of the tunnelID.
     */
    function activate(uint64 tunnelId, uint64 latestSeq) external payable;

    /**
     * @dev Deactivates the sender and associated tunnel ID.
     *
     * @param tunnelId The tunnel ID being deactivated.
     */
    function deactivate(uint64 tunnelId) external;

    /**
     * @dev Returns the minimum balance required to keep the tunnel active.
     *
     * @return uint256 The minimum balance threshold.
     */
    function minimumBalanceThreshold() external view returns (uint256);

    /**
     * @dev Returns whether the tunnel is active or not.
     *
     * @param tunnelId The ID of the tunnel.
     * @param addr The target contract address.
     *
     * @return bool True if the tunnel is active, false otherwise.
     */
    function isActive(
        uint64 tunnelId,
        address addr
    ) external view returns (bool);

    /**
     * @dev Returns the tunnel information.
     *
     * @param tunnelId The ID of the tunnel.
     * @param addr The target contract address.
     *
     * @return bool True if the tunnel is active, false otherwise.
     */
    function tunnelInfo(
        uint64 tunnelId,
        address addr
    ) external view returns (TunnelInfo memory);

    /**
     * @dev Returns the vault contract address.
     */
    function vault() external view returns (IVault);
}

File 5 of 12 : IVault.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

interface IVault {
    // ========================================
    // Events
    // ========================================

    /**
     * @notice Emitted when the tunnel router contract address is set.
     *
     * @param tunnelRouter The new tunnel router contract address.
     */
    event TunnelRouterSet(address tunnelRouter);

    /**
     * @notice Emitted when the caller deposit native token into the contract.
     *
     * @param tunnelId The ID of the tunnel into which the sender is depositing tokens..
     * @param from The account from which the token is deposited.
     * @param to The account into which the token is deposited.
     * @param amount The amount of tokens deposited.
     */
    event Deposited(
        uint256 indexed tunnelId,
        address indexed from,
        address indexed to,
        uint256 amount
    );

    /**
     * @notice Emitted when the caller withdraw native token from the contract.
     *
     * @param tunnelId The ID of the tunnel from which the sender is withdrawing tokens.
     * @param from The account from which the token is withdrawn.
     * @param to The account to which the token is withdrawn.
     * @param amount The amount of tokens withdrawn.
     */
    event Withdrawn(
        uint256 indexed tunnelId,
        address indexed from,
        address indexed to,
        uint256 amount
    );

    // ========================================
    // Custom Errors
    // ========================================

    /**
     * @notice The caller is not the tunnelRouter contract.
     */
    error UnauthorizedTunnelRouter();

    /**
     * @notice Reverts if the remaining balance is insufficient.
     */
    error InsufficientRemainingBalance();

    /**
     * @notice Reverts if contract cannot send fee to the specific address.
     *
     * @param addr The address to which the token transfer failed.
     */
    error TokenTransferFailed(address addr);

    // ========================================
    // Functions
    // ========================================

    /**
     * @dev Deposits the native tokens into the vault on behalf of the given account and tunnelID.
     * The deposit amount is provided via `msg.value`.
     *
     * @param tunnelId The ID of the tunnel into which the sender is depositing tokens.
     * @param account The account into which the sender is depositing tokens.
     */
    function deposit(uint64 tunnelId, address account) external payable;

    /**
     * @dev Withdraws native tokens from the sender's account associated with the given tunnelID.
     *
     * @param tunnelId the ID of the tunnel from which the sender is withdrawing tokens.
     * @param amount the amount of tokens to withdraw.
     */
    function withdraw(uint64 tunnelId, uint256 amount) external;

    /**
     * @dev Withdraws the entire deposit from the sender's account for the specified tunnel ID.
     *
     * @param tunnelId the ID of the tunnel from which the sender is withdrawing tokens.
     */
    function withdrawAll(uint64 tunnelId) external;

    /**
     * @dev Collects the fee from the account and the given tunnel ID.
     *
     * This function should be called by the tunnelRouter contract only.
     *
     * @param tunnelId the ID of the tunnel from which the caller is withdrawing tokens.
     * @param account The account from which the caller is withdrawing tokens.
     * @param amount the amount of tokens to withdraw.
     */
    function collectFee(
        uint64 tunnelId,
        address account,
        uint256 amount
    ) external;

    /**
     * @dev Returns the balance of the account.
     *
     * @param tunnelId The ID of the tunnel to check the balance.
     * @param account The account to check the balance.
     */
    function balance(
        uint64 tunnelId,
        address account
    ) external view returns (uint256);

    /**
     * @dev Returns the tunnel router contract address.
     */
    function tunnelRouter() external view returns (address);
}

File 6 of 12 : PacketDecoder.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

library PacketDecoder {
    bytes4 private constant _FIXED_POINT_ENCODER_SELECTOR = 0xcba0ad5a; // keccak256("FixedPointABI");
    bytes4 private constant _TICK_ENCODER_SELECTOR = 0xdb99b2b3; // keccak256("TickABI");

    enum EncoderType {
        Unidentified,
        FixedPoint,
        Tick
    }

    // info of signals being published in the packet.
    struct SignalPrice {
        bytes32 signal;
        uint64 price;
    }

    // the packet information generated from the tunnel.
    struct Packet {
        uint64 tunnelId;
        uint64 sequence;
        string chainId;
        string targetAddr;
        SignalPrice[] signals;
        int64 timestamp;
    }

    // the TSS message structure that is generated by the tunnel and is signed by the tss module.
    struct TssMessage {
        bytes32 hashOriginator;
        uint64 sourceBlocktimestamp;
        uint64 signingId;
        EncoderType encoderType;
        Packet packet;
    }

    /**
     * @dev Decode the TSS message from the encoded message.
     * @param message The encoded message.
     * @return TssMessage The decoded TSS message object.
     */
    function decodeTssMessage(
        bytes calldata message
    ) internal pure returns (TssMessage memory) {
        EncoderType encoder = _toEncoderType(bytes4(message[52:56]));

        Packet memory packet = _decodePacket(message);

        TssMessage memory tssMessage = TssMessage(
            bytes32(message[0:32]),
            uint64(bytes8(message[32:40])),
            uint64(bytes8(message[40:48])),
            encoder,
            packet
        );

        return tssMessage;
    }

    /**
     * @dev Decode the TSS message from the encoded message.
     * @param message The encoded message.
     * @return TssMessage The decoded TSS message object.
     */
    function _decodePacket(
        bytes calldata message
    ) internal pure returns (Packet memory) {
        Packet memory packet = abi.decode(message[56:], (Packet));
        return packet;
    }

    /**
     * @dev Convert the selector to the encoder type.
     * @param selector The selector to be converted.
     * @return EncoderType The encoder type.
     */
    function _toEncoderType(
        bytes4 selector
    ) internal pure returns (EncoderType) {
        if (selector == _FIXED_POINT_ENCODER_SELECTOR) {
            return EncoderType.FixedPoint;
        } else if (selector == _TICK_ENCODER_SELECTOR) {
            return EncoderType.Tick;
        } else {
            return EncoderType.Unidentified;
        }
    }
}

File 7 of 12 : Address.sol
// SPDX-License-Identifier: MIT
// ref: https://gist.github.com/Y5Yash/721a5f5c3e392a6a28f47db1d3114501
// ref: https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md

pragma solidity ^0.8.23;

library Address {
    bytes16 private constant _LOWER = "0123456789abcdef";
    bytes16 private constant _CAPITAL = "0123456789ABCDEF";

    ///@dev convert address to checksum address string.
    function toChecksumString(
        address addr
    ) internal pure returns (string memory) {
        // get the hash of the lowercase address
        bytes memory lowercaseAddr = ToLowercaseBytes(addr);
        bytes32 hashedAddr = keccak256(abi.encodePacked(lowercaseAddr));

        // store checksum address with '0x' prepended in this.
        bytes memory result = new bytes(42);
        result[0] = "0";
        result[1] = "x";

        uint160 addrValue = uint160(addr);
        uint160 hashValue = uint160(bytes20(hashedAddr));

        // checksum logic from EIP-55
        for (uint i = 41; i > 1; --i) {
            uint b = addrValue & 0xf;

            if (hashValue & 0xf > 7) {
                result[i] = _CAPITAL[b];
            } else {
                result[i] = _LOWER[b];
            }

            addrValue >>= 4;
            hashValue >>= 4;
        }

        return string(abi.encodePacked(result));
    }

    ///@dev get convert address bytes to lowercase char hex bytes (without '0x').
    function ToLowercaseBytes(
        address addr
    ) internal pure returns (bytes memory) {
        bytes memory s = new bytes(40);

        uint160 x = uint160(addr);
        for (uint i = 0; i < 20; i++) {
            uint8 b = uint8(x) & 0xff;
            s[39 - 2 * i - 1] = _LOWER[b >> 4]; // higher index
            s[39 - 2 * i] = _LOWER[b & 0xf]; // lower index
            x >>= 8;
        }

        return s;
    }
}

File 8 of 12 : TssVerifier.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";

import "./interfaces/ITssVerifier.sol";

contract TssVerifier is Pausable, Ownable2Step, ITssVerifier {
    struct PublicKey {
        uint64 activeTime; // The timestamp that the public key is activated.
        uint8 parity; // The parity value of the public key.
        uint256 px; // The x-coordinate value of the public key.
    }

    // The group order of secp256k1.
    uint256 constant _ORDER =
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
    // The grace period for the public key.
    uint64 immutable transitionPeriod;
    // The prefix for the hashing process in bandchain.
    string constant _CONTEXT = "BAND-TSS-secp256k1-v0";
    // The prefix for the challenging hash message.
    string constant _CHALLENGE_PREFIX = "challenge";

    // The list of public keys that are used for the verification process.
    PublicKey[] public publicKeys;

    constructor(
        uint64 transitionPeriod_,
        address initialAddr
    ) Ownable(initialAddr) {
        transitionPeriod = transitionPeriod_;
    }

    /**
     * @dev See {ITssVerifier-addPubKeyWithProof}.
     */
    function addPubKeyWithProof(
        bytes calldata message,
        address randomAddr,
        uint256 s
    ) external whenNotPaused {
        if (!verify(keccak256(message), randomAddr, s)) {
            revert InvalidSignature();
        }

        // Extract the public key from the message. The message is in the form of
        // hashedOrignator (32 bytes) || timestamp (uint64; 8-bytes) || signingId (uint64; 8 bytes)
        // || modulePrefix (8 bytes) || parity (1 byte) || px (32 bytes) || timestamp (8 bytes)
        uint8 parity = uint8(bytes1(message[56:57]));
        uint256 px = uint(bytes32(message[57:89]));
        uint64 timestamp = uint64(bytes8(message[89:97]));

        _updatePublicKey(timestamp, parity, px);
    }

    /**
     * @dev See {ITssVerifier-addPubKeyByOwner}.
     */
    function addPubKeyByOwner(
        uint64 timestamp,
        uint8 parity,
        uint256 px
    ) external onlyOwner {
        _updatePublicKey(timestamp, parity, px);
    }

    /**
     * @dev See {ITssVerifier-verify}.
     */
    function verify(
        bytes32 hashedMessage,
        address randomAddr,
        uint256 signature
    ) public view whenNotPaused returns (bool result) {
        if (randomAddr == address(0)) {
            return false;
        }

        // Get the public key that is valid at the given timestamp.
        uint256 pubKeyIdx = _getPubKeyIndexByTimestamp(uint64(block.timestamp));

        // if the active time of the public key is in the transition period, then
        // we need to check the previous public key as it may be the signature from
        // the previous group.
        bool isInTransitionPeriod = publicKeys[pubKeyIdx].activeTime +
            transitionPeriod >=
            block.timestamp;
        if (
            pubKeyIdx > 0 &&
            isInTransitionPeriod &&
            _verifyWithPublickKey(
                hashedMessage,
                randomAddr,
                signature,
                publicKeys[pubKeyIdx - 1]
            )
        ) {
            return true;
        }

        return
            _verifyWithPublickKey(
                hashedMessage,
                randomAddr,
                signature,
                publicKeys[pubKeyIdx]
            );
    }

    /// @dev Pauses the contract to prevent any further updates.
    function pause() external onlyOwner {
        _pause();
    }

    /// @dev Unpauses the contract to prevent any further updates.
    function unpause() external onlyOwner {
        _unpause();
    }

    /// @dev Returns the number of public keys.
    function publicKeysLength() public view returns (uint256 length) {
        length = publicKeys.length;
    }

    /// @dev returns true if the signature is valid against the given public key.
    function _verifyWithPublickKey(
        bytes32 hashedMessage,
        address randomAddr,
        uint256 signature,
        PublicKey memory publicKey
    ) internal pure returns (bool) {
        uint256 content = uint256(
            keccak256(
                abi.encodePacked(
                    _CONTEXT,
                    bytes1(0x00),
                    _CHALLENGE_PREFIX,
                    bytes1(0x00),
                    randomAddr,
                    publicKey.parity,
                    publicKey.px,
                    hashedMessage
                )
            )
        );

        uint256 spx = _ORDER - mulmod(signature, publicKey.px, _ORDER);
        uint256 cpx = _ORDER - mulmod(content, publicKey.px, _ORDER);

        // Because the ecrecover precompile implementation verifies that the 'r' and's'
        // input positions must be non-zero
        // So in this case, there is no need to verify them('px' > 0 and 'cpx' > 0).
        if (spx == 0) {
            revert ProcessingSignatureFailed();
        }

        address addr = ecrecover(
            bytes32(spx),
            publicKey.parity,
            bytes32(publicKey.px),
            bytes32(cpx)
        );
        return randomAddr == addr;
    }

    /// @dev push the public key to the list.
    function _updatePublicKey(
        uint64 timestamp,
        uint8 parity,
        uint256 px
    ) internal {
        // Note: Offset parity by 25 to match with the calculation in TSS module
        // In etheruem, the parity is typically 27 or 28.
        PublicKey memory pubKey = PublicKey({
            activeTime: timestamp,
            parity: parity + 25,
            px: px
        });
        publicKeys.push(pubKey);

        emit GroupPubKeyUpdated(publicKeys.length, timestamp, parity, px, true);
    }

    ///@dev get the public key index that is valid at th given timestamp.
    function _getPubKeyIndexByTimestamp(
        uint64 timestamp
    ) internal view returns (uint256) {
        for (uint256 i = publicKeys.length; i > 0; i--) {
            if (publicKeys[i - 1].activeTime <= timestamp) {
                return i - 1;
            }
        }

        revert PublicKeyNotFound(timestamp);
    }
}

File 9 of 12 : Ownable.sol
// 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);
    }
}

File 10 of 12 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 11 of 12 : ITssVerifier.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

interface ITssVerifier {
    // ========================================
    // Events
    // ========================================

    /**
     * @dev Emitted when the group public key is updated.
     * @param index The index of the public key in the group.
     * @param timestamp The timestamp of the update.
     * @param parity The parity value of the public key.
     * @param px The x-coordinate value of the public key.
     * @param isByAdmin True if the public key is updated by the admin, false otherwise.
     */
    event GroupPubKeyUpdated(
        uint256 index,
        uint256 timestamp,
        uint8 parity,
        uint256 px,
        bool isByAdmin
    );

    // ========================================
    // Custom Errors
    // ========================================

    /**
     * @notice Reverts if the message and its signature doesn't match.
     */
    error InvalidSignature();

    /**
     * @notice Reverts if the contract fails to processes the signature.
     */
    error ProcessingSignatureFailed();

    /**
     * @notice Reverts if there is no valid public key.
     *
     * @param timestamp The given timestamp of the message.
     */
    error PublicKeyNotFound(uint256 timestamp);

    // ========================================
    // Functions
    // ========================================

    /**
     * @dev Verifies the signature of the message against the given signature.
     *
     * The contract is not allowed to verify the message with obsolete public key.
     *
     * @param hashedMessage The hashed message to be verified.
     * @param randomAddr The random address that is generated during the processing tss signature.
     * @param signature The tss signature.
     * @return true If the signature is valid, false otherwise.
     */
    function verify(
        bytes32 hashedMessage,
        address randomAddr,
        uint256 signature
    ) external view returns (bool);

    /**
     * @dev Adds a new public key with proof from the current group.
     *
     * @param message The message being used for updating public key.
     * @param randomAddr The address form of the commitment R.
     * @param s The Schnorr signature.
     */
    function addPubKeyWithProof(
        bytes calldata message,
        address randomAddr,
        uint256 s
    ) external;

    /**
     * @dev Adds the new public key by the owner.
     *
     * @param timestamp The timestamp of the new public key.
     * @param parity The parity value of the new public key.
     * @param px The x-coordinate value of the new public key.
     */
    function addPubKeyByOwner(
        uint64 timestamp,
        uint8 parity,
        uint256 px
    ) external;
}

File 12 of 12 : Context.sol
// 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;
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"tunnelRouter_","type":"address"},{"internalType":"bytes32","name":"hashedSourceChainId_","type":"bytes32"},{"internalType":"bytes32","name":"hashedTargetChainId_","type":"bytes32"},{"internalType":"address","name":"initialOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidHashOriginator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"TokenTransferFailed","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"UnauthorizedFactory","type":"error"},{"inputs":[],"name":"UnauthorizedTunnelRouter","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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":"bytes32","name":"signalId","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"price","type":"uint64"},{"indexed":false,"internalType":"int64","name":"timestamp","type":"int64"}],"name":"SignalPriceUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"latestSeq","type":"uint64"}],"name":"activate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"deactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"hashOriginator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hashedSourceChainId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hashedTargetChainId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"prices","outputs":[{"internalType":"uint64","name":"price","type":"uint64"},{"internalType":"int64","name":"timestamp","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"hashOriginator","type":"bytes32"},{"internalType":"uint64","name":"sourceBlocktimestamp","type":"uint64"},{"internalType":"uint64","name":"signingId","type":"uint64"},{"internalType":"enum PacketDecoder.EncoderType","name":"encoderType","type":"uint8"},{"components":[{"internalType":"uint64","name":"tunnelId","type":"uint64"},{"internalType":"uint64","name":"sequence","type":"uint64"},{"internalType":"string","name":"chainId","type":"string"},{"internalType":"string","name":"targetAddr","type":"string"},{"components":[{"internalType":"bytes32","name":"signal","type":"bytes32"},{"internalType":"uint64","name":"price","type":"uint64"}],"internalType":"struct PacketDecoder.SignalPrice[]","name":"signals","type":"tuple[]"},{"internalType":"int64","name":"timestamp","type":"int64"}],"internalType":"struct PacketDecoder.Packet","name":"packet","type":"tuple"}],"internalType":"struct PacketDecoder.TssMessage","name":"data","type":"tuple"}],"name":"process","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"tunnelId_","type":"uint64"}],"name":"setTunnelId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tunnelId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tunnelRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e060405234801561000f575f5ffd5b5060405161160238038061160283398101604081905261002e91610107565b806001600160a01b03811661005c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b61006581610081565b505060a09190915260c0526001600160a01b031660805261014b565b600180546001600160a01b031916905561009a8161009d565b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114610102575f5ffd5b919050565b5f5f5f5f6080858703121561011a575f5ffd5b610123856100ec565b6020860151604087015191955093509150610140606086016100ec565b905092959194509250565b60805160a05160c0516114556101ad5f395f818161033c01526109f701525f8181610309015261097e01525f81816101df015281816103b8015281816105960152818161072a0152818161081c01528181610a3c0152610bda01526114555ff3fe608060405260043610610108575f3560e01c80638da5cb5b11610092578063d366747f11610062578063d366747f146102f8578063e2bd914c1461032b578063e30c39781461035e578063f2fde38b1461037b578063fc7e47461461039a575f5ffd5b80638da5cb5b14610274578063a0e062fa14610290578063a79c8c09146102b2578063d0e30db0146102f0575f5ffd5b80636a5d9b59116100d85780636a5d9b59146101ce578063715018a61461021957806379ba50971461022d5780637b30d81314610241578063853828b614610260575f5ffd5b806317b7ee4f146101135780632e1a7d4d1461013457806351b42b001461015357806360846bc614610167575f5ffd5b3661010f57005b5f5ffd5b34801561011e575f5ffd5b5061013261012d3660046111b5565b6103ad565b005b34801561013f575f5ffd5b5061013261014e36600461131b565b61058b565b34801561015e575f5ffd5b506101326106fa565b348015610172575f5ffd5b506101a761018136600461131b565b60026020525f90815260409020546001600160401b03811690600160401b900460070b82565b604080516001600160401b03909316835260079190910b6020830152015b60405180910390f35b3480156101d9575f5ffd5b506102017f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101c5565b348015610224575f5ffd5b50610132610785565b348015610238575f5ffd5b50610132610798565b34801561024c575f5ffd5b5061013261025b366004611332565b6107dc565b34801561026b575f5ffd5b50610132610811565b34801561027f575f5ffd5b505f546001600160a01b0316610201565b34801561029b575f5ffd5b506102a461096e565b6040519081526020016101c5565b3480156102bd575f5ffd5b506001546102d890600160a01b90046001600160401b031681565b6040516001600160401b0390911681526020016101c5565b610132610a39565b348015610303575f5ffd5b506102a47f000000000000000000000000000000000000000000000000000000000000000081565b348015610336575f5ffd5b506102a47f000000000000000000000000000000000000000000000000000000000000000081565b348015610369575f5ffd5b506001546001600160a01b0316610201565b348015610386575f5ffd5b50610132610395366004611366565b610b31565b6101326103a8366004611332565b610ba1565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103f657604051636e96d7e160e11b815260040160405180910390fd5b6103fe61096e565b81511461041e576040516397946a9d60e01b815260040160405180910390fd5b60808101515f5b8160800151518110156105865760405180604001604052808360800151838151811061045357610453611381565b6020026020010151602001516001600160401b031681526020018360a0015160070b81525060025f8460800151848151811061049157610491611381565b602090810291909101810151518252818101929092526040015f208251815493909201516001600160401b03908116600160401b026fffffffffffffffffffffffffffffffff1990941692169190911791909117905560808201518051829081106104fe576104fe611381565b60200260200101515f01517fa623627c34311a71b36da152b44076ede51861d701868671c9a6dfdd0f5da00a8360800151838151811061054057610540611381565b6020026020010151602001518460a001516040516105769291906001600160401b0392909216825260070b602082015260400190565b60405180910390a2600101610425565b505050565b610593610c13565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105f0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106149190611395565b600154604051634338798360e01b8152600160a01b9091046001600160401b03166004820152602481018490529091506001600160a01b038216906343387983906044015f604051808303815f87803b15801561066f575f5ffd5b505af1158015610681573d5f5f3e3d5ffd5b50506040514792505f9150339083908381818185875af1925050503d805f81146106c6576040519150601f19603f3d011682016040523d82523d5f602084013e6106cb565b606091505b50509050806106f457604051632b596cb760e01b81523360048201526024015b60405180910390fd5b50505050565b610702610c13565b6001546040516343ba231760e11b8152600160a01b9091046001600160401b031660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690638774462e906024015f604051808303815f87803b158015610773575f5ffd5b505af11580156106f4573d5f5f3e3d5ffd5b61078d610c13565b6107965f610c3f565b565b60015433906001600160a01b031681146107d05760405163118cdaa760e01b81526001600160a01b03821660048201526024016106eb565b6107d981610c3f565b50565b6107e4610c13565b600180546001600160401b03909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b610819610c13565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610876573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061089a9190611395565b60015460405163f4465fd560e01b8152600160a01b9091046001600160401b031660048201529091506001600160a01b0382169063f4465fd5906024015f604051808303815f87803b1580156108ee575f5ffd5b505af1158015610900573d5f5f3e3d5ffd5b50506040514792505f9150339083908381818185875af1925050503d805f8114610945576040519150601f19603f3d011682016040523d82523d5f602084013e61094a565b606091505b505090508061058657604051632b596cb760e01b81523360048201526024016106eb565b6001545f9063a466d31360e01b907f000000000000000000000000000000000000000000000000000000000000000090600160a01b90046001600160401b03166109b730610c58565b80516020918201206040516001600160e01b03199590951691850191909152602484019290925260c01b6001600160c01b0319166044830152604c8201527f0000000000000000000000000000000000000000000000000000000000000000606c820152608c0160405160208183030381529060405280519060200120905090565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aba9190611395565b60015460405163576bcd2f60e01b8152600160a01b9091046001600160401b031660048201523060248201529091506001600160a01b0382169063576bcd2f9034906044015b5f604051808303818588803b158015610b17575f5ffd5b505af1158015610b29573d5f5f3e3d5ffd5b505050505050565b610b39610c13565b600180546001600160a01b0383166001600160a01b03199091168117909155610b695f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b610ba9610c13565b60015460405163ba1b5f2360e01b8152600160a01b9091046001600160401b039081166004830152821660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ba1b5f23903490604401610b00565b5f546001600160a01b031633146107965760405163118cdaa760e01b81523360048201526024016106eb565b600180546001600160a01b03191690556107d981610e2c565b60605f610c6483610e7b565b90505f81604051602001610c7891906113b0565b60408051808303601f190181528282528051602090910120602a8084526060840190925292505f9190602082018180368337019050509050600360fc1b815f81518110610cc757610cc7611381565b60200101906001600160f81b03191690815f1a905350600f60fb1b81600181518110610cf557610cf5611381565b60200101906001600160f81b03191690815f1a90535084606083901c60295b6001811115610dff57600f8084169060079084161115610d84576f181899199a1a9b1b9c1ca0a121a222a360811b8160108110610d5357610d53611381565b1a60f81b858381518110610d6957610d69611381565b60200101906001600160f81b03191690815f1a905350610dd6565b6f181899199a1a9b1b9c1cb0b131b232b360811b8160108110610da957610da9611381565b1a60f81b858381518110610dbf57610dbf611381565b60200101906001600160f81b03191690815f1a9053505b5060016001609c1b03600493841c8116939290921c90911690610df8816113da565b9050610d14565b5082604051602001610e1191906113b0565b60405160208183030381529060405295505050505050919050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60408051602880825260608281019093525f9190602082018180368337019050509050825f5b6014811015610fb55760ff82166f181899199a1a9b1b9c1cb0b131b232b360811b600f600485901c1660108110610eda57610eda611381565b1a60f81b846001610eec8560026113ef565b610ef790602761140c565b610f01919061140c565b81518110610f1157610f11611381565b60200101906001600160f81b03191690815f1a9053506f181899199a1a9b1b9c1cb0b131b232b360811b600f821660108110610f4f57610f4f611381565b1a60f81b84610f5f8460026113ef565b610f6a90602761140c565b81518110610f7a57610f7a611381565b60200101906001600160f81b03191690815f1a9053505060089190911c72ffffffffffffffffffffffffffffffffffffff1690600101610ea1565b50909392505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715610ff457610ff4610fbe565b60405290565b60405160a081016001600160401b0381118282101715610ff457610ff4610fbe565b60405160c081016001600160401b0381118282101715610ff457610ff4610fbe565b604051601f8201601f191681016001600160401b038111828210171561106657611066610fbe565b604052919050565b80356001600160401b0381168114611084575f5ffd5b919050565b5f82601f830112611098575f5ffd5b81356001600160401b038111156110b1576110b1610fbe565b6110c4601f8201601f191660200161103e565b8181528460208386010111156110d8575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f82601f830112611103575f5ffd5b81356001600160401b0381111561111c5761111c610fbe565b61112b60208260051b0161103e565b8082825260208201915060208360061b86010192508583111561114c575f5ffd5b602085015b8381101561119a5760408188031215611168575f5ffd5b611170610fd2565b813581526111806020830161106e565b602082015280845250602083019250604081019050611151565b5095945050505050565b8035600781900b8114611084575f5ffd5b5f602082840312156111c5575f5ffd5b81356001600160401b038111156111da575f5ffd5b820160a081850312156111eb575f5ffd5b6111f3610ffa565b813581526112036020830161106e565b60208201526112146040830161106e565b604082015260608201356003811061122a575f5ffd5b606082015260808201356001600160401b03811115611247575f5ffd5b919091019060c0828603121561125b575f5ffd5b61126361101c565b61126c8361106e565b815261127a6020840161106e565b602082015260408301356001600160401b03811115611297575f5ffd5b6112a387828601611089565b60408301525060608301356001600160401b038111156112c1575f5ffd5b6112cd87828601611089565b60608301525060808301356001600160401b038111156112eb575f5ffd5b6112f7878286016110f4565b60808301525061130960a084016111a4565b60a08201526080820152949350505050565b5f6020828403121561132b575f5ffd5b5035919050565b5f60208284031215611342575f5ffd5b61134b8261106e565b9392505050565b6001600160a01b03811681146107d9575f5ffd5b5f60208284031215611376575f5ffd5b813561134b81611352565b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156113a5575f5ffd5b815161134b81611352565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52601160045260245ffd5b5f816113e8576113e86113c6565b505f190190565b8082028115828204841417611406576114066113c6565b92915050565b81810381811115611406576114066113c656fea26469706673582212206b015543a79287239e49636728761f1308bdef4983c2b851fe1fa9eea9531b0764736f6c634300081b00330000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db40609ec3611e361c61412ffb6b15662be2ac6cd9bdcfa9227177b218f80700f1c6a4f5af6d81c929088b10c1f0eec52fd2ce69844fa9be1d417a4d7bda2928581dbd200000000000000000000000086fda4ed3492d1f1da4ff52cf0c2352b64922fc3

Deployed Bytecode

0x608060405260043610610108575f3560e01c80638da5cb5b11610092578063d366747f11610062578063d366747f146102f8578063e2bd914c1461032b578063e30c39781461035e578063f2fde38b1461037b578063fc7e47461461039a575f5ffd5b80638da5cb5b14610274578063a0e062fa14610290578063a79c8c09146102b2578063d0e30db0146102f0575f5ffd5b80636a5d9b59116100d85780636a5d9b59146101ce578063715018a61461021957806379ba50971461022d5780637b30d81314610241578063853828b614610260575f5ffd5b806317b7ee4f146101135780632e1a7d4d1461013457806351b42b001461015357806360846bc614610167575f5ffd5b3661010f57005b5f5ffd5b34801561011e575f5ffd5b5061013261012d3660046111b5565b6103ad565b005b34801561013f575f5ffd5b5061013261014e36600461131b565b61058b565b34801561015e575f5ffd5b506101326106fa565b348015610172575f5ffd5b506101a761018136600461131b565b60026020525f90815260409020546001600160401b03811690600160401b900460070b82565b604080516001600160401b03909316835260079190910b6020830152015b60405180910390f35b3480156101d9575f5ffd5b506102017f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db4060981565b6040516001600160a01b0390911681526020016101c5565b348015610224575f5ffd5b50610132610785565b348015610238575f5ffd5b50610132610798565b34801561024c575f5ffd5b5061013261025b366004611332565b6107dc565b34801561026b575f5ffd5b50610132610811565b34801561027f575f5ffd5b505f546001600160a01b0316610201565b34801561029b575f5ffd5b506102a461096e565b6040519081526020016101c5565b3480156102bd575f5ffd5b506001546102d890600160a01b90046001600160401b031681565b6040516001600160401b0390911681526020016101c5565b610132610a39565b348015610303575f5ffd5b506102a47fec3611e361c61412ffb6b15662be2ac6cd9bdcfa9227177b218f80700f1c6a4f81565b348015610336575f5ffd5b506102a47f5af6d81c929088b10c1f0eec52fd2ce69844fa9be1d417a4d7bda2928581dbd281565b348015610369575f5ffd5b506001546001600160a01b0316610201565b348015610386575f5ffd5b50610132610395366004611366565b610b31565b6101326103a8366004611332565b610ba1565b336001600160a01b037f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db4060916146103f657604051636e96d7e160e11b815260040160405180910390fd5b6103fe61096e565b81511461041e576040516397946a9d60e01b815260040160405180910390fd5b60808101515f5b8160800151518110156105865760405180604001604052808360800151838151811061045357610453611381565b6020026020010151602001516001600160401b031681526020018360a0015160070b81525060025f8460800151848151811061049157610491611381565b602090810291909101810151518252818101929092526040015f208251815493909201516001600160401b03908116600160401b026fffffffffffffffffffffffffffffffff1990941692169190911791909117905560808201518051829081106104fe576104fe611381565b60200260200101515f01517fa623627c34311a71b36da152b44076ede51861d701868671c9a6dfdd0f5da00a8360800151838151811061054057610540611381565b6020026020010151602001518460a001516040516105769291906001600160401b0392909216825260070b602082015260400190565b60405180910390a2600101610425565b505050565b610593610c13565b5f7f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db406096001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105f0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106149190611395565b600154604051634338798360e01b8152600160a01b9091046001600160401b03166004820152602481018490529091506001600160a01b038216906343387983906044015f604051808303815f87803b15801561066f575f5ffd5b505af1158015610681573d5f5f3e3d5ffd5b50506040514792505f9150339083908381818185875af1925050503d805f81146106c6576040519150601f19603f3d011682016040523d82523d5f602084013e6106cb565b606091505b50509050806106f457604051632b596cb760e01b81523360048201526024015b60405180910390fd5b50505050565b610702610c13565b6001546040516343ba231760e11b8152600160a01b9091046001600160401b031660048201527f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db406096001600160a01b031690638774462e906024015f604051808303815f87803b158015610773575f5ffd5b505af11580156106f4573d5f5f3e3d5ffd5b61078d610c13565b6107965f610c3f565b565b60015433906001600160a01b031681146107d05760405163118cdaa760e01b81526001600160a01b03821660048201526024016106eb565b6107d981610c3f565b50565b6107e4610c13565b600180546001600160401b03909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b610819610c13565b5f7f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db406096001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610876573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061089a9190611395565b60015460405163f4465fd560e01b8152600160a01b9091046001600160401b031660048201529091506001600160a01b0382169063f4465fd5906024015f604051808303815f87803b1580156108ee575f5ffd5b505af1158015610900573d5f5f3e3d5ffd5b50506040514792505f9150339083908381818185875af1925050503d805f8114610945576040519150601f19603f3d011682016040523d82523d5f602084013e61094a565b606091505b505090508061058657604051632b596cb760e01b81523360048201526024016106eb565b6001545f9063a466d31360e01b907fec3611e361c61412ffb6b15662be2ac6cd9bdcfa9227177b218f80700f1c6a4f90600160a01b90046001600160401b03166109b730610c58565b80516020918201206040516001600160e01b03199590951691850191909152602484019290925260c01b6001600160c01b0319166044830152604c8201527f5af6d81c929088b10c1f0eec52fd2ce69844fa9be1d417a4d7bda2928581dbd2606c820152608c0160405160208183030381529060405280519060200120905090565b5f7f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db406096001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aba9190611395565b60015460405163576bcd2f60e01b8152600160a01b9091046001600160401b031660048201523060248201529091506001600160a01b0382169063576bcd2f9034906044015b5f604051808303818588803b158015610b17575f5ffd5b505af1158015610b29573d5f5f3e3d5ffd5b505050505050565b610b39610c13565b600180546001600160a01b0383166001600160a01b03199091168117909155610b695f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b610ba9610c13565b60015460405163ba1b5f2360e01b8152600160a01b9091046001600160401b039081166004830152821660248201527f0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db406096001600160a01b03169063ba1b5f23903490604401610b00565b5f546001600160a01b031633146107965760405163118cdaa760e01b81523360048201526024016106eb565b600180546001600160a01b03191690556107d981610e2c565b60605f610c6483610e7b565b90505f81604051602001610c7891906113b0565b60408051808303601f190181528282528051602090910120602a8084526060840190925292505f9190602082018180368337019050509050600360fc1b815f81518110610cc757610cc7611381565b60200101906001600160f81b03191690815f1a905350600f60fb1b81600181518110610cf557610cf5611381565b60200101906001600160f81b03191690815f1a90535084606083901c60295b6001811115610dff57600f8084169060079084161115610d84576f181899199a1a9b1b9c1ca0a121a222a360811b8160108110610d5357610d53611381565b1a60f81b858381518110610d6957610d69611381565b60200101906001600160f81b03191690815f1a905350610dd6565b6f181899199a1a9b1b9c1cb0b131b232b360811b8160108110610da957610da9611381565b1a60f81b858381518110610dbf57610dbf611381565b60200101906001600160f81b03191690815f1a9053505b5060016001609c1b03600493841c8116939290921c90911690610df8816113da565b9050610d14565b5082604051602001610e1191906113b0565b60405160208183030381529060405295505050505050919050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60408051602880825260608281019093525f9190602082018180368337019050509050825f5b6014811015610fb55760ff82166f181899199a1a9b1b9c1cb0b131b232b360811b600f600485901c1660108110610eda57610eda611381565b1a60f81b846001610eec8560026113ef565b610ef790602761140c565b610f01919061140c565b81518110610f1157610f11611381565b60200101906001600160f81b03191690815f1a9053506f181899199a1a9b1b9c1cb0b131b232b360811b600f821660108110610f4f57610f4f611381565b1a60f81b84610f5f8460026113ef565b610f6a90602761140c565b81518110610f7a57610f7a611381565b60200101906001600160f81b03191690815f1a9053505060089190911c72ffffffffffffffffffffffffffffffffffffff1690600101610ea1565b50909392505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715610ff457610ff4610fbe565b60405290565b60405160a081016001600160401b0381118282101715610ff457610ff4610fbe565b60405160c081016001600160401b0381118282101715610ff457610ff4610fbe565b604051601f8201601f191681016001600160401b038111828210171561106657611066610fbe565b604052919050565b80356001600160401b0381168114611084575f5ffd5b919050565b5f82601f830112611098575f5ffd5b81356001600160401b038111156110b1576110b1610fbe565b6110c4601f8201601f191660200161103e565b8181528460208386010111156110d8575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f82601f830112611103575f5ffd5b81356001600160401b0381111561111c5761111c610fbe565b61112b60208260051b0161103e565b8082825260208201915060208360061b86010192508583111561114c575f5ffd5b602085015b8381101561119a5760408188031215611168575f5ffd5b611170610fd2565b813581526111806020830161106e565b602082015280845250602083019250604081019050611151565b5095945050505050565b8035600781900b8114611084575f5ffd5b5f602082840312156111c5575f5ffd5b81356001600160401b038111156111da575f5ffd5b820160a081850312156111eb575f5ffd5b6111f3610ffa565b813581526112036020830161106e565b60208201526112146040830161106e565b604082015260608201356003811061122a575f5ffd5b606082015260808201356001600160401b03811115611247575f5ffd5b919091019060c0828603121561125b575f5ffd5b61126361101c565b61126c8361106e565b815261127a6020840161106e565b602082015260408301356001600160401b03811115611297575f5ffd5b6112a387828601611089565b60408301525060608301356001600160401b038111156112c1575f5ffd5b6112cd87828601611089565b60608301525060808301356001600160401b038111156112eb575f5ffd5b6112f7878286016110f4565b60808301525061130960a084016111a4565b60a08201526080820152949350505050565b5f6020828403121561132b575f5ffd5b5035919050565b5f60208284031215611342575f5ffd5b61134b8261106e565b9392505050565b6001600160a01b03811681146107d9575f5ffd5b5f60208284031215611376575f5ffd5b813561134b81611352565b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156113a5575f5ffd5b815161134b81611352565b5f82518060208501845e5f920191825250919050565b634e487b7160e01b5f52601160045260245ffd5b5f816113e8576113e86113c6565b505f190190565b8082028115828204841417611406576114066113c6565b92915050565b81810381811115611406576114066113c656fea26469706673582212206b015543a79287239e49636728761f1308bdef4983c2b851fe1fa9eea9531b0764736f6c634300081b0033

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

0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db40609ec3611e361c61412ffb6b15662be2ac6cd9bdcfa9227177b218f80700f1c6a4f5af6d81c929088b10c1f0eec52fd2ce69844fa9be1d417a4d7bda2928581dbd200000000000000000000000086fda4ed3492d1f1da4ff52cf0c2352b64922fc3

-----Decoded View---------------
Arg [0] : tunnelRouter_ (address): 0x6986C48e7EA039acc415Ab2Df4d3c0D36DB40609
Arg [1] : hashedSourceChainId_ (bytes32): 0xec3611e361c61412ffb6b15662be2ac6cd9bdcfa9227177b218f80700f1c6a4f
Arg [2] : hashedTargetChainId_ (bytes32): 0x5af6d81c929088b10c1f0eec52fd2ce69844fa9be1d417a4d7bda2928581dbd2
Arg [3] : initialOwner (address): 0x86FdA4Ed3492D1F1Da4Ff52Cf0c2352b64922fC3

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000006986c48e7ea039acc415ab2df4d3c0d36db40609
Arg [1] : ec3611e361c61412ffb6b15662be2ac6cd9bdcfa9227177b218f80700f1c6a4f
Arg [2] : 5af6d81c929088b10c1f0eec52fd2ce69844fa9be1d417a4d7bda2928581dbd2
Arg [3] : 00000000000000000000000086fda4ed3492d1f1da4ff52cf0c2352b64922fc3


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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