MessageBus

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/interfaces/IBridge.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface IBridge {\r
    function send(\r
        address _receiver,\r
        address _token,\r
        uint256 _amount,\r
        uint64 _dstChainId,\r
        uint64 _nonce,\r
        uint32 _maxSlippage\r
    ) external;\r
\r
    function sendNative(\r
        address _receiver,\r
        uint256 _amount,\r
        uint64 _dstChainId,\r
        uint64 _nonce,\r
        uint32 _maxSlippage\r
    ) external payable;\r
\r
    function relay(\r
        bytes calldata _relayRequest,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external;\r
\r
    function transfers(bytes32 transferId) external view returns (bool);\r
\r
    function withdraws(bytes32 withdrawId) external view returns (bool);\r
\r
    function withdraw(\r
        bytes calldata _wdmsg,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external;\r
\r
    /**\r
     * @notice Verifies that a message is signed by a quorum among the signers.\r
     * @param _msg signed message\r
     * @param _sigs list of signatures sorted by signer addresses in ascending order\r
     * @param _signers sorted list of current signers\r
     * @param _powers powers of current signers\r
     */\r
    function verifySigs(\r
        bytes memory _msg,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external view;\r
}\r
"
    },
    "contracts/interfaces/IDelayedTransfer.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.9;\r
\r
interface IDelayedTransfer {\r
    struct delayedTransfer {\r
        address receiver;\r
        address token;\r
        uint256 amount;\r
        uint256 timestamp;\r
    }\r
\r
    function delayedTransfers(bytes32 transferId) external view returns (delayedTransfer memory);\r
}\r
"
    },
    "contracts/interfaces/IOriginalTokenVault.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface IOriginalTokenVault {\r
    /**\r
     * @notice Lock original tokens to trigger mint at a remote chain's PeggedTokenBridge\r
     * @param _token local token address\r
     * @param _amount locked token amount\r
     * @param _mintChainId destination chainId to mint tokens\r
     * @param _mintAccount destination account to receive minted tokens\r
     * @param _nonce user input to guarantee unique depositId\r
     */\r
    function deposit(\r
        address _token,\r
        uint256 _amount,\r
        uint64 _mintChainId,\r
        address _mintAccount,\r
        uint64 _nonce\r
    ) external;\r
\r
    /**\r
     * @notice Lock native token as original token to trigger mint at a remote chain's PeggedTokenBridge\r
     * @param _amount locked token amount\r
     * @param _mintChainId destination chainId to mint tokens\r
     * @param _mintAccount destination account to receive minted tokens\r
     * @param _nonce user input to guarantee unique depositId\r
     */\r
    function depositNative(\r
        uint256 _amount,\r
        uint64 _mintChainId,\r
        address _mintAccount,\r
        uint64 _nonce\r
    ) external payable;\r
\r
    /**\r
     * @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.\r
     * @param _request The serialized Withdraw protobuf.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the bridge's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function withdraw(\r
        bytes calldata _request,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external;\r
\r
    function records(bytes32 recordId) external view returns (bool);\r
}\r
"
    },
    "contracts/interfaces/IOriginalTokenVaultV2.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface IOriginalTokenVaultV2 {\r
    /**\r
     * @notice Lock original tokens to trigger mint at a remote chain's PeggedTokenBridge\r
     * @param _token local token address\r
     * @param _amount locked token amount\r
     * @param _mintChainId destination chainId to mint tokens\r
     * @param _mintAccount destination account to receive minted tokens\r
     * @param _nonce user input to guarantee unique depositId\r
     */\r
    function deposit(\r
        address _token,\r
        uint256 _amount,\r
        uint64 _mintChainId,\r
        address _mintAccount,\r
        uint64 _nonce\r
    ) external returns (bytes32);\r
\r
    /**\r
     * @notice Lock native token as original token to trigger mint at a remote chain's PeggedTokenBridge\r
     * @param _amount locked token amount\r
     * @param _mintChainId destination chainId to mint tokens\r
     * @param _mintAccount destination account to receive minted tokens\r
     * @param _nonce user input to guarantee unique depositId\r
     */\r
    function depositNative(\r
        uint256 _amount,\r
        uint64 _mintChainId,\r
        address _mintAccount,\r
        uint64 _nonce\r
    ) external payable returns (bytes32);\r
\r
    /**\r
     * @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.\r
     * @param _request The serialized Withdraw protobuf.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the bridge's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function withdraw(\r
        bytes calldata _request,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external returns (bytes32);\r
\r
    function records(bytes32 recordId) external view returns (bool);\r
}\r
"
    },
    "contracts/interfaces/IPeggedTokenBridge.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface IPeggedTokenBridge {\r
    /**\r
     * @notice Burn tokens to trigger withdrawal at a remote chain's OriginalTokenVault\r
     * @param _token local token address\r
     * @param _amount locked token amount\r
     * @param _withdrawAccount account who withdraw original tokens on the remote chain\r
     * @param _nonce user input to guarantee unique depositId\r
     */\r
    function burn(\r
        address _token,\r
        uint256 _amount,\r
        address _withdrawAccount,\r
        uint64 _nonce\r
    ) external;\r
\r
    /**\r
     * @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.\r
     * @param _request The serialized Mint protobuf.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the sigsVerifier's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function mint(\r
        bytes calldata _request,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external;\r
\r
    function records(bytes32 recordId) external view returns (bool);\r
}\r
"
    },
    "contracts/interfaces/IPeggedTokenBridgeV2.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface IPeggedTokenBridgeV2 {\r
    /**\r
     * @notice Burn pegged tokens to trigger a cross-chain withdrawal of the original tokens at a remote chain's\r
     * OriginalTokenVault, or mint at another remote chain\r
     * @param _token The pegged token address.\r
     * @param _amount The amount to burn.\r
     * @param _toChainId If zero, withdraw from original vault; otherwise, the remote chain to mint tokens.\r
     * @param _toAccount The account to receive tokens on the remote chain\r
     * @param _nonce A number to guarantee unique depositId. Can be timestamp in practice.\r
     */\r
    function burn(\r
        address _token,\r
        uint256 _amount,\r
        uint64 _toChainId,\r
        address _toAccount,\r
        uint64 _nonce\r
    ) external returns (bytes32);\r
\r
    // same with `burn` above, use openzeppelin ERC20Burnable interface\r
    function burnFrom(\r
        address _token,\r
        uint256 _amount,\r
        uint64 _toChainId,\r
        address _toAccount,\r
        uint64 _nonce\r
    ) external returns (bytes32);\r
\r
    /**\r
     * @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.\r
     * @param _request The serialized Mint protobuf.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the sigsVerifier's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function mint(\r
        bytes calldata _request,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external returns (bytes32);\r
\r
    function records(bytes32 recordId) external view returns (bool);\r
}\r
"
    },
    "contracts/interfaces/ISigsVerifier.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface ISigsVerifier {\r
    /**\r
     * @notice Verifies that a message is signed by a quorum among the signers.\r
     * @param _msg signed message\r
     * @param _sigs list of signatures sorted by signer addresses in ascending order\r
     * @param _signers sorted list of current signers\r
     * @param _powers powers of current signers\r
     */\r
    function verifySigs(\r
        bytes memory _msg,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external view;\r
}\r
"
    },
    "contracts/libraries/Utils.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
