Sepolia Testnet

Contract

0x25C6b2346cD0287B7b94d53bb36F7B611D911664

Overview

ETH Balance

0.0000025 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Verify Remote En...60137042024-05-31 16:58:36228 days ago1717174716IN
0x25C6b234...11D911664
0 ETH0.000942757.18134436
Verify Remote En...60137012024-05-31 16:57:36228 days ago1717174656IN
0x25C6b234...11D911664
0 ETH0.00094297.183149
Verify Remote En...59965502024-05-28 22:27:12230 days ago1716935232IN
0x25C6b234...11D911664
0 ETH0.000132411.00884035
Verify Remote En...59965462024-05-28 22:26:12230 days ago1716935172IN
0x25C6b234...11D911664
0 ETH0.000145491.1083045
Verify Remote En...59965442024-05-28 22:25:48230 days ago1716935148IN
0x25C6b234...11D911664
0 ETH0.000132321.00836398
Verify Remote En...59965412024-05-28 22:25:12230 days ago1716935112IN
0x25C6b234...11D911664
0 ETH0.000132591.00995197
Verify Remote En...59965332024-05-28 22:23:36230 days ago1716935016IN
0x25C6b234...11D911664
0 ETH0.000133081.01393874
Verify Remote En...59965312024-05-28 22:23:12230 days ago1716934992IN
0x25C6b234...11D911664
0 ETH0.000133311.01555256
Verify Remote En...59965302024-05-28 22:23:00230 days ago1716934980IN
0x25C6b234...11D911664
0 ETH0.000146481.11606279
Verify Remote En...59965282024-05-28 22:22:36230 days ago1716934956IN
0x25C6b234...11D911664
0 ETH0.000133431.01645848
Verify Remote En...59579122024-05-23 0:02:24236 days ago1716422544IN
0x25C6b234...11D911664
0 ETH0.0014389810.96133664
Verify Remote En...59233082024-05-17 20:18:48241 days ago1715977128IN
0x25C6b234...11D911664
0 ETH0.0021554916.41785101
Verify Remote En...59233062024-05-17 20:18:24241 days ago1715977104IN
0x25C6b234...11D911664
0 ETH0.0022126816.85498682
Verify Remote En...59232992024-05-17 20:16:48241 days ago1715977008IN
0x25C6b234...11D911664
0 ETH0.0019471314.83213692
Verify Remote En...59232962024-05-17 20:16:12241 days ago1715976972IN
0x25C6b234...11D911664
0 ETH0.0017706513.48781813
Verify Remote En...59232922024-05-17 20:15:24241 days ago1715976924IN
0x25C6b234...11D911664
0 ETH0.001687512.85443003
Verify Remote En...59232902024-05-17 20:15:00241 days ago1715976900IN
0x25C6b234...11D911664
0 ETH0.0016969112.92610537
Verify Remote En...59232862024-05-17 20:14:12241 days ago1715976852IN
0x25C6b234...11D911664
0 ETH0.0017015812.96402839
Verify Remote En...59232842024-05-17 20:13:48241 days ago1715976828IN
0x25C6b234...11D911664
0 ETH0.0017954913.67954772
Verify Remote En...59221702024-05-17 16:08:36242 days ago1715962116IN
0x25C6b234...11D911664
0 ETH0.02020923153.95632281
Verify Remote En...59221562024-05-17 16:05:36242 days ago1715961936IN
0x25C6b234...11D911664
0 ETH0.02113933161.08611422
Verify Remote En...59221502024-05-17 16:04:24242 days ago1715961864IN
0x25C6b234...11D911664
0 ETH0.02151585163.91030818
Verify Remote En...59221482024-05-17 16:04:00242 days ago1715961840IN
0x25C6b234...11D911664
0 ETH0.01989454151.54518998
Verify Remote En...59221462024-05-17 16:03:36242 days ago1715961816IN
0x25C6b234...11D911664
0 ETH0.02115384161.16725058
Verify Remote En...59035542024-05-14 21:15:48244 days ago1715721348IN
0x25C6b234...11D911664
0 ETH0.0024036218.31279101
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
60137032024-05-31 16:58:12228 days ago1717174692
0x25C6b234...11D911664
0.0000001 ETH
59965482024-05-28 22:26:36230 days ago1716935196
0x25C6b234...11D911664
0.0000001 ETH
59965462024-05-28 22:26:12230 days ago1716935172
0x25C6b234...11D911664
0.0000001 ETH
59965372024-05-28 22:24:24230 days ago1716935064
0x25C6b234...11D911664
0.0000001 ETH
59965352024-05-28 22:24:00230 days ago1716935040
0x25C6b234...11D911664
0.0000001 ETH
59965332024-05-28 22:23:36230 days ago1716935016
0x25C6b234...11D911664
0.0000001 ETH
59965302024-05-28 22:23:00230 days ago1716934980
0x25C6b234...11D911664
0.0000001 ETH
59233052024-05-17 20:18:12241 days ago1715977092
0x25C6b234...11D911664
0.0000001 ETH
59233032024-05-17 20:17:48241 days ago1715977068
0x25C6b234...11D911664
0.0000001 ETH
59232942024-05-17 20:15:48241 days ago1715976948
0x25C6b234...11D911664
0.0000001 ETH
59232922024-05-17 20:15:24241 days ago1715976924
0x25C6b234...11D911664
0.0000001 ETH
59232862024-05-17 20:14:12241 days ago1715976852
0x25C6b234...11D911664
0.0000001 ETH
59232822024-05-17 20:13:24241 days ago1715976804
0x25C6b234...11D911664
0.0000001 ETH
59232812024-05-17 20:13:12241 days ago1715976792
0x25C6b234...11D911664
0.0000001 ETH
59232812024-05-17 20:13:12241 days ago1715976792
0x25C6b234...11D911664
0.0000001 ETH
59232802024-05-17 20:13:00241 days ago1715976780
0x25C6b234...11D911664
0.0000001 ETH
59221682024-05-17 16:08:12242 days ago1715962092
0x25C6b234...11D911664
0.0000001 ETH
59221632024-05-17 16:07:12242 days ago1715962032
0x25C6b234...11D911664
0.0000001 ETH
59221482024-05-17 16:04:00242 days ago1715961840
0x25C6b234...11D911664
0.0000001 ETH
59221452024-05-17 16:03:24242 days ago1715961804
0x25C6b234...11D911664
0.0000001 ETH
59221442024-05-17 16:03:12242 days ago1715961792
0x25C6b234...11D911664
0.0000001 ETH
59221432024-05-17 16:03:00242 days ago1715961780
0x25C6b234...11D911664
0.0000001 ETH
59035562024-05-14 21:16:12244 days ago1715721372
0x25C6b234...11D911664
0.0000001 ETH
59035482024-05-14 21:14:36244 days ago1715721276
0x25C6b234...11D911664
0.0000001 ETH
59026922024-05-14 18:04:36244 days ago1715709876
0x25C6b234...11D911664
0.0000001 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CortexModule

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 26 : CortexModule.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

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

import {CortexModuleEvents} from "../events/CortexModuleEvents.sol";
import {ICortexGasOracle} from "../interfaces/ICortexGasOracle.sol";
import {ICortexModule} from "../interfaces/ICortexModule.sol";
import {InterchainEntry, InterchainEntryLib} from "../libs/InterchainEntry.sol";
import {ModuleEntryLib} from "../libs/ModuleEntry.sol";
import {ThresholdECDSA} from "../libs/ThresholdECDSA.sol";
import {VersionedPayloadLib} from "../libs/VersionedPayload.sol";

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

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

