Sepolia Testnet

Contract

0x93391bD1De68aFBAB10BB94BF3d36a4484B60eA2
Source Code Source Code

Overview

ETH Balance

0.0000017 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount
Verify Remote Ba...58829852024-05-11 20:15:36614 days ago1715458536IN
0x93391bD1...484B60eA2
0 ETH0.000132341.00051463
Verify Remote Ba...58771172024-05-10 22:41:24615 days ago1715380884IN
0x93391bD1...484B60eA2
0 ETH0.000132341.00035431
Verify Remote Ba...58770902024-05-10 22:35:36615 days ago1715380536IN
0x93391bD1...484B60eA2
0 ETH0.000132321.00038745
Verify Remote Ba...58761362024-05-10 19:09:48615 days ago1715368188IN
0x93391bD1...484B60eA2
0 ETH0.000132351.00052651
Verify Remote Ba...58760182024-05-10 18:45:12615 days ago1715366712IN
0x93391bD1...484B60eA2
0 ETH0.000124410.94051406
Verify Remote Ba...58753852024-05-10 16:29:12615 days ago1715358552IN
0x93391bD1...484B60eA2
0 ETH0.000132531.0020028
Verify Remote Ba...58753832024-05-10 16:28:48615 days ago1715358528IN
0x93391bD1...484B60eA2
0 ETH0.000145761.10200268
Verify Remote Ba...58751082024-05-10 15:29:00615 days ago1715354940IN
0x93391bD1...484B60eA2
0 ETH0.000132551.0020211
Verify Remote Ba...58750112024-05-10 15:08:12615 days ago1715353692IN
0x93391bD1...484B60eA2
0 ETH0.000132381.00087355
Verify Remote Ba...58711052024-05-10 1:26:00616 days ago1715304360IN
0x93391bD1...484B60eA2
0 ETH0.000047140.35642304
Verify Remote Ba...58710852024-05-10 1:22:00616 days ago1715304120IN
0x93391bD1...484B60eA2
0 ETH0.000052190.3947295
Verify Remote Ba...58705802024-05-09 23:35:36616 days ago1715297736IN
0x93391bD1...484B60eA2
0 ETH0.000079420.60038797
Verify Remote Ba...58698452024-05-09 21:02:12616 days ago1715288532IN
0x93391bD1...484B60eA2
0 ETH0.000145621.10071972
Verify Remote Ba...58698322024-05-09 20:59:24616 days ago1715288364IN
0x93391bD1...484B60eA2
0 ETH0.000132341.00051839
Verify Remote Ba...58689762024-05-09 17:58:00616 days ago1715277480IN
0x93391bD1...484B60eA2
0 ETH0.000970287.33684037
Add Verifiers58479902024-05-06 10:42:00620 days ago1714992120IN
0x93391bD1...484B60eA2
0 ETH0.0307371159.86425058
Set Threshold58479902024-05-06 10:42:00620 days ago1714992120IN
0x93391bD1...484B60eA2
0 ETH0.0028085959.86425058
Set Gas Oracle58479902024-05-06 10:42:00620 days ago1714992120IN
0x93391bD1...484B60eA2
0 ETH0.0029802859.86425058
Set Fee Recipien...58479902024-05-06 10:42:00620 days ago1714992120IN
0x93391bD1...484B60eA2
0 ETH0.0028241559.86425058
Set Claimer Frac...58479902024-05-06 10:42:00620 days ago1714992120IN
0x93391bD1...484B60eA2
0 ETH0.0028103259.86425058

Latest 17 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Amount
Request Batch Ve...58829772024-05-11 20:14:00614 days ago1715458440
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58771092024-05-10 22:39:48615 days ago1715380788
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58770862024-05-10 22:34:36615 days ago1715380476
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58761402024-05-10 19:10:36615 days ago1715368236
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58761282024-05-10 19:08:00615 days ago1715368080
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58760232024-05-10 18:46:12615 days ago1715366772
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58760112024-05-10 18:43:36615 days ago1715366616
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58753802024-05-10 16:28:00615 days ago1715358480
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58753752024-05-10 16:26:48615 days ago1715358408
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58751032024-05-10 15:28:00615 days ago1715354880
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58750062024-05-10 15:07:00615 days ago1715353620
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58710982024-05-10 1:24:36616 days ago1715304276
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58710762024-05-10 1:20:12616 days ago1715304012
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58705752024-05-09 23:34:24616 days ago1715297664
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58698362024-05-09 21:00:12616 days ago1715288412
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58698242024-05-09 20:57:48616 days ago1715288268
0x93391bD1...484B60eA2
0.0000001 ETH
Request Batch Ve...58689672024-05-09 17:56:12616 days ago1715277372
0x93391bD1...484B60eA2
0.0000001 ETH
Loading...
Loading

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SynapseModule

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

import {SynapseModuleEvents} from "../events/SynapseModuleEvents.sol";
import {ISynapseGasOracle} from "../interfaces/ISynapseGasOracle.sol";
import {ISynapseModule} from "../interfaces/ISynapseModule.sol";
import {ThresholdECDSA} from "../libs/ThresholdECDSA.sol";

import {ClaimableFees} from "../fees/ClaimableFees.sol";

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