library Utils {\r
    // https://ethereum.stackexchange.com/a/83577\r
    // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol\r
    function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {\r
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)\r
        if (_returnData.length < 68) return "Transaction reverted silently";\r
        assembly {\r
            // Slice the sighash.\r
            _returnData := add(_returnData, 0x04)\r
        }\r
        return abi.decode(_returnData, (string)); // All that remains is the revert string\r
    }\r
}\r
"
    },
    "contracts/message/interfaces/IMessageReceiverApp.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
interface IMessageReceiverApp {\r
    enum ExecutionStatus {\r
        Fail, // execution failed, finalized\r
        Success, // execution succeeded, finalized\r
        Retry // execution rejected, can retry later\r
    }\r
\r
    /**\r
     * @notice Called by MessageBus to execute a message\r
     * @param _sender The address of the source app contract\r
     * @param _srcChainId The source chain ID where the transfer is originated from\r
     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\r
     * @param _executor Address who called the MessageBus execution function\r
     */\r
    function executeMessage(\r
        address _sender,\r
        uint64 _srcChainId,\r
        bytes calldata _message,\r
        address _executor\r
    ) external payable returns (ExecutionStatus);\r
\r
    // same as above, except that sender is an non-evm chain address,\r
    // otherwise same as above.\r
    function executeMessage(\r
        bytes calldata _sender,\r
        uint64 _srcChainId,\r
        bytes calldata _message,\r
        address _executor\r
    ) external payable returns (ExecutionStatus);\r
\r
    /**\r
     * @notice Called by MessageBus to execute a message with an associated token transfer.\r
     * The contract is guaranteed to have received the right amount of tokens before this function is called.\r
     * @param _sender The address of the source app contract\r
     * @param _token The address of the token that comes out of the bridge\r
     * @param _amount The amount of tokens received at this contract through the cross-chain bridge.\r
     * @param _srcChainId The source chain ID where the transfer is originated from\r
     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\r
     * @param _executor Address who called the MessageBus execution function\r
     */\r
    function executeMessageWithTransfer(\r
        address _sender,\r
        address _token,\r
        uint256 _amount,\r
        uint64 _srcChainId,\r
        bytes calldata _message,\r
        address _executor\r
    ) external payable returns (ExecutionStatus);\r
\r
    /**\r
     * @notice Only called by MessageBus if\r
     *         1. executeMessageWithTransfer reverts, or\r
     *         2. executeMessageWithTransfer returns ExecutionStatus.Fail\r
     * The contract is guaranteed to have received the right amount of tokens before this function is called.\r
     * @param _sender The address of the source app contract\r
     * @param _token The address of the token that comes out of the bridge\r
     * @param _amount The amount of tokens received at this contract through the cross-chain bridge.\r
     * @param _srcChainId The source chain ID where the transfer is originated from\r
     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\r
     * @param _executor Address who called the MessageBus execution function\r
     */\r
    function executeMessageWithTransferFallback(\r
        address _sender,\r
        address _token,\r
        uint256 _amount,\r
        uint64 _srcChainId,\r
        bytes calldata _message,\r
        address _executor\r
    ) external payable returns (ExecutionStatus);\r
\r
    /**\r
     * @notice Called by MessageBus to process refund of the original transfer from this contract.\r
     * The contract is guaranteed to have received the refund before this function is called.\r
     * @param _token The token address of the original transfer\r
     * @param _amount The amount of the original transfer\r
     * @param _message The same message associated with the original transfer\r
     * @param _executor Address who called the MessageBus execution function\r
     */\r
    function executeMessageWithTransferRefund(\r
        address _token,\r
        uint256 _amount,\r
        bytes calldata _message,\r
        address _executor\r
    ) external payable returns (ExecutionStatus);\r
}\r
"
    },
    "contracts/message/libraries/MsgDataTypes.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.0;\r
