Sepolia Testnet

Contract

0x886ade76fBe5b6702C2B61bb1b07ab397A339376

Overview

ETH Balance

0.0001397 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Verify Remote Ba...57116232024-04-16 15:39:24341 days ago1713281964IN
0x886ade76...97A339376
0 ETH0.000132941.00492663
Verify Remote Ba...57116212024-04-16 15:39:00341 days ago1713281940IN
0x886ade76...97A339376
0 ETH0.000132931.00476799
Verify Remote Ba...57116182024-04-16 15:38:24341 days ago1713281904IN
0x886ade76...97A339376
0 ETH0.000132951.00496982
Verify Remote Ba...57116172024-04-16 15:38:12341 days ago1713281892IN
0x886ade76...97A339376
0 ETH0.000132991.00520868
Verify Remote Ba...57116152024-04-16 15:37:48341 days ago1713281868IN
0x886ade76...97A339376
0 ETH0.0001331.00533082
Verify Remote Ba...57116142024-04-16 15:37:36341 days ago1713281856IN
0x886ade76...97A339376
0 ETH0.000132971.00531445
Verify Remote Ba...57116122024-04-16 15:37:12341 days ago1713281832IN
0x886ade76...97A339376
0 ETH0.000146261.10565919
Verify Remote Ba...57116112024-04-16 15:37:00341 days ago1713281820IN
0x886ade76...97A339376
0 ETH0.000146261.10587278
Verify Remote Ba...57116092024-04-16 15:36:36341 days ago1713281796IN
0x886ade76...97A339376
0 ETH0.000146271.10577957
Verify Remote Ba...57116072024-04-16 15:36:12341 days ago1713281772IN
0x886ade76...97A339376
0 ETH0.000146231.10570012
Verify Remote Ba...57116052024-04-16 15:35:48341 days ago1713281748IN
0x886ade76...97A339376
0 ETH0.000146261.105569
Verify Remote Ba...57116032024-04-16 15:35:24341 days ago1713281724IN
0x886ade76...97A339376
0 ETH0.000146191.10505793
Verify Remote Ba...57116012024-04-16 15:35:00341 days ago1713281700IN
0x886ade76...97A339376
0 ETH0.000146191.10536559
Verify Remote Ba...57115992024-04-16 15:34:36341 days ago1713281676IN
0x886ade76...97A339376
0 ETH0.00014611.10460601
Verify Remote Ba...57115972024-04-16 15:34:12341 days ago1713281652IN
0x886ade76...97A339376
0 ETH0.000146071.10461998
Verify Remote Ba...57115952024-04-16 15:33:48341 days ago1713281628IN
0x886ade76...97A339376
0 ETH0.00014611.10464534
Verify Remote Ba...57115932024-04-16 15:33:24341 days ago1713281604IN
0x886ade76...97A339376
0 ETH0.000132851.00425912
Verify Remote Ba...57115922024-04-16 15:33:12341 days ago1713281592IN
0x886ade76...97A339376
0 ETH0.000146081.10434485
Verify Remote Ba...57115892024-04-16 15:32:36341 days ago1713281556IN
0x886ade76...97A339376
0 ETH0.000132851.00430105
Verify Remote Ba...57115872024-04-16 15:32:12341 days ago1713281532IN
0x886ade76...97A339376
0 ETH0.000132861.00432339
Verify Remote Ba...57115852024-04-16 15:31:48341 days ago1713281508IN
0x886ade76...97A339376
0 ETH0.000132881.00445464
Verify Remote Ba...57115842024-04-16 15:31:36341 days ago1713281496IN
0x886ade76...97A339376
0 ETH0.000146061.10416605
Verify Remote Ba...57115822024-04-16 15:31:12341 days ago1713281472IN
0x886ade76...97A339376
0 ETH0.000132841.0042399
Verify Remote Ba...57115802024-04-16 15:30:48341 days ago1713281448IN
0x886ade76...97A339376
0 ETH0.000146151.10476443
Verify Remote Ba...57115782024-04-16 15:30:24341 days ago1713281424IN
0x886ade76...97A339376
0 ETH0.000132851.00420541
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Request Batch Ve...57116142024-04-16 15:37:36341 days ago1713281856
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116122024-04-16 15:37:12341 days ago1713281832
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116102024-04-16 15:36:48341 days ago1713281808
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116082024-04-16 15:36:24341 days ago1713281784
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116062024-04-16 15:36:00341 days ago1713281760
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116042024-04-16 15:35:36341 days ago1713281736
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116022024-04-16 15:35:12341 days ago1713281712
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57116002024-04-16 15:34:48341 days ago1713281688
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115982024-04-16 15:34:24341 days ago1713281664
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115962024-04-16 15:34:00341 days ago1713281640
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115942024-04-16 15:33:36341 days ago1713281616
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115922024-04-16 15:33:12341 days ago1713281592
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115902024-04-16 15:32:48341 days ago1713281568
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115882024-04-16 15:32:24341 days ago1713281544
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115862024-04-16 15:32:00341 days ago1713281520
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115842024-04-16 15:31:36341 days ago1713281496
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115832024-04-16 15:31:24341 days ago1713281484
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115812024-04-16 15:31:00341 days ago1713281460
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115792024-04-16 15:30:36341 days ago1713281436
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115772024-04-16 15:30:12341 days ago1713281412
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115752024-04-16 15:29:48341 days ago1713281388
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115732024-04-16 15:29:24341 days ago1713281364
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115712024-04-16 15:29:00341 days ago1713281340
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115692024-04-16 15:28:36341 days ago1713281316
0x886ade76...97A339376
0.0000001 ETH
Request Batch Ve...57115672024-04-16 15:28:12341 days ago1713281292
0x886ade76...97A339376
0.0000001 ETH
View All Internal Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x92e44eee...Fc9c1EE18
The constructor portion of the code might be different and could alter the actual behaviour of the contract

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)