contract SynapseModule is InterchainModule, ClaimableFees, Ownable, SynapseModuleEvents, ISynapseModule {
    // TODO: make sure this is a good enough default value
    uint256 public constant DEFAULT_VERIFY_GAS_LIMIT = 100_000;

    /// @dev Struct to hold the verifiers and the threshold for the module.
    ThresholdECDSA internal _verifiers;

    /// @dev Gas limit for the verifyBatch function on the remote chain.
    mapping(uint64 chainId => uint256 gasLimit) internal _verifyGasLimit;
    /// @dev Hash of the last gas data sent to the remote chain.
    mapping(uint64 chainId => bytes32 gasDataHash) internal _lastGasDataHash;
    /// @dev Nonce of the last gas data received from the remote chain.
    mapping(uint64 chainId => uint64 gasDataNonce) internal _lastGasDataNonce;

    /// @dev Fraction of the fees to be paid to the claimer (100% = 1e18).
    uint256 internal _claimerFraction;
    /// @dev Recipient of the fees collected by the module.
    address internal _feeRecipient;

    /// @inheritdoc ISynapseModule
    address public gasOracle;

    constructor(address interchainDB, address owner_) InterchainModule(interchainDB) Ownable(owner_) {
        // ThresholdECDSA throws an explicit error if threshold is not set, so default value is not needed
    }

    // ═══════════════════════════════════════════════ PERMISSIONED ════════════════════════════════════════════════════

    /// @inheritdoc ISynapseModule
    function addVerifier(address verifier) external onlyOwner {
        _addVerifier(verifier);
    }

    /// @inheritdoc ISynapseModule
    function addVerifiers(address[] calldata verifiers) external onlyOwner {
        uint256 length = verifiers.length;
        for (uint256 i = 0; i < length; ++i) {
            _addVerifier(verifiers[i]);
        }
    }

    /// @inheritdoc ISynapseModule
    function removeVerifier(address verifier) external onlyOwner {
        _removeVerifier(verifier);
    }

    /// @inheritdoc ISynapseModule
    function removeVerifiers(address[] calldata verifiers) external onlyOwner {
        uint256 length = verifiers.length;
        for (uint256 i = 0; i < length; ++i) {
            _removeVerifier(verifiers[i]);
        }
    }

    /// @inheritdoc ISynapseModule
    function setThreshold(uint256 threshold) external onlyOwner {
        _verifiers.modifyThreshold(threshold);
        emit ThresholdSet(threshold);
    }

    /// @inheritdoc ISynapseModule
    function setFeeRecipient(address feeRecipient) external onlyOwner {
        if (feeRecipient == address(0)) {
            revert SynapseModule__FeeRecipientZeroAddress();
        }
        _feeRecipient = feeRecipient;
        emit FeeRecipientSet(feeRecipient);
    }

    /// @inheritdoc ISynapseModule
    function setClaimerFraction(uint256 claimerFraction) external onlyOwner {
        if (claimerFraction > MAX_CLAIMER_FRACTION) {
            revert ClaimableFees__ClaimerFractionAboveMax(claimerFraction, MAX_CLAIMER_FRACTION);
        }
        _claimerFraction = claimerFraction;
        emit ClaimerFractionSet(claimerFraction);
    }

    /// @inheritdoc ISynapseModule
    function setGasOracle(address gasOracle_) external onlyOwner {
        if (gasOracle_.code.length == 0) {
            revert SynapseModule__GasOracleNotContract(gasOracle_);
        }
        gasOracle = gasOracle_;
        emit GasOracleSet(gasOracle_);
    }

    /// @inheritdoc ISynapseModule
    function setVerifyGasLimit(uint64 chainId, uint256 gasLimit) external onlyOwner {
        _verifyGasLimit[chainId] = gasLimit;
        emit VerifyGasLimitSet(chainId, gasLimit);
    }

    // ══════════════════════════════════════════════ PERMISSIONLESS ═══════════════════════════════════════════════════

    /// @inheritdoc ISynapseModule
    function verifyRemoteBatch(bytes calldata encodedBatch, bytes calldata signatures) external {
        bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash(keccak256(encodedBatch));
        _verifiers.verifySignedHash(ethSignedHash, signatures);
        _verifyBatch(encodedBatch);
    }

    // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

    /// @inheritdoc ISynapseModule
    function getVerifiers() external view returns (address[] memory) {
        return _verifiers.getSigners();
    }

    /// @inheritdoc ISynapseModule
    function isVerifier(address account) external view returns (bool) {
        return _verifiers.isSigner(account);
    }

    /// @inheritdoc ISynapseModule
    function getThreshold() public view returns (uint256) {
        return _verifiers.getThreshold();
    }

    /// @inheritdoc ISynapseModule
    function getVerifyGasLimit(uint64 chainId) public view override returns (uint256 gasLimit) {
        gasLimit = _verifyGasLimit[chainId];
        if (gasLimit == 0) {
            gasLimit = DEFAULT_VERIFY_GAS_LIMIT;
        }
    }

    /// @notice Returns the amount of fees that can be claimed.
    function getClaimableAmount() public view override returns (uint256) {
        return address(this).balance;
    }

    /// @notice Returns the fraction of the fees that the claimer will receive.
    /// The result is in the range [0, 1e18], where 1e18 is 100%.
    function getClaimerFraction() public view override returns (uint256) {
        return _claimerFraction;
    }

    /// @notice Returns the address that will receive the claimed fees.
    function getFeeRecipient() public view override returns (address) {
        return _feeRecipient;
    }

    // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════

    /// @dev Adds a verifier to the module. Permissions should be checked in the calling function.
    function _addVerifier(address verifier) internal {
        _verifiers.addSigner(verifier);
        emit VerifierAdded(verifier);
    }

    /// @dev Removes a verifier from the module. Permissions should be checked in the calling function.
    function _removeVerifier(address verifier) internal {
        _verifiers.removeSigner(verifier);
        emit VerifierRemoved(verifier);
    }

    /// @dev Hook that is called before the fees are claimed.
    /// Useful if the inheriting contract needs to manage the state when the fees are claimed.
    // solhint-disable-next-line no-empty-blocks
    function _beforeFeesClaimed(uint256, uint256) internal override {
        // No op, as the claimable amount is tracked as the contract balance
    }

    /// @dev Internal logic to fill the module data for the specified destination chain.
    function _fillModuleData(
        uint64 dstChainId,
        uint64 // dbNonce
    )
        internal
        override
        returns (bytes memory moduleData)
    {
        moduleData = _getSynapseGasOracle().getLocalGasData();
        // Exit early if data is empty
        if (moduleData.length == 0) {
            return moduleData;
        }
        bytes32 dataHash = keccak256(moduleData);
        // Don't send the same data twice
        if (dataHash == _lastGasDataHash[dstChainId]) {
            moduleData = "";
        } else {
            _lastGasDataHash[dstChainId] = dataHash;
            emit GasDataSent(dstChainId, moduleData);
        }
    }

    /// @dev Internal logic to handle the auxiliary module data relayed from the remote chain.
    function _receiveModuleData(uint64 srcChainId, uint64 dbNonce, bytes memory moduleData) internal override {
        // Exit early if data is empty
        if (moduleData.length == 0) {
            return;
        }
        // Don't process outdated data
        uint64 lastNonce = _lastGasDataNonce[srcChainId];
        if (lastNonce == 0 || lastNonce < dbNonce) {
            _lastGasDataNonce[srcChainId] = dbNonce;
            _getSynapseGasOracle().receiveRemoteGasData(srcChainId, moduleData);
            emit GasDataReceived(srcChainId, moduleData);
        }
    }

    // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════

    /// @dev Internal logic to get the module fee for verifying an batch on the specified destination chain.
    function _getModuleFee(
        uint64 dstChainId,
        uint64 // dbNonce
    )
        internal
        view
        override
        returns (uint256)
    {
        // On the remote chain the verifyRemoteBatch(batch, signatures) function will be called.
        // We need to figure out the calldata size for the remote call.
        // selector (4 bytes) + batch + signatures
        // batch is 32 (length) + 32*3 (fields) = 128
        // signatures: 32 (length) + 65*threshold (padded up to be a multiple of 32 bytes)
        // Total formula is: 4 + 32 (batch offset) + 32 (signatures offset) + 128 + 32
        return _getSynapseGasOracle().estimateTxCostInLocalUnits({
            remoteChainId: dstChainId,
            gasLimit: getVerifyGasLimit(dstChainId),
            calldataSize: 260 + 64 * getThreshold()
        });
    }

    /// @dev Internal logic to get the Synapse Gas Oracle. Reverts if the gas oracle is not set.
    function _getSynapseGasOracle() internal view returns (ISynapseGasOracle synapseGasOracle) {
        synapseGasOracle = ISynapseGasOracle(gasOracle);
        if (address(synapseGasOracle) == address(0)) {
            revert SynapseModule__GasOracleZeroAddress();
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {InterchainModuleEvents} from "../events/InterchainModuleEvents.sol";
import {IInterchainDB} from "../interfaces/IInterchainDB.sol";
import {IInterchainModule} from "../interfaces/IInterchainModule.sol";

import {InterchainBatch, InterchainBatchLib} from "../libs/InterchainBatch.sol";
import {ModuleBatchLib} from "../libs/ModuleBatch.sol";
import {VersionedPayloadLib} from "../libs/VersionedPayload.sol";

import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

/// @notice Common logic for all Interchain Modules.
abstract contract InterchainModule is InterchainModuleEvents, IInterchainModule {
    using VersionedPayloadLib for bytes;

    address public immutable INTERCHAIN_DB;

    constructor(address interchainDB) {
        INTERCHAIN_DB = interchainDB;
    }

    /// @inheritdoc IInterchainModule
    function requestBatchVerification(
        uint64 dstChainId,
        uint64 batchNonce,
        bytes calldata versionedBatch
    )
        external
        payable
    {
        if (msg.sender != INTERCHAIN_DB) {
            revert InterchainModule__CallerNotInterchainDB(msg.sender);
        }
        if (dstChainId == block.chainid) {
            revert InterchainModule__ChainIdNotRemote(dstChainId);
        }
        uint256 requiredFee = _getModuleFee(dstChainId, batchNonce);
        if (msg.value < requiredFee) {
            revert InterchainModule__FeeAmountBelowMin({feeAmount: msg.value, minRequired: requiredFee});
        }
        bytes memory moduleData = _fillModuleData(dstChainId, batchNonce);
        bytes memory encodedBatch = ModuleBatchLib.encodeVersionedModuleBatch(versionedBatch, moduleData);
        bytes32 ethSignedBatchHash = MessageHashUtils.toEthSignedMessageHash(keccak256(encodedBatch));
        _requestVerification(dstChainId, encodedBatch);
        emit BatchVerificationRequested(dstChainId, encodedBatch, ethSignedBatchHash);
    }

    /// @inheritdoc IInterchainModule
    function getModuleFee(uint64 dstChainId, uint64 dbNonce) external view returns (uint256) {
        return _getModuleFee(dstChainId, dbNonce);
    }

    /// @dev Should be called once the Module has verified the batch and needs to signal this
    /// to the InterchainDB.
    function _verifyBatch(bytes memory encodedModuleBatch) internal {
        (bytes memory versionedBatch, bytes memory moduleData) =
            ModuleBatchLib.decodeVersionedModuleBatch(encodedModuleBatch);
        InterchainBatch memory batch = InterchainBatchLib.decodeBatchFromMemory(versionedBatch.getPayloadFromMemory());
        if (batch.srcChainId == block.chainid) {
            revert InterchainModule__ChainIdNotRemote(batch.srcChainId);
        }
        IInterchainDB(INTERCHAIN_DB).verifyRemoteBatch(versionedBatch);
        _receiveModuleData(batch.srcChainId, batch.dbNonce, moduleData);
        emit BatchVerified(
            batch.srcChainId, encodedModuleBatch, MessageHashUtils.toEthSignedMessageHash(keccak256(encodedModuleBatch))
        );
    }

    // solhint-disable no-empty-blocks
    /// @dev Internal logic to request the verification of an batch on the destination chain.
    function _requestVerification(uint64 dstChainId, bytes memory encodedBatch) internal virtual {}

    /// @dev Internal logic to fill the module data for the specified destination chain.
    function _fillModuleData(uint64 dstChainId, uint64 dbNonce) internal virtual returns (bytes memory) {}

    /// @dev Internal logic to handle the auxiliary module data relayed from the remote chain.
    function _receiveModuleData(uint64 srcChainId, uint64 dbNonce, bytes memory moduleData) internal virtual {}

    /// @dev Internal logic to get the module fee for verifying an batch on the specified destination chain.
    function _getModuleFee(uint64 dstChainId, uint64 dbNonce) internal view virtual returns (uint256);
}

File 3 of 28 : SynapseModuleEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract SynapseModuleEvents {
    /// @notice Emitted when a verifier is added. The verifier signatures are required to verify a batch.
    /// @param verifier         The address of the verifier.
    event VerifierAdded(address verifier);

    /// Emitted when a verifier is removed.
    /// @param verifier         The address of the verifier.
    event VerifierRemoved(address verifier);

    /// @notice Emitted when a threshold is set.
    /// The threshold is the minimum number of verifiers required to verify a batch.
    /// @param threshold        The threshold value.
    event ThresholdSet(uint256 threshold);

    /// @notice Emitted when a gas oracle is set. The gas oracle will be used to estimate the gas cost of
    /// verifying a batch on the remote chain.
    /// @param gasOracle        The address of the gas oracle.
    event GasOracleSet(address gasOracle);

    /// @notice Emitted when the gas limit estimate is set for a chain.
    /// @param chainId          The chain ID of the chain.
    /// @param gasLimit         The gas limit estimate for verifying a batch on the chain.
    event VerifyGasLimitSet(uint64 chainId, uint256 gasLimit);

    /// @notice Emitted when the gas data from the gas oracle is sent to the remote chain.
    /// @param dstChainId       The chain ID of the destination chain.
    /// @param data             The encoded gas data.
    event GasDataSent(uint64 dstChainId, bytes data);

    /// @notice Emitted when the gas data from the remote chain is received.
    /// @param srcChainId       The chain ID of the source chain.
    /// @param data             The encoded gas data.
    event GasDataReceived(uint64 srcChainId, bytes data);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface ISynapseGasOracle is IGasOracle {
    /// @notice Allows Synapse Module to pass the gas data from a remote chain to the Gas Oracle.
    /// @dev Could only be called by Synapse Module.
    /// @param srcChainId        The chain id of the remote chain.
    /// @param data              The gas data from the remote chain.
    function receiveRemoteGasData(uint64 srcChainId, bytes calldata data) external;

    /// @notice Gets the gas data for the local chain.
    function getLocalGasData() external view returns (bytes memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface ISynapseModule is IInterchainModule {
    error SynapseModule__GasOracleNotContract(address gasOracle);
    error SynapseModule__GasOracleZeroAddress();
    error SynapseModule__FeeRecipientZeroAddress();

    // ═══════════════════════════════════════════════ PERMISSIONED ════════════════════════════════════════════════════

    /// @notice Adds a new verifier to the module.
    /// @dev Could be only called by the owner. Will revert if the verifier is already added.
    /// @param verifier     The address of the verifier to add
    function addVerifier(address verifier) external;

    /// @notice Adds a list of new verifiers to the module.
    /// @dev Could be only called by the owner. Will revert if any of the verifiers is already added.
    /// @param verifiers    The list of addresses of the verifiers to add
    function addVerifiers(address[] calldata verifiers) external;

    /// @notice Removes a verifier from the module.
    /// @dev Could be only called by the owner. Will revert if the verifier is not added.
    /// @param verifier     The address of the verifier to remove
    function removeVerifier(address verifier) external;

    /// @notice Removes a list of verifiers from the module.
    /// @dev Could be only called by the owner. Will revert if any of the verifiers is not added.
    /// @param verifiers    The list of addresses of the verifiers to remove
    function removeVerifiers(address[] calldata verifiers) external;

    /// @notice Sets the threshold of the module.
    /// @dev Could be only called by the owner. Will revert if the threshold is zero.
    /// @param threshold    The new threshold value
    function setThreshold(uint256 threshold) external;

    /// @notice Sets the address of the fee collector, which will have the verification fees forwarded to it.
    /// @dev Could be only called by the owner.
    /// @param feeRecipient     The address of the fee collector
    function setFeeRecipient(address feeRecipient) external;

    /// @notice Sets the fraction of the accumulated fees to be paid to caller of `claimFees`.
    /// This encourages rational actors to call the function as soon as claim fee is higher than the gas cost.
    /// @dev Could be only called by the owner. Could not exceed 1%.
    /// @param claimerFraction  The fraction of the fees to be paid to the claimer (100% = 1e18)
    function setClaimerFraction(uint256 claimerFraction) external;

    /// @notice Sets the address of the gas oracle to be used for estimating the verification fees.
    /// @dev Could be only called by the owner. Will revert if the gas oracle is not a contract.
    /// @param gasOracle_   The address of the gas oracle contract
    function setGasOracle(address gasOracle_) external;

    /// @notice Sets the estimated gas limit for verifying a batch on the given chain.
    /// @dev Could be only called by the owner.
    /// @param chainId      The chain ID for which to set the gas limit
    /// @param gasLimit     The new gas limit
    function setVerifyGasLimit(uint64 chainId, uint256 gasLimit) external;

    // ══════════════════════════════════════════════ PERMISSIONLESS ═══════════════════════════════════════════════════

    /// @notice Verifies a batch from the remote chain using a set of verifier signatures.
    /// If the threshold is met, the batch will be marked as verified in the Interchain DataBase.
    /// @dev List of recovered signers from the signatures must be sorted in the ascending order.
    /// @param encodedBatch The encoded batch to verify
    /// @param signatures   Signatures used to verify the batch, concatenated
    function verifyRemoteBatch(bytes calldata encodedBatch, bytes calldata signatures) external;

    // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

    /// @notice Returns the address of the gas oracle used for estimating the verification fees.
    function gasOracle() external view returns (address);

    /// @notice Returns the list of verifiers for the module.
    function getVerifiers() external view returns (address[] memory);

    /// @notice Gets the threshold of the module.
    /// This is the minimum number of signatures required for verification.
    function getThreshold() external view returns (uint256);

    /// @notice Checks if the given account is a verifier for the module.
    function isVerifier(address account) external view returns (bool);

    /// @notice Returns the estimated gas limit for verifying a batch on the given chain.
    /// Note: this defaults to DEFAULT_VERIFY_GAS_LIMIT if not set.
    function getVerifyGasLimit(uint64 chainId) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

struct ThresholdECDSA {
    uint256 _threshold;
    EnumerableSet.AddressSet _signers;
}

using ThresholdECDSALib for ThresholdECDSA global;

// solhint-disable code-complexity
library ThresholdECDSALib {
    using EnumerableSet for EnumerableSet.AddressSet;

    uint256 private constant SIGNATURE_LENGTH = 65;

    error ThresholdECDSA__RecoveredSignersNotSorted();
    error ThresholdECDSA__SignaturesAmountBelowThreshold(uint256 signaturesAmount, uint256 threshold);
    error ThresholdECDSA__SignaturesPayloadLengthInvalid(uint256 length);
    error ThresholdECDSA__SignerAlreadyAdded(address account);
    error ThresholdECDSA__SignerNotAdded(address account);
    error ThresholdECDSA__SignerRecoveryFailed(bytes signature);
    error ThresholdECDSA__SignerZeroAddress();
    error ThresholdECDSA__ThresholdZero();

    /// @notice Adds a new signer to the list of signers.
    /// @dev Will revert if the account is already a signer.
    function addSigner(ThresholdECDSA storage self, address account) internal {
        if (account == address(0)) revert ThresholdECDSA__SignerZeroAddress();
        bool added = self._signers.add(account);
        if (!added) {
            revert ThresholdECDSA__SignerAlreadyAdded(account);
        }
    }

    /// @notice Removes a signer from the list of signers.
    /// @dev Will revert if the account is not a signer.
    function removeSigner(ThresholdECDSA storage self, address account) internal {
        bool removed = self._signers.remove(account);
        if (!removed) {
            revert ThresholdECDSA__SignerNotAdded(account);
        }
    }

    /// @notice Modifies the threshold of signatures required.
    function modifyThreshold(ThresholdECDSA storage self, uint256 threshold) internal {
        if (threshold == 0) {
            revert ThresholdECDSA__ThresholdZero();
        }
        self._threshold = threshold;
    }

    /// @notice Checks if the account is a signer.
    function isSigner(ThresholdECDSA storage self, address account) internal view returns (bool) {
        return self._signers.contains(account);
    }

    /// @notice Gets the full list of signers.
    function getSigners(ThresholdECDSA storage self) internal view returns (address[] memory) {
        return self._signers.values();
    }

    /// @notice Gets the threshold of signatures required.
    function getThreshold(ThresholdECDSA storage self) internal view returns (uint256) {
        return self._threshold;
    }

    /// @notice Verifies that the number of signatures is greater than or equal to the threshold.
    /// Note: the list of signers recovered from the signatures is required to be sorted in ascending order.
    /// @dev Will revert if either of the conditions is met:
    /// - Threshold is not configured.
    /// - Any of the payloads is not a valid signature payload.
    /// - The number of signatures is less than the threshold.
    /// - The recovered list of signers is not sorted in the ascending order.
    function verifySignedHash(ThresholdECDSA storage self, bytes32 hash, bytes calldata signatures) internal view {
        // Figure out the signaturesAmount of signatures provided
        uint256 signaturesAmount = signatures.length / SIGNATURE_LENGTH;
        if (signaturesAmount * SIGNATURE_LENGTH != signatures.length) {
            revert ThresholdECDSA__SignaturesPayloadLengthInvalid(signatures.length);
        }
        // First, check that threshold is configured and enough signatures are provided
        uint256 threshold = self._threshold;
        if (threshold == 0) {
            revert ThresholdECDSA__ThresholdZero();
        }
        uint256 offset = 0;
        uint256 validSignatures = 0;
        address lastSigner = address(0);
        for (uint256 i = 0; i < signaturesAmount; ++i) {
            bytes memory signature = signatures[offset:offset + SIGNATURE_LENGTH];
            (address recovered, ECDSA.RecoverError error,) = ECDSA.tryRecover(hash, signature);
            if (error != ECDSA.RecoverError.NoError) {
                revert ThresholdECDSA__SignerRecoveryFailed(signature);
            }
            // Check that the recovered addresses list is strictly increasing
            if (recovered <= lastSigner) {
                revert ThresholdECDSA__RecoveredSignersNotSorted();
            }
            lastSigner = recovered;
            // Since the signers list is sorted, every time we find a valid signer it's not a duplicate
            if (isSigner(self, recovered)) {
                validSignatures += 1;
            }
            offset += SIGNATURE_LENGTH;
        }
        if (validSignatures < threshold) {
            revert ThresholdECDSA__SignaturesAmountBelowThreshold(validSignatures, threshold);
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ClaimableFeesEvents} from "../events/ClaimableFeesEvents.sol";
import {IClaimableFees} from "../interfaces/IClaimableFees.sol";

import {Address} from "@openzeppelin/contracts/utils/Address.sol";

/// @notice A simple abstraction for a contract that is collecting fees in native chain token.
/// The claim process could be performed by anyone, but the fees will be sent to
/// the predefined address. The claimer will receive a fraction of the fees to offset
/// the gas costs.
/// @dev The contract is implemented in a stateless way to allow the inheriting
/// contract to be immutable or upgradeable.
abstract contract ClaimableFees is ClaimableFeesEvents, IClaimableFees {
    uint256 private constant FEE_PRECISION = 1e18;
    /// @dev The maximum fraction that the claimer can receive is 1%.
    uint256 internal constant MAX_CLAIMER_FRACTION = 1e16;

    /// @notice Transfers the accumulated fees to the fee recipient.
    /// Message caller receives a fraction of the fees as a reward to offset the gas costs.
    /// The reward amount could be obtained by calling the `getClaimerReward` function beforehand.
    /// @dev Will revert if the claimable amount is zero or the fee recipient is not set.
    function claimFees() external {
        uint256 amount = getClaimableAmount();
        if (amount == 0) {
            revert ClaimableFees__FeeAmountZero();
        }
        address recipient = getFeeRecipient();
        if (recipient == address(0)) {
            revert ClaimableFees__FeeRecipientZeroAddress();
        }
        // Subtract the claimer reward from the total amount
        uint256 reward = _getClaimerReward(amount);
        _beforeFeesClaimed(amount, reward);
        // We can do unchecked subtraction because `getClaimerReward` ensures that `reward <= amount * 0.01`
        unchecked {
            amount -= reward;
        }
        // Emit the event before transferring the fees
        emit FeesClaimed(recipient, amount, msg.sender, reward);
        Address.sendValue(payable(recipient), amount);
        Address.sendValue(payable(msg.sender), reward);
    }

    /// @notice Returns the amount of native chain token that the claimer will receive
    /// after calling the `claimFees` function.
    function getClaimerReward() external view returns (uint256) {
        uint256 amount = getClaimableAmount();
        return _getClaimerReward(amount);
    }

    /// @notice Returns the amount of fees that can be claimed.
    function getClaimableAmount() public view virtual returns (uint256);

    /// @notice Returns the fraction of the fees that the claimer will receive.
    /// The result is in the range [0, 1e18], where 1e18 is 100%.
    function getClaimerFraction() public view virtual returns (uint256);

    /// @notice Returns the address that will receive the claimed fees.
    function getFeeRecipient() public view virtual returns (address);

    /// @dev Hook that is called before the fees are claimed.
    /// Useful if the inheriting contract needs to manage the state when the fees are claimed.
    function _beforeFeesClaimed(uint256 fullAmount, uint256 reward) internal virtual;

    /// @dev Returns the claimer reward for the given amount.
    function _getClaimerReward(uint256 amount) internal view returns (uint256) {
        uint256 fraction = getClaimerFraction();
        if (fraction > MAX_CLAIMER_FRACTION) {
            revert ClaimableFees__ClaimerFractionAboveMax(fraction, MAX_CLAIMER_FRACTION);
        }
        // The returned value is in the range [0, amount * 0.01]
        return (amount * fraction) / FEE_PRECISION;
    }
}

// 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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

File 10 of 28 : InterchainModuleEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract InterchainModuleEvents {
    /// @notice Emitted when a batch verification on a remote chain is requested.
    /// @param dstChainId           The chain ID of the destination chain.
    /// @param batch                The encoded batch to be verified.
    /// @param ethSignedBatchHash   The digest of the batch (EIP-191 personal signed).
    event BatchVerificationRequested(uint64 indexed dstChainId, bytes batch, bytes32 ethSignedBatchHash);

    /// @notice Emitted when a batch from the remote chain is verified.
    /// @param srcChainId           The chain ID of the source chain.
    /// @param batch                The encoded batch that was verified.
    /// @param ethSignedBatchHash   The digest of the batch (EIP-191 personal signed).
    event BatchVerified(uint64 indexed srcChainId, bytes batch, bytes32 ethSignedBatchHash);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {InterchainBatch} from "../libs/InterchainBatch.sol";
import {InterchainEntry} from "../libs/InterchainEntry.sol";

interface IInterchainDB {
    error InterchainDB__BatchConflict(address module, bytes32 existingBatchRoot, InterchainBatch newBatch);
    error InterchainDB__BatchVersionMismatch(uint16 version, uint16 required);
    error InterchainDB__ChainIdNotRemote(uint64 chainId);
    error InterchainDB__EntryIndexOutOfRange(uint64 dbNonce, uint64 entryIndex, uint64 batchSize);
    error InterchainDB__EntryRangeInvalid(uint64 dbNonce, uint64 start, uint64 end);
    error InterchainDB__FeeAmountBelowMin(uint256 feeAmount, uint256 minRequired);
    error InterchainDB__ModulesNotProvided();

    /// @notice Write data to the Interchain DataBase as a new entry in the current batch.
    /// Note: there are no guarantees that this entry will be available for reading on any of the remote chains.
    /// Use `requestBatchVerification` to ensure that the entry is available for reading on the destination chain.
    /// @param dataHash     The hash of the data to be written to the Interchain DataBase as a new entry
    /// @return dbNonce     The database nonce of the batch containing the written entry
    /// @return entryIndex  The index of the written entry within the batch
    function writeEntry(bytes32 dataHash) external returns (uint64 dbNonce, uint64 entryIndex);

    /// @notice Request the given Interchain Modules to verify an existing batch.
    /// If the batch is not finalized, the module will verify it after finalization.
    /// For the finalized batch the batch root is already available, and the module can verify it immediately.
    /// Note: every module has a separate fee paid in the native gas token of the source chain,
    /// and `msg.value` must be equal to the sum of all fees.
    /// Note: this method is permissionless, and anyone can request verification for any batch.
    /// @dev Will revert if the batch with the given nonce does not exist.
    /// @param dstChainId    The chain id of the destination chain
    /// @param dbNonce       The database nonce of the existing batch
    /// @param srcModules    The source chain addresses of the Interchain Modules to use for verification
    function requestBatchVerification(
        uint64 dstChainId,
        uint64 dbNonce,
        address[] memory srcModules
    )
        external
        payable;

    /// @notice Write data to the Interchain DataBase as a new entry in the current batch.
    /// Then request the Interchain Modules to verify the batch containing the written entry on the destination chain.
    /// See `writeEntry` and `requestBatchVerification` for more details.
    /// @dev Will revert if the empty array of modules is provided.
    /// @param dstChainId   The chain id of the destination chain
    /// @param dataHash     The hash of the data to be written to the Interchain DataBase as a new entry
    /// @param srcModules   The source chain addresses of the Interchain Modules to use for verification
    /// @return dbNonce     The database nonce of the batch containing the written entry
    /// @return entryIndex  The index of the written entry within the batch
    function writeEntryWithVerification(
        uint64 dstChainId,
        bytes32 dataHash,
        address[] memory srcModules
    )
        external
        payable
        returns (uint64 dbNonce, uint64 entryIndex);

    /// @notice Allows the Interchain Module to verify the batch coming from the remote chain.
    /// The module SHOULD verify the exact finalized batch from the remote chain. If the batch with a given nonce
    /// is not finalized or does not exist, module CAN verify it with an empty root value. Once the batch is
    /// finalized, the module SHOULD re-verify the batch with the correct root value.
    /// Note: The DB will only accept the batch of the same version as the DB itself.
    /// @dev Will revert if the batch with the same nonce but a different non-empty root is already verified.
    /// @param versionedBatch   The versioned Interchain Batch to verify
    function verifyRemoteBatch(bytes memory versionedBatch) external;

    // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

    /// @notice Get the fee for writing data to the Interchain DataBase, and verifying it on the destination chain
    /// using the provided Interchain Modules.
    /// @dev Will revert if the empty array of modules is provided.
    /// @param dstChainId   The chain id of the destination chain
    /// @param srcModules   The source chain addresses of the Interchain Modules to use for verification
    function getInterchainFee(uint64 dstChainId, address[] memory srcModules) external view returns (uint256);

    /// @notice Returns the list of leafs of the finalized batch with the given nonce.
    /// Note: the leafs are ordered by the index of the written entry in the current batch,
    /// and the leafs value match the value of the written entry (srcWriter + dataHash hashed together).
    /// @dev Will revert if the batch with the given nonce does not exist, or is not finalized.
    /// @param dbNonce      The database nonce of the finalized batch
    function getBatchLeafs(uint64 dbNonce) external view returns (bytes32[] memory);

    /// @notice Returns the list of leafs of the finalized batch with the given nonce,
    /// paginated by the given start and end indexes. The end index is exclusive.
    /// Note: this is useful when the batch contains a large number of leafs, and calling `getBatchLeafs`
    /// would result in a gas limit exceeded error.
    /// @dev Will revert if the batch with the given nonce does not exist, or is not finalized.
    /// Will revert if the provided range is invalid.
    /// @param dbNonce      The database nonce of the finalized batch
    /// @param start        The start index of the paginated leafs, inclusive
    /// @param end          The end index of the paginated leafs, exclusive
    function getBatchLeafsPaginated(
        uint64 dbNonce,
        uint64 start,
        uint64 end
    )
        external
        view
        returns (bytes32[] memory);

    /// @notice Returns the size of the finalized batch with the given nonce.
    /// @dev Will revert if the batch with the given nonce does not exist, or is not finalized.
    /// @param dbNonce      The database nonce of the finalized batch
    function getBatchSize(uint64 dbNonce) external view returns (uint64);

    /// @notice Get the finalized Interchain Batch with the given nonce.
    /// @dev Will revert if the batch with the given nonce does not exist, or is not finalized.
    /// @param dbNonce      The database nonce of the finalized batch
    function getBatch(uint64 dbNonce) external view returns (InterchainBatch memory);

    /// @notice Get the versioned Interchain Batch with the given nonce.
    /// Note: will return a batch with an empty root if the batch does not exist, or is not finalized.
    /// @param dbNonce      The database nonce of the batch
    function getVersionedBatch(uint64 dbNonce) external view returns (bytes memory);

    /// @notice Get the Interchain Entry's value written on the local chain with the given batch nonce and entry index.
    /// Entry value is calculated as the hash of the writer address and the written data hash.
    /// Note: the batch does not have to be finalized to fetch the entry value.
    /// @dev Will revert if the batch with the given nonce does not exist,
    /// or the entry with the given index does not exist within the batch.
    /// @param dbNonce      The database nonce of the existing batch
    /// @param entryIndex   The index of the written entry within the batch
    function getEntryValue(uint64 dbNonce, uint64 entryIndex) external view returns (bytes32);

    /// @notice Get the Merkle proof of inclusion for the entry with the given index
    /// in the finalized batch with the given nonce.
    /// @dev Will revert if the batch with the given nonce does not exist, or is not finalized.
    /// Will revert if the entry with the given index does not exist within the batch.
    /// @param dbNonce      The database nonce of the finalized batch
    /// @param entryIndex   The index of the written entry within the batch
    /// @return proof       The Merkle proof of inclusion for the entry
    function getEntryProof(uint64 dbNonce, uint64 entryIndex) external view returns (bytes32[] memory proof);

    /// @notice Get the nonce of the database, which is incremented every time a new batch is finalized.
    /// This is the nonce of the current non-finalized batch.
    function getDBNonce() external view returns (uint64);

    /// @notice Get the index of the next entry to be written to the database.
    /// @return dbNonce      The database nonce of the batch including the next entry
    /// @return entryIndex   The index of the next entry within that batch
    function getNextEntryIndex() external view returns (uint64 dbNonce, uint64 entryIndex);

    /// @notice Check if the batch is verified by the Interchain Module on the destination chain.
    /// Note: returned zero value indicates that the module has not verified the batch.
    /// @param dstModule    The destination chain addresses of the Interchain Modules to use for verification
    /// @param batch        The Interchain Batch to check
    /// @return moduleVerifiedAt    The block timestamp at which the batch was verified by the module,
    ///                             or ZERO if the module has not verified the batch.
    function checkBatchVerification(
        address dstModule,
        InterchainBatch memory batch
    )
        external
        view
        returns (uint256 moduleVerifiedAt);

    /// @notice Get the batch root containing the Interchain Entry with the given index.
    /// @param entry         The Interchain Entry to get the batch root for
    /// @param proof         The Merkle proof of inclusion for the entry in the batch
    function getBatchRoot(InterchainEntry memory entry, bytes32[] memory proof) external pure returns (bytes32);

    /// @notice Get the version of the Interchain DataBase.
    // solhint-disable-next-line func-name-mixedcase
    function DB_VERSION() external pure returns (uint16);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Every Module may opt a different method to confirm the verified entries on destination chain,
/// therefore this is not a part of a common interface.
interface IInterchainModule {
    error InterchainModule__CallerNotInterchainDB(address caller);
    error InterchainModule__ChainIdNotRemote(uint64 chainId);
    error InterchainModule__FeeAmountBelowMin(uint256 feeAmount, uint256 minRequired);

    /// @notice Request the verification of a batch from the Interchain DataBase by the module.
    /// If the batch is not yet finalized, the verification on destination chain will be delayed until
    /// the finalization is done and batch root is saved on the source chain.
    /// Note: a fee is paid to the module for verification, and could be retrieved by using `getModuleFee`.
    /// Note: this will eventually trigger `InterchainDB.verifyRemoteBatch(batch)` function on destination chain,
    /// with no guarantee of ordering.
    /// @dev Could be only called by the Interchain DataBase contract.
    /// @param dstChainId       The chain id of the destination chain
    /// @param batchNonce       The nonce of the batch on the source chain
    /// @param versionedBatch   The versioned batch to verify
    function requestBatchVerification(
        uint64 dstChainId,
        uint64 batchNonce,
        bytes memory versionedBatch
    )
        external
        payable;

    /// @notice Get the Module fee for verifying a batch on the specified destination chain.
    /// @param dstChainId   The chain id of the destination chain
    /// @param dbNonce      The database nonce of the batch on the source chain
    function getModuleFee(uint64 dstChainId, uint64 dbNonce) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {VersionedPayloadLib} from "./VersionedPayload.sol";

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

type BatchKey is uint128;

/// @notice Struct representing a batch of entries in the Interchain DataBase.
/// Batched entries are put together in a Merkle tree, which root is saved.
/// Batch has a globally unique identifier (key) and a value.
/// - key: srcChainId + dbNonce
/// - value: batchRoot
/// @param srcChainId   The chain id of the source chain
/// @param dbNonce      The database nonce of the batch
/// @param batchRoot    The root of the Merkle tree containing the batched entries
struct InterchainBatch {
    uint64 srcChainId;
    uint64 dbNonce;
    bytes32 batchRoot;
}

/// @dev Signals that the module has not verified any batch with the given key.
uint256 constant BATCH_UNVERIFIED = 0;
/// @dev Signals that the module has verified a conflicting batch with the given key.
uint256 constant BATCH_CONFLICT = type(uint256).max;

library InterchainBatchLib {
    using VersionedPayloadLib for bytes;

    /// @notice Constructs an InterchainBatch struct to be saved on the local chain.
    /// @param dbNonce      The database nonce of the batch
    /// @param batchRoot    The root of the Merkle tree containing the batched entries
    /// @return batch       The constructed InterchainBatch struct
    function constructLocalBatch(
        uint64 dbNonce,
        bytes32 batchRoot
    )
        internal
        view
        returns (InterchainBatch memory batch)
    {
        return InterchainBatch({srcChainId: SafeCast.toUint64(block.chainid), dbNonce: dbNonce, batchRoot: batchRoot});
    }

    /// @notice Encodes the InterchainBatch struct into a non-versioned batch payload.
    function encodeBatch(InterchainBatch memory batch) internal pure returns (bytes memory) {
        return abi.encode(encodeBatchKey(batch.srcChainId, batch.dbNonce), batch.batchRoot);
    }

    /// @notice Decodes the InterchainBatch struct from a non-versioned batch payload in calldata.
    function decodeBatch(bytes calldata data) internal pure returns (InterchainBatch memory batch) {
        BatchKey key;
        (key, batch.batchRoot) = abi.decode(data, (BatchKey, bytes32));
        (batch.srcChainId, batch.dbNonce) = decodeBatchKey(key);
    }

    /// @notice Decodes the InterchainBatch struct from a non-versioned batch payload in memory.
    function decodeBatchFromMemory(bytes memory data) internal pure returns (InterchainBatch memory batch) {
        BatchKey key;
        (key, batch.batchRoot) = abi.decode(data, (BatchKey, bytes32));
        (batch.srcChainId, batch.dbNonce) = decodeBatchKey(key);
    }

    /// @notice Encodes the uint128 key of the batch from uint64 srcChainId and uint64 dbNonce.
    function encodeBatchKey(uint64 srcChainId, uint64 dbNonce) internal pure returns (BatchKey) {
        return BatchKey.wrap((uint128(srcChainId) << 64) | dbNonce);
    }

    /// @notice Decodes the uint128 key of the batch into uint64 srcChainId and uint64 dbNonce.
    function decodeBatchKey(BatchKey key) internal pure returns (uint64 srcChainId, uint64 dbNonce) {
        srcChainId = uint64(BatchKey.unwrap(key) >> 64);
        dbNonce = uint64(BatchKey.unwrap(key));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library ModuleBatchLib {
    /// @notice Encodes the versioned batch and the auxiliary module data into a single bytes array
    /// @param versionedBatch       The versioned batch to encode
    /// @param moduleData           The auxiliary module data to encode
    /// @return encodedModuleBatch  The encoded versioned module batch
    function encodeVersionedModuleBatch(
        bytes memory versionedBatch,
        bytes memory moduleData
    )
        internal
        pure
        returns (bytes memory encodedModuleBatch)
    {
        return abi.encode(versionedBatch, moduleData);
    }

    /// @notice Decodes the bytes array into the versioned batch and the auxiliary module data
    /// @param encodedModuleBatch   The bytes array to decode
    /// @return versionedBatch      The decoded versioned batch
    /// @return moduleData          The decoded auxiliary module data
    function decodeVersionedModuleBatch(bytes memory encodedModuleBatch)
        internal
        pure
        returns (bytes memory versionedBatch, bytes memory moduleData)
    {
        return abi.decode(encodedModuleBatch, (bytes, bytes));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable no-inline-assembly
// solhint-disable ordering
library VersionedPayloadLib {
    /// @notice Amount of bytes reserved for the version (uint16) in the versioned payload
    uint256 internal constant VERSION_LENGTH = 2;

    error VersionedPayload__PayloadTooShort(bytes versionedPayload);
    error VersionedPayload__PrecompileFailed();

    /// @notice Encodes the versioned payload into a single bytes array.
    /// @param version  The payload's version.
    /// @param payload  The payload to encode.
    function encodeVersionedPayload(uint16 version, bytes memory payload) internal pure returns (bytes memory) {
        return abi.encodePacked(version, payload);
    }

    /// @notice Extracts the version from the versioned payload (calldata reference).
    /// @param versionedPayload     The versioned payload (calldata reference).
    function getVersion(bytes calldata versionedPayload) internal pure returns (uint16 version) {
        if (versionedPayload.length < VERSION_LENGTH) {
            revert VersionedPayload__PayloadTooShort(versionedPayload);
        }
        assembly {
            // We are only interested in the highest 16 bits of the loaded full 32 bytes word.
            version := shr(240, calldataload(versionedPayload.offset))
        }
    }

    /// @notice Extracts the payload from the versioned payload (calldata reference).
    /// @dev The extracted payload is also returned as a calldata reference.
    /// @param versionedPayload     The versioned payload.
    function getPayload(bytes calldata versionedPayload) internal pure returns (bytes calldata) {
        if (versionedPayload.length < VERSION_LENGTH) {
            revert VersionedPayload__PayloadTooShort(versionedPayload);
        }
        return versionedPayload[VERSION_LENGTH:];
    }

    /// @notice Extracts the version from the versioned payload (memory reference).
    /// @param versionedPayload     The versioned payload (memory reference).
    function getVersionFromMemory(bytes memory versionedPayload) internal pure returns (uint16 version) {
        if (versionedPayload.length < VERSION_LENGTH) {
            revert VersionedPayload__PayloadTooShort(versionedPayload);
        }
        assembly {
            // We are only interested in the highest 16 bits of the loaded full 32 bytes word.
            // We add 0x20 to skip the length of the bytes array.
            version := shr(240, mload(add(versionedPayload, 0x20)))
        }
    }

    /// @notice Extracts the payload from the versioned payload (memory reference).
    /// @dev The extracted payload is copied into a new memory location. Use `getPayload` when possible
    /// to avoid extra memory allocation.
    /// @param versionedPayload     The versioned payload (memory reference).
    function getPayloadFromMemory(bytes memory versionedPayload) internal view returns (bytes memory payload) {
        if (versionedPayload.length < VERSION_LENGTH) {
            revert VersionedPayload__PayloadTooShort(versionedPayload);
        }
        // Figure how many bytes to copy and allocate the memory for the extracted payload.
        uint256 toCopy;
        unchecked {
            toCopy = versionedPayload.length - VERSION_LENGTH;
        }
        payload = new bytes(toCopy);
        // Use identity precompile (0x04) to copy the payload. Unlike MCOPY, this is available on all EVM chains.
        bool res;
        assembly {
            // We add 0x20 to skip the length of the bytes array.
            // We add 0x02 to skip the 2 bytes reserved for the version.
            // Copy the payload to the previously allocated memory.
            res := staticcall(gas(), 0x04, add(versionedPayload, 0x22), toCopy, add(payload, 0x20), toCopy)
        }
        if (!res) {
            revert VersionedPayload__PrecompileFailed();
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGasOracle {
    /// @notice Convert a value from the native token of a remote chain to the local native token.
    /// @dev Will revert if no price is available for the remote chain.
    /// @param remoteChainId        The chain id of the remote chain.
    /// @param value                The value to convert.
    function convertRemoteValueToLocalUnits(uint64 remoteChainId, uint256 value) external view returns (uint256);

    /// @notice Estimate the cost of execution a transaction on a remote chain,
    /// and convert it to the local native token.
    /// @dev Will revert if no price is available for the remote chain.
    /// @param remoteChainId        The chain id of the remote chain.
    /// @param gasLimit             The gas limit of the transaction.
    /// @param calldataSize         The size of the transaction calldata.
    function estimateTxCostInLocalUnits(
        uint64 remoteChainId,
        uint256 gasLimit,
        uint256 calldataSize
    )
        external
        view
        returns (uint256);

    /// @notice Estimate the cost of execution a transaction on a remote chain,
    /// and return it as is in the remote chain's native token.
    /// @dev Will revert if no price is available for the remote chain.
    /// @param remoteChainId        The chain id of the remote chain.
    /// @param gasLimit             The gas limit of the transaction.
    /// @param calldataSize         The size of the transaction calldata.
    function estimateTxCostInRemoteUnits(
        uint64 remoteChainId,
        uint256 gasLimit,
        uint256 calldataSize
    )
        external
        view
        returns (uint256);
}

// 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) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

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

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

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

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

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

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

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

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

        return (signer, RecoverError.NoError, bytes32(0));
    }

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

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

File 19 of 28 : ClaimableFeesEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract ClaimableFeesEvents {
    /// @notice Emitted when the claim fee fraction is set. This fraction of the fees will be paid
    /// to the caller of the `claimFees` function.
    /// This encourages rational actors to call the function as soon as claim fee is higher than the gas cost.
    /// @param claimerFraction  The fraction of the fees to be paid to the claimer (100% = 1e18)
    event ClaimerFractionSet(uint256 claimerFraction);

    /// @notice Emitted when a fee recipient is set. The fee recipient receives the claimed fees.
    /// @param feeRecipient     The address of the fee recipient.
    event FeeRecipientSet(address feeRecipient);

    /// @notice Emitted when fees are claimed to the fee recipient address.
    /// @param feeRecipient     The address that receives the claimed fees.
    /// @param claimedFees      The amount of fees claimed, after the claimer reward is deducted.
    /// @param claimer          The address of the claimer (who called `claimFees`)
    /// @param claimerReward    The reward paid to the claimer for calling the `claimFees` function.
    event FeesClaimed(address feeRecipient, uint256 claimedFees, address claimer, uint256 claimerReward);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IClaimableFees {
    error ClaimableFees__ClaimerFractionAboveMax(uint256 claimerFraction, uint256 maxAllowed);
    error ClaimableFees__FeeAmountZero();
    error ClaimableFees__FeeRecipientZeroAddress();

    function claimFees() external;

    function getClaimableAmount() external view returns (uint256);
    function getClaimerFraction() external view returns (uint256);
    function getClaimerReward() external view returns (uint256);
    function getFeeRecipient() external view returns (address);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

// 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;
    }
}

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

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

/// @notice Struct representing an entry in the Interchain DataBase.
/// Entry has a globally unique identifier (key) and a value.
/// - key: srcChainId + dbNonce + entryIndex
/// - value: srcWriter + dataHash
/// @param srcChainId   The chain id of the source chain
/// @param dbNonce      The database nonce of the batch containing the entry
/// @param entryIndex   The index of the entry in the batch
/// @param srcWriter    The address of the writer on the source chain
/// @param dataHash     The hash of the data written on the source chain
struct InterchainEntry {
    uint64 srcChainId;
    uint64 dbNonce;
    uint64 entryIndex;
    bytes32 srcWriter;
    bytes32 dataHash;
}

using InterchainEntryLib for InterchainEntry global;

library InterchainEntryLib {
    /// @notice Constructs an InterchainEntry struct to be written on the local chain
    /// @param dbNonce      The database nonce of the entry on the source chain
    /// @param writer       The address of the writer on the local chain
    /// @param dataHash     The hash of the data written on the local chain
    /// @return entry       The constructed InterchainEntry struct
    function constructLocalEntry(
        uint64 dbNonce,
        uint64 entryIndex,
        address writer,
        bytes32 dataHash
    )
        internal
        view
        returns (InterchainEntry memory entry)
    {
        return InterchainEntry({
            srcChainId: SafeCast.toUint64(block.chainid),
            dbNonce: dbNonce,
            entryIndex: entryIndex,
            srcWriter: TypeCasts.addressToBytes32(writer),
            dataHash: dataHash
        });
    }

    /// @notice Returns the value of the entry: writer + dataHash hashed together
    function entryValue(InterchainEntry memory entry) internal pure returns (bytes32) {
        return getEntryValue(entry.srcWriter, entry.dataHash);
    }

    /// @notice Returns the value of the entry: writer + dataHash hashed together
    function getEntryValue(bytes32 srcWriter, bytes32 dataHash) internal pure returns (bytes32) {
        return keccak256(abi.encode(srcWriter, dataHash));
    }
}

File 25 of 28 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

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

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

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

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

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

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

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

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

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library TypeCasts {
    function addressToBytes32(address addr) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(addr)));
    }

    function bytes32ToAddress(bytes32 b) internal pure returns (address) {
        return address(uint160(uint256(b)));
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@synapsecns/=node_modules/@synapsecns/",
    "ds-test/=node_modules/ds-test/src/",
    "forge-std/=node_modules/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"interchainDB","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"claimerFraction","type":"uint256"},{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"name":"ClaimableFees__ClaimerFractionAboveMax","type":"error"},{"inputs":[],"name":"ClaimableFees__FeeAmountZero","type":"error"},{"inputs":[],"name":"ClaimableFees__FeeRecipientZeroAddress","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"InterchainModule__CallerNotInterchainDB","type":"error"},{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"}],"name":"InterchainModule__ChainIdNotRemote","type":"error"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"minRequired","type":"uint256"}],"name":"InterchainModule__FeeAmountBelowMin","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":[],"name":"SynapseModule__FeeRecipientZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"gasOracle","type":"address"}],"name":"SynapseModule__GasOracleNotContract","type":"error"},{"inputs":[],"name":"SynapseModule__GasOracleZeroAddress","type":"error"},{"inputs":[],"name":"ThresholdECDSA__RecoveredSignersNotSorted","type":"error"},{"inputs":[{"internalType":"uint256","name":"signaturesAmount","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ThresholdECDSA__SignaturesAmountBelowThreshold","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ThresholdECDSA__SignaturesPayloadLengthInvalid","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ThresholdECDSA__SignerAlreadyAdded","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ThresholdECDSA__SignerNotAdded","type":"error"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"ThresholdECDSA__SignerRecoveryFailed","type":"error"},{"inputs":[],"name":"ThresholdECDSA__SignerZeroAddress","type":"error"},{"inputs":[],"name":"ThresholdECDSA__ThresholdZero","type":"error"},{"inputs":[{"internalType":"bytes","name":"versionedPayload","type":"bytes"}],"name":"VersionedPayload__PayloadTooShort","type":"error"},{"inputs":[],"name":"VersionedPayload__PrecompileFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"dstChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"batch","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"ethSignedBatchHash","type":"bytes32"}],"name":"BatchVerificationRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"srcChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"batch","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"ethSignedBatchHash","type":"bytes32"}],"name":"BatchVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"claimerFraction","type":"uint256"}],"name":"ClaimerFractionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"FeeRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedFees","type":"uint256"},{"indexed":false,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimerReward","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"srcChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"GasDataReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"dstChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"GasDataSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gasOracle","type":"address"}],"name":"GasOracleSet","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":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ThresholdSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"verifier","type":"address"}],"name":"VerifierAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"verifier","type":"address"}],"name":"VerifierRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"chainId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"VerifyGasLimitSet","type":"event"},{"inputs":[],"name":"DEFAULT_VERIFY_GAS_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INTERCHAIN_DB","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"}],"name":"addVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"verifiers","type":"address[]"}],"name":"addVerifiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gasOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimerFraction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimerReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"dstChainId","type":"uint64"},{"internalType":"uint64","name":"dbNonce","type":"uint64"}],"name":"getModuleFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVerifiers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"}],"name":"getVerifyGasLimit","outputs":[{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isVerifier","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"}],"name":"removeVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"verifiers","type":"address[]"}],"name":"removeVerifiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"dstChainId","type":"uint64"},{"internalType":"uint64","name":"batchNonce","type":"uint64"},{"internalType":"bytes","name":"versionedBatch","type":"bytes"}],"name":"requestBatchVerification","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"claimerFraction","type":"uint256"}],"name":"setClaimerFraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gasOracle_","type":"address"}],"name":"setGasOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"setThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"},{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"setVerifyGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedBatch","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"verifyRemoteBatch","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b50604051620020b0380380620020b08339810160408190526200003491620000ec565b6001600160a01b03808316608052819081166200006b57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b62000076816200007f565b50505062000124565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114620000e757600080fd5b919050565b600080604083850312156200010057600080fd5b6200010b83620000cf565b91506200011b60208401620000cf565b90509250929050565b608051611f626200014e6000396000818161045b0152818161059101526111820152611f626000f3fe60806040526004361061019c5760003560e01c8063a87b8152116100ec578063d294f0931161008a578063e74b981b11610064578063e74b981b1461047d578063e75235b81461049d578063f0b8cb1d146104b2578063f2fde38b146104d257600080fd5b8063d294f0931461041d578063e232d19114610432578063e4c612471461044957600080fd5b8063b5344257116100c6578063b5344257146103aa578063b80cb14b146103ca578063c354bd6e146103ea578063ca2dfd0a146103fd57600080fd5b8063a87b815214610348578063a935e76614610368578063a9bc769b1461038a57600080fd5b80635d62a8dd1161015957806386ae47f01161013357806386ae47f0146102ca5780638da5cb5b146102ea5780639000b3d614610308578063960bfe041461032857600080fd5b80635d62a8dd146102825780636b8d469f146102a2578063715018a6146102b557600080fd5b80631888f4d4146101a157806326533fe9146101d457806333105218146101e95780634ccb20c0146102195780634d8ccff91461024b5780634f1991141461026d575b600080fd5b3480156101ad57600080fd5b506101c16101bc366004611933565b6104f2565b6040519081526020015b60405180910390f35b3480156101e057600080fd5b506101c1610507565b3480156101f557600080fd5b50610209610204366004611966565b610519565b60405190151581526020016101cb565b34801561022557600080fd5b506008546001600160a01b03165b6040516001600160a01b0390911681526020016101cb565b34801561025757600080fd5b5061026b61026636600461198f565b610526565b005b34801561027957600080fd5b506007546101c1565b34801561028e57600080fd5b50600954610233906001600160a01b031681565b61026b6102b0366004611a01565b610586565b3480156102c157600080fd5b5061026b6106f6565b3480156102d657600080fd5b5061026b6102e5366004611a61565b61070a565b3480156102f657600080fd5b506000546001600160a01b0316610233565b34801561031457600080fd5b5061026b610323366004611966565b610763565b34801561033457600080fd5b5061026b610343366004611ad5565b610777565b34801561035457600080fd5b5061026b610363366004611966565b6107c1565b34801561037457600080fd5b5061037d61084d565b6040516101cb9190611aee565b34801561039657600080fd5b5061026b6103a5366004611ad5565b61085e565b3480156103b657600080fd5b5061026b6103c5366004611a61565b6108d3565b3480156103d657600080fd5b5061026b6103e5366004611b3b565b610926565b3480156103f657600080fd5b50476101c1565b34801561040957600080fd5b5061026b610418366004611966565b61099d565b34801561042957600080fd5b5061026b6109ae565b34801561043e57600080fd5b506101c1620186a081565b34801561045557600080fd5b506102337f000000000000000000000000000000000000000000000000000000000000000081565b34801561048957600080fd5b5061026b610498366004611966565b610a8a565b3480156104a957600080fd5b506101c1610b07565b3480156104be57600080fd5b506101c16104cd366004611b9a565b610b12565b3480156104de57600080fd5b5061026b6104ed366004611966565b610b3e565b60006104fe8383610b79565b90505b92915050565b60004761051381610c2f565b91505090565b6000610501600183610c99565b61052e610ca8565b6001600160401b038216600081815260046020908152604091829020849055815192835282018390527f51178ef7476261c9f8257978aa4f938e564be17543cea415e92527f11dd81498910160405180910390a15050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105d657604051639005749360e01b81523360048201526024015b60405180910390fd5b46846001600160401b03160361060a576040516306434f9560e41b81526001600160401b03851660048201526024016105cd565b60006106168585610b79565b90508034101561064257604051634290de2160e01b8152346004820152602481018290526044016105cd565b600061064e8686610cd5565b9050600061069385858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250869250610ded915050565b905060006106a78280519060200120610e19565b9050876001600160401b03167fb843b64b261f738887ce2abb3c81cf03cb335944dea7923c22f6d97acbda8d4483836040516106e4929190611c05565b60405180910390a25050505050505050565b6106fe610ca8565b6107086000610e4c565b565b610712610ca8565b8060005b8181101561075d5761074d84848381811061073357610733611c27565b90506020020160208101906107489190611966565b610e9c565b61075681611c53565b9050610716565b50505050565b61076b610ca8565b61077481610ee0565b50565b61077f610ca8565b61078a600182610f24565b6040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd7906020015b60405180910390a150565b6107c9610ca8565b806001600160a01b03163b6000036107ff5760405163d129a3eb60e01b81526001600160a01b03821660048201526024016105cd565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f3efbbb00c39812fb98647af6e9e2c3f4ec2b53d368cedd1e148330a05b652cfa906020016107b6565b60606108596001610f49565b905090565b610866610ca8565b662386f26fc1000081111561089e57604051630ae993dd60e01b815260048101829052662386f26fc1000060248201526044016105cd565b60078190556040518181527f2b76ed3837bd14c860020e473bce45e560d5bca9b5109ef2f08b2051d1cf6cc9906020016107b6565b6108db610ca8565b8060005b8181101561075d576109168484838181106108fc576108fc611c27565b90506020020160208101906109119190611966565b610ee0565b61091f81611c53565b90506108df565b6000610948858560405161093b929190611c6c565b6040518091039020610e19565b90506109576001828585610f57565b61099685858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061110b92505050565b5050505050565b6109a5610ca8565b61077481610e9c565b4760008190036109d157604051636e95c0a760e01b815260040160405180910390fd5b60006109e56008546001600160a01b031690565b90506001600160a01b038116610a0e57604051631e39f76760e11b815260040160405180910390fd5b6000610a1983610c2f565b604080516001600160a01b03851681529482900360208601819052338683015260608601839052905190949192507ff4e6bc0a6951927d4db8490fb63528b3c4ccb43865870fe4e3db7a090cbb14b19181900360800190a1610a7b8284611258565b610a853382611258565b505050565b610a92610ca8565b6001600160a01b038116610ab9576040516361d0f62960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527fbf9a9534339a9d6b81696e05dcfb614b7dc518a31d48be3cfb757988381fb323906020016107b6565b600061085960015490565b6001600160401b03811660009081526004602052604081205490819003610b395750620186a05b919050565b610b46610ca8565b6001600160a01b038116610b7057604051631e4fbdf760e01b8152600060048201526024016105cd565b61077481610e4c565b6000610b836112ef565b6001600160a01b031663bf495c8884610b9b86610b12565b610ba3610b07565b610bae906040611c7c565b610bba90610104611c93565b6040516001600160e01b031960e086901b1681526001600160401b03909316600484015260248301919091526044820152606401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fe9190611ca6565b600080610c3b60075490565b9050662386f26fc10000811115610c7557604051630ae993dd60e01b815260048101829052662386f26fc1000060248201526044016105cd565b670de0b6b3a7640000610c888285611c7c565b610c929190611cbf565b9392505050565b60006104fe600184018361131c565b6000546001600160a01b031633146107085760405163118cdaa760e01b81523360048201526024016105cd565b6060610cdf6112ef565b6001600160a01b0316636f928aa76040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d1c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d449190810190611d7e565b8051909150156105015780516020808301919091206001600160401b038516600090815260059092526040909120548103610d9057604051806020016040528060008152509150610de6565b6001600160401b03841660009081526005602052604090819020829055517fb2243d133e6d54117d7ce9f172219373d8c0f601f4976bcb7860abe1addb9b2d90610ddd9086908590611dba565b60405180910390a15b5092915050565b60608282604051602001610e02929190611ddc565b604051602081830303815290604052905092915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610ea760018261133e565b6040516001600160a01b03821681527f44a3cd4eb5cc5748f6169df057b1cb2ae4c383e87cd94663c430e095d4cba424906020016107b6565b610eeb600182611378565b6040516001600160a01b03821681527f6d05492139c5ea989514a5d2150c028041e5c087e2a39967f67dc7d2655adb81906020016107b6565b80600003610f4557604051635e1a7b8b60e11b815260040160405180910390fd5b9055565b6060610501826001016113d9565b6000610f64604183611cbf565b905081610f72604183611c7c565b14610f93576040516319d9b69960e31b8152600481018390526024016105cd565b84546000819003610fb757604051635e1a7b8b60e11b815260040160405180910390fd5b6000806000805b858110156110d4576000888589610fd6604183611c93565b92610fe393929190611e0a565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093945083925061102791508d9050846113e6565b509092509050600081600381111561104157611041611e34565b1461106157826040516301fb043f60e61b81526004016105cd9190611e4a565b846001600160a01b0316826001600160a01b03161161109357604051630da2019960e01b815260040160405180910390fd5b8194506110a08d83610c99565b156110b3576110b0600187611c93565b95505b6110be604188611c93565b9650505050806110cd90611c53565b9050610fbe565b508382101561110057604051630affed5760e21b815260048101839052602481018590526044016105cd565b505050505050505050565b60008061111783611433565b91509150600061112e61112984611453565b6114fc565b90504681600001516001600160401b03160361116b5780516040516306434f9560e41b81526001600160401b0390911660048201526024016105cd565b604051636cb0d24760e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d961a48e906111b7908690600401611e4a565b600060405180830381600087803b1580156111d157600080fd5b505af11580156111e5573d6000803e3d6000fd5b505050506111fc8160000151826020015184611564565b80600001516001600160401b03167f68f4bb93e8c001407d02d37b4d097d303de40e999c66e924fda668c671578df88561123c8780519060200120610e19565b60405161124a929190611c05565b60405180910390a250505050565b8047101561127b5760405163cd78605960e01b81523060048201526024016105cd565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146112c8576040519150601f19603f3d011682016040523d82523d6000602084013e6112cd565b606091505b5050905080610a8557604051630a12f52160e11b815260040160405180910390fd5b6009546001600160a01b0316806113195760405163e929642360e01b815260040160405180910390fd5b90565b6001600160a01b038116600090815260018301602052604081205415156104fe565b600061134d6001840183611685565b905080610a85576040516375a1d0c960e01b81526001600160a01b03831660048201526024016105cd565b6001600160a01b03811661139f576040516373e66c2d60e01b815260040160405180910390fd5b60006113ae600184018361169a565b905080610a85576040516328a0fc2960e21b81526001600160a01b03831660048201526024016105cd565b60606000610c92836116af565b600080600083516041036114205760208401516040850151606086015160001a6114128882858561170b565b95509550955050505061142c565b50508151600091506002905b9250925092565b6060808280602001905181019061144a9190611e5d565b91509150915091565b606060028251101561147a5781604051635840c5b160e11b81526004016105cd9190611e4a565b815160011901806001600160401b0381111561149857611498611ce1565b6040519080825280601f01601f1916602001820160405280156114c2576020820181803683370190505b50915060008160208401836022870160045afa9050806114f55760405163080f227d60e11b815260040160405180910390fd5b5050919050565b604080516060810182526000808252602082018190529181019190915260008280602001905181019061152f9190611ec0565b6040840152905061154b816001600160401b03604082901c1691565b6001600160401b03908116602085015216825250919050565b805160000361157257505050565b6001600160401b03808416600090815260066020526040902054168015806115ab5750826001600160401b0316816001600160401b0316105b1561075d576001600160401b038481166000908152600660205260409020805467ffffffffffffffff19169185169190911790556115e76112ef565b6001600160a01b03166383389de785846040518363ffffffff1660e01b8152600401611614929190611dba565b600060405180830381600087803b15801561162e57600080fd5b505af1158015611642573d6000803e3d6000fd5b505050507f02c233a01329dc53cb24eb5e8e0131ad57c2d982e4aaa5bf8a75ee90e95b1c008483604051611677929190611dba565b60405180910390a150505050565b60006104fe836001600160a01b0384166117da565b60006104fe836001600160a01b0384166118cd565b6060816000018054806020026020016040519081016040528092919081815260200182805480156116ff57602002820191906000526020600020905b8154815260200190600101908083116116eb575b50505050509050919050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561174657506000915060039050826117d0565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa15801561179a573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117c6575060009250600191508290506117d0565b9250600091508190505b9450945094915050565b600081815260018301602052604081205480156118c35760006117fe600183611f03565b855490915060009061181290600190611f03565b905080821461187757600086600001828154811061183257611832611c27565b906000526020600020015490508087600001848154811061185557611855611c27565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061188857611888611f16565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610501565b6000915050610501565b600081815260018301602052604081205461191457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610501565b506000610501565b80356001600160401b0381168114610b3957600080fd5b6000806040838503121561194657600080fd5b61194f8361191c565b915061195d6020840161191c565b90509250929050565b60006020828403121561197857600080fd5b81356001600160a01b0381168114610c9257600080fd5b600080604083850312156119a257600080fd5b6119ab8361191c565b946020939093013593505050565b60008083601f8401126119cb57600080fd5b5081356001600160401b038111156119e257600080fd5b6020830191508360208285010111156119fa57600080fd5b9250929050565b60008060008060608587031215611a1757600080fd5b611a208561191c565b9350611a2e6020860161191c565b925060408501356001600160401b03811115611a4957600080fd5b611a55878288016119b9565b95989497509550505050565b60008060208385031215611a7457600080fd5b82356001600160401b0380821115611a8b57600080fd5b818501915085601f830112611a9f57600080fd5b813581811115611aae57600080fd5b8660208260051b8501011115611ac357600080fd5b60209290920196919550909350505050565b600060208284031215611ae757600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b81811015611b2f5783516001600160a01b031683529284019291840191600101611b0a565b50909695505050505050565b60008060008060408587031215611b5157600080fd5b84356001600160401b0380821115611b6857600080fd5b611b74888389016119b9565b90965094506020870135915080821115611b8d57600080fd5b50611a55878288016119b9565b600060208284031215611bac57600080fd5b6104fe8261191c565b60005b83811015611bd0578181015183820152602001611bb8565b50506000910152565b60008151808452611bf1816020860160208601611bb5565b601f01601f19169290920160200192915050565b604081526000611c186040830185611bd9565b90508260208301529392505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201611c6557611c65611c3d565b5060010190565b8183823760009101908152919050565b808202811582820484141761050157610501611c3d565b8082018082111561050157610501611c3d565b600060208284031215611cb857600080fd5b5051919050565b600082611cdc57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b600082601f830112611d0857600080fd5b81516001600160401b0380821115611d2257611d22611ce1565b604051601f8301601f19908116603f01168101908282118183101715611d4a57611d4a611ce1565b81604052838152866020858801011115611d6357600080fd5b611d74846020830160208901611bb5565b9695505050505050565b600060208284031215611d9057600080fd5b81516001600160401b03811115611da657600080fd5b611db284828501611cf7565b949350505050565b6001600160401b0383168152604060208201526000611db26040830184611bd9565b604081526000611def6040830185611bd9565b8281036020840152611e018185611bd9565b95945050505050565b60008085851115611e1a57600080fd5b83861115611e2757600080fd5b5050820193919092039150565b634e487b7160e01b600052602160045260246000fd5b6020815260006104fe6020830184611bd9565b60008060408385031215611e7057600080fd5b82516001600160401b0380821115611e8757600080fd5b611e9386838701611cf7565b93506020850151915080821115611ea957600080fd5b50611eb685828601611cf7565b9150509250929050565b60008060408385031215611ed357600080fd5b82516fffffffffffffffffffffffffffffffff81168114611ef357600080fd5b6020939093015192949293505050565b8181038181111561050157610501611c3d565b634e487b7160e01b600052603160045260246000fdfea26469706673582212200bab0257022773195f385a6f34fdda4658aa7a734524b97d674f6b4ed9c6378864736f6c634300081400330000000000000000000000008d50e833331a0d01d6f286881ce2c3a5dad12e26000000000000000000000000e7353bedc72d29f99d6ca5cde69f807cce5d57e4

Deployed Bytecode

0x60806040526004361061019c5760003560e01c8063a87b8152116100ec578063d294f0931161008a578063e74b981b11610064578063e74b981b1461047d578063e75235b81461049d578063f0b8cb1d146104b2578063f2fde38b146104d257600080fd5b8063d294f0931461041d578063e232d19114610432578063e4c612471461044957600080fd5b8063b5344257116100c6578063b5344257146103aa578063b80cb14b146103ca578063c354bd6e146103ea578063ca2dfd0a146103fd57600080fd5b8063a87b815214610348578063a935e76614610368578063a9bc769b1461038a57600080fd5b80635d62a8dd1161015957806386ae47f01161013357806386ae47f0146102ca5780638da5cb5b146102ea5780639000b3d614610308578063960bfe041461032857600080fd5b80635d62a8dd146102825780636b8d469f146102a2578063715018a6146102b557600080fd5b80631888f4d4146101a157806326533fe9146101d457806333105218146101e95780634ccb20c0146102195780634d8ccff91461024b5780634f1991141461026d575b600080fd5b3480156101ad57600080fd5b506101c16101bc366004611933565b6104f2565b6040519081526020015b60405180910390f35b3480156101e057600080fd5b506101c1610507565b3480156101f557600080fd5b50610209610204366004611966565b610519565b60405190151581526020016101cb565b34801561022557600080fd5b506008546001600160a01b03165b6040516001600160a01b0390911681526020016101cb565b34801561025757600080fd5b5061026b61026636600461198f565b610526565b005b34801561027957600080fd5b506007546101c1565b34801561028e57600080fd5b50600954610233906001600160a01b031681565b61026b6102b0366004611a01565b610586565b3480156102c157600080fd5b5061026b6106f6565b3480156102d657600080fd5b5061026b6102e5366004611a61565b61070a565b3480156102f657600080fd5b506000546001600160a01b0316610233565b34801561031457600080fd5b5061026b610323366004611966565b610763565b34801561033457600080fd5b5061026b610343366004611ad5565b610777565b34801561035457600080fd5b5061026b610363366004611966565b6107c1565b34801561037457600080fd5b5061037d61084d565b6040516101cb9190611aee565b34801561039657600080fd5b5061026b6103a5366004611ad5565b61085e565b3480156103b657600080fd5b5061026b6103c5366004611a61565b6108d3565b3480156103d657600080fd5b5061026b6103e5366004611b3b565b610926565b3480156103f657600080fd5b50476101c1565b34801561040957600080fd5b5061026b610418366004611966565b61099d565b34801561042957600080fd5b5061026b6109ae565b34801561043e57600080fd5b506101c1620186a081565b34801561045557600080fd5b506102337f0000000000000000000000008d50e833331a0d01d6f286881ce2c3a5dad12e2681565b34801561048957600080fd5b5061026b610498366004611966565b610a8a565b3480156104a957600080fd5b506101c1610b07565b3480156104be57600080fd5b506101c16104cd366004611b9a565b610b12565b3480156104de57600080fd5b5061026b6104ed366004611966565b610b3e565b60006104fe8383610b79565b90505b92915050565b60004761051381610c2f565b91505090565b6000610501600183610c99565b61052e610ca8565b6001600160401b038216600081815260046020908152604091829020849055815192835282018390527f51178ef7476261c9f8257978aa4f938e564be17543cea415e92527f11dd81498910160405180910390a15050565b336001600160a01b037f0000000000000000000000008d50e833331a0d01d6f286881ce2c3a5dad12e2616146105d657604051639005749360e01b81523360048201526024015b60405180910390fd5b46846001600160401b03160361060a576040516306434f9560e41b81526001600160401b03851660048201526024016105cd565b60006106168585610b79565b90508034101561064257604051634290de2160e01b8152346004820152602481018290526044016105cd565b600061064e8686610cd5565b9050600061069385858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250869250610ded915050565b905060006106a78280519060200120610e19565b9050876001600160401b03167fb843b64b261f738887ce2abb3c81cf03cb335944dea7923c22f6d97acbda8d4483836040516106e4929190611c05565b60405180910390a25050505050505050565b6106fe610ca8565b6107086000610e4c565b565b610712610ca8565b8060005b8181101561075d5761074d84848381811061073357610733611c27565b90506020020160208101906107489190611966565b610e9c565b61075681611c53565b9050610716565b50505050565b61076b610ca8565b61077481610ee0565b50565b61077f610ca8565b61078a600182610f24565b6040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd7906020015b60405180910390a150565b6107c9610ca8565b806001600160a01b03163b6000036107ff5760405163d129a3eb60e01b81526001600160a01b03821660048201526024016105cd565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f3efbbb00c39812fb98647af6e9e2c3f4ec2b53d368cedd1e148330a05b652cfa906020016107b6565b60606108596001610f49565b905090565b610866610ca8565b662386f26fc1000081111561089e57604051630ae993dd60e01b815260048101829052662386f26fc1000060248201526044016105cd565b60078190556040518181527f2b76ed3837bd14c860020e473bce45e560d5bca9b5109ef2f08b2051d1cf6cc9906020016107b6565b6108db610ca8565b8060005b8181101561075d576109168484838181106108fc576108fc611c27565b90506020020160208101906109119190611966565b610ee0565b61091f81611c53565b90506108df565b6000610948858560405161093b929190611c6c565b6040518091039020610e19565b90506109576001828585610f57565b61099685858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061110b92505050565b5050505050565b6109a5610ca8565b61077481610e9c565b4760008190036109d157604051636e95c0a760e01b815260040160405180910390fd5b60006109e56008546001600160a01b031690565b90506001600160a01b038116610a0e57604051631e39f76760e11b815260040160405180910390fd5b6000610a1983610c2f565b604080516001600160a01b03851681529482900360208601819052338683015260608601839052905190949192507ff4e6bc0a6951927d4db8490fb63528b3c4ccb43865870fe4e3db7a090cbb14b19181900360800190a1610a7b8284611258565b610a853382611258565b505050565b610a92610ca8565b6001600160a01b038116610ab9576040516361d0f62960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527fbf9a9534339a9d6b81696e05dcfb614b7dc518a31d48be3cfb757988381fb323906020016107b6565b600061085960015490565b6001600160401b03811660009081526004602052604081205490819003610b395750620186a05b919050565b610b46610ca8565b6001600160a01b038116610b7057604051631e4fbdf760e01b8152600060048201526024016105cd565b61077481610e4c565b6000610b836112ef565b6001600160a01b031663bf495c8884610b9b86610b12565b610ba3610b07565b610bae906040611c7c565b610bba90610104611c93565b6040516001600160e01b031960e086901b1681526001600160401b03909316600484015260248301919091526044820152606401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fe9190611ca6565b600080610c3b60075490565b9050662386f26fc10000811115610c7557604051630ae993dd60e01b815260048101829052662386f26fc1000060248201526044016105cd565b670de0b6b3a7640000610c888285611c7c565b610c929190611cbf565b9392505050565b60006104fe600184018361131c565b6000546001600160a01b031633146107085760405163118cdaa760e01b81523360048201526024016105cd565b6060610cdf6112ef565b6001600160a01b0316636f928aa76040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d1c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d449190810190611d7e565b8051909150156105015780516020808301919091206001600160401b038516600090815260059092526040909120548103610d9057604051806020016040528060008152509150610de6565b6001600160401b03841660009081526005602052604090819020829055517fb2243d133e6d54117d7ce9f172219373d8c0f601f4976bcb7860abe1addb9b2d90610ddd9086908590611dba565b60405180910390a15b5092915050565b60608282604051602001610e02929190611ddc565b604051602081830303815290604052905092915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610ea760018261133e565b6040516001600160a01b03821681527f44a3cd4eb5cc5748f6169df057b1cb2ae4c383e87cd94663c430e095d4cba424906020016107b6565b610eeb600182611378565b6040516001600160a01b03821681527f6d05492139c5ea989514a5d2150c028041e5c087e2a39967f67dc7d2655adb81906020016107b6565b80600003610f4557604051635e1a7b8b60e11b815260040160405180910390fd5b9055565b6060610501826001016113d9565b6000610f64604183611cbf565b905081610f72604183611c7c565b14610f93576040516319d9b69960e31b8152600481018390526024016105cd565b84546000819003610fb757604051635e1a7b8b60e11b815260040160405180910390fd5b6000806000805b858110156110d4576000888589610fd6604183611c93565b92610fe393929190611e0a565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093945083925061102791508d9050846113e6565b509092509050600081600381111561104157611041611e34565b1461106157826040516301fb043f60e61b81526004016105cd9190611e4a565b846001600160a01b0316826001600160a01b03161161109357604051630da2019960e01b815260040160405180910390fd5b8194506110a08d83610c99565b156110b3576110b0600187611c93565b95505b6110be604188611c93565b9650505050806110cd90611c53565b9050610fbe565b508382101561110057604051630affed5760e21b815260048101839052602481018590526044016105cd565b505050505050505050565b60008061111783611433565b91509150600061112e61112984611453565b6114fc565b90504681600001516001600160401b03160361116b5780516040516306434f9560e41b81526001600160401b0390911660048201526024016105cd565b604051636cb0d24760e11b81526001600160a01b037f0000000000000000000000008d50e833331a0d01d6f286881ce2c3a5dad12e26169063d961a48e906111b7908690600401611e4a565b600060405180830381600087803b1580156111d157600080fd5b505af11580156111e5573d6000803e3d6000fd5b505050506111fc8160000151826020015184611564565b80600001516001600160401b03167f68f4bb93e8c001407d02d37b4d097d303de40e999c66e924fda668c671578df88561123c8780519060200120610e19565b60405161124a929190611c05565b60405180910390a250505050565b8047101561127b5760405163cd78605960e01b81523060048201526024016105cd565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146112c8576040519150601f19603f3d011682016040523d82523d6000602084013e6112cd565b606091505b5050905080610a8557604051630a12f52160e11b815260040160405180910390fd5b6009546001600160a01b0316806113195760405163e929642360e01b815260040160405180910390fd5b90565b6001600160a01b038116600090815260018301602052604081205415156104fe565b600061134d6001840183611685565b905080610a85576040516375a1d0c960e01b81526001600160a01b03831660048201526024016105cd565b6001600160a01b03811661139f576040516373e66c2d60e01b815260040160405180910390fd5b60006113ae600184018361169a565b905080610a85576040516328a0fc2960e21b81526001600160a01b03831660048201526024016105cd565b60606000610c92836116af565b600080600083516041036114205760208401516040850151606086015160001a6114128882858561170b565b95509550955050505061142c565b50508151600091506002905b9250925092565b6060808280602001905181019061144a9190611e5d565b91509150915091565b606060028251101561147a5781604051635840c5b160e11b81526004016105cd9190611e4a565b815160011901806001600160401b0381111561149857611498611ce1565b6040519080825280601f01601f1916602001820160405280156114c2576020820181803683370190505b50915060008160208401836022870160045afa9050806114f55760405163080f227d60e11b815260040160405180910390fd5b5050919050565b604080516060810182526000808252602082018190529181019190915260008280602001905181019061152f9190611ec0565b6040840152905061154b816001600160401b03604082901c1691565b6001600160401b03908116602085015216825250919050565b805160000361157257505050565b6001600160401b03808416600090815260066020526040902054168015806115ab5750826001600160401b0316816001600160401b0316105b1561075d576001600160401b038481166000908152600660205260409020805467ffffffffffffffff19169185169190911790556115e76112ef565b6001600160a01b03166383389de785846040518363ffffffff1660e01b8152600401611614929190611dba565b600060405180830381600087803b15801561162e57600080fd5b505af1158015611642573d6000803e3d6000fd5b505050507f02c233a01329dc53cb24eb5e8e0131ad57c2d982e4aaa5bf8a75ee90e95b1c008483604051611677929190611dba565b60405180910390a150505050565b60006104fe836001600160a01b0384166117da565b60006104fe836001600160a01b0384166118cd565b6060816000018054806020026020016040519081016040528092919081815260200182805480156116ff57602002820191906000526020600020905b8154815260200190600101908083116116eb575b50505050509050919050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561174657506000915060039050826117d0565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa15801561179a573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117c6575060009250600191508290506117d0565b9250600091508190505b9450945094915050565b600081815260018301602052604081205480156118c35760006117fe600183611f03565b855490915060009061181290600190611f03565b905080821461187757600086600001828154811061183257611832611c27565b906000526020600020015490508087600001848154811061185557611855611c27565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061188857611888611f16565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610501565b6000915050610501565b600081815260018301602052604081205461191457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610501565b506000610501565b80356001600160401b0381168114610b3957600080fd5b6000806040838503121561194657600080fd5b61194f8361191c565b915061195d6020840161191c565b90509250929050565b60006020828403121561197857600080fd5b81356001600160a01b0381168114610c9257600080fd5b600080604083850312156119a257600080fd5b6119ab8361191c565b946020939093013593505050565b60008083601f8401126119cb57600080fd5b5081356001600160401b038111156119e257600080fd5b6020830191508360208285010111156119fa57600080fd5b9250929050565b60008060008060608587031215611a1757600080fd5b611a208561191c565b9350611a2e6020860161191c565b925060408501356001600160401b03811115611a4957600080fd5b611a55878288016119b9565b95989497509550505050565b60008060208385031215611a7457600080fd5b82356001600160401b0380821115611a8b57600080fd5b818501915085601f830112611a9f57600080fd5b813581811115611aae57600080fd5b8660208260051b8501011115611ac357600080fd5b60209290920196919550909350505050565b600060208284031215611ae757600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b81811015611b2f5783516001600160a01b031683529284019291840191600101611b0a565b50909695505050505050565b60008060008060408587031215611b5157600080fd5b84356001600160401b0380821115611b6857600080fd5b611b74888389016119b9565b90965094506020870135915080821115611b8d57600080fd5b50611a55878288016119b9565b600060208284031215611bac57600080fd5b6104fe8261191c565b60005b83811015611bd0578181015183820152602001611bb8565b50506000910152565b60008151808452611bf1816020860160208601611bb5565b601f01601f19169290920160200192915050565b604081526000611c186040830185611bd9565b90508260208301529392505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201611c6557611c65611c3d565b5060010190565b8183823760009101908152919050565b808202811582820484141761050157610501611c3d565b8082018082111561050157610501611c3d565b600060208284031215611cb857600080fd5b5051919050565b600082611cdc57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b600082601f830112611d0857600080fd5b81516001600160401b0380821115611d2257611d22611ce1565b604051601f8301601f19908116603f01168101908282118183101715611d4a57611d4a611ce1565b81604052838152866020858801011115611d6357600080fd5b611d74846020830160208901611bb5565b9695505050505050565b600060208284031215611d9057600080fd5b81516001600160401b03811115611da657600080fd5b611db284828501611cf7565b949350505050565b6001600160401b0383168152604060208201526000611db26040830184611bd9565b604081526000611def6040830185611bd9565b8281036020840152611e018185611bd9565b95945050505050565b60008085851115611e1a57600080fd5b83861115611e2757600080fd5b5050820193919092039150565b634e487b7160e01b600052602160045260246000fd5b6020815260006104fe6020830184611bd9565b60008060408385031215611e7057600080fd5b82516001600160401b0380821115611e8757600080fd5b611e9386838701611cf7565b93506020850151915080821115611ea957600080fd5b50611eb685828601611cf7565b9150509250929050565b60008060408385031215611ed357600080fd5b82516fffffffffffffffffffffffffffffffff81168114611ef357600080fd5b6020939093015192949293505050565b8181038181111561050157610501611c3d565b634e487b7160e01b600052603160045260246000fdfea26469706673582212200bab0257022773195f385a6f34fdda4658aa7a734524b97d674f6b4ed9c6378864736f6c63430008140033

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

0000000000000000000000008d50e833331a0d01d6f286881ce2c3a5dad12e26000000000000000000000000e7353bedc72d29f99d6ca5cde69f807cce5d57e4

-----Decoded View---------------
Arg [0] : interchainDB (address): 0x8d50e833331A0D01d6F286881ce2C3A5DAD12e26
Arg [1] : owner_ (address): 0xE7353BEdc72D29f99D6cA5CDE69F807cCE5d57e4

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000008d50e833331a0d01d6f286881ce2c3a5dad12e26
Arg [1] : 000000000000000000000000e7353bedc72d29f99d6ca5cde69f807cce5d57e4


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