\r
library MsgDataTypes {\r
    string constant ABORT_PREFIX = "MSG::ABORT:";\r
\r
    // Add abort prefix in the reason string for require or revert.\r
    // This will abort (revert) the message execution without markig it as failed state,\r
    // making it possible to retry later.\r
    function abortReason(string memory reason) internal pure returns (string memory) {\r
        return string.concat(MsgDataTypes.ABORT_PREFIX, reason);\r
    }\r
\r
    // bridge operation type at the sender side (src chain)\r
    enum BridgeSendType {\r
        Null,\r
        Liquidity,\r
        PegDeposit,\r
        PegBurn,\r
        PegV2Deposit,\r
        PegV2Burn,\r
        PegV2BurnFrom\r
    }\r
\r
    // bridge operation type at the receiver side (dst chain)\r
    enum TransferType {\r
        Null,\r
        LqRelay, // relay through liquidity bridge\r
        LqWithdraw, // withdraw from liquidity bridge\r
        PegMint, // mint through pegged token bridge\r
        PegWithdraw, // withdraw from original token vault\r
        PegV2Mint, // mint through pegged token bridge v2\r
        PegV2Withdraw // withdraw from original token vault v2\r
    }\r
\r
    enum MsgType {\r
        MessageWithTransfer,\r
        MessageOnly\r
    }\r
\r
    enum TxStatus {\r
        Null,\r
        Success,\r
        Fail,\r
        Fallback,\r
        Pending // transient state within a transaction\r
    }\r
\r
    struct TransferInfo {\r
        TransferType t;\r
        address sender;\r
        address receiver;\r
        address token;\r
        uint256 amount;\r
        uint64 wdseq; // only needed for LqWithdraw (refund)\r
        uint64 srcChainId;\r
        bytes32 refId;\r
        bytes32 srcTxHash; // src chain msg tx hash\r
    }\r
\r
    struct RouteInfo {\r
        address sender;\r
        address receiver;\r
        uint64 srcChainId;\r
        bytes32 srcTxHash; // src chain msg tx hash\r
    }\r
\r
    // used for msg from non-evm chains with longer-bytes address\r
    struct RouteInfo2 {\r
        bytes sender;\r
        address receiver;\r
        uint64 srcChainId;\r
        bytes32 srcTxHash;\r
    }\r
\r
    // combination of RouteInfo and RouteInfo2 for easier processing\r
    struct Route {\r
        address sender; // from RouteInfo\r
        bytes senderBytes; // from RouteInfo2\r
        address receiver;\r
        uint64 srcChainId;\r
        bytes32 srcTxHash;\r
    }\r
\r
    struct MsgWithTransferExecutionParams {\r
        bytes message;\r
        TransferInfo transfer;\r
        bytes[] sigs;\r
        address[] signers;\r
        uint256[] powers;\r
    }\r
\r
    struct BridgeTransferParams {\r
        bytes request;\r
        bytes[] sigs;\r
        address[] signers;\r
        uint256[] powers;\r
    }\r
}\r
"
    },
    "contracts/message/messagebus/MessageBus.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity 0.8.17;\r
\r
import "./MessageBusSender.sol";\r
import "./MessageBusReceiver.sol";\r
\r
contract MessageBus is MessageBusSender, MessageBusReceiver {\r
    constructor(\r
        ISigsVerifier _sigsVerifier,\r
        address _liquidityBridge,\r
        address _pegBridge,\r
        address _pegVault,\r
        address _pegBridgeV2,\r
        address _pegVaultV2\r
    )\r
        MessageBusSender(_sigsVerifier)\r
        MessageBusReceiver(_liquidityBridge, _pegBridge, _pegVault, _pegBridgeV2, _pegVaultV2)\r
    {}\r
\r
    // this is only to be called by Proxy via delegateCall as initOwner will require _owner is 0.\r
    // so calling init on this contract directly will guarantee to fail\r
    function init(\r
        address _liquidityBridge,\r
        address _pegBridge,\r
        address _pegVault,\r
        address _pegBridgeV2,\r
        address _pegVaultV2\r
    ) external {\r
        // MUST manually call ownable init and must only call once\r
        initOwner();\r
        // we don't need sender init as _sigsVerifier is immutable so already in the deployed code\r
        initReceiver(_liquidityBridge, _pegBridge, _pegVault, _pegBridgeV2, _pegVaultV2);\r
    }\r
}\r
"
    },
    "contracts/message/messagebus/MessageBusReceiver.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity >=0.8.9;\r