File 1 of 24 : SynapseModule.sol
// 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 {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

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

    uint256 internal constant MAX_CLAIM_FEE_FRACTION = 0.01e18; // 1%
    uint256 internal constant FEE_PRECISION = 1e18;

    /// @dev Struct to hold the verifiers and the threshold for the module.
    ThresholdECDSA internal _verifiers;
    /// @dev Claim fee fraction, 100% = 1e18
    uint256 internal _claimFeeFraction;
    /// @dev Gas limit for the verifyBatch function on the remote chain.
    mapping(uint256 chainId => uint256 gasLimit) internal _verifyGasLimit;
    /// @dev Hash of the last gas data sent to the remote chain.
    mapping(uint256 chainId => bytes32 gasDataHash) internal _lastGasDataHash;
    /// @dev Nonce of the last gas data received from the remote chain.
    mapping(uint256 chainId => uint256 gasDataNonce) internal _lastGasDataNonce;

    /// @inheritdoc ISynapseModule
    address public feeCollector;
    /// @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 ThresholdChanged(threshold);
    }

    /// @inheritdoc ISynapseModule
    function setFeeCollector(address feeCollector_) external onlyOwner {
        feeCollector = feeCollector_;
        emit FeeCollectorChanged(feeCollector_);
    }

    /// @inheritdoc ISynapseModule
    function setClaimFeeFraction(uint256 claimFeeFraction) external onlyOwner {
        if (claimFeeFraction > MAX_CLAIM_FEE_FRACTION) {
            revert SynapseModule__ClaimFeeFractionExceedsMax(claimFeeFraction);
        }
        _claimFeeFraction = claimFeeFraction;
        emit ClaimFeeFractionChanged(claimFeeFraction);
    }

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

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

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

    /// @inheritdoc ISynapseModule
    function claimFees() external {
        if (feeCollector == address(0)) {
            revert SynapseModule__FeeCollectorNotSet();
        }
        if (address(this).balance == 0) {
            revert SynapseModule__NoFeesToClaim();
        }
        uint256 claimFee = getClaimFeeAmount();
        uint256 collectedFee = address(this).balance - claimFee;
        Address.sendValue(payable(feeCollector), collectedFee);
        Address.sendValue(payable(msg.sender), claimFee);
        emit FeesClaimed(feeCollector, collectedFee, msg.sender, claimFee);
    }

    /// @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 getClaimFeeFraction() external view returns (uint256) {
        return _claimFeeFraction;
    }

    /// @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 getClaimFeeAmount() public view returns (uint256) {
        return address(this).balance * _claimFeeFraction / FEE_PRECISION;
    }

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

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

    // ══════════════════════════════════════════════ 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 Internal logic to fill the module data for the specified destination chain.
    function _fillModuleData(
        uint256 dstChainId,
        uint256 // 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(uint256 srcChainId, uint256 dbNonce, bytes memory moduleData) internal override {
        // Exit early if data is empty
        if (moduleData.length == 0) {
            return;
        }
        // Don't process outdated data
        uint256 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(
        uint256 dstChainId,
        uint256 // 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__GasOracleNotSet();
        }
    }
}

File 2 of 24 : InterchainModule.sol
// 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(uint256 dstChainId, bytes calldata versionedBatch) external payable {
        if (msg.sender != INTERCHAIN_DB) {
            revert InterchainModule__NotInterchainDB(msg.sender);
        }
        InterchainBatch memory batch = InterchainBatchLib.decodeBatch(versionedBatch.getPayload());
        if (dstChainId == block.chainid) {
            revert InterchainModule__SameChainId(block.chainid);
        }
        if (batch.srcChainId != block.chainid) {
            revert InterchainModule__IncorrectSourceChainId({chainId: batch.srcChainId});
        }
        uint256 requiredFee = _getModuleFee(dstChainId, batch.dbNonce);
        if (msg.value < requiredFee) {
            revert InterchainModule__InsufficientFee({actual: msg.value, required: requiredFee});
        }
        bytes memory moduleData = _fillModuleData(dstChainId, batch.dbNonce);
        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(uint256 dstChainId, uint256 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__SameChainId(block.chainid);
        }
        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(uint256 dstChainId, bytes memory encodedBatch) internal virtual {}

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

    /// @dev Internal logic to handle the auxiliary module data relayed from the remote chain.
    function _receiveModuleData(uint256 srcChainId, uint256 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(uint256 dstChainId, uint256 dbNonce) internal view virtual returns (uint256);
}

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

