@hyperlane-xyz/core
Version:
Core solidity contracts for Hyperlane
23 lines • 214 kB
JSON
{
"language": "Solidity",
"sources": {
"contracts/L1/L1ERC721Bridge.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport { ERC721Bridge } from \"../universal/op-erc721/ERC721Bridge.sol\";\nimport { IERC721 } from \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport { L2ERC721Bridge } from \"../L2/L2ERC721Bridge.sol\";\nimport { Semver } from \"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\";\n\n/**\n * @title L1ERC721Bridge\n * @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to\n * make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract\n * acts as an escrow for ERC721 tokens deposited into L2.\n */\ncontract L1ERC721Bridge is ERC721Bridge, Semver {\n /**\n * @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token\n * by ID was deposited for a given L2 token.\n */\n mapping(address => mapping(address => mapping(uint256 => bool))) public deposits;\n\n /**\n * @custom:semver 1.0.0\n *\n * @param _messenger Address of the CrossDomainMessenger on this network.\n * @param _otherBridge Address of the ERC721 bridge on the other network.\n */\n constructor(address _messenger, address _otherBridge)\n Semver(1, 0, 0)\n ERC721Bridge(_messenger, _otherBridge)\n {}\n\n /*************************\n * Cross-chain Functions *\n *************************/\n\n /**\n * @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the\n * recipient on this domain.\n *\n * @param _localToken Address of the ERC721 token on this domain.\n * @param _remoteToken Address of the ERC721 token on the other domain.\n * @param _from Address that triggered the bridge on the other domain.\n * @param _to Address to receive the token on this domain.\n * @param _tokenId ID of the token being deposited.\n * @param _extraData Optional data to forward to L2. Data supplied here will not be used to\n * execute any code on L2 and is only emitted as extra data for the\n * convenience of off-chain tooling.\n */\n function finalizeBridgeERC721(\n address _localToken,\n address _remoteToken,\n address _from,\n address _to,\n uint256 _tokenId,\n bytes calldata _extraData\n ) external onlyOtherBridge {\n require(_localToken != address(this), \"L1ERC721Bridge: local token cannot be self\");\n\n // Checks that the L1/L2 NFT pair has a token ID that is escrowed in the L1 Bridge.\n require(\n deposits[_localToken][_remoteToken][_tokenId] == true,\n \"L1ERC721Bridge: Token ID is not escrowed in the L1 Bridge\"\n );\n\n // Mark that the token ID for this L1/L2 token pair is no longer escrowed in the L1\n // Bridge.\n deposits[_localToken][_remoteToken][_tokenId] = false;\n\n // When a withdrawal is finalized on L1, the L1 Bridge transfers the NFT to the\n // withdrawer.\n IERC721(_localToken).safeTransferFrom(address(this), _to, _tokenId);\n\n // slither-disable-next-line reentrancy-events\n emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);\n }\n\n /**\n * @inheritdoc ERC721Bridge\n */\n function _initiateBridgeERC721(\n address _localToken,\n address _remoteToken,\n address _from,\n address _to,\n uint256 _tokenId,\n uint32 _minGasLimit,\n bytes calldata _extraData\n ) internal override {\n require(_remoteToken != address(0), \"ERC721Bridge: remote token cannot be address(0)\");\n\n // Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId)\n bytes memory message = abi.encodeWithSelector(\n L2ERC721Bridge.finalizeBridgeERC721.selector,\n _remoteToken,\n _localToken,\n _from,\n _to,\n _tokenId,\n _extraData\n );\n\n // Lock token into bridge\n deposits[_localToken][_remoteToken][_tokenId] = true;\n IERC721(_localToken).transferFrom(_from, address(this), _tokenId);\n\n // Send calldata into L2\n messenger.sendMessage(otherBridge, message, _minGasLimit);\n emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);\n }\n}\n"
},
"contracts/universal/op-erc721/ERC721Bridge.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport {\n CrossDomainMessenger\n} from \"@eth-optimism/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title ERC721Bridge\n * @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges.\n */\nabstract contract ERC721Bridge {\n /**\n * @notice Emitted when an ERC721 bridge to the other network is initiated.\n *\n * @param localToken Address of the token on this domain.\n * @param remoteToken Address of the token on the remote domain.\n * @param from Address that initiated bridging action.\n * @param to Address to receive the token.\n * @param tokenId ID of the specific token deposited.\n * @param extraData Extra data for use on the client-side.\n */\n event ERC721BridgeInitiated(\n address indexed localToken,\n address indexed remoteToken,\n address indexed from,\n address to,\n uint256 tokenId,\n bytes extraData\n );\n\n /**\n * @notice Emitted when an ERC721 bridge from the other network is finalized.\n *\n * @param localToken Address of the token on this domain.\n * @param remoteToken Address of the token on the remote domain.\n * @param from Address that initiated bridging action.\n * @param to Address to receive the token.\n * @param tokenId ID of the specific token deposited.\n * @param extraData Extra data for use on the client-side.\n */\n event ERC721BridgeFinalized(\n address indexed localToken,\n address indexed remoteToken,\n address indexed from,\n address to,\n uint256 tokenId,\n bytes extraData\n );\n\n /**\n * @notice Messenger contract on this domain.\n */\n CrossDomainMessenger public immutable messenger;\n\n /**\n * @notice Address of the bridge on the other network.\n */\n address public immutable otherBridge;\n\n /**\n * @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.\n */\n uint256[49] private __gap;\n\n /**\n * @notice Ensures that the caller is a cross-chain message from the other bridge.\n */\n modifier onlyOtherBridge() {\n require(\n msg.sender == address(messenger) && messenger.xDomainMessageSender() == otherBridge,\n \"ERC721Bridge: function can only be called from the other bridge\"\n );\n _;\n }\n\n /**\n * @param _messenger Address of the CrossDomainMessenger on this network.\n * @param _otherBridge Address of the ERC721 bridge on the other network.\n */\n constructor(address _messenger, address _otherBridge) {\n require(_messenger != address(0), \"ERC721Bridge: messenger cannot be address(0)\");\n require(_otherBridge != address(0), \"ERC721Bridge: other bridge cannot be address(0)\");\n\n messenger = CrossDomainMessenger(_messenger);\n otherBridge = _otherBridge;\n }\n\n /**\n * @notice Initiates a bridge of an NFT to the caller's account on the other chain. Note that\n * this function can only be called by EOAs. Smart contract wallets should use the\n * `bridgeERC721To` function after ensuring that the recipient address on the remote\n * chain exists. Also note that the current owner of the token on this chain must\n * approve this contract to operate the NFT before it can be bridged.\n * **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This\n * bridge only supports ERC721s originally deployed on Ethereum. Users will need to\n * wait for the one-week challenge period to elapse before their Optimism-native NFT\n * can be refunded on L2.\n *\n * @param _localToken Address of the ERC721 on this domain.\n * @param _remoteToken Address of the ERC721 on the remote domain.\n * @param _tokenId Token ID to bridge.\n * @param _minGasLimit Minimum gas limit for the bridge message on the other domain.\n * @param _extraData Optional data to forward to the other chain. Data supplied here will not\n * be used to execute any code on the other chain and is only emitted as\n * extra data for the convenience of off-chain tooling.\n */\n function bridgeERC721(\n address _localToken,\n address _remoteToken,\n uint256 _tokenId,\n uint32 _minGasLimit,\n bytes calldata _extraData\n ) external {\n // Modifier requiring sender to be EOA. This prevents against a user error that would occur\n // if the sender is a smart contract wallet that has a different address on the remote chain\n // (or doesn't have an address on the remote chain at all). The user would fail to receive\n // the NFT if they use this function because it sends the NFT to the same address as the\n // caller. This check could be bypassed by a malicious contract via initcode, but it takes\n // care of the user error we want to avoid.\n require(!Address.isContract(msg.sender), \"ERC721Bridge: account is not externally owned\");\n\n _initiateBridgeERC721(\n _localToken,\n _remoteToken,\n msg.sender,\n msg.sender,\n _tokenId,\n _minGasLimit,\n _extraData\n );\n }\n\n /**\n * @notice Initiates a bridge of an NFT to some recipient's account on the other chain. Note\n * that the current owner of the token on this chain must approve this contract to\n * operate the NFT before it can be bridged.\n * **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This\n * bridge only supports ERC721s originally deployed on Ethereum. Users will need to\n * wait for the one-week challenge period to elapse before their Optimism-native NFT\n * can be refunded on L2.\n *\n * @param _localToken Address of the ERC721 on this domain.\n * @param _remoteToken Address of the ERC721 on the remote domain.\n * @param _to Address to receive the token on the other domain.\n * @param _tokenId Token ID to bridge.\n * @param _minGasLimit Minimum gas limit for the bridge message on the other domain.\n * @param _extraData Optional data to forward to the other chain. Data supplied here will not\n * be used to execute any code on the other chain and is only emitted as\n * extra data for the convenience of off-chain tooling.\n */\n function bridgeERC721To(\n address _localToken,\n address _remoteToken,\n address _to,\n uint256 _tokenId,\n uint32 _minGasLimit,\n bytes calldata _extraData\n ) external {\n require(_to != address(0), \"ERC721Bridge: nft recipient cannot be address(0)\");\n\n _initiateBridgeERC721(\n _localToken,\n _remoteToken,\n msg.sender,\n _to,\n _tokenId,\n _minGasLimit,\n _extraData\n );\n }\n\n /**\n * @notice Internal function for initiating a token bridge to the other domain.\n *\n * @param _localToken Address of the ERC721 on this domain.\n * @param _remoteToken Address of the ERC721 on the remote domain.\n * @param _from Address of the sender on this domain.\n * @param _to Address to receive the token on the other domain.\n * @param _tokenId Token ID to bridge.\n * @param _minGasLimit Minimum gas limit for the bridge message on the other domain.\n * @param _extraData Optional data to forward to the other domain. Data supplied here will\n * not be used to execute any code on the other domain and is only emitted\n * as extra data for the convenience of off-chain tooling.\n */\n function _initiateBridgeERC721(\n address _localToken,\n address _remoteToken,\n address _from,\n address _to,\n uint256 _tokenId,\n uint32 _minGasLimit,\n bytes calldata _extraData\n ) internal virtual;\n}\n"
},
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
},
"contracts/L2/L2ERC721Bridge.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport { ERC721Bridge } from \"../universal/op-erc721/ERC721Bridge.sol\";\nimport { ERC165Checker } from \"@openzeppelin/contracts/utils/introspection/ERC165Checker.sol\";\nimport { L1ERC721Bridge } from \"../L1/L1ERC721Bridge.sol\";\nimport { IOptimismMintableERC721 } from \"../universal/op-erc721/IOptimismMintableERC721.sol\";\nimport { Semver } from \"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol\";\n\n/**\n * @title L2ERC721Bridge\n * @notice The L2 ERC721 bridge is a contract which works together with the L1 ERC721 bridge to\n * make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract\n * acts as a minter for new tokens when it hears about deposits into the L1 ERC721 bridge.\n * This contract also acts as a burner for tokens being withdrawn.\n * **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This\n * bridge ONLY supports ERC721s originally deployed on Ethereum. Users will need to\n * wait for the one-week challenge period to elapse before their Optimism-native NFT\n * can be refunded on L2.\n */\ncontract L2ERC721Bridge is ERC721Bridge, Semver {\n /**\n * @custom:semver 1.0.0\n *\n * @param _messenger Address of the CrossDomainMessenger on this network.\n * @param _otherBridge Address of the ERC721 bridge on the other network.\n */\n constructor(address _messenger, address _otherBridge)\n Semver(1, 0, 0)\n ERC721Bridge(_messenger, _otherBridge)\n {}\n\n /**\n * @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the\n * recipient on this domain.\n *\n * @param _localToken Address of the ERC721 token on this domain.\n * @param _remoteToken Address of the ERC721 token on the other domain.\n * @param _from Address that triggered the bridge on the other domain.\n * @param _to Address to receive the token on this domain.\n * @param _tokenId ID of the token being deposited.\n * @param _extraData Optional data to forward to L1. Data supplied here will not be used to\n * execute any code on L1 and is only emitted as extra data for the\n * convenience of off-chain tooling.\n */\n function finalizeBridgeERC721(\n address _localToken,\n address _remoteToken,\n address _from,\n address _to,\n uint256 _tokenId,\n bytes calldata _extraData\n ) external onlyOtherBridge {\n require(_localToken != address(this), \"L2ERC721Bridge: local token cannot be self\");\n\n // Note that supportsInterface makes a callback to the _localToken address which is user\n // provided.\n require(\n ERC165Checker.supportsInterface(_localToken, type(IOptimismMintableERC721).interfaceId),\n \"L2ERC721Bridge: local token interface is not compliant\"\n );\n\n require(\n _remoteToken == IOptimismMintableERC721(_localToken).remoteToken(),\n \"L2ERC721Bridge: wrong remote token for Optimism Mintable ERC721 local token\"\n );\n\n // When a deposit is finalized, we give the NFT with the same tokenId to the account\n // on L2. Note that safeMint makes a callback to the _to address which is user provided.\n IOptimismMintableERC721(_localToken).safeMint(_to, _tokenId);\n\n // slither-disable-next-line reentrancy-events\n emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);\n }\n\n /**\n * @inheritdoc ERC721Bridge\n */\n function _initiateBridgeERC721(\n address _localToken,\n address _remoteToken,\n address _from,\n address _to,\n uint256 _tokenId,\n uint32 _minGasLimit,\n bytes calldata _extraData\n ) internal override {\n require(_remoteToken != address(0), \"ERC721Bridge: remote token cannot be address(0)\");\n\n // Check that the withdrawal is being initiated by the NFT owner\n require(\n _from == IOptimismMintableERC721(_localToken).ownerOf(_tokenId),\n \"Withdrawal is not being initiated by NFT owner\"\n );\n\n // Construct calldata for l1ERC721Bridge.finalizeBridgeERC721(_to, _tokenId)\n // slither-disable-next-line reentrancy-events\n address remoteToken = IOptimismMintableERC721(_localToken).remoteToken();\n require(\n remoteToken == _remoteToken,\n \"L2ERC721Bridge: remote token does not match given value\"\n );\n\n // When a withdrawal is initiated, we burn the withdrawer's NFT to prevent subsequent L2\n // usage\n // slither-disable-next-line reentrancy-events\n IOptimismMintableERC721(_localToken).burn(_from, _tokenId);\n\n bytes memory message = abi.encodeWithSelector(\n L1ERC721Bridge.finalizeBridgeERC721.selector,\n remoteToken,\n _localToken,\n _from,\n _to,\n _tokenId,\n _extraData\n );\n\n // Send message to L1 bridge\n // slither-disable-next-line reentrancy-events\n messenger.sendMessage(otherBridge, message, _minGasLimit);\n\n // slither-disable-next-line reentrancy-events\n emit ERC721BridgeInitiated(_localToken, remoteToken, _from, _to, _tokenId, _extraData);\n }\n}\n"
},
"@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.15;\n\nimport { Strings } from \"@openzeppelin/contracts/utils/Strings.sol\";\n\n/**\n * @title Semver\n * @notice Semver is a simple contract for managing contract versions.\n */\ncontract Semver {\n /**\n * @notice Contract version number (major).\n */\n // solhint-disable-next-line var-name-mixedcase\n uint256 private immutable MAJOR_VERSION;\n\n /**\n * @notice Contract version number (minor).\n */\n // solhint-disable-next-line var-name-mixedcase\n uint256 private immutable MINOR_VERSION;\n\n /**\n * @notice Contract version number (patch).\n */\n // solhint-disable-next-line var-name-mixedcase\n uint256 private immutable PATCH_VERSION;\n\n /**\n * @param _major Version number (major).\n * @param _minor Version number (minor).\n * @param _patch Version number (patch).\n */\n constructor(\n uint256 _major,\n uint256 _minor,\n uint256 _patch\n ) {\n MAJOR_VERSION = _major;\n MINOR_VERSION = _minor;\n PATCH_VERSION = _patch;\n }\n\n /**\n * @notice Returns the full semver contract version.\n *\n * @return Semver contract version as a string.\n */\n function version() public view returns (string memory) {\n return\n string(\n abi.encodePacked(\n Strings.toString(MAJOR_VERSION),\n \".\",\n Strings.toString(MINOR_VERSION),\n \".\",\n Strings.toString(PATCH_VERSION)\n )\n );\n }\n}\n"
},
"@eth-optimism/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.15;\n\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n PausableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol\";\nimport {\n ReentrancyGuardUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport { SafeCall } from \"../libraries/SafeCall.sol\";\nimport { Hashing } from \"../libraries/Hashing.sol\";\nimport { Encoding } from \"../libraries/Encoding.sol\";\n\n/**\n * @custom:legacy\n * @title CrossDomainMessengerLegacySpacer\n * @notice Contract only exists to add a spacer to the CrossDomainMessenger where the\n * libAddressManager variable used to exist. Must be the first contract in the inheritance\n * tree of the CrossDomainMessenger\n */\ncontract CrossDomainMessengerLegacySpacer {\n /**\n * @custom:legacy\n * @custom:spacer libAddressManager\n * @notice Spacer for backwards compatibility.\n */\n address private spacer_0_0_20;\n}\n\n/**\n * @custom:upgradeable\n * @title CrossDomainMessenger\n * @notice CrossDomainMessenger is a base contract that provides the core logic for the L1 and L2\n * cross-chain messenger contracts. It's designed to be a universal interface that only\n * needs to be extended slightly to provide low-level message passing functionality on each\n * chain it's deployed on. Currently only designed for message passing between two paired\n * chains and does not support one-to-many interactions.\n */\nabstract contract CrossDomainMessenger is\n CrossDomainMessengerLegacySpacer,\n OwnableUpgradeable,\n PausableUpgradeable,\n ReentrancyGuardUpgradeable\n{\n /**\n * @notice Current message version identifier.\n */\n uint16 public constant MESSAGE_VERSION = 1;\n\n /**\n * @notice Constant overhead added to the base gas for a message.\n */\n uint32 public constant MIN_GAS_CONSTANT_OVERHEAD = 200_000;\n\n /**\n * @notice Numerator for dynamic overhead added to the base gas for a message.\n */\n uint32 public constant MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR = 1016;\n\n /**\n * @notice Denominator for dynamic overhead added to the base gas for a message.\n */\n uint32 public constant MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR = 1000;\n\n /**\n * @notice Extra gas added to base gas for each byte of calldata in a message.\n */\n uint32 public constant MIN_GAS_CALLDATA_OVERHEAD = 16;\n\n /**\n * @notice Minimum amount of gas required to relay a message.\n */\n uint256 internal constant RELAY_GAS_REQUIRED = 45_000;\n\n /**\n * @notice Amount of gas held in reserve to guarantee that relay execution completes.\n */\n uint256 internal constant RELAY_GAS_BUFFER = RELAY_GAS_REQUIRED - 5000;\n\n /**\n * @notice Initial value for the xDomainMsgSender variable. We set this to a non-zero value\n * because performing an SSTORE on a non-zero value is significantly cheaper than on a\n * zero value.\n */\n address internal constant DEFAULT_XDOMAIN_SENDER = 0x000000000000000000000000000000000000dEaD;\n\n /**\n * @notice Address of the paired CrossDomainMessenger contract on the other chain.\n */\n address public immutable otherMessenger;\n\n /**\n * @custom:legacy\n * @custom:spacer blockedMessages\n * @notice Spacer for backwards compatibility.\n */\n mapping(bytes32 => bool) private spacer_201_0_32;\n\n /**\n * @custom:legacy\n * @custom:spacer relayedMessages\n * @notice Spacer for backwards compatibility.\n */\n mapping(bytes32 => bool) private spacer_202_0_32;\n\n /**\n * @notice Mapping of message hashes to boolean receipt values. Note that a message will only\n * be present in this mapping if it has successfully been relayed on this chain, and\n * can therefore not be relayed again.\n */\n mapping(bytes32 => bool) public successfulMessages;\n\n /**\n * @notice Address of the sender of the currently executing message on the other chain. If the\n * value of this variable is the default value (0x00000000...dead) then no message is\n * currently being executed. Use the xDomainMessageSender getter which will throw an\n * error if this is the case.\n */\n address internal xDomainMsgSender;\n\n /**\n * @notice Nonce for the next message to be sent, without the message version applied. Use the\n * messageNonce getter which will insert the message version into the nonce to give you\n * the actual nonce to be used for the message.\n */\n uint240 internal msgNonce;\n\n /**\n * @notice Mapping of message hashes to boolean receipt values. Note that a message will only\n * be present in this mapping if it failed to be relayed on this chain at least once.\n * If a message is successfully relayed on the first attempt, then it will only be\n * present within the successfulMessages mapping.\n */\n mapping(bytes32 => bool) public receivedMessages;\n\n /**\n * @notice Reserve extra slots in the storage layout for future upgrades.\n * A gap size of 41 was chosen here, so that the first slot used in a child contract\n * would be a multiple of 50.\n */\n uint256[42] private __gap;\n\n /**\n * @notice Emitted whenever a message is sent to the other chain.\n *\n * @param target Address of the recipient of the message.\n * @param sender Address of the sender of the message.\n * @param message Message to trigger the recipient address with.\n * @param messageNonce Unique nonce attached to the message.\n * @param gasLimit Minimum gas limit that the message can be executed with.\n */\n event SentMessage(\n address indexed target,\n address sender,\n bytes message,\n uint256 messageNonce,\n uint256 gasLimit\n );\n\n /**\n * @notice Additional event data to emit, required as of Bedrock. Cannot be merged with the\n * SentMessage event without breaking the ABI of this contract, this is good enough.\n *\n * @param sender Address of the sender of the message.\n * @param value ETH value sent along with the message to the recipient.\n */\n event SentMessageExtension1(address indexed sender, uint256 value);\n\n /**\n * @notice Emitted whenever a message is successfully relayed on this chain.\n *\n * @param msgHash Hash of the message that was relayed.\n */\n event RelayedMessage(bytes32 indexed msgHash);\n\n /**\n * @notice Emitted whenever a message fails to be relayed on this chain.\n *\n * @param msgHash Hash of the message that failed to be relayed.\n */\n event FailedRelayedMessage(bytes32 indexed msgHash);\n\n /**\n * @param _otherMessenger Address of the messenger on the paired chain.\n */\n constructor(address _otherMessenger) {\n otherMessenger = _otherMessenger;\n }\n\n /**\n * @notice Allows the owner of this contract to temporarily pause message relaying. Backup\n * security mechanism just in case. Owner should be the same as the upgrade wallet to\n * maintain the security model of the system as a whole.\n */\n function pause() external onlyOwner {\n _pause();\n }\n\n /**\n * @notice Allows the owner of this contract to resume message relaying once paused.\n */\n function unpause() external onlyOwner {\n _unpause();\n }\n\n /**\n * @notice Sends a message to some target address on the other chain. Note that if the call\n * always reverts, then the message will be unrelayable, and any ETH sent will be\n * permanently locked. The same will occur if the target on the other chain is\n * considered unsafe (see the _isUnsafeTarget() function).\n *\n * @param _target Target contract or wallet address.\n * @param _message Message to trigger the target address with.\n * @param _minGasLimit Minimum gas limit that the message can be executed with.\n */\n function sendMessage(\n address _target,\n bytes calldata _message,\n uint32 _minGasLimit\n ) external payable {\n // Triggers a message to the other messenger. Note that the amount of gas provided to the\n // message is the amount of gas requested by the user PLUS the base gas value. We want to\n // guarantee the property that the call to the target contract will always have at least\n // the minimum gas limit specified by the user.\n _sendMessage(\n otherMessenger,\n baseGas(_message, _minGasLimit),\n msg.value,\n abi.encodeWithSelector(\n this.relayMessage.selector,\n messageNonce(),\n msg.sender,\n _target,\n msg.value,\n _minGasLimit,\n _message\n )\n );\n\n emit SentMessage(_target, msg.sender, _message, messageNonce(), _minGasLimit);\n emit SentMessageExtension1(msg.sender, msg.value);\n\n unchecked {\n ++msgNonce;\n }\n }\n\n /**\n * @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only\n * be executed via cross-chain call from the other messenger OR if the message was\n * already received once and is currently being replayed.\n *\n * @param _nonce Nonce of the message being relayed.\n * @param _sender Address of the user who sent the message.\n * @param _target Address that the message is targeted at.\n * @param _value ETH value to send with the message.\n * @param _minGasLimit Minimum amount of gas that the message can be executed with.\n * @param _message Message to send to the target.\n */\n function relayMessage(\n uint256 _nonce,\n address _sender,\n address _target,\n uint256 _value,\n uint256 _minGasLimit,\n bytes calldata _message\n ) external payable nonReentrant whenNotPaused {\n (, uint16 version) = Encoding.decodeVersionedNonce(_nonce);\n\n // Block any messages that aren't version 1. All version 0 messages have been guaranteed to\n // be relayed OR have been migrated to version 1 messages. Version 0 messages do not commit\n // to the value or minGasLimit fields, which can create unexpected issues for end-users.\n require(\n version == 1,\n \"CrossDomainMessenger: only version 1 messages are supported after the Bedrock upgrade\"\n );\n\n bytes32 versionedHash = Hashing.hashCrossDomainMessageV1(\n _nonce,\n _sender,\n _target,\n _value,\n _minGasLimit,\n _message\n );\n\n if (_isOtherMessenger()) {\n // This property should always hold when the message is first submitted (as opposed to\n // being replayed).\n assert(msg.value == _value);\n } else {\n require(\n msg.value == 0,\n \"CrossDomainMessenger: value must be zero unless message is from a system address\"\n );\n\n require(\n receivedMessages[versionedHash],\n \"CrossDomainMessenger: message cannot be replayed\"\n );\n }\n\n require(\n _isUnsafeTarget(_target) == false,\n \"CrossDomainMessenger: cannot send message to blocked system address\"\n );\n\n require(\n successfulMessages[versionedHash] == false,\n \"CrossDomainMessenger: message has already been relayed\"\n );\n\n require(\n gasleft() >= _minGasLimit + RELAY_GAS_REQUIRED,\n \"CrossDomainMessenger: insufficient gas to relay message\"\n );\n\n xDomainMsgSender = _sender;\n bool success = SafeCall.call(_target, gasleft() - RELAY_GAS_BUFFER, _value, _message);\n xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;\n\n if (success == true) {\n successfulMessages[versionedHash] = true;\n emit RelayedMessage(versionedHash);\n } else {\n receivedMessages[versionedHash] = true;\n emit FailedRelayedMessage(versionedHash);\n }\n }\n\n /**\n * @notice Retrieves the address of the contract or wallet that initiated the currently\n * executing message on the other chain. Will throw an error if there is no message\n * currently being executed. Allows the recipient of a call to see who triggered it.\n *\n * @return Address of the sender of the currently executing message on the other chain.\n */\n function xDomainMessageSender() external view returns (address) {\n require(\n xDomainMsgSender != DEFAULT_XDOMAIN_SENDER,\n \"CrossDomainMessenger: xDomainMessageSender is not set\"\n );\n\n return xDomainMsgSender;\n }\n\n /**\n * @notice Retrieves the next message nonce. Message version will be added to the upper two\n * bytes of the message nonce. Message version allows us to treat messages as having\n * different structures.\n *\n * @return Nonce of the next message to be sent, with added message version.\n */\n function messageNonce() public view returns (uint256) {\n return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION);\n }\n\n /**\n * @notice Computes the amount of gas required to guarantee that a given message will be\n * received on the other chain without running out of gas. Guaranteeing that a message\n * will not run out of gas is important because this ensures that a message can always\n * be replayed on the other chain if it fails to execute completely.\n *\n * @param _message Message to compute the amount of required gas for.\n * @param _minGasLimit Minimum desired gas limit when message goes to target.\n *\n * @return Amount of gas required to guarantee message receipt.\n */\n function baseGas(bytes calldata _message, uint32 _minGasLimit) public pure returns (uint32) {\n return\n // Dynamic overhead\n ((_minGasLimit * MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR) /\n MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR) +\n // Calldata overhead\n (uint32(_message.length) * MIN_GAS_CALLDATA_OVERHEAD) +\n // Constant overhead\n MIN_GAS_CONSTANT_OVERHEAD;\n }\n\n /**\n * @notice Intializer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function __CrossDomainMessenger_init() internal onlyInitializing {\n xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;\n __Context_init_unchained();\n __Ownable_init_unchained();\n __Pausable_init_unchained();\n __ReentrancyGuard_init_unchained();\n }\n\n /**\n * @notice Sends a low-level message to the other messenger. Needs to be implemented by child\n * contracts because the logic for this depends on the network where the messenger is\n * being deployed.\n *\n * @param _to Recipient of the message on the other chain.\n * @param _gasLimit Minimum gas limit the message can be executed with.\n * @param _value Amount of ETH to send with the message.\n * @param _data Message data.\n */\n function _sendMessage(\n address _to,\n uint64 _gasLimit,\n uint256 _value,\n bytes memory _data\n ) internal virtual;\n\n /**\n * @notice Checks whether the message is coming from the other messenger. Implemented by child\n * contracts because the logic for this depends on the network where the messenger is\n * being deployed.\n *\n * @return Whether the message is coming from the other messenger.\n */\n function _isOtherMessenger() internal view virtual returns (bool);\n\n /**\n * @notice Checks whether a given call target is a system address that could cause the\n * messenger to peform an unsafe action. This is NOT a mechanism for blocking user\n * addresses. This is ONLY used to prevent the execution of messages to specific\n * system addresses that could cause security issues, e.g., having the\n * CrossDomainMessenger send messages to itself.\n *\n * @param _target Address of the contract to check.\n *\n * @return Whether or not the address is an unsafe system address.\n */\n function _isUnsafeTarget(address _target) internal view virtual returns (bool);\n}\n"
},
"@openzeppelin/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but perform