\r
import "../libraries/MsgDataTypes.sol";\r
import "../interfaces/IMessageReceiverApp.sol";\r
import "../../interfaces/IBridge.sol";\r
import "../../interfaces/IOriginalTokenVault.sol";\r
import "../../interfaces/IOriginalTokenVaultV2.sol";\r
import "../../interfaces/IPeggedTokenBridge.sol";\r
import "../../interfaces/IPeggedTokenBridgeV2.sol";\r
import "../../interfaces/IDelayedTransfer.sol";\r
import "../../safeguard/Ownable.sol";\r
import "../../libraries/Utils.sol";\r
\r
contract MessageBusReceiver is Ownable {\r
    mapping(bytes32 => MsgDataTypes.TxStatus) public executedMessages;\r
\r
    address public liquidityBridge; // liquidity bridge address\r
    address public pegBridge; // peg bridge address\r
    address public pegVault; // peg original vault address\r
    address public pegBridgeV2; // peg bridge address\r
    address public pegVaultV2; // peg original vault address\r
\r
    // minimum amount of gas needed by this contract before it tries to\r
    // deliver a message to the target contract.\r
    uint256 public preExecuteMessageGasUsage;\r
\r
    event Executed(\r
        MsgDataTypes.MsgType msgType,\r
        bytes32 msgId,\r
        MsgDataTypes.TxStatus status,\r
        address indexed receiver,\r
        uint64 srcChainId,\r
        bytes32 srcTxHash\r
    );\r
    event NeedRetry(MsgDataTypes.MsgType msgType, bytes32 msgId, uint64 srcChainId, bytes32 srcTxHash);\r
    event CallReverted(string reason); // help debug\r
\r
    event LiquidityBridgeUpdated(address liquidityBridge);\r
    event PegBridgeUpdated(address pegBridge);\r
    event PegVaultUpdated(address pegVault);\r
    event PegBridgeV2Updated(address pegBridgeV2);\r
    event PegVaultV2Updated(address pegVaultV2);\r
\r
    constructor(\r
        address _liquidityBridge,\r
        address _pegBridge,\r
        address _pegVault,\r
        address _pegBridgeV2,\r
        address _pegVaultV2\r
    ) {\r
        liquidityBridge = _liquidityBridge;\r
        pegBridge = _pegBridge;\r
        pegVault = _pegVault;\r
        pegBridgeV2 = _pegBridgeV2;\r
        pegVaultV2 = _pegVaultV2;\r
    }\r
\r
    function initReceiver(\r
        address _liquidityBridge,\r
        address _pegBridge,\r
        address _pegVault,\r
        address _pegBridgeV2,\r
        address _pegVaultV2\r
    ) internal {\r
        require(liquidityBridge == address(0), "liquidityBridge already set");\r
        liquidityBridge = _liquidityBridge;\r
        pegBridge = _pegBridge;\r
        pegVault = _pegVault;\r
        pegBridgeV2 = _pegBridgeV2;\r
        pegVaultV2 = _pegVaultV2;\r
    }\r
\r
    // ============== functions called by executor ==============\r
\r
    /**\r
     * @notice Execute a message with a successful transfer.\r
     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\r
     * @param _transfer The transfer info.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the sigsVerifier's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function executeMessageWithTransfer(\r
        bytes calldata _message,\r
        MsgDataTypes.TransferInfo calldata _transfer,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) public payable {\r
        // For message with token transfer, message Id is computed through transfer info\r
        // in order to guarantee that each transfer can only be used once.\r
        bytes32 messageId = verifyTransfer(_transfer);\r
        require(executedMessages[messageId] == MsgDataTypes.TxStatus.Null, "transfer already executed");\r
        executedMessages[messageId] = MsgDataTypes.TxStatus.Pending;\r
\r
        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "MessageWithTransfer"));\r
        IBridge(liquidityBridge).verifySigs(\r
            abi.encodePacked(domain, messageId, _message, _transfer.srcTxHash),\r
            _sigs,\r
            _signers,\r
            _powers\r
        );\r
        MsgDataTypes.TxStatus status;\r
        IMessageReceiverApp.ExecutionStatus est = executeMessageWithTransfer(_transfer, _message);\r
        if (est == IMessageReceiverApp.ExecutionStatus.Success) {\r
            status = MsgDataTypes.TxStatus.Success;\r
        } else if (est == IMessageReceiverApp.ExecutionStatus.Retry) {\r
            executedMessages[messageId] = MsgDataTypes.TxStatus.Null;\r
            emit NeedRetry(\r
                MsgDataTypes.MsgType.MessageWithTransfer,\r
                messageId,\r
                _transfer.srcChainId,\r
                _transfer.srcTxHash\r
            );\r
            return;\r
        } else {\r
            est = executeMessageWithTransferFallback(_transfer, _message);\r
            if (est == IMessageReceiverApp.ExecutionStatus.Success) {\r
                status = MsgDataTypes.TxStatus.Fallback;\r
            } else {\r
                status = MsgDataTypes.TxStatus.Fail;\r
            }\r
        }\r
        executedMessages[messageId] = status;\r
        emitMessageWithTransferExecutedEvent(messageId, status, _transfer);\r
    }\r
\r
    /**\r
     * @notice Execute a message with a refunded transfer.\r
     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\r
     * @param _transfer The transfer info.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the sigsVerifier's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function executeMessageWithTransferRefund(\r
        bytes calldata _message, // the same message associated with the original transfer\r
        MsgDataTypes.TransferInfo calldata _transfer,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) public payable {\r
        // similar to executeMessageWithTransfer\r
        bytes32 messageId = verifyTransfer(_transfer);\r
        require(executedMessages[messageId] == MsgDataTypes.TxStatus.Null, "transfer already executed");\r
        executedMessages[messageId] = MsgDataTypes.TxStatus.Pending;\r
\r
        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "MessageWithTransferRefund"));\r
        IBridge(liquidityBridge).verifySigs(\r
            abi.encodePacked(domain, messageId, _message, _transfer.srcTxHash),\r
            _sigs,\r
            _signers,\r
            _powers\r
        );\r
        MsgDataTypes.TxStatus status;\r
        IMessageReceiverApp.ExecutionStatus est = executeMessageWithTransferRefund(_transfer, _message);\r
        if (est == IMessageReceiverApp.ExecutionStatus.Success) {\r
            status = MsgDataTypes.TxStatus.Success;\r
        } else if (est == IMessageReceiverApp.ExecutionStatus.Retry) {\r
            executedMessages[messageId] = MsgDataTypes.TxStatus.Null;\r
            emit NeedRetry(\r
                MsgDataTypes.MsgType.MessageWithTransfer,\r
                messageId,\r
                _transfer.srcChainId,\r
                _transfer.srcTxHash\r
            );\r
            return;\r
        } else {\r
            status = MsgDataTypes.TxStatus.Fail;\r
        }\r
        executedMessages[messageId] = status;\r
        emitMessageWithTransferExecutedEvent(messageId, status, _transfer);\r
    }\r
\r
    /**\r
     * @notice Execute a message not associated with a transfer.\r
     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\r
     * @param _route The info about the sender and the receiver.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\r
     * +2/3 of the sigsVerifier's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function executeMessage(\r
        bytes calldata _message,\r
        MsgDataTypes.RouteInfo calldata _route,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external payable {\r
        MsgDataTypes.Route memory route = getRouteInfo(_route);\r
        executeMessage(_message, route, _sigs, _signers, _powers, "Message");\r
    }\r
\r
    // execute message from non-evm chain with bytes for sender address,\r
    // otherwise same as above.\r
    function executeMessage(\r
        bytes calldata _message,\r
        MsgDataTypes.RouteInfo2 calldata _route,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external payable {\r
        MsgDataTypes.Route memory route = getRouteInfo(_route);\r
        executeMessage(_message, route, _sigs, _signers, _powers, "Message2");\r
    }\r
\r
    function executeMessage(\r
        bytes calldata _message,\r
        MsgDataTypes.Route memory _route,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers,\r
        string memory domainName\r
    ) private {\r
        // For message without associated token transfer, message Id is computed through message info,\r
        // in order to guarantee that each message can only be applied once\r
        bytes32 messageId = computeMessageOnlyId(_route, _message);\r
        require(executedMessages[messageId] == MsgDataTypes.TxStatus.Null, "message already executed");\r
        executedMessages[messageId] = MsgDataTypes.TxStatus.Pending;\r
\r
        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), domainName));\r
        IBridge(liquidityBridge).verifySigs(abi.encodePacked(domain, messageId), _sigs, _signers, _powers);\r
        MsgDataTypes.TxStatus status;\r
        IMessageReceiverApp.ExecutionStatus est = executeMessage(_route, _message);\r
        if (est == IMessageReceiverApp.ExecutionStatus.Success) {\r
            status = MsgDataTypes.TxStatus.Success;\r
        } else if (est == IMessageReceiverApp.ExecutionStatus.Retry) {\r
            executedMessages[messageId] = MsgDataTypes.TxStatus.Null;\r
            emit NeedRetry(MsgDataTypes.MsgType.MessageOnly, messageId, _route.srcChainId, _route.srcTxHash);\r
            return;\r
        } else {\r
            status = MsgDataTypes.TxStatus.Fail;\r
        }\r
        executedMessages[messageId] = status;\r
        emitMessageOnlyExecutedEvent(messageId, status, _route);\r
    }\r
\r
    // ================= utils (to avoid stack too deep) =================\r
\r
    function emitMessageWithTransferExecutedEvent(\r
        bytes32 _messageId,\r
        MsgDataTypes.TxStatus _status,\r
        MsgDataTypes.TransferInfo calldata _transfer\r
    ) private {\r
        emit Executed(\r
            MsgDataTypes.MsgType.MessageWithTransfer,\r
            _messageId,\r
            _status,\r
            _transfer.receiver,\r
            _transfer.srcChainId,\r
            _transfer.srcTxHash\r
        );\r
    }\r
\r
    function emitMessageOnlyExecutedEvent(\r
        bytes32 _messageId,\r
        MsgDataTypes.TxStatus _status,\r
        MsgDataTypes.Route memory _route\r
    ) private {\r
        emit Executed(\r
            MsgDataTypes.MsgType.MessageOnly,\r
            _messageId,\r
            _status,\r
            _route.receiver,\r
            _route.srcChainId,\r
            _route.srcTxHash\r
        );\r
    }\r
\r
    function executeMessageWithTransfer(MsgDataTypes.TransferInfo calldata _transfer, bytes calldata _message)\r
        private\r
        returns (IMessageReceiverApp.ExecutionStatus)\r
    {\r
        uint256 gasLeftBeforeExecution = gasleft();\r
        (bool ok, bytes memory res) = address(_transfer.receiver).call{value: msg.value}(\r
            abi.encodeWithSelector(\r
                IMessageReceiverApp.executeMessageWithTransfer.selector,\r
                _transfer.sender,\r
                _transfer.token,\r
                _transfer.amount,\r
                _transfer.srcChainId,\r
                _message,\r
                msg.sender\r
            )\r
        );\r
        if (ok) {\r
            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\r
        }\r
        handleExecutionRevert(gasLeftBeforeExecution, res);\r
        return IMessageReceiverApp.ExecutionStatus.Fail;\r
    }\r
\r
    function executeMessageWithTransferFallback(MsgDataTypes.TransferInfo calldata _transfer, bytes calldata _message)\r
        private\r
        returns (IMessageReceiverApp.ExecutionStatus)\r
    {\r
        uint256 gasLeftBeforeExecution = gasleft();\r
        (bool ok, bytes memory res) = address(_transfer.receiver).call{value: msg.value}(\r
            abi.encodeWithSelector(\r
                IMessageReceiverApp.executeMessageWithTransferFallback.selector,\r
                _transfer.sender,\r
                _transfer.token,\r
                _transfer.amount,\r
                _transfer.srcChainId,\r
                _message,\r
                msg.sender\r
            )\r
        );\r
        if (ok) {\r
            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\r
        }\r
        handleExecutionRevert(gasLeftBeforeExecution, res);\r
        return IMessageReceiverApp.ExecutionStatus.Fail;\r
    }\r
\r
    function executeMessageWithTransferRefund(MsgDataTypes.TransferInfo calldata _transfer, bytes calldata _message)\r
        private\r
        returns (IMessageReceiverApp.ExecutionStatus)\r
    {\r
        uint256 gasLeftBeforeExecution = gasleft();\r
        (bool ok, bytes memory res) = address(_transfer.receiver).call{value: msg.value}(\r
            abi.encodeWithSelector(\r
                IMessageReceiverApp.executeMessageWithTransferRefund.selector,\r
                _transfer.token,\r
                _transfer.amount,\r
                _message,\r
                msg.sender\r
            )\r
        );\r
        if (ok) {\r
            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\r
        }\r
        handleExecutionRevert(gasLeftBeforeExecution, res);\r
        return IMessageReceiverApp.ExecutionStatus.Fail;\r
    }\r
\r
    function verifyTransfer(MsgDataTypes.TransferInfo calldata _transfer) private view returns (bytes32) {\r
        bytes32 transferId;\r
        address bridgeAddr;\r
        MsgDataTypes.TransferType t = _transfer.t;\r
        if (t == MsgDataTypes.TransferType.LqRelay) {\r
            bridgeAddr = liquidityBridge;\r
            transferId = keccak256(\r
                abi.encodePacked(\r
                    _transfer.sender,\r
                    _transfer.receiver,\r
                    _transfer.token,\r
                    _transfer.amount,\r
                    _transfer.srcChainId,\r
                    uint64(block.chainid),\r
                    _transfer.refId\r
                )\r
            );\r
            require(IBridge(bridgeAddr).transfers(transferId) == true, "relay not exist");\r
        } else if (t == MsgDataTypes.TransferType.LqWithdraw) {\r
            bridgeAddr = liquidityBridge;\r
            transferId = keccak256(\r
                abi.encodePacked(\r
                    uint64(block.chainid),\r
                    _transfer.wdseq,\r
                    _transfer.receiver,\r
                    _transfer.token,\r
                    _transfer.amount\r
                )\r
            );\r
            require(IBridge(bridgeAddr).withdraws(transferId) == true, "withdraw not exist");\r
        } else {\r
            if (t == MsgDataTypes.TransferType.PegMint || t == MsgDataTypes.TransferType.PegWithdraw) {\r
                bridgeAddr = (t == MsgDataTypes.TransferType.PegMint) ? pegBridge : pegVault;\r
                transferId = keccak256(\r
                    abi.encodePacked(\r
                        _transfer.receiver,\r
                        _transfer.token,\r
                        _transfer.amount,\r
                        _transfer.sender,\r
                        _transfer.srcChainId,\r
                        _transfer.refId\r
                    )\r
                );\r
            } else {\r
                bridgeAddr = (t == MsgDataTypes.TransferType.PegV2Mint) ? pegBridgeV2 : pegVaultV2;\r
                transferId = keccak256(\r
                    abi.encodePacked(\r
                        _transfer.receiver,\r
                        _transfer.token,\r
                        _transfer.amount,\r
                        _transfer.sender,\r
                        _transfer.srcChainId,\r
                        _transfer.refId,\r
                        bridgeAddr\r
                    )\r
                );\r
            }\r
            // function is same for peg, peg2, vault, vault2\r
            require(IPeggedTokenBridge(bridgeAddr).records(transferId) == true, "record not exist");\r
        }\r
        require(IDelayedTransfer(bridgeAddr).delayedTransfers(transferId).timestamp == 0, "transfer delayed");\r
        return keccak256(abi.encodePacked(MsgDataTypes.MsgType.MessageWithTransfer, bridgeAddr, transferId));\r
    }\r
\r
    function computeMessageOnlyId(MsgDataTypes.Route memory _route, bytes calldata _message)\r
        private\r
        view\r
        returns (bytes32)\r
    {\r
        bytes memory sender = _route.senderBytes;\r
        if (sender.length == 0) {\r
            sender = abi.encodePacked(_route.sender);\r
        }\r
        return\r
            keccak256(\r
                abi.encodePacked(\r
                    MsgDataTypes.MsgType.MessageOnly,\r
                    sender,\r
                    _route.receiver,\r
                    _route.srcChainId,\r
                    _route.srcTxHash,\r
                    uint64(block.chainid),\r
                    _message\r
                )\r
            );\r
    }\r
\r
    function executeMessage(MsgDataTypes.Route memory _route, bytes calldata _message)\r
        private\r
        returns (IMessageReceiverApp.ExecutionStatus)\r
    {\r
        uint256 gasLeftBeforeExecution = gasleft();\r
        bool ok;\r
        bytes memory res;\r
        if (_route.senderBytes.length == 0) {\r
            (ok, res) = address(_route.receiver).call{value: msg.value}(\r
                abi.encodeWithSelector(\r
                    bytes4(keccak256(bytes("executeMessage(address,uint64,bytes,address)"))),\r
                    _route.sender,\r
                    _route.srcChainId,\r
                    _message,\r
                    msg.sender\r
                )\r
            );\r
        } else {\r
            (ok, res) = address(_route.receiver).call{value: msg.value}(\r
                abi.encodeWithSelector(\r
                    bytes4(keccak256(bytes("executeMessage(bytes,uint64,bytes,address)"))),\r
                    _route.senderBytes,\r
                    _route.srcChainId,\r
                    _message,\r
                    msg.sender\r
                )\r
            );\r
        }\r
        if (ok) {\r
            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\r
        }\r
        handleExecutionRevert(gasLeftBeforeExecution, res);\r
        return IMessageReceiverApp.ExecutionStatus.Fail;\r
    }\r
\r
    function handleExecutionRevert(uint256 _gasLeftBeforeExecution, bytes memory _returnData) private {\r
        uint256 gasLeftAfterExecution = gasleft();\r
        uint256 maxTargetGasLimit = block.gaslimit - preExecuteMessageGasUsage;\r
        if (_gasLeftBeforeExecution < maxTargetGasLimit && gasLeftAfterExecution <= _gasLeftBeforeExecution / 64) {\r
            // if this happens, the executor must have not provided sufficient gas limit,\r
            // then the tx should revert instead of recording a non-retryable failure status\r
            // https://github.com/wolflo/evm-opcodes/blob/main/gas.md#aa-f-gas-to-send-with-call-operations\r
            assembly {\r
                invalid()\r
            }\r
        }\r
        string memory revertMsg = Utils.getRevertMsg(_returnData);\r
        // revert the execution if the revert message has the ABORT prefix\r
        checkAbortPrefix(revertMsg);\r
        // otherwiase, emit revert message, return and mark the execution as failed (non-retryable)\r
        emit CallReverted(revertMsg);\r
    }\r
\r
    function checkAbortPrefix(string memory _revertMsg) private pure {\r
        bytes memory prefixBytes = bytes(MsgDataTypes.ABORT_PREFIX);\r
        bytes memory msgBytes = bytes(_revertMsg);\r
        if (msgBytes.length >= prefixBytes.length) {\r
            for (uint256 i = 0; i < prefixBytes.length; i++) {\r
                if (msgBytes[i] != prefixBytes[i]) {\r
                    return; // prefix not match, return\r
                }\r
            }\r
            revert(_revertMsg); // prefix match, revert\r
        }\r
    }\r
\r
    function getRouteInfo(MsgDataTypes.RouteInfo calldata _route) private pure returns (MsgDataTypes.Route memory) {\r
        return MsgDataTypes.Route(_route.sender, "", _route.receiver, _route.srcChainId, _route.srcTxHash);\r
    }\r
\r
    function getRouteInfo(MsgDataTypes.RouteInfo2 calldata _route) private pure returns (MsgDataTypes.Route memory) {\r
        return MsgDataTypes.Route(address(0), _route.sender, _route.receiver, _route.srcChainId, _route.srcTxHash);\r
    }\r
\r
    // ================= helper functions =====================\r
\r
    /**\r
     * @notice combine bridge transfer and msg execution calls into a single tx\r
     * @dev caller needs to get the required input params from SGN\r
     * @param _tp params to call bridge transfer\r
     * @param _mp params to execute message\r
     */\r
    function transferAndExecuteMsg(\r
        MsgDataTypes.BridgeTransferParams calldata _tp,\r
        MsgDataTypes.MsgWithTransferExecutionParams calldata _mp\r
    ) external {\r
        _bridgeTransfer(_mp.transfer.t, _tp);\r
        executeMessageWithTransfer(_mp.message, _mp.transfer, _mp.sigs, _mp.signers, _mp.powers);\r
    }\r
\r
    /**\r
     * @notice combine bridge refund and msg execution calls into a single tx\r
     * @dev caller needs to get the required input params from SGN\r
     * @param _tp params to call bridge transfer for refund\r
     * @param _mp params to execute message for refund\r
     */\r
    function refundAndExecuteMsg(\r
        MsgDataTypes.BridgeTransferParams calldata _tp,\r
        MsgDataTypes.MsgWithTransferExecutionParams calldata _mp\r
    ) external {\r
        _bridgeTransfer(_mp.transfer.t, _tp);\r
        executeMessageWithTransferRefund(_mp.message, _mp.transfer, _mp.sigs, _mp.signers, _mp.powers);\r
    }\r
\r
    function _bridgeTransfer(MsgDataTypes.TransferType t, MsgDataTypes.BridgeTransferParams calldata _params) private {\r
        if (t == MsgDataTypes.TransferType.LqRelay) {\r
            IBridge(liquidityBridge).relay(_params.request, _params.sigs, _params.signers, _params.powers);\r
        } else if (t == MsgDataTypes.TransferType.LqWithdraw) {\r
            IBridge(liquidityBridge).withdraw(_params.request, _params.sigs, _params.signers, _params.powers);\r
        } else if (t == MsgDataTypes.TransferType.PegMint) {\r
            IPeggedTokenBridge(pegBridge).mint(_params.request, _params.sigs, _params.signers, _params.powers);\r
        } else if (t == MsgDataTypes.TransferType.PegV2Mint) {\r
            IPeggedTokenBridgeV2(pegBridgeV2).mint(_params.request, _params.sigs, _params.signers, _params.powers);\r
        } else if (t == MsgDataTypes.TransferType.PegWithdraw) {\r
            IOriginalTokenVault(pegVault).withdraw(_params.request, _params.sigs, _params.signers, _params.powers);\r
        } else if (t == MsgDataTypes.TransferType.PegV2Withdraw) {\r
            IOriginalTokenVaultV2(pegVaultV2).withdraw(_params.request, _params.sigs, _params.signers, _params.powers);\r
        }\r
    }\r
\r
    // ================= contract config =================\r
\r
    function setLiquidityBridge(address _addr) public onlyOwner {\r
        require(_addr != address(0), "invalid address");\r
        liquidityBridge = _addr;\r
        emit LiquidityBridgeUpdated(liquidityBridge);\r
    }\r
\r
    function setPegBridge(address _addr) public onlyOwner {\r
        require(_addr != address(0), "invalid address");\r
        pegBridge = _addr;\r
        emit PegBridgeUpdated(pegBridge);\r
    }\r
\r
    function setPegVault(address _addr) public onlyOwner {\r
        require(_addr != address(0), "invalid address");\r
        pegVault = _addr;\r
        emit PegVaultUpdated(pegVault);\r
    }\r
\r
    function setPegBridgeV2(address _addr) public onlyOwner {\r
        require(_addr != address(0), "invalid address");\r
        pegBridgeV2 = _addr;\r
        emit PegBridgeV2Updated(pegBridgeV2);\r
    }\r
\r
    function setPegVaultV2(address _addr) public onlyOwner {\r
        require(_addr != address(0), "invalid address");\r
        pegVaultV2 = _addr;\r
        emit PegVaultV2Updated(pegVaultV2);\r
    }\r
\r
    function setPreExecuteMessageGasUsage(uint256 _usage) public onlyOwner {\r
        preExecuteMessageGasUsage = _usage;\r
    }\r
}\r
"
    },
    "contracts/message/messagebus/MessageBusSender.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity 0.8.17;\r