contract CortexModule is InterchainModule, ClaimableFees, Ownable, CortexModuleEvents, ICortexModule {
    using VersionedPayloadLib for bytes;

    // TODO: make sure this is a good enough default value
    uint256 public constant DEFAULT_VERIFY_GAS_LIMIT = 100_000;

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

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

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

    /// @notice Address of the gas oracle used for estimating the verification fees.
    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 ════════════════════════════════════════════════════

    /// @notice Adds a new verifier to the module.
    /// @dev Could be only called by the owner. Will revert if the verifier is already added.
    function addVerifier(address verifier) external onlyOwner {
        _addVerifier(verifier);
    }

    /// @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.
    function addVerifiers(address[] calldata verifiers) external onlyOwner {
        uint256 length = verifiers.length;
        for (uint256 i = 0; i < length; ++i) {
            _addVerifier(verifiers[i]);
        }
    }

    /// @notice Removes a verifier from the module.
    /// @dev Could be only called by the owner. Will revert if the verifier is not added.
    function removeVerifier(address verifier) external onlyOwner {
        _removeVerifier(verifier);
    }

    /// @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.
    function removeVerifiers(address[] calldata verifiers) external onlyOwner {
        uint256 length = verifiers.length;
        for (uint256 i = 0; i < length; ++i) {
            _removeVerifier(verifiers[i]);
        }
    }

    /// @notice Sets the threshold of the module.
    /// @dev Could be only called by the owner. Will revert if the threshold is zero.
    function setThreshold(uint256 threshold) external onlyOwner {
        _verifiers.modifyThreshold(threshold);
        emit ThresholdSet(threshold);
    }

    /// @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.
    function setFeeRecipient(address feeRecipient) external onlyOwner {
        if (feeRecipient == address(0)) {
            revert CortexModule__FeeRecipientZeroAddress();
        }
        _feeRecipient = feeRecipient;
        emit FeeRecipientSet(feeRecipient);
    }

    /// @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% (1e16).
    function setClaimerFraction(uint256 claimerFraction) external onlyOwner {
        if (claimerFraction > MAX_CLAIMER_FRACTION) {
            revert ClaimableFees__ClaimerFractionAboveMax(claimerFraction, MAX_CLAIMER_FRACTION);
        }
        _claimerFraction = claimerFraction;
        emit ClaimerFractionSet(claimerFraction);
    }

    /// @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.
    function setGasOracle(address gasOracle_) external onlyOwner {
        if (gasOracle_.code.length == 0) {
            revert CortexModule__GasOracleNotContract(gasOracle_);
        }
        gasOracle = gasOracle_;
        emit GasOracleSet(gasOracle_);
    }

    /// @notice Sets the estimated gas limit for verifying an entry 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 for the verification on the specified chain
    function setVerifyGasLimit(uint64 chainId, uint256 gasLimit) external onlyOwner {
        _verifyGasLimit[chainId] = gasLimit;
        emit VerifyGasLimitSet(chainId, gasLimit);
    }

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

    /// @notice Verifies an entry from the remote chain using a set of verifier signatures.
    /// If the threshold is met, the entry 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 encodedEntry The encoded entry to verify
    /// @param signatures   Signatures used to verify the entry, concatenated
    function verifyRemoteEntry(bytes calldata encodedEntry, bytes calldata signatures) external {
        bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash(keccak256(encodedEntry));
        _verifiers.verifySignedHash(ethSignedHash, signatures);
        (bytes memory versionedEntry, bytes memory data) = ModuleEntryLib.decodeVersionedModuleEntry(encodedEntry);
        InterchainEntry memory entry = InterchainEntryLib.decodeEntryFromMemory(versionedEntry.getPayloadFromMemory());
        if (entry.srcChainId == block.chainid) {
            revert InterchainModule__ChainIdNotRemote(entry.srcChainId);
        }
        _verifyRemoteEntry(versionedEntry);
        emit EntryVerified(entry.srcChainId, encodedEntry, ethSignedHash);
        _receiveModuleData(entry.srcChainId, entry.dbNonce, data);
    }

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

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

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

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

    /// @notice Returns the estimated gas limit for verifying an entry on the given chain.
    /// Note: this defaults to DEFAULT_VERIFY_GAS_LIMIT if not set.
    function getVerifyGasLimit(uint64 chainId) public view override returns (uint256 gasLimit) {
        gasLimit = _verifyGasLimit[chainId];
        if (gasLimit == 0) {
            gasLimit = DEFAULT_VERIFY_GAS_LIMIT;
        }
    }

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

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

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

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

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

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

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

    /// @dev Internal logic to request the verification of an entry on the destination chain.
    /// Following checks have been done at this point:
    /// - Entry is a valid versioned entry coming from the Interchain DataBase.
    /// - Enough fees have been paid for the verification.
    ///
    /// Derived contracts should implement the logic to relay the entry to the destination chain:
    /// the destination module counterpart should call `db.verifyRemoteEntry(versionedEntry)`.
    function _relayDBEntry(uint64 dstChainId, bytes memory versionedEntry) internal override {
        bytes memory moduleData = _fillModuleData(dstChainId);
        bytes memory encodedEntry = ModuleEntryLib.encodeVersionedModuleEntry(versionedEntry, moduleData);
        bytes32 ethSignedEntryHash = MessageHashUtils.toEthSignedMessageHash(keccak256(encodedEntry));
        emit EntryVerificationRequested(dstChainId, encodedEntry, ethSignedEntryHash);
    }

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

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

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

    /// @dev Internal logic to get the module fee for verifying an entry on the specified destination chain.
    function _getModuleFee(uint64 dstChainId) internal view override returns (uint256) {
        // On the remote chain the verifyRemoteEntry(entry, signatures) function will be called.
        // We need to figure out the calldata size for the remote call.
        // selector (4 bytes) + entry + signatures
        // entry 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 (entry offset) + 32 (signatures offset) + 128 + 32
        return _getCortexGasOracle().estimateTxCostInLocalUnits({
            remoteChainId: dstChainId,
            gasLimit: getVerifyGasLimit(dstChainId),
            calldataSize: 260 + 64 * getThreshold()
        });
    }

    /// @dev Internal logic to get the Cortex Gas Oracle. Reverts if the gas oracle is not set.
    function _getCortexGasOracle() internal view returns (ICortexGasOracle cortexGasOracle) {
        cortexGasOracle = ICortexGasOracle(gasOracle);
        if (address(cortexGasOracle) == address(0)) {
            revert CortexModule__GasOracleZeroAddress();
        }
    }
}