abstract contract SynapseModuleEvents {
    event VerifierAdded(address verifier);
    event VerifierRemoved(address verifier);
    event ThresholdChanged(uint256 threshold);

    event FeeCollectorChanged(address feeCollector);
    event GasOracleChanged(address gasOracle);
    event VerifyGasLimitChanged(uint256 chainId, uint256 gasLimit);

    event ClaimFeeFractionChanged(uint256 claimFeeFraction);
    event FeesClaimed(address feeCollector, uint256 collectedFees, address claimer, uint256 claimerFee);

    event GasDataSent(uint256 dstChainId, bytes data);
    event GasDataReceived(uint256 srcChainId, bytes data);
}

File 4 of 24 : ISynapseGasOracle.sol
// 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(uint256 srcChainId, bytes calldata data) external;

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

File 5 of 24 : ISynapseModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface ISynapseModule is IInterchainModule {
    error SynapseModule__ClaimFeeFractionExceedsMax(uint256 claimFeeFraction);
    error SynapseModule__FeeCollectorNotSet();
    error SynapseModule__GasOracleNotContract(address gasOracle);
    error SynapseModule__GasOracleNotSet();
    error SynapseModule__NoFeesToClaim();

    // ═══════════════════════════════════════════════ 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 feeCollector_   The address of the fee collector
    function setFeeCollector(address feeCollector_) 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 claimFeeFraction The fraction of the fees to be paid to the claimer (100% = 1e18)
    function setClaimFeeFraction(uint256 claimFeeFraction) 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(uint256 chainId, uint256 gasLimit) external;

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

    /// @notice Transfers the accumulated fees to the fee collector.
    /// Message caller receives a percentage of the fees, this ensures that the module is self-sustainable.
    /// The claim fee amount could be retrieved using `getClaimFeeAmount`. The rest of the fees
    /// will be transferred to the fee collector.
    /// @dev Will revert if the fee collector is not set.
    function claimFees() external;

    /// @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 fee collector for the module.
    function feeCollector() external view returns (address);

    /// @notice Returns the current claim fee to be paid to the caller of `claimFees`.
    function getClaimFeeAmount() external view returns (uint256);

    /// @notice Returns the fraction of the fees to be paid to the caller of `claimFees`.
    /// The returned value is in the range [0, 1e18], where 1e18 corresponds to 100% of the fees.
    function getClaimFeeFraction() external view returns (uint256);

    /// @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(uint256 chainId) external view returns (uint256);
}

File 6 of 24 : ThresholdECDSA.sol
// 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__AlreadySigner(address account);
    error ThresholdECDSA__IncorrectSignaturesLength(uint256 length);
    error ThresholdECDSA__InvalidSignature(bytes signature);
    error ThresholdECDSA__NotEnoughSignatures(uint256 provided, uint256 threshold);
    error ThresholdECDSA__NotSigner(address account);
    error ThresholdECDSA__RecoveredSignersNotSorted();
    error ThresholdECDSA__ZeroAddress();
    error ThresholdECDSA__ZeroThreshold();

    /// @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__ZeroAddress();
        bool added = self._signers.add(account);
        if (!added) {
            revert ThresholdECDSA__AlreadySigner(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__NotSigner(account);
        }
    }

    /// @notice Modifies the threshold of signatures required.
    function modifyThreshold(ThresholdECDSA storage self, uint256 threshold) internal {
        if (threshold == 0) {
            revert ThresholdECDSA__ZeroThreshold();
        }
        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__IncorrectSignaturesLength(signatures.length);
        }
        // First, check that threshold is configured and enough signatures are provided
        uint256 threshold = self._threshold;
        if (threshold == 0) {
            revert ThresholdECDSA__ZeroThreshold();
        }
        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__InvalidSignature(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__NotEnoughSignatures(validSignatures, threshold);
        }
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

File 8 of 24 : Address.sol
// 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();
        }
    }
}

File 9 of 24 : MessageHashUtils.sol
// 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 24 : InterchainModuleEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract InterchainModuleEvents {
    event BatchVerificationRequested(uint256 indexed dstChainId, bytes batch, bytes32 ethSignedBatchHash);

    event BatchVerified(uint256 indexed srcChainId, bytes batch, bytes32 ethSignedBatchHash);
}