\r
import "../../safeguard/Ownable.sol";\r
import "../../interfaces/ISigsVerifier.sol";\r
\r
contract MessageBusSender is Ownable {\r
    ISigsVerifier public immutable sigsVerifier;\r
\r
    uint256 public feeBase;\r
    uint256 public feePerByte;\r
    mapping(address => uint256) public withdrawnFees;\r
\r
    event Message(address indexed sender, address receiver, uint256 dstChainId, bytes message, uint256 fee);\r
    // message to non-evm chain with >20 bytes addr\r
    event Message2(address indexed sender, bytes receiver, uint256 dstChainId, bytes message, uint256 fee);\r
\r
    event MessageWithTransfer(\r
        address indexed sender,\r
        address receiver,\r
        uint256 dstChainId,\r
        address bridge,\r
        bytes32 srcTransferId,\r
        bytes message,\r
        uint256 fee\r
    );\r
\r
    event FeeWithdrawn(address receiver, uint256 amount);\r
\r
    event FeeBaseUpdated(uint256 feeBase);\r
    event FeePerByteUpdated(uint256 feePerByte);\r
\r
    constructor(ISigsVerifier _sigsVerifier) {\r
        sigsVerifier = _sigsVerifier;\r
    }\r
\r
    /**\r
     * @notice Sends a message to a contract on another chain.\r
     * Sender needs to make sure the uniqueness of the message Id, which is computed as\r
     * hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message).\r
     * If messages with the same Id are sent, only one of them will succeed at dst chain.\r
     * A fee is charged in the native gas token.\r
     * @param _receiver The address of the destination app contract.\r
     * @param _dstChainId The destination chain ID.\r
     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\r
     */\r
    function sendMessage(\r
        address _receiver,\r
        uint256 _dstChainId,\r
        bytes calldata _message\r
    ) external payable {\r
        _sendMessage(_dstChainId, _message);\r
        emit Message(msg.sender, _receiver, _dstChainId, _message, msg.value);\r
    }\r
\r
    // Send message to non-evm chain with bytes for receiver address,\r
    // otherwise same as above.\r
    function sendMessage(\r
        bytes calldata _receiver,\r
        uint256 _dstChainId,\r
        bytes calldata _message\r
    ) external payable {\r
        _sendMessage(_dstChainId, _message);\r
        emit Message2(msg.sender, _receiver, _dstChainId, _message, msg.value);\r
    }\r
\r
    function _sendMessage(uint256 _dstChainId, bytes calldata _message) private {\r
        require(_dstChainId != block.chainid, "Invalid chainId");\r
        uint256 minFee = calcFee(_message);\r
        require(msg.value >= minFee, "Insufficient fee");\r
    }\r
\r
    /**\r
     * @notice Sends a message associated with a transfer to a contract on another chain.\r
     * If messages with the same srcTransferId are sent, only one of them will succeed.\r
     * A fee is charged in the native token.\r
     * @param _receiver The address of the destination app contract.\r
     * @param _dstChainId The destination chain ID.\r
     * @param _srcBridge The bridge contract to send the transfer with.\r
     * @param _srcTransferId The transfer ID.\r
     * @param _dstChainId The destination chain ID.\r
     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\r
     */\r
    function sendMessageWithTransfer(\r
        address _receiver,\r
        uint256 _dstChainId,\r
        address _srcBridge,\r
        bytes32 _srcTransferId,\r
        bytes calldata _message\r
    ) external payable {\r
        require(_dstChainId != block.chainid, "Invalid chainId");\r
        uint256 minFee = calcFee(_message);\r
        require(msg.value >= minFee, "Insufficient fee");\r
        // SGN needs to verify\r
        // 1. msg.sender matches sender of the src transfer\r
        // 2. dstChainId matches dstChainId of the src transfer\r
        // 3. bridge is either liquidity bridge, peg src vault, or peg dst bridge\r
        emit MessageWithTransfer(msg.sender, _receiver, _dstChainId, _srcBridge, _srcTransferId, _message, msg.value);\r
    }\r
\r
    /**\r
     * @notice Withdraws message fee in the form of native gas token.\r
     * @param _account The address receiving the fee.\r
     * @param _cumulativeFee The cumulative fee credited to the account. Tracked by SGN.\r
     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be\r
     * signed-off by +2/3 of the sigsVerifier's current signing power to be delivered.\r
     * @param _signers The sorted list of signers.\r
     * @param _powers The signing powers of the signers.\r
     */\r
    function withdrawFee(\r
        address _account,\r
        uint256 _cumulativeFee,\r
        bytes[] calldata _sigs,\r
        address[] calldata _signers,\r
        uint256[] calldata _powers\r
    ) external {\r
        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "withdrawFee"));\r
        sigsVerifier.verifySigs(abi.encodePacked(domain, _account, _cumulativeFee), _sigs, _signers, _powers);\r
        uint256 amount = _cumulativeFee - withdrawnFees[_account];\r
        require(amount > 0, "No new amount to withdraw");\r
        withdrawnFees[_account] = _cumulativeFee;\r
        (bool sent, ) = _account.call{value: amount, gas: 50000}("");\r
        require(sent, "failed to withdraw fee");\r
        emit FeeWithdrawn(_account, amount);\r
    }\r