File 2 of 26 : InterchainModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @notice Common logic for all Interchain Modules.
abstract contract InterchainModule is IInterchainModule {
    /// @notice The address of the Interchain DataBase contract: used for verifying the entries.
    address public immutable INTERCHAIN_DB;

    constructor(address interchainDB) {
        INTERCHAIN_DB = interchainDB;
    }

    /// @notice Request the verification of an entry from the Interchain DataBase by the module.
    /// Note: a fee is paid to the module for verification, and could be retrieved by using `getModuleFee`.
    /// Note: this will eventually trigger `InterchainDB.verifyRemoteEntry(entry)` 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 versionedEntry   The versioned entry to verify
    function requestEntryVerification(uint64 dstChainId, bytes memory versionedEntry) external payable {
        if (msg.sender != INTERCHAIN_DB) {
            revert InterchainModule__CallerNotInterchainDB(msg.sender);
        }
        if (dstChainId == block.chainid) {
            revert InterchainModule__ChainIdNotRemote(dstChainId);
        }
        uint256 requiredFee = _getModuleFee(dstChainId);
        if (msg.value < requiredFee) {
            revert InterchainModule__FeeAmountBelowMin({feeAmount: msg.value, minRequired: requiredFee});
        }
        // Note: we don't emit an event here, the derived contract could emit an event if needed.
        _relayDBEntry(dstChainId, versionedEntry);
    }

    /// @notice Get the Module fee for verifying an entry on the specified destination chain.
    /// @param dstChainId   The chain id of the destination chain
    function getModuleFee(uint64 dstChainId) external view returns (uint256) {
        return _getModuleFee(dstChainId);
    }

    /// @dev Should be called once the Module has verified the entry and needs to signal this
    /// to the InterchainDB.
    function _verifyRemoteEntry(bytes memory versionedEntry) internal {
        IInterchainDB(INTERCHAIN_DB).verifyRemoteEntry(versionedEntry);
    }

    // solhint-disable no-empty-blocks
    /// @dev Internal logic to relay a DB entry to the destination chain.
    /// Following checks have been done at this point:
    /// - Entry is a valid versioned entry coming from the Interchain DataBase.
    /// - Enough fees have been paid for the verification.
    ///
    /// Derived contracts should implement the logic so that eventually the destination counterpart
    /// of this module calls `_verifyRemoteEntry(versionedEntry)`.
    function _relayDBEntry(uint64 dstChainId, bytes memory versionedEntry) internal virtual;

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

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

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

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

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

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

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

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

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

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

File 4 of 26 : ICortexGasOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface ICortexGasOracle is IGasOracle {
    function receiveRemoteGasData(uint64 srcChainId, bytes calldata data) external;

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

    function getLocalGasData() external view returns (bytes memory);
}

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

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

interface ICortexModule is IInterchainModule {
    error CortexModule__GasOracleNotContract(address gasOracle);
    error CortexModule__GasOracleZeroAddress();
    error CortexModule__FeeRecipientZeroAddress();

    function addVerifier(address verifier) external;
    function addVerifiers(address[] calldata verifiers) external;
    function removeVerifier(address verifier) external;
    function removeVerifiers(address[] calldata verifiers) external;
    function setThreshold(uint256 threshold) external;

    function setFeeRecipient(address feeRecipient) external;
    function setClaimerFraction(uint256 claimerFraction) external;
    function setGasOracle(address gasOracle_) external;
    function setVerifyGasLimit(uint64 chainId, uint256 gasLimit) external;

    function verifyRemoteEntry(bytes calldata encodedEntry, bytes calldata signatures) external;

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

    function gasOracle() external view returns (address);
    function getVerifiers() external view returns (address[] memory);
    function getThreshold() external view returns (uint256);
    function isVerifier(address account) external view returns (bool);
    function getVerifyGasLimit(uint64 chainId) external view returns (uint256);
}

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

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

/// @notice Struct representing an entry in the Interchain DataBase.
/// Entry has a globally unique identifier (key) and a value.
/// Assuming `srcWriter` has written data `digest` on the source chain:
/// - key: (srcChainId, dbNonce)
/// - entryValue = keccak256(srcWriter, digest)
/// @param srcChainId   The chain id of the source chain
/// @param dbNonce      The database nonce of the entry
/// @param entryValue   The entry value
struct InterchainEntry {
    uint64 srcChainId;
    uint64 dbNonce;
    bytes32 entryValue;
}

type EntryKey is uint128;

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

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 entryValue   The value of the entry
    /// @return entry       The constructed InterchainEntry struct
    function constructLocalEntry(
        uint64 dbNonce,
        bytes32 entryValue
    )
        internal
        view
        returns (InterchainEntry memory entry)
    {
        uint64 srcChainId = SafeCast.toUint64(block.chainid);
        return InterchainEntry({srcChainId: srcChainId, dbNonce: dbNonce, entryValue: entryValue});
    }

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

    /// @notice Returns the value of the entry: writer + digest hashed together.
    /// Note: this is exposed for convenience to avoid typecasts prior to abi-encoding.
    function getEntryValue(address srcWriter, bytes32 digest) internal pure returns (bytes32) {
        return keccak256(abi.encode(srcWriter, digest));
    }

    /// @notice Encodes the InterchainEntry struct into a non-versioned entry payload.
    function encodeEntry(InterchainEntry memory entry) internal pure returns (bytes memory) {
        return abi.encode(encodeEntryKey(entry.srcChainId, entry.dbNonce), entry.entryValue);
    }

    /// @notice Decodes the InterchainEntry struct from a non-versioned entry payload in calldata.
    function decodeEntry(bytes calldata data) internal pure returns (InterchainEntry memory entry) {
        EntryKey key;
        (key, entry.entryValue) = abi.decode(data, (EntryKey, bytes32));
        (entry.srcChainId, entry.dbNonce) = decodeEntryKey(key);
    }

    /// @notice Decodes the InterchainEntry struct from a non-versioned entry payload in memory.
    function decodeEntryFromMemory(bytes memory data) internal pure returns (InterchainEntry memory entry) {
        EntryKey key;
        (key, entry.entryValue) = abi.decode(data, (EntryKey, bytes32));
        (entry.srcChainId, entry.dbNonce) = decodeEntryKey(key);
    }

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

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

File 7 of 26 : ModuleEntry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

library ModuleEntryLib {
    /// @notice Encodes the versioned entry and the auxiliary module data into a single bytes array
    /// @param versionedEntry       The versioned entry to encode
    /// @param moduleData           The auxiliary module data to encode
    /// @return encodedModuleEntry  The encoded versioned module entry
    function encodeVersionedModuleEntry(
        bytes memory versionedEntry,
        bytes memory moduleData
    )
        internal
        pure
        returns (bytes memory encodedModuleEntry)
    {
        return abi.encode(versionedEntry, moduleData);
    }

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

File 8 of 26 : ThresholdECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

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

using ThresholdECDSALib for ThresholdECDSA global;

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

    uint256 private constant SIGNATURE_LENGTH = 65;

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

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

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

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

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

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

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

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

File 9 of 26 : VersionedPayload.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

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

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

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

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

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

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

File 10 of 26 : ClaimableFees.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

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

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

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

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

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

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

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

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

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

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

File 11 of 26 : 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 12 of 26 : 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 13 of 26 : IInterchainDB.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface IInterchainDB {
    error InterchainDB__ChainIdNotRemote(uint64 chainId);
    error InterchainDB__EntryConflict(address module, InterchainEntry newEntry);
    error InterchainDB__EntryVersionMismatch(uint16 version, uint16 required);
    error InterchainDB__FeeAmountBelowMin(uint256 feeAmount, uint256 minRequired);
    error InterchainDB__ModulesNotProvided();

    function writeEntry(bytes32 digest) external returns (uint64 dbNonce);

    function requestEntryVerification(
        uint64 dstChainId,
        uint64 dbNonce,
        address[] memory srcModules
    )
        external
        payable;

    function writeEntryRequestVerification(
        uint64 dstChainId,
        bytes32 digest,
        address[] memory srcModules
    )
        external
        payable
        returns (uint64 dbNonce);

    function verifyRemoteEntry(bytes memory encodedEntry) external;

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

    function getInterchainFee(uint64 dstChainId, address[] memory srcModules) external view returns (uint256);

    function getEncodedEntry(uint64 dbNonce) external view returns (bytes memory);
    function getEntry(uint64 dbNonce) external view returns (InterchainEntry memory);
    function getEntryValue(uint64 dbNonce) external view returns (bytes32);

    function getDBNonce() external view returns (uint64);

    function checkEntryVerification(
        address dstModule,
        InterchainEntry memory entry
    )
        external
        view
        returns (uint256 moduleVerifiedAt);

    // solhint-disable-next-line func-name-mixedcase
    function DB_VERSION() external pure returns (uint16);
}

File 14 of 26 : 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__CallerNotInterchainDB(address caller);
    error InterchainModule__ChainIdNotRemote(uint64 chainId);
    error InterchainModule__FeeAmountBelowMin(uint256 feeAmount, uint256 minRequired);

    function requestEntryVerification(uint64 dstChainId, bytes memory versionedEntry) external payable;

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

    function getModuleFee(uint64 dstChainId) external view returns (uint256);
}

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

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

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

File 16 of 26 : IGasOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGasOracle {
    // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

    function convertRemoteValueToLocalUnits(uint64 remoteChainId, uint256 value) external view returns (uint256);

    function estimateTxCostInLocalUnits(
        uint64 remoteChainId,
        uint256 gasLimit,
        uint256 calldataSize
    )
        external
        view
        returns (uint256);

    function estimateTxCostInRemoteUnits(
        uint64 remoteChainId,
        uint256 gasLimit,
        uint256 calldataSize
    )
        external
        view
        returns (uint256);
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 18 of 26 : 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 19 of 26 : 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 20 of 26 : ClaimableFeesEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

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

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

File 21 of 26 : IClaimableFees.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

    function claimFees() external;

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

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

File 22 of 26 : 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 23 of 26 : 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 24 of 26 : 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 25 of 26 : 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 26 of 26 : 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);
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@synapsecns/=node_modules/@synapsecns/",
    "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

[{"inputs":[{"internalType":"address","name":"interchainDB","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"claimerFraction","type":"uint256"},{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"name":"ClaimableFees__ClaimerFractionAboveMax","type":"error"},{"inputs":[],"name":"ClaimableFees__FeeAmountZero","type":"error"},{"inputs":[],"name":"ClaimableFees__FeeRecipientZeroAddress","type":"error"},{"inputs":[],"name":"CortexModule__FeeRecipientZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"gasOracle","type":"address"}],"name":"CortexModule__GasOracleNotContract","type":"error"},{"inputs":[],"name":"CortexModule__GasOracleZeroAddress","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"InterchainModule__CallerNotInterchainDB","type":"error"},{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"}],"name":"InterchainModule__ChainIdNotRemote","type":"error"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"minRequired","type":"uint256"}],"name":"InterchainModule__FeeAmountBelowMin","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ThresholdECDSA__RecoveredSignersNotSorted","type":"error"},{"inputs":[{"internalType":"uint256","name":"signaturesAmount","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ThresholdECDSA__SignaturesAmountBelowThreshold","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ThresholdECDSA__SignaturesPayloadLengthInvalid","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ThresholdECDSA__SignerAlreadyAdded","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ThresholdECDSA__SignerNotAdded","type":"error"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"ThresholdECDSA__SignerRecoveryFailed","type":"error"},{"inputs":[],"name":"ThresholdECDSA__SignerZeroAddress","type":"error"},{"inputs":[],"name":"ThresholdECDSA__ThresholdZero","type":"error"},{"inputs":[{"internalType":"bytes","name":"versionedPayload","type":"bytes"}],"name":"VersionedPayload__PayloadTooShort","type":"error"},{"inputs":[],"name":"VersionedPayload__PrecompileFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"claimerFraction","type":"uint256"}],"name":"ClaimerFractionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"dstChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"entry","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"ethSignedEntryHash","type":"bytes32"}],"name":"EntryVerificationRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"srcChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"entry","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"ethSignedEntryHash","type":"bytes32"}],"name":"EntryVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"FeeRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedFees","type":"uint256"},{"indexed":false,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimerReward","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"srcChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"GasDataReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"dstChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"GasDataSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gasOracle","type":"address"}],"name":"GasOracleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ThresholdSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"verifier","type":"address"}],"name":"VerifierAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"verifier","type":"address"}],"name":"VerifierRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"chainId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"VerifyGasLimitSet","type":"event"},{"inputs":[],"name":"DEFAULT_VERIFY_GAS_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INTERCHAIN_DB","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"}],"name":"addVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"verifiers","type":"address[]"}],"name":"addVerifiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gasOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimerFraction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimerReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"dstChainId","type":"uint64"}],"name":"getModuleFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVerifiers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"}],"name":"getVerifyGasLimit","outputs":[{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isVerifier","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"verifier","type":"address"}],"name":"removeVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"verifiers","type":"address[]"}],"name":"removeVerifiers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"dstChainId","type":"uint64"},{"internalType":"bytes","name":"versionedEntry","type":"bytes"}],"name":"requestEntryVerification","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"claimerFraction","type":"uint256"}],"name":"setClaimerFraction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gasOracle_","type":"address"}],"name":"setGasOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"setThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"},{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"setVerifyGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedEntry","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"verifyRemoteEntry","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b50604051620020ba380380620020ba8339810160408190526200003491620000ec565b6001600160a01b03808316608052819081166200006b57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b62000076816200007f565b50505062000124565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114620000e757600080fd5b919050565b600080604083850312156200010057600080fd5b6200010b83620000cf565b91506200011b60208401620000cf565b90509250929050565b608051611f6c6200014e6000396000818161041b015281816104fd01526112390152611f6c6000f3fe60806040526004361061019c5760003560e01c8063a935e766116100ec578063e4c612471161008a578063e8c4606a11610064578063e8c4606a14610472578063ecff27ac14610492578063f0b8cb1d146104b2578063f2fde38b146104d257600080fd5b8063e4c6124714610409578063e74b981b1461043d578063e75235b81461045d57600080fd5b8063c354bd6e116100c6578063c354bd6e146103aa578063ca2dfd0a146103bd578063d294f093146103dd578063e232d191146103f257600080fd5b8063a935e76614610348578063a9bc769b1461036a578063b53442571461038a57600080fd5b80635d62a8dd116101595780638da5cb5b116101335780638da5cb5b146102ca5780639000b3d6146102e8578063960bfe0414610308578063a87b81521461032857600080fd5b80635d62a8dd14610275578063715018a61461029557806386ae47f0146102aa57600080fd5b806325236ac0146101a157806326533fe9146101b657806333105218146101de5780634ccb20c01461020e5780634d8ccff9146102405780634f19911414610260575b600080fd5b6101b46101af36600461196c565b6104f2565b005b3480156101c257600080fd5b506101cb6105bc565b6040519081526020015b60405180910390f35b3480156101ea57600080fd5b506101fe6101f93660046119fc565b6105ce565b60405190151581526020016101d5565b34801561021a57600080fd5b506008546001600160a01b03165b6040516001600160a01b0390911681526020016101d5565b34801561024c57600080fd5b506101b461025b366004611a25565b6105e1565b34801561026c57600080fd5b506007546101cb565b34801561028157600080fd5b50600954610228906001600160a01b031681565b3480156102a157600080fd5b506101b4610641565b3480156102b657600080fd5b506101b46102c5366004611a4f565b610655565b3480156102d657600080fd5b506000546001600160a01b0316610228565b3480156102f457600080fd5b506101b46103033660046119fc565b6106a6565b34801561031457600080fd5b506101b4610323366004611ac3565b6106ba565b34801561033457600080fd5b506101b46103433660046119fc565b610704565b34801561035457600080fd5b5061035d610790565b6040516101d59190611adc565b34801561037657600080fd5b506101b4610385366004611ac3565b6107a1565b34801561039657600080fd5b506101b46103a5366004611a4f565b610816565b3480156103b657600080fd5b50476101cb565b3480156103c957600080fd5b506101b46103d83660046119fc565b610861565b3480156103e957600080fd5b506101b4610872565b3480156103fe57600080fd5b506101cb620186a081565b34801561041557600080fd5b506102287f000000000000000000000000000000000000000000000000000000000000000081565b34801561044957600080fd5b506101b46104583660046119fc565b610949565b34801561046957600080fd5b506101cb6109c6565b34801561047e57600080fd5b506101cb61048d366004611b29565b6109d1565b34801561049e57600080fd5b506101b46104ad366004611b8c565b6109dc565b3480156104be57600080fd5b506101cb6104cd366004611b29565b610b40565b3480156104de57600080fd5b506101b46104ed3660046119fc565b610b6c565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461054257604051639005749360e01b81523360048201526024015b60405180910390fd5b46826001600160401b031603610576576040516306434f9560e41b81526001600160401b0383166004820152602401610539565b600061058183610ba7565b9050803410156105ad57604051634290de2160e01b815234600482015260248101829052604401610539565b6105b78383610c5d565b505050565b6000476105c881610cfb565b91505090565b60006105db600183610d65565b92915050565b6105e9610d74565b6001600160401b038216600081815260046020908152604091829020849055815192835282018390527f51178ef7476261c9f8257978aa4f938e564be17543cea415e92527f11dd81498910160405180910390a15050565b610649610d74565b6106536000610da1565b565b61065d610d74565b8060005b818110156106a05761069884848381811061067e5761067e611bf7565b905060200201602081019061069391906119fc565b610df1565b600101610661565b50505050565b6106ae610d74565b6106b781610e35565b50565b6106c2610d74565b6106cd600182610e79565b6040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd7906020015b60405180910390a150565b61070c610d74565b806001600160a01b03163b60000361074257604051631ee268bd60e21b81526001600160a01b0382166004820152602401610539565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f3efbbb00c39812fb98647af6e9e2c3f4ec2b53d368cedd1e148330a05b652cfa906020016106f9565b606061079c6001610e9e565b905090565b6107a9610d74565b662386f26fc100008111156107e157604051630ae993dd60e01b815260048101829052662386f26fc100006024820152604401610539565b60078190556040518181527f2b76ed3837bd14c860020e473bce45e560d5bca9b5109ef2f08b2051d1cf6cc9906020016106f9565b61081e610d74565b8060005b818110156106a05761085984848381811061083f5761083f611bf7565b905060200201602081019061085491906119fc565b610e35565b600101610822565b610869610d74565b6106b781610df1565b47600081900361089557604051636e95c0a760e01b815260040160405180910390fd5b60006108a96008546001600160a01b031690565b90506001600160a01b0381166108d257604051631e39f76760e11b815260040160405180910390fd5b60006108dd83610cfb565b604080516001600160a01b03851681529482900360208601819052338683015260608601839052905190949192507ff4e6bc0a6951927d4db8490fb63528b3c4ccb43865870fe4e3db7a090cbb14b19181900360800190a161093f8284610eac565b6105b73382610eac565b610951610d74565b6001600160a01b0381166109785760405163d2a199f960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527fbf9a9534339a9d6b81696e05dcfb614b7dc518a31d48be3cfb757988381fb323906020016106f9565b600061079c60015490565b60006105db82610ba7565b6000610a2c85856040516109f1929190611c0d565b60405180910390207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b9050610a3b6001828585610f43565b600080610a7d87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110f192505050565b915091506000610a94610a8f84611111565b6111ba565b90504681600001516001600160401b031603610ad15780516040516306434f9560e41b81526001600160401b039091166004820152602401610539565b610ada83611222565b80600001516001600160401b03167f6b7c865224ce4dcba4e353bfba2379f0c0832295fb3694d1a5e106f74ac98dc8898987604051610b1b93929190611c1d565b60405180910390a2610b3681600001518260200151846112a3565b5050505050505050565b6001600160401b03811660009081526004602052604081205490819003610b675750620186a05b919050565b610b74610d74565b6001600160a01b038116610b9e57604051631e4fbdf760e01b815260006004820152602401610539565b6106b781610da1565b6000610bb16113c4565b6001600160a01b031663bf495c8883610bc985610b40565b610bd16109c6565b610bdc906040611c6c565b610be890610104611c83565b6040516001600160e01b031960e086901b1681526001600160401b03909316600484015260248301919091526044820152606401602060405180830381865afa158015610c39573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105db9190611c96565b6000610c68836113f1565b90506000610c76838361150e565b805160208201207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c812091925050846001600160401b03167fd140ba08e8b9b97c50a524fb5e049bf8f43442dcdd883c97b9826387100ace2c8383604051610cec929190611cff565b60405180910390a25050505050565b600080610d0760075490565b9050662386f26fc10000811115610d4157604051630ae993dd60e01b815260048101829052662386f26fc100006024820152604401610539565b670de0b6b3a7640000610d548285611c6c565b610d5e9190611d21565b9392505050565b6000610d5e600184018361153a565b6000546001600160a01b031633146106535760405163118cdaa760e01b8152336004820152602401610539565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610dfc60018261155c565b6040516001600160a01b03821681527f44a3cd4eb5cc5748f6169df057b1cb2ae4c383e87cd94663c430e095d4cba424906020016106f9565b610e40600182611596565b6040516001600160a01b03821681527f6d05492139c5ea989514a5d2150c028041e5c087e2a39967f67dc7d2655adb81906020016106f9565b80600003610e9a57604051635e1a7b8b60e11b815260040160405180910390fd5b9055565b60606105db826001016115f7565b80471015610ecf5760405163cd78605960e01b8152306004820152602401610539565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610f1c576040519150601f19603f3d011682016040523d82523d6000602084013e610f21565b606091505b50509050806105b757604051630a12f52160e11b815260040160405180910390fd5b6000610f50604183611d21565b905081610f5e604183611c6c565b14610f7f576040516319d9b69960e31b815260048101839052602401610539565b84546000819003610fa357604051635e1a7b8b60e11b815260040160405180910390fd5b6000806000805b858110156110ba576000888589610fc2604183611c83565b92610fcf93929190611d43565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093945083925061101391508d905084611604565b509092509050600081600381111561102d5761102d611d6d565b1461104d57826040516301fb043f60e61b81526004016105399190611d83565b846001600160a01b0316826001600160a01b03161161107f57604051630da2019960e01b815260040160405180910390fd5b81945061108c8d83610d65565b1561109f5761109c600187611c83565b95505b6110aa604188611c83565b9650505050806001019050610faa565b50838210156110e657604051630affed5760e21b81526004810183905260248101859052604401610539565b505050505050505050565b606080828060200190518101906111089190611de3565b91509150915091565b60606002825110156111385781604051635840c5b160e11b81526004016105399190611d83565b815160011901806001600160401b03811115611156576111566118ff565b6040519080825280601f01601f191660200182016040528015611180576020820181803683370190505b50915060008160208401836022870160045afa9050806111b35760405163080f227d60e11b815260040160405180910390fd5b5050919050565b60408051606081018252600080825260208201819052918101919091526000828060200190518101906111ed9190611e46565b60408401529050611209816001600160401b03604082901c1691565b6001600160401b03908116602085015216825250919050565b604051636cd46d4d60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d9a8da9a9061126e908490600401611d83565b600060405180830381600087803b15801561128857600080fd5b505af115801561129c573d6000803e3d6000fd5b5050505050565b80516000036112b157505050565b6001600160401b03808416600090815260066020526040902054168015806112ea5750826001600160401b0316816001600160401b0316105b156106a0576001600160401b038481166000908152600660205260409020805467ffffffffffffffff19169185169190911790556113266113c4565b6001600160a01b03166383389de785846040518363ffffffff1660e01b8152600401611353929190611e89565b600060405180830381600087803b15801561136d57600080fd5b505af1158015611381573d6000803e3d6000fd5b505050507f02c233a01329dc53cb24eb5e8e0131ad57c2d982e4aaa5bf8a75ee90e95b1c0084836040516113b6929190611e89565b60405180910390a150505050565b6009546001600160a01b0316806113ee57604051632545dc0d60e11b815260040160405180910390fd5b90565b60606113fb6113c4565b6001600160a01b0316636f928aa76040518163ffffffff1660e01b8152600401600060405180830381865afa158015611438573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114609190810190611eab565b9050805160000361147057919050565b80516020808301919091206001600160401b0384166000908152600590925260409091205481036114b257604051806020016040528060008152509150611508565b6001600160401b03831660009081526005602052604090819020829055517fb2243d133e6d54117d7ce9f172219373d8c0f601f4976bcb7860abe1addb9b2d906114ff9085908590611e89565b60405180910390a15b50919050565b60608282604051602001611523929190611edf565b604051602081830303815290604052905092915050565b6001600160a01b03811660009081526001830160205260408120541515610d5e565b600061156b6001840183611651565b9050806105b7576040516375a1d0c960e01b81526001600160a01b0383166004820152602401610539565b6001600160a01b0381166115bd576040516373e66c2d60e01b815260040160405180910390fd5b60006115cc6001840183611666565b9050806105b7576040516328a0fc2960e21b81526001600160a01b0383166004820152602401610539565b60606000610d5e8361167b565b6000806000835160410361163e5760208401516040850151606086015160001a611630888285856116d7565b95509550955050505061164a565b50508151600091506002905b9250925092565b6000610d5e836001600160a01b0384166117a6565b6000610d5e836001600160a01b038416611899565b6060816000018054806020026020016040519081016040528092919081815260200182805480156116cb57602002820191906000526020600020905b8154815260200190600101908083116116b7575b50505050509050919050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611712575060009150600390508261179c565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611766573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117925750600092506001915082905061179c565b9250600091508190505b9450945094915050565b6000818152600183016020526040812054801561188f5760006117ca600183611f0d565b85549091506000906117de90600190611f0d565b90508082146118435760008660000182815481106117fe576117fe611bf7565b906000526020600020015490508087600001848154811061182157611821611bf7565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061185457611854611f20565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105db565b60009150506105db565b60008181526001830160205260408120546118e0575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105db565b5060006105db565b80356001600160401b0381168114610b6757600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561193d5761193d6118ff565b604052919050565b60006001600160401b0382111561195e5761195e6118ff565b50601f01601f191660200190565b6000806040838503121561197f57600080fd5b611988836118e8565b915060208301356001600160401b038111156119a357600080fd5b8301601f810185136119b457600080fd5b80356119c76119c282611945565b611915565b8181528660208385010111156119dc57600080fd5b816020840160208301376000602083830101528093505050509250929050565b600060208284031215611a0e57600080fd5b81356001600160a01b0381168114610d5e57600080fd5b60008060408385031215611a3857600080fd5b611a41836118e8565b946020939093013593505050565b60008060208385031215611a6257600080fd5b82356001600160401b0380821115611a7957600080fd5b818501915085601f830112611a8d57600080fd5b813581811115611a9c57600080fd5b8660208260051b8501011115611ab157600080fd5b60209290920196919550909350505050565b600060208284031215611ad557600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b81811015611b1d5783516001600160a01b031683529284019291840191600101611af8565b50909695505050505050565b600060208284031215611b3b57600080fd5b610d5e826118e8565b60008083601f840112611b5657600080fd5b5081356001600160401b03811115611b6d57600080fd5b602083019150836020828501011115611b8557600080fd5b9250929050565b60008060008060408587031215611ba257600080fd5b84356001600160401b0380821115611bb957600080fd5b611bc588838901611b44565b90965094506020870135915080821115611bde57600080fd5b50611beb87828801611b44565b95989497509550505050565b634e487b7160e01b600052603260045260246000fd5b8183823760009101908152919050565b604081528260408201528284606083013760006060848301015260006060601f19601f8601168301019050826020830152949350505050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176105db576105db611c56565b808201808211156105db576105db611c56565b600060208284031215611ca857600080fd5b5051919050565b60005b83811015611cca578181015183820152602001611cb2565b50506000910152565b60008151808452611ceb816020860160208601611caf565b601f01601f19169290920160200192915050565b604081526000611d126040830185611cd3565b90508260208301529392505050565b600082611d3e57634e487b7160e01b600052601260045260246000fd5b500490565b60008085851115611d5357600080fd5b83861115611d6057600080fd5b5050820193919092039150565b634e487b7160e01b600052602160045260246000fd5b602081526000610d5e6020830184611cd3565b600082601f830112611da757600080fd5b8151611db56119c282611945565b818152846020838601011115611dca57600080fd5b611ddb826020830160208701611caf565b949350505050565b60008060408385031215611df657600080fd5b82516001600160401b0380821115611e0d57600080fd5b611e1986838701611d96565b93506020850151915080821115611e2f57600080fd5b50611e3c85828601611d96565b9150509250929050565b60008060408385031215611e5957600080fd5b82516fffffffffffffffffffffffffffffffff81168114611e7957600080fd5b6020939093015192949293505050565b6001600160401b0383168152604060208201526000611ddb6040830184611cd3565b600060208284031215611ebd57600080fd5b81516001600160401b03811115611ed357600080fd5b611ddb84828501611d96565b604081526000611ef26040830185611cd3565b8281036020840152611f048185611cd3565b95945050505050565b818103818111156105db576105db611c56565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220f2426520a08269d582c43284551d7cd9d25cdf33d702a4bf093a834080ca568864736f6c63430008180033000000000000000000000000b5bee19adc002e194e76e8fa9e50dbb2d92d999c000000000000000000000000e7353bedc72d29f99d6ca5cde69f807cce5d57e4