File 11 of 24 : IInterchainDB.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface IInterchainDB {
    /// @notice Struct representing a batch of entries from the remote Interchain DataBase,
    /// verified by the Interchain Module.
    /// @param verifiedAt   The block timestamp at which the entry was verified by the module
    /// @param batchRoot    The Merkle root of the batch
    struct RemoteBatch {
        uint256 verifiedAt;
        bytes32 batchRoot;
    }

    error InterchainDB__BatchDoesNotExist(uint256 dbNonce);
    error InterchainDB__BatchNotFinalized(uint256 dbNonce);
    error InterchainDB__ConflictingBatches(address module, bytes32 existingBatchRoot, InterchainBatch newBatch);
    error InterchainDB__EntryIndexOutOfRange(uint256 dbNonce, uint64 entryIndex, uint64 batchSize);
    error InterchainDB__IncorrectFeeAmount(uint256 actualFee, uint256 expectedFee);
    error InterchainDB__InvalidBatchVersion(uint16 version);
    error InterchainDB__InvalidEntryRange(uint256 dbNonce, uint64 start, uint64 end);
    error InterchainDB__NoModulesSpecified();
    error InterchainDB__SameChainId(uint256 chainId);

    /// @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 (uint256 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(
        uint256 dstChainId,
        uint256 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(
        uint256 dstChainId,
        bytes32 dataHash,
        address[] memory srcModules
    )
        external
        payable
        returns (uint256 dbNonce, uint64 entryIndex);

    /// @notice Allows the Interchain Module to verify the batch coming from the remote chain.
    /// Note: The DB will only accept the batch of the same version as the DB itself.
    /// @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(uint256 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(uint256 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(
        uint256 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(uint256 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(uint256 dbNonce) external view returns (InterchainBatch 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(uint256 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(uint256 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 (uint256);

    /// @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 (uint256 dbNonce, uint64 entryIndex);

    /// @notice Read the data written on specific source chain by a specific writer,
    /// and verify it on the destination chain using the provided Interchain Module.
    /// Note: returned zero value indicates that the module has not verified the entry.
    /// @param entry        The Interchain Entry to read
    /// @param dstModule    The destination chain addresses of the Interchain Modules to use for verification
    /// @return moduleVerifiedAt   The block timestamp at which the entry was verified by the module,
    ///                             or ZERO if the module has not verified the entry.
    function checkVerification(
        address dstModule,
        InterchainEntry memory entry,
        bytes32[] memory proof
    )
        external
        view
        returns (uint256 moduleVerifiedAt);

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

File 12 of 24 : IInterchainModule.sol
// 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__NotInterchainDB(address caller);
    error InterchainModule__IncorrectSourceChainId(uint256 chainId);
    error InterchainModule__InsufficientFee(uint256 actual, uint256 required);
    error InterchainModule__SameChainId(uint256 chainId);

    /// @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 versionedBatch   The versioned batch to verify
    function requestBatchVerification(uint256 dstChainId, 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(uint256 dstChainId, uint256 dbNonce) external view returns (uint256);
}

File 13 of 24 : InterchainBatch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @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 {
    // TODO: can we use uint64 for chain id?
    uint256 srcChainId;
    uint256 dbNonce;
    bytes32 batchRoot;
}

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(
        uint256 dbNonce,
        bytes32 batchRoot
    )
        internal
        view
        returns (InterchainBatch memory batch)
    {
        return InterchainBatch({srcChainId: 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(batch);
    }

    /// @notice Decodes the InterchainBatch struct from a non-versioned batch payload in calldata.
    function decodeBatch(bytes calldata data) internal pure returns (InterchainBatch memory) {
        return abi.decode(data, (InterchainBatch));
    }

    /// @notice Decodes the InterchainBatch struct from a non-versioned batch payload in memory.
    function decodeBatchFromMemory(bytes memory data) internal pure returns (InterchainBatch memory) {
        return abi.decode(data, (InterchainBatch));
    }

    /// @notice Returns the globally unique identifier of the batch
    function batchKey(InterchainBatch memory batch) internal pure returns (bytes32) {
        return keccak256(abi.encode(batch.srcChainId, batch.dbNonce));
    }
}

File 14 of 24 : ModuleBatch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

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

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

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

File 15 of 24 : VersionedPayload.sol
// 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__TooShort(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__TooShort(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__TooShort(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__TooShort(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__TooShort(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();
        }
    }
}

File 16 of 24 : IGasOracle.sol
// 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(uint256 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(
        uint256 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(
        uint256 remoteChainId,
        uint256 gasLimit,
        uint256 calldataSize
    )
        external
        view
        returns (uint256);
}

File 17 of 24 : EnumerableSet.sol
// 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;
    }
}

File 18 of 24 : ECDSA.sol
// 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 24 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

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

File 20 of 24 : Strings.sol
// 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));
    }
}