\r
    /**\r
     * @notice Calculates the required fee for the message.\r
     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\r
     @ @return The required fee.\r
     */\r
    function calcFee(bytes calldata _message) public view returns (uint256) {\r
        return feeBase + _message.length * feePerByte;\r
    }\r
\r
    // -------------------- Admin --------------------\r
\r
    function setFeePerByte(uint256 _fee) external onlyOwner {\r
        feePerByte = _fee;\r
        emit FeePerByteUpdated(feePerByte);\r
    }\r
\r
    function setFeeBase(uint256 _fee) external onlyOwner {\r
        feeBase = _fee;\r
        emit FeeBaseUpdated(feeBase);\r
    }\r
}\r
"
    },
    "contracts/safeguard/Ownable.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only\r
\r
pragma solidity ^0.8.0;\r
\r
/**\r
 * @dev Contract module which provides a basic access control mechanism, where\r
 * there is an account (an owner) that can be granted exclusive access to\r
 * specific functions.\r
 *\r
 * By default, the owner account will be the one that deploys the contract. This\r
 * can later be changed with {transferOwnership}.\r
 *\r
 * This module is used through inheritance. It will make available the modifier\r
 * `onlyOwner`, which can be applied to your functions to restrict their use to\r
 * the owner.\r
 *\r
 * This adds a normal func that setOwner if _owner is address(0). So we can't allow\r
 * renounceOwnership. So we can support Proxy based upgradable contract\r
 */\r