Deployed Bytecode

0x60806040526004361061019c5760003560e01c8063a935e766116100ec578063e4c612471161008a578063e8c4606a11610064578063e8c4606a14610472578063ecff27ac14610492578063f0b8cb1d146104b2578063f2fde38b146104d257600080fd5b8063e4c6124714610409578063e74b981b1461043d578063e75235b81461045d57600080fd5b8063c354bd6e116100c6578063c354bd6e146103aa578063ca2dfd0a146103bd578063d294f093146103dd578063e232d191146103f257600080fd5b8063a935e76614610348578063a9bc769b1461036a578063b53442571461038a57600080fd5b80635d62a8dd116101595780638da5cb5b116101335780638da5cb5b146102ca5780639000b3d6146102e8578063960bfe0414610308578063a87b81521461032857600080fd5b80635d62a8dd14610275578063715018a61461029557806386ae47f0146102aa57600080fd5b806325236ac0146101a157806326533fe9146101b657806333105218146101de5780634ccb20c01461020e5780634d8ccff9146102405780634f19911414610260575b600080fd5b6101b46101af36600461196c565b6104f2565b005b3480156101c257600080fd5b506101cb6105bc565b6040519081526020015b60405180910390f35b3480156101ea57600080fd5b506101fe6101f93660046119fc565b6105ce565b60405190151581526020016101d5565b34801561021a57600080fd5b506008546001600160a01b03165b6040516001600160a01b0390911681526020016101d5565b34801561024c57600080fd5b506101b461025b366004611a25565b6105e1565b34801561026c57600080fd5b506007546101cb565b34801561028157600080fd5b50600954610228906001600160a01b031681565b3480156102a157600080fd5b506101b4610641565b3480156102b657600080fd5b506101b46102c5366004611a4f565b610655565b3480156102d657600080fd5b506000546001600160a01b0316610228565b3480156102f457600080fd5b506101b46103033660046119fc565b6106a6565b34801561031457600080fd5b506101b4610323366004611ac3565b6106ba565b34801561033457600080fd5b506101b46103433660046119fc565b610704565b34801561035457600080fd5b5061035d610790565b6040516101d59190611adc565b34801561037657600080fd5b506101b4610385366004611ac3565b6107a1565b34801561039657600080fd5b506101b46103a5366004611a4f565b610816565b3480156103b657600080fd5b50476101cb565b3480156103c957600080fd5b506101b46103d83660046119fc565b610861565b3480156103e957600080fd5b506101b4610872565b3480156103fe57600080fd5b506101cb620186a081565b34801561041557600080fd5b506102287f000000000000000000000000b5bee19adc002e194e76e8fa9e50dbb2d92d999c81565b34801561044957600080fd5b506101b46104583660046119fc565b610949565b34801561046957600080fd5b506101cb6109c6565b34801561047e57600080fd5b506101cb61048d366004611b29565b6109d1565b34801561049e57600080fd5b506101b46104ad366004611b8c565b6109dc565b3480156104be57600080fd5b506101cb6104cd366004611b29565b610b40565b3480156104de57600080fd5b506101b46104ed3660046119fc565b610b6c565b336001600160a01b037f000000000000000000000000b5bee19adc002e194e76e8fa9e50dbb2d92d999c161461054257604051639005749360e01b81523360048201526024015b60405180910390fd5b46826001600160401b031603610576576040516306434f9560e41b81526001600160401b0383166004820152602401610539565b600061058183610ba7565b9050803410156105ad57604051634290de2160e01b815234600482015260248101829052604401610539565b6105b78383610c5d565b505050565b6000476105c881610cfb565b91505090565b60006105db600183610d65565b92915050565b6105e9610d74565b6001600160401b038216600081815260046020908152604091829020849055815192835282018390527f51178ef7476261c9f8257978aa4f938e564be17543cea415e92527f11dd81498910160405180910390a15050565b610649610d74565b6106536000610da1565b565b61065d610d74565b8060005b818110156106a05761069884848381811061067e5761067e611bf7565b905060200201602081019061069391906119fc565b610df1565b600101610661565b50505050565b6106ae610d74565b6106b781610e35565b50565b6106c2610d74565b6106cd600182610e79565b6040518181527f6e8a187d7944998085dbd1f16b84c51c903bb727536cdba86962439aded2cfd7906020015b60405180910390a150565b61070c610d74565b806001600160a01b03163b60000361074257604051631ee268bd60e21b81526001600160a01b0382166004820152602401610539565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527f3efbbb00c39812fb98647af6e9e2c3f4ec2b53d368cedd1e148330a05b652cfa906020016106f9565b606061079c6001610e9e565b905090565b6107a9610d74565b662386f26fc100008111156107e157604051630ae993dd60e01b815260048101829052662386f26fc100006024820152604401610539565b60078190556040518181527f2b76ed3837bd14c860020e473bce45e560d5bca9b5109ef2f08b2051d1cf6cc9906020016106f9565b61081e610d74565b8060005b818110156106a05761085984848381811061083f5761083f611bf7565b905060200201602081019061085491906119fc565b610e35565b600101610822565b610869610d74565b6106b781610df1565b47600081900361089557604051636e95c0a760e01b815260040160405180910390fd5b60006108a96008546001600160a01b031690565b90506001600160a01b0381166108d257604051631e39f76760e11b815260040160405180910390fd5b60006108dd83610cfb565b604080516001600160a01b03851681529482900360208601819052338683015260608601839052905190949192507ff4e6bc0a6951927d4db8490fb63528b3c4ccb43865870fe4e3db7a090cbb14b19181900360800190a161093f8284610eac565b6105b73382610eac565b610951610d74565b6001600160a01b0381166109785760405163d2a199f960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527fbf9a9534339a9d6b81696e05dcfb614b7dc518a31d48be3cfb757988381fb323906020016106f9565b600061079c60015490565b60006105db82610ba7565b6000610a2c85856040516109f1929190611c0d565b60405180910390207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b9050610a3b6001828585610f43565b600080610a7d87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110f192505050565b915091506000610a94610a8f84611111565b6111ba565b90504681600001516001600160401b031603610ad15780516040516306434f9560e41b81526001600160401b039091166004820152602401610539565b610ada83611222565b80600001516001600160401b03167f6b7c865224ce4dcba4e353bfba2379f0c0832295fb3694d1a5e106f74ac98dc8898987604051610b1b93929190611c1d565b60405180910390a2610b3681600001518260200151846112a3565b5050505050505050565b6001600160401b03811660009081526004602052604081205490819003610b675750620186a05b919050565b610b74610d74565b6001600160a01b038116610b9e57604051631e4fbdf760e01b815260006004820152602401610539565b6106b781610da1565b6000610bb16113c4565b6001600160a01b031663bf495c8883610bc985610b40565b610bd16109c6565b610bdc906040611c6c565b610be890610104611c83565b6040516001600160e01b031960e086901b1681526001600160401b03909316600484015260248301919091526044820152606401602060405180830381865afa158015610c39573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105db9190611c96565b6000610c68836113f1565b90506000610c76838361150e565b805160208201207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c812091925050846001600160401b03167fd140ba08e8b9b97c50a524fb5e049bf8f43442dcdd883c97b9826387100ace2c8383604051610cec929190611cff565b60405180910390a25050505050565b600080610d0760075490565b9050662386f26fc10000811115610d4157604051630ae993dd60e01b815260048101829052662386f26fc100006024820152604401610539565b670de0b6b3a7640000610d548285611c6c565b610d5e9190611d21565b9392505050565b6000610d5e600184018361153a565b6000546001600160a01b031633146106535760405163118cdaa760e01b8152336004820152602401610539565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610dfc60018261155c565b6040516001600160a01b03821681527f44a3cd4eb5cc5748f6169df057b1cb2ae4c383e87cd94663c430e095d4cba424906020016106f9565b610e40600182611596565b6040516001600160a01b03821681527f6d05492139c5ea989514a5d2150c028041e5c087e2a39967f67dc7d2655adb81906020016106f9565b80600003610e9a57604051635e1a7b8b60e11b815260040160405180910390fd5b9055565b60606105db826001016115f7565b80471015610ecf5760405163cd78605960e01b8152306004820152602401610539565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610f1c576040519150601f19603f3d011682016040523d82523d6000602084013e610f21565b606091505b50509050806105b757604051630a12f52160e11b815260040160405180910390fd5b6000610f50604183611d21565b905081610f5e604183611c6c565b14610f7f576040516319d9b69960e31b815260048101839052602401610539565b84546000819003610fa357604051635e1a7b8b60e11b815260040160405180910390fd5b6000806000805b858110156110ba576000888589610fc2604183611c83565b92610fcf93929190611d43565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093945083925061101391508d905084611604565b509092509050600081600381111561102d5761102d611d6d565b1461104d57826040516301fb043f60e61b81526004016105399190611d83565b846001600160a01b0316826001600160a01b03161161107f57604051630da2019960e01b815260040160405180910390fd5b81945061108c8d83610d65565b1561109f5761109c600187611c83565b95505b6110aa604188611c83565b9650505050806001019050610faa565b50838210156110e657604051630affed5760e21b81526004810183905260248101859052604401610539565b505050505050505050565b606080828060200190518101906111089190611de3565b91509150915091565b60606002825110156111385781604051635840c5b160e11b81526004016105399190611d83565b815160011901806001600160401b03811115611156576111566118ff565b6040519080825280601f01601f191660200182016040528015611180576020820181803683370190505b50915060008160208401836022870160045afa9050806111b35760405163080f227d60e11b815260040160405180910390fd5b5050919050565b60408051606081018252600080825260208201819052918101919091526000828060200190518101906111ed9190611e46565b60408401529050611209816001600160401b03604082901c1691565b6001600160401b03908116602085015216825250919050565b604051636cd46d4d60e11b81526001600160a01b037f000000000000000000000000b5bee19adc002e194e76e8fa9e50dbb2d92d999c169063d9a8da9a9061126e908490600401611d83565b600060405180830381600087803b15801561128857600080fd5b505af115801561129c573d6000803e3d6000fd5b5050505050565b80516000036112b157505050565b6001600160401b03808416600090815260066020526040902054168015806112ea5750826001600160401b0316816001600160401b0316105b156106a0576001600160401b038481166000908152600660205260409020805467ffffffffffffffff19169185169190911790556113266113c4565b6001600160a01b03166383389de785846040518363ffffffff1660e01b8152600401611353929190611e89565b600060405180830381600087803b15801561136d57600080fd5b505af1158015611381573d6000803e3d6000fd5b505050507f02c233a01329dc53cb24eb5e8e0131ad57c2d982e4aaa5bf8a75ee90e95b1c0084836040516113b6929190611e89565b60405180910390a150505050565b6009546001600160a01b0316806113ee57604051632545dc0d60e11b815260040160405180910390fd5b90565b60606113fb6113c4565b6001600160a01b0316636f928aa76040518163ffffffff1660e01b8152600401600060405180830381865afa158015611438573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114609190810190611eab565b9050805160000361147057919050565b80516020808301919091206001600160401b0384166000908152600590925260409091205481036114b257604051806020016040528060008152509150611508565b6001600160401b03831660009081526005602052604090819020829055517fb2243d133e6d54117d7ce9f172219373d8c0f601f4976bcb7860abe1addb9b2d906114ff9085908590611e89565b60405180910390a15b50919050565b60608282604051602001611523929190611edf565b604051602081830303815290604052905092915050565b6001600160a01b03811660009081526001830160205260408120541515610d5e565b600061156b6001840183611651565b9050806105b7576040516375a1d0c960e01b81526001600160a01b0383166004820152602401610539565b6001600160a01b0381166115bd576040516373e66c2d60e01b815260040160405180910390fd5b60006115cc6001840183611666565b9050806105b7576040516328a0fc2960e21b81526001600160a01b0383166004820152602401610539565b60606000610d5e8361167b565b6000806000835160410361163e5760208401516040850151606086015160001a611630888285856116d7565b95509550955050505061164a565b50508151600091506002905b9250925092565b6000610d5e836001600160a01b0384166117a6565b6000610d5e836001600160a01b038416611899565b6060816000018054806020026020016040519081016040528092919081815260200182805480156116cb57602002820191906000526020600020905b8154815260200190600101908083116116b7575b50505050509050919050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611712575060009150600390508261179c565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611766573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117925750600092506001915082905061179c565b9250600091508190505b9450945094915050565b6000818152600183016020526040812054801561188f5760006117ca600183611f0d565b85549091506000906117de90600190611f0d565b90508082146118435760008660000182815481106117fe576117fe611bf7565b906000526020600020015490508087600001848154811061182157611821611bf7565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061185457611854611f20565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105db565b60009150506105db565b60008181526001830160205260408120546118e0575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105db565b5060006105db565b80356001600160401b0381168114610b6757600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561193d5761193d6118ff565b604052919050565b60006001600160401b0382111561195e5761195e6118ff565b50601f01601f191660200190565b6000806040838503121561197f57600080fd5b611988836118e8565b915060208301356001600160401b038111156119a357600080fd5b8301601f810185136119b457600080fd5b80356119c76119c282611945565b611915565b8181528660208385010111156119dc57600080fd5b816020840160208301376000602083830101528093505050509250929050565b600060208284031215611a0e57600080fd5b81356001600160a01b0381168114610d5e57600080fd5b60008060408385031215611a3857600080fd5b611a41836118e8565b946020939093013593505050565b60008060208385031215611a6257600080fd5b82356001600160401b0380821115611a7957600080fd5b818501915085601f830112611a8d57600080fd5b813581811115611a9c57600080fd5b8660208260051b8501011115611ab157600080fd5b60209290920196919550909350505050565b600060208284031215611ad557600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b81811015611b1d5783516001600160a01b031683529284019291840191600101611af8565b50909695505050505050565b600060208284031215611b3b57600080fd5b610d5e826118e8565b60008083601f840112611b5657600080fd5b5081356001600160401b03811115611b6d57600080fd5b602083019150836020828501011115611b8557600080fd5b9250929050565b60008060008060408587031215611ba257600080fd5b84356001600160401b0380821115611bb957600080fd5b611bc588838901611b44565b90965094506020870135915080821115611bde57600080fd5b50611beb87828801611b44565b95989497509550505050565b634e487b7160e01b600052603260045260246000fd5b8183823760009101908152919050565b604081528260408201528284606083013760006060848301015260006060601f19601f8601168301019050826020830152949350505050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176105db576105db611c56565b808201808211156105db576105db611c56565b600060208284031215611ca857600080fd5b5051919050565b60005b83811015611cca578181015183820152602001611cb2565b50506000910152565b60008151808452611ceb816020860160208601611caf565b601f01601f19169290920160200192915050565b604081526000611d126040830185611cd3565b90508260208301529392505050565b600082611d3e57634e487b7160e01b600052601260045260246000fd5b500490565b60008085851115611d5357600080fd5b83861115611d6057600080fd5b5050820193919092039150565b634e487b7160e01b600052602160045260246000fd5b602081526000610d5e6020830184611cd3565b600082601f830112611da757600080fd5b8151611db56119c282611945565b818152846020838601011115611dca57600080fd5b611ddb826020830160208701611caf565b949350505050565b60008060408385031215611df657600080fd5b82516001600160401b0380821115611e0d57600080fd5b611e1986838701611d96565b93506020850151915080821115611e2f57600080fd5b50611e3c85828601611d96565b9150509250929050565b60008060408385031215611e5957600080fd5b82516fffffffffffffffffffffffffffffffff81168114611e7957600080fd5b6020939093015192949293505050565b6001600160401b0383168152604060208201526000611ddb6040830184611cd3565b600060208284031215611ebd57600080fd5b81516001600160401b03811115611ed357600080fd5b611ddb84828501611d96565b604081526000611ef26040830185611cd3565b8281036020840152611f048185611cd3565b95945050505050565b818103818111156105db576105db611c56565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220f2426520a08269d582c43284551d7cd9d25cdf33d702a4bf093a834080ca568864736f6c63430008180033

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

000000000000000000000000b5bee19adc002e194e76e8fa9e50dbb2d92d999c000000000000000000000000e7353bedc72d29f99d6ca5cde69f807cce5d57e4

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

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


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
[ 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.