File 21 of 24 : InterchainEntry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {TypeCasts} from "./TypeCasts.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 {
    // TODO: can we use uint64 for chain id?
    uint256 srcChainId;
    uint256 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(
        uint256 dbNonce,
        uint64 entryIndex,
        address writer,
        bytes32 dataHash
    )
        internal
        view
        returns (InterchainEntry memory entry)
    {
        return InterchainEntry({
            srcChainId: block.chainid,
            dbNonce: dbNonce,
            entryIndex: entryIndex,
            srcWriter: TypeCasts.addressToBytes32(writer),
            dataHash: dataHash
        });
    }

    /// @notice Returns the globally unique identifier of the entry
    function entryKey(InterchainEntry memory entry) internal pure returns (bytes32) {
        return keccak256(abi.encode(entry.srcChainId, entry.dbNonce, entry.entryIndex));
    }

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

    /// @notice Returns the globally unique identifier of the batch containing the entry
    function batchKey(InterchainEntry memory entry) internal pure returns (bytes32) {
        return keccak256(abi.encode(entry.srcChainId, entry.dbNonce));
    }
}

File 22 of 24 : Math.sol
// 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;
    }
}

File 23 of 24 : SignedMath.sol
// 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);
        }
    }
}

File 24 of 24 : TypeCasts.sol
// 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":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"InterchainModule__IncorrectSourceChainId","type":"error"},{"inputs":[{"internalType":"uint256","name":"actual","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InterchainModule__InsufficientFee","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"InterchainModule__NotInterchainDB","type":"error"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"InterchainModule__SameChainId","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"claimFeeFraction","type":"uint256"}],"name":"SynapseModule__ClaimFeeFractionExceedsMax","type":"error"},{"inputs":[],"name":"SynapseModule__FeeCollectorNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"gasOracle","type":"address"}],"name":"SynapseModule__GasOracleNotContract","type":"error"},{"inputs":[],"name":"SynapseModule__GasOracleNotSet","type":"error"},{"inputs":[],"name":"SynapseModule__NoFeesToClaim","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ThresholdECDSA__AlreadySigner","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ThresholdECDSA__IncorrectSignaturesLength","type":"error"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"ThresholdECDSA__InvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ThresholdECDSA__NotEnoughSignatures","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ThresholdECDSA__NotSigner","type":"error"},{"inputs":[],"name":"ThresholdECDSA__RecoveredSignersNotSorted","type":"error"},{"inputs":[],"name":"ThresholdECDSA__ZeroAddress","type":"error"},{"inputs":[],"name":"ThresholdECDSA__ZeroThreshold","type":"error"},{"inputs":[],"name":"VersionedPayload__PrecompileFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"versionedPayload","type":"bytes"}],"name":"VersionedPayload__TooShort","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"dstChainId","type":"uint256"},{"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":"uint256","name":"srcChainId","type":"uint256"},{"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":"claimFeeFraction","type":"uint256"}],"name":"ClaimFeeFractionChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"}],"name":"FeeCollectorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"uint256","name":"collectedFees","type":"uint256"},{"indexed":false,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimerFee","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"srcChainId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"GasDataReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"dstChainId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"GasDataSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gasOracle","type":"address"}],"name":"GasOracleChanged","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":"ThresholdChanged","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":"uint256","name":"chainId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"VerifyGasLimitChanged","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":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimFeeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimFeeFraction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"dstChainId","type":"uint256"},{"internalType":"uint256","name":"dbNonce","type":"uint256"}],"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":"uint256","name":"chainId","type":"uint256"}],"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":"uint256","name":"dstChainId","type":"uint256"},{"internalType":"bytes","name":"versionedBatch","type":"bytes"}],"name":"requestBatchVerification","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"claimFeeFraction","type":"uint256"}],"name":"setClaimFeeFraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeCollector_","type":"address"}],"name":"setFeeCollector","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":"uint256","name":"chainId","type":"uint256"},{"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"}]

Deployed Bytecode

