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
}
}
}}
Submitted on: 2025-11-07 10:38:20
Comments
Log in to comment.
No comments yet.