abstract contract Ownable {\r
    address private _owner;\r
\r
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\r
\r
    /**\r
     * @dev Initializes the contract setting the deployer as the initial owner.\r
     */\r
    constructor() {\r
        _setOwner(msg.sender);\r
    }\r
\r
    /**\r
     * @dev Only to be called by inherit contracts, in their init func called by Proxy\r
     * we require _owner == address(0), which is only possible when it's a delegateCall\r
     * because constructor sets _owner in contract state.\r
     */\r
    function initOwner() internal {\r
        require(_owner == address(0), "owner already set");\r
        _setOwner(msg.sender);\r
    }\r
\r
    /**\r
     * @dev Returns the address of the current owner.\r
     */\r
    function owner() public view virtual returns (address) {\r
        return _owner;\r
    }\r
\r
    /**\r
     * @dev Throws if called by any account other than the owner.\r
     */\r
    modifier onlyOwner() {\r
        require(owner() == msg.sender, "Ownable: caller is not the owner");\r
        _;\r
    }\r
\r
    /**\r
     * @dev Transfers ownership of the contract to a new account (`newOwner`).\r
     * Can only be called by the current owner.\r
     */\r
    function transferOwnership(address newOwner) public virtual onlyOwner {\r
        require(newOwner != address(0), "Ownable: new owner is the zero address");\r
        _setOwner(newOwner);\r
    }\r
\r
    function _setOwner(address newOwner) private {\r
        address oldOwner = _owner;\r
        _owner = newOwner;\r
        emit OwnershipTransferred(oldOwner, newOwner);\r
    }\r
}\r
"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 800
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "metadata": {
      "useLiteralContent": true
    }
  }
}}

Tags:
Multisig, Liquidity, Voting, Upgradeable, Multi-Signature, Factory|addr:0xc5c8338457abd4cec6b48031880bd29520ffc1bb|verified:true|block:23742659|tx:0x47b9bb889df2959056281cdaa16094df97fed740a2d609bb59b6f359e38ab6ec|first_check:1762508299

Submitted on: 2025-11-07 10:38:20

Comments

Log in to comment.

No comments yet.