0x6080604052600436106101815760003560e01c80639a96f35b116100d1578063c415b95c1161008a578063e232d19111610064578063e232d1911461044a578063e4c6124714610461578063e75235b814610495578063f2fde38b146104aa57600080fd5b8063c415b95c146103f5578063ca2dfd0a14610415578063d294f0931461043557600080fd5b80639a96f35b14610333578063a42dce8014610353578063a87b815214610373578063a935e76614610393578063b5344257146103b5578063b80cb14b146103d557600080fd5b806366d023931161013e57806386ae47f01161011857806386ae47f0146102b55780638da5cb5b146102d55780639000b3d6146102f3578063960bfe041461031357600080fd5b806366d023931461026b5780636adb16b51461028b578063715018a6146102a057600080fd5b8063178977c91461018657806320c8eed2146101a857806333105218146101d05780634a114f72146102005780634bd7e4dd146102205780635d62a8dd14610233575b600080fd5b34801561019257600080fd5b506101a66101a136600461183e565b6104ca565b005b3480156101b457600080fd5b506101bd610522565b6040519081526020015b60405180910390f35b3480156101dc57600080fd5b506101f06101eb366004611860565b61054a565b60405190151581526020016101c7565b34801561020c57600080fd5b506101bd61021b36600461183e565b61055d565b6101a661022e3660046118cb565b610570565b34801561023f57600080fd5b50600954610253906001600160a01b031681565b6040516001600160a01b0390911681526020016101c7565b34801561027757600080fd5b506101bd610286366004611917565b61070a565b34801561029757600080fd5b506004546101bd565b3480156102ac57600080fd5b506101a661072c565b3480156102c157600080fd5b506101a66102d0366004611930565b610740565b3480156102e157600080fd5b506000546001600160a01b0316610253565b3480156102ff57600080fd5b506101a661030e366004611860565b610799565b34801561031f57600080fd5b506101a661032e366004611917565b6107ad565b34801561033f57600080fd5b506101a661034e366004611917565b6107f7565b34801561035f57600080fd5b506101a661036e366004611860565b61085f565b34801561037f57600080fd5b506101a661038e366004611860565b6108b5565b34801561039f57600080fd5b506103a8610941565b6040516101c791906119a5565b3480156103c157600080fd5b506101a66103d0366004611930565b61094d565b3480156103e157600080fd5b506101a66103f03660046119f2565b6109a0565b34801561040157600080fd5b50600854610253906001600160a01b031681565b34801561042157600080fd5b506101a6610430366004611860565b610a17565b34801561044157600080fd5b506101a6610a28565b34801561045657600080fd5b506101bd620186a081565b34801561046d57600080fd5b506102537f000000000000000000000000acaaaa98b9e23725046eda88d371b4864d90496b81565b3480156104a157600080fd5b506101bd610afe565b3480156104b657600080fd5b506101a66104c5366004611860565b610b09565b6104d2610b44565b60008281526005602090815260409182902083905581518481529081018390527f16fd6efb66614022ccb496c36757ea86f51445694c2d6433be02d0ddc23be06991015b60405180910390a15050565b6000670de0b6b3a76400006004544761053b9190611a74565b6105459190611a8b565b905090565b6000610557600183610b71565b92915050565b60006105698383610b80565b9392505050565b336001600160a01b037f000000000000000000000000acaaaa98b9e23725046eda88d371b4864d90496b16146105c057604051635c85788760e11b81523360048201526024015b60405180910390fd5b60006105d46105cf8484610c2e565b610c70565b90504684036105f85760405163ad5612e360e01b81524660048201526024016105b7565b8051461461061f57805160405163047c777d60e31b815260048101919091526024016105b7565b600061062f858360200151610b80565b90508034101561065b576040516343dd228560e11b8152346004820152602481018290526044016105b7565b600061066b868460200151610c99565b905060006106b086868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250869250610d9d915050565b905060006106c48280519060200120610dc9565b9050877f99dfc0061f727d353cc6eb34ccdd188242f0b6e6ed2df287a551edf53219f20483836040516106f8929190611afd565b60405180910390a25050505050505050565b600081815260056020526040812054908190036107275750620186a05b919050565b610734610b44565b61073e6000610dfc565b565b610748610b44565b8060005b818110156107935761078384848381811061076957610769611b1f565b905060200201602081019061077e9190611860565b610e4c565b61078c81611b35565b905061074c565b50505050565b6107a1610b44565b6107aa81610e90565b50565b6107b5610b44565b6107c0600182610ed4565b6040518181527f6c4ce60fd690e1216286a10b875c5662555f10774484e58142cedd7a90781baa906020015b60405180910390a150565b6107ff610b44565b662386f26fc1000081111561082a57604051638a7acbcd60e01b8152600481018290526024016105b7565b60048190556040518181527fff6eea4807f1d9f8369b26f163207ca7fbbc91ec6bf92c3cd02119f9dcbb299b906020016107ec565b610867610b44565b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527f9c1996a14d26c3ecd833c10222d012447ef07b09b15000f3a34318ff039c0bdc906020016107ec565b6108bd610b44565b806001600160a01b03163b6000036108f35760405163d129a3eb60e01b81526001600160a01b03821660048201526024016105b7565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f1c045b93ecd363a3ccd287c43f9ab97490903b354e7d99b149992b1e244254a9906020016107ec565b60606105456001610ef9565b610955610b44565b8060005b818110156107935761099084848381811061097657610976611b1f565b905060200201602081019061098b9190611860565b610e90565b61099981611b35565b9050610959565b60006109c285856040516109b5929190611b4e565b6040518091039020610dc9565b90506109d16001828585610f07565b610a1085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110ba92505050565b5050505050565b610a1f610b44565b6107aa81610e4c565b6008546001600160a01b0316610a515760405163caa4422960e01b815260040160405180910390fd5b47600003610a7257604051631dd7191360e01b815260040160405180910390fd5b6000610a7c610522565b90506000610a8a8247611b5e565b600854909150610aa3906001600160a01b0316826111e9565b610aad33836111e9565b600854604080516001600160a01b039092168252602082018390523390820152606081018390527ff4e6bc0a6951927d4db8490fb63528b3c4ccb43865870fe4e3db7a090cbb14b190608001610516565b600061054560015490565b610b11610b44565b6001600160a01b038116610b3b57604051631e4fbdf760e01b8152600060048201526024016105b7565b6107aa81610dfc565b6000546001600160a01b0316331461073e5760405163118cdaa760e01b81523360048201526024016105b7565b60006105696001840183611285565b6000610b8a6112a7565b6001600160a01b0316635cbd3c4884610ba28661070a565b610baa610afe565b610bb5906040611a74565b610bc190610104611b71565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526044820152606401602060405180830381865afa158015610c0a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105699190611b84565b3660006002831015610c575783836040516332ce7cfd60e11b81526004016105b7929190611b9d565b610c648360028187611bcc565b915091505b9250929050565b604080516060810182526000808252602082018190529181019190915261056982840184611c35565b6060610ca36112a7565b6001600160a01b0316636f928aa76040518163ffffffff1660e01b8152600401600060405180830381865afa158015610ce0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d089190810190611cf9565b805190915015610557578051602080830191909120600085815260069092526040909120548103610d4a57604051806020016040528060008152509150610d96565b60008481526006602052604090819020829055517fc8f0247ee5309fd4ba1e0bb1827f91d488ef9aa208d06a77ac10d7771f85b2e490610d8d9086908590611d36565b60405180910390a15b5092915050565b60608282604051602001610db2929190611d4f565b604051602081830303815290604052905092915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610e576001826112d4565b6040516001600160a01b03821681527f44a3cd4eb5cc5748f6169df057b1cb2ae4c383e87cd94663c430e095d4cba424906020016107ec565b610e9b60018261130e565b6040516001600160a01b03821681527f6d05492139c5ea989514a5d2150c028041e5c087e2a39967f67dc7d2655adb81906020016107ec565b80600003610ef557604051632698de3560e21b815260040160405180910390fd5b9055565b60606105578260010161136f565b6000610f14604183611a8b565b905081610f22604183611a74565b14610f425760405162ca4f9160e81b8152600481018390526024016105b7565b84546000819003610f6657604051632698de3560e21b815260040160405180910390fd5b6000806000805b85811015611083576000888589610f85604183611b71565b92610f9293929190611bcc565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250939450839250610fd691508d90508461137c565b5090925090506000816003811115610ff057610ff0611d7d565b146110105782604051637642b2f560e11b81526004016105b79190611d93565b846001600160a01b0316826001600160a01b03161161104257604051630da2019960e01b815260040160405180910390fd5b81945061104f8d83610b71565b156110625761105f600187611b71565b95505b61106d604188611b71565b96505050508061107c90611b35565b9050610f6d565b50838210156110af57604051631bf8177960e31b815260048101839052602481018590526044016105b7565b505050505050505050565b6000806110c6836113c9565b9150915060006110dd6110d8846113e9565b611493565b9050468160000151036111055760405163ad5612e360e01b81524660048201526024016105b7565b604051636cb0d24760e11b81526001600160a01b037f000000000000000000000000acaaaa98b9e23725046eda88d371b4864d90496b169063d961a48e90611151908690600401611d93565b600060405180830381600087803b15801561116b57600080fd5b505af115801561117f573d6000803e3d6000fd5b5050505061119681600001518260200151846114c2565b80600001517f3753b65288c95291b47fb4665e4dfc7531eb8d9301de678db6d54ebed5d2ae54856111cd8780519060200120610dc9565b6040516111db929190611afd565b60405180910390a250505050565b8047101561120c5760405163cd78605960e01b81523060048201526024016105b7565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611259576040519150601f19603f3d011682016040523d82523d6000602084013e61125e565b606091505b505090508061128057604051630a12f52160e11b815260040160405180910390fd5b505050565b6001600160a01b03811660009081526001830160205260408120541515610569565b6009546001600160a01b0316806112d15760405163598e308f60e11b815260040160405180910390fd5b90565b60006112e360018401836115a7565b90508061128057604051635689319160e01b81526001600160a01b03831660048201526024016105b7565b6001600160a01b038116611335576040516372ffc6b960e11b815260040160405180910390fd5b600061134460018401836115bc565b9050806112805760405163f09690b160e01b81526001600160a01b03831660048201526024016105b7565b60606000610569836115d1565b600080600083516041036113b65760208401516040850151606086015160001a6113a88882858561162d565b9550955095505050506113c2565b50508151600091506002905b9250925092565b606080828060200190518101906113e09190611da6565b91509150915091565b606060028251101561141057816040516332ce7cfd60e11b81526004016105b79190611d93565b8151600119018067ffffffffffffffff81111561142f5761142f611bf6565b6040519080825280601f01601f191660200182016040528015611459576020820181803683370190505b50915060008160208401836022870160045afa90508061148c5760405163080f227d60e11b815260040160405180910390fd5b5050919050565b604080516060810182526000808252602080830182905292820152825190916105579184018101908401611e0a565b80516000036114d057505050565b6000838152600760205260409020548015806114eb57508281105b156107935760008481526007602052604090208390556115096112a7565b6001600160a01b0316635299976985846040518363ffffffff1660e01b8152600401611536929190611d36565b600060405180830381600087803b15801561155057600080fd5b505af1158015611564573d6000803e3d6000fd5b505050507fca8f9b769e73367330805ba4c14b20e6976ebc6478999c6a9be1cfa9dc2d432b8483604051611599929190611d36565b60405180910390a150505050565b6000610569836001600160a01b0384166116fc565b6000610569836001600160a01b0384166117ef565b60608160000180548060200260200160405190810160405280929190818152602001828054801561162157602002820191906000526020600020905b81548152602001906001019080831161160d575b50505050509050919050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561166857506000915060039050826116f2565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa1580156116bc573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166116e8575060009250600191508290506116f2565b9250600091508190505b9450945094915050565b600081815260018301602052604081205480156117e5576000611720600183611b5e565b855490915060009061173490600190611b5e565b905080821461179957600086600001828154811061175457611754611b1f565b906000526020600020015490508087600001848154811061177757611777611b1f565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806117aa576117aa611e46565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610557565b6000915050610557565b600081815260018301602052604081205461183657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610557565b506000610557565b6000806040838503121561185157600080fd5b50508035926020909101359150565b60006020828403121561187257600080fd5b81356001600160a01b038116811461056957600080fd5b60008083601f84011261189b57600080fd5b50813567ffffffffffffffff8111156118b357600080fd5b602083019150836020828501011115610c6957600080fd5b6000806000604084860312156118e057600080fd5b83359250602084013567ffffffffffffffff8111156118fe57600080fd5b61190a86828701611889565b9497909650939450505050565b60006020828403121561192957600080fd5b5035919050565b6000806020838503121561194357600080fd5b823567ffffffffffffffff8082111561195b57600080fd5b818501915085601f83011261196f57600080fd5b81358181111561197e57600080fd5b8660208260051b850101111561199357600080fd5b60209290920196919550909350505050565b6020808252825182820181905260009190848201906040850190845b818110156119e65783516001600160a01b0316835292840192918401916001016119c1565b50909695505050505050565b60008060008060408587031215611a0857600080fd5b843567ffffffffffffffff80821115611a2057600080fd5b611a2c88838901611889565b90965094506020870135915080821115611a4557600080fd5b50611a5287828801611889565b95989497509550505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761055757610557611a5e565b600082611aa857634e487b7160e01b600052601260045260246000fd5b500490565b60005b83811015611ac8578181015183820152602001611ab0565b50506000910152565b60008151808452611ae9816020860160208601611aad565b601f01601f19169290920160200192915050565b604081526000611b106040830185611ad1565b90508260208301529392505050565b634e487b7160e01b600052603260045260246000fd5b600060018201611b4757611b47611a5e565b5060010190565b8183823760009101908152919050565b8181038181111561055757610557611a5e565b8082018082111561055757610557611a5e565b600060208284031215611b9657600080fd5b5051919050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60008085851115611bdc57600080fd5b83861115611be957600080fd5b5050820193919092039150565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715611c2f57611c2f611bf6565b60405290565b600060608284031215611c4757600080fd5b611c4f611c0c565b8235815260208301356020820152604083013560408201528091505092915050565b600082601f830112611c8257600080fd5b815167ffffffffffffffff80821115611c9d57611c9d611bf6565b604051601f8301601f19908116603f01168101908282118183101715611cc557611cc5611bf6565b81604052838152866020858801011115611cde57600080fd5b611cef846020830160208901611aad565b9695505050505050565b600060208284031215611d0b57600080fd5b815167ffffffffffffffff811115611d2257600080fd5b611d2e84828501611c71565b949350505050565b828152604060208201526000611d2e6040830184611ad1565b604081526000611d626040830185611ad1565b8281036020840152611d748185611ad1565b95945050505050565b634e487b7160e01b600052602160045260246000fd5b6020815260006105696020830184611ad1565b60008060408385031215611db957600080fd5b825167ffffffffffffffff80821115611dd157600080fd5b611ddd86838701611c71565b93506020850151915080821115611df357600080fd5b50611e0085828601611c71565b9150509250929050565b600060608284031215611e1c57600080fd5b611e24611c0c565b8251815260208301516020820152604083015160408201528091505092915050565b634e487b7160e01b600052603160045260246000fdfea264697066735822122054f7041458cc1e180012e8c734aa367ec0815599ffa3992062aef1405f764e8564736f6c63430008140033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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