@etherspot/contracts
Version:
Etherspot Solidity contracts
35 lines • 353 kB
JSON
{
"language": "Solidity",
"sources": {
"src/bridges/Diamond.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.4 <0.9.0;\n\nimport {LibDiamond} from \"./libs/LibDiamond.sol\";\nimport {IDiamondCut} from \"./interfaces/IDiamondCut.sol\";\n\ncontract Diamond {\n constructor(address _contractOwner, address _diamondCutFacet) payable {\n LibDiamond.setContractOwner(_contractOwner);\n\n // Add the diamondCut external function from the diamondCutFacet\n IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);\n bytes4[] memory functionSelectors = new bytes4[](1);\n functionSelectors[0] = IDiamondCut.diamondCut.selector;\n cut[0] = IDiamondCut.FacetCut({\n facetAddress: _diamondCutFacet,\n action: IDiamondCut.FacetCutAction.Add,\n functionSelectors: functionSelectors\n });\n LibDiamond.diamondCut(cut, address(0), \"\");\n }\n\n // Find facet for function that is called and execute the\n // function if a facet is found and return any value.\n // solhint-disable-next-line no-complex-fallback\n fallback() external payable {\n LibDiamond.DiamondStorage storage ds;\n bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;\n\n // get diamond storage\n // solhint-disable-next-line no-inline-assembly\n assembly {\n ds.slot := position\n }\n\n // get facet from function selector\n address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;\n require(facet != address(0), \"Diamond: Function does not exist\");\n\n // Execute external function from facet using delegatecall and return any value.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 0, calldatasize())\n // execute function call using the facet\n let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n}\n"
},
"src/bridges/libs/LibDiamond.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.4 <0.9.0;\n\nimport { IDiamondCut } from \"../interfaces/IDiamondCut.sol\";\n\nlibrary LibDiamond {\n bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256(\"diamond.standard.diamond.storage\");\n\n struct FacetAddressAndPosition {\n address facetAddress;\n uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array\n }\n\n struct FacetFunctionSelectors {\n bytes4[] functionSelectors;\n uint256 facetAddressPosition; // position of facetAddress in facetAddresses array\n }\n\n struct DiamondStorage {\n // maps function selector to the facet address and\n // the position of the selector in the facetFunctionSelectors.selectors array\n mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;\n // maps facet addresses to function selectors\n mapping(address => FacetFunctionSelectors) facetFunctionSelectors;\n // facet addresses\n address[] facetAddresses;\n // Used to query if a contract implements an interface.\n // Used to implement ERC-165.\n mapping(bytes4 => bool) supportedInterfaces;\n // owner of the contract\n address contractOwner;\n }\n\n function diamondStorage() internal pure returns (DiamondStorage storage ds) {\n bytes32 position = DIAMOND_STORAGE_POSITION;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n ds.slot := position\n }\n }\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n function setContractOwner(address _newOwner) internal {\n DiamondStorage storage ds = diamondStorage();\n address previousOwner = ds.contractOwner;\n ds.contractOwner = _newOwner;\n emit OwnershipTransferred(previousOwner, _newOwner);\n }\n\n function contractOwner() internal view returns (address contractOwner_) {\n contractOwner_ = diamondStorage().contractOwner;\n }\n\n function enforceIsContractOwner() internal view {\n require(msg.sender == diamondStorage().contractOwner, \"LibDiamond: Must be contract owner\");\n }\n\n event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);\n\n // Internal function version of diamondCut\n function diamondCut(\n IDiamondCut.FacetCut[] memory _diamondCut,\n address _init,\n bytes memory _calldata\n ) internal {\n for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {\n IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;\n if (action == IDiamondCut.FacetCutAction.Add) {\n addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);\n } else if (action == IDiamondCut.FacetCutAction.Replace) {\n replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);\n } else if (action == IDiamondCut.FacetCutAction.Remove) {\n removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);\n } else {\n revert(\"LibDiamondCut: Incorrect FacetCutAction\");\n }\n }\n emit DiamondCut(_diamondCut, _init, _calldata);\n initializeDiamondCut(_init, _calldata);\n }\n\n function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {\n require(_functionSelectors.length > 0, \"LibDiamondCut: No selectors in facet to cut\");\n DiamondStorage storage ds = diamondStorage();\n require(_facetAddress != address(0), \"LibDiamondCut: Add facet can't be address(0)\");\n uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);\n // add new facet address if it does not exist\n if (selectorPosition == 0) {\n addFacet(ds, _facetAddress);\n }\n for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {\n bytes4 selector = _functionSelectors[selectorIndex];\n address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;\n require(oldFacetAddress == address(0), \"LibDiamondCut: Can't add function that already exists\");\n addFunction(ds, selector, selectorPosition, _facetAddress);\n selectorPosition++;\n }\n }\n\n function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {\n require(_functionSelectors.length > 0, \"LibDiamondCut: No selectors in facet to cut\");\n DiamondStorage storage ds = diamondStorage();\n require(_facetAddress != address(0), \"LibDiamondCut: Add facet can't be address(0)\");\n uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);\n // add new facet address if it does not exist\n if (selectorPosition == 0) {\n addFacet(ds, _facetAddress);\n }\n for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {\n bytes4 selector = _functionSelectors[selectorIndex];\n address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;\n require(oldFacetAddress != _facetAddress, \"LibDiamondCut: Can't replace function with same function\");\n removeFunction(ds, oldFacetAddress, selector);\n addFunction(ds, selector, selectorPosition, _facetAddress);\n selectorPosition++;\n }\n }\n\n function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {\n require(_functionSelectors.length > 0, \"LibDiamondCut: No selectors in facet to cut\");\n DiamondStorage storage ds = diamondStorage();\n // if function does not exist then do nothing and return\n require(_facetAddress == address(0), \"LibDiamondCut: Remove facet address must be address(0)\");\n for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {\n bytes4 selector = _functionSelectors[selectorIndex];\n address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;\n removeFunction(ds, oldFacetAddress, selector);\n }\n }\n\n function addFacet(DiamondStorage storage ds, address _facetAddress) internal {\n enforceHasContractCode(_facetAddress, \"LibDiamondCut: New facet has no code\");\n ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;\n ds.facetAddresses.push(_facetAddress);\n }\n\n function addFunction(\n DiamondStorage storage ds,\n bytes4 _selector,\n uint96 _selectorPosition,\n address _facetAddress\n ) internal {\n ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;\n ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);\n ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;\n }\n\n function removeFunction(\n DiamondStorage storage ds,\n address _facetAddress,\n bytes4 _selector\n ) internal {\n require(_facetAddress != address(0), \"LibDiamondCut: Can't remove function that doesn't exist\");\n // an immutable function is a function defined directly in a diamond\n require(_facetAddress != address(this), \"LibDiamondCut: Can't remove immutable function\");\n // replace selector with last selector, then delete last selector\n uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;\n uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;\n // if not the same then replace _selector with lastSelector\n if (selectorPosition != lastSelectorPosition) {\n bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];\n ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;\n ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);\n }\n // delete the last selector\n ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();\n delete ds.selectorToFacetAndPosition[_selector];\n\n // if no more selectors for facet address then delete the facet address\n if (lastSelectorPosition == 0) {\n // replace facet address with last facet address and delete last facet address\n uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;\n uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;\n if (facetAddressPosition != lastFacetAddressPosition) {\n address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];\n ds.facetAddresses[facetAddressPosition] = lastFacetAddress;\n ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;\n }\n ds.facetAddresses.pop();\n delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;\n }\n }\n\n function initializeDiamondCut(address _init, bytes memory _calldata) internal {\n if (_init == address(0)) {\n require(_calldata.length == 0, \"LibDiamondCut: _init is address(0) but_calldata is not empty\");\n } else {\n require(_calldata.length > 0, \"LibDiamondCut: _calldata is empty but _init is not address(0)\");\n if (_init != address(this)) {\n enforceHasContractCode(_init, \"LibDiamondCut: _init address has no code\");\n }\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory error) = _init.delegatecall(_calldata);\n if (!success) {\n if (error.length > 0) {\n // bubble up the error\n revert(string(error));\n } else {\n revert(\"LibDiamondCut: _init function reverted\");\n }\n }\n }\n }\n\n function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {\n uint256 contractSize;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n contractSize := extcodesize(_contract)\n }\n require(contractSize > 0, _errorMessage);\n }\n}\n"
},
"src/bridges/interfaces/IDiamondCut.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.4 <0.9.0;\n\ninterface IDiamondCut {\n enum FacetCutAction {\n Add,\n Replace,\n Remove\n }\n // Add=0, Replace=1, Remove=2\n\n struct FacetCut {\n address facetAddress;\n FacetCutAction action;\n bytes4[] functionSelectors;\n }\n\n /// @notice Add/replace/remove any number of functions and optionally execute\n /// a function with delegatecall\n /// @param _diamondCut Contains the facet addresses and function selectors\n /// @param _init The address of the contract or facet to execute _calldata\n /// @param _calldata A function call, including function selector and arguments\n /// _calldata is executed with delegatecall on _init\n function diamondCut(\n FacetCut[] calldata _diamondCut,\n address _init,\n bytes calldata _calldata\n ) external;\n\n event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);\n}\n"
},
"src/bridges/facets/DiamondCutFacet.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.4 <0.9.0;\n\nimport { IDiamondCut } from \"../interfaces/IDiamondCut.sol\";\nimport { LibDiamond } from \"../libs/LibDiamond.sol\";\n\ncontract DiamondCutFacet is IDiamondCut {\n /// @notice Add/replace/remove any number of functions and optionally execute\n /// a function with delegatecall\n /// @param _diamondCut Contains the facet addresses and function selectors\n /// @param _init The address of the contract or facet to execute _calldata\n /// @param _calldata A function call, including function selector and arguments\n /// _calldata is executed with delegatecall on _init\n function diamondCut(\n FacetCut[] calldata _diamondCut,\n address _init,\n bytes calldata _calldata\n ) external override {\n LibDiamond.enforceIsContractOwner();\n LibDiamond.diamondCut(_diamondCut, _init, _calldata);\n }\n}\n"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n"
},
"@openzeppelin/contracts/interfaces/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC20/IERC20.sol\";\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 performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
},
"@connext/nxtp-contracts/contracts/nomad-core/contracts/Home.sol": {
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity >=0.6.11;\n\n// ============ Internal Imports ============\nimport {Version0} from \"./Version0.sol\";\nimport {NomadBase} from \"./NomadBase.sol\";\nimport {QueueLib} from \"../libs/Queue.sol\";\nimport {MerkleLib} from \"../libs/Merkle.sol\";\nimport {Message} from \"../libs/Message.sol\";\nimport {MerkleTreeManager} from \"./Merkle.sol\";\nimport {QueueManager} from \"./Queue.sol\";\nimport {IUpdaterManager} from \"../interfaces/IUpdaterManager.sol\";\n// ============ External Imports ============\nimport {Address} from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Home\n * @author Illusory Systems Inc.\n * @notice Accepts messages to be dispatched to remote chains,\n * constructs a Merkle tree of the messages,\n * and accepts signatures from a bonded Updater\n * which notarize the Merkle tree roots.\n * Accepts submissions of fraudulent signatures\n * by the Updater and slashes the Updater in this case.\n */\ncontract Home is Version0, QueueManager, MerkleTreeManager, NomadBase {\n // ============ Libraries ============\n\n using QueueLib for QueueLib.Queue;\n using MerkleLib for MerkleLib.Tree;\n\n // ============ Constants ============\n\n // Maximum bytes per message = 2 KiB\n // (somewhat arbitrarily set to begin)\n uint256 public constant MAX_MESSAGE_BODY_BYTES = 2 * 2**10;\n\n // ============ Public Storage Variables ============\n\n // domain => next available nonce for the domain\n mapping(uint32 => uint32) public nonces;\n // contract responsible for Updater bonding, slashing and rotation\n IUpdaterManager public updaterManager;\n\n // ============ Upgrade Gap ============\n\n // gap for upgrade safety\n uint256[48] private __GAP;\n\n // ============ Events ============\n\n /**\n * @notice Emitted when a new message is dispatched via Nomad\n * @param leafIndex Index of message's leaf in merkle tree\n * @param destinationAndNonce Destination and destination-specific\n * nonce combined in single field ((destination << 32) & nonce)\n * @param messageHash Hash of message; the leaf inserted to the Merkle tree for the message\n * @param committedRoot the latest notarized root submitted in the last signed Update\n * @param message Raw bytes of message\n */\n event Dispatch(\n bytes32 indexed messageHash,\n uint256 indexed leafIndex,\n uint64 indexed destinationAndNonce,\n bytes32 committedRoot,\n bytes message\n );\n\n /**\n * @notice Emitted when proof of an improper update is submitted,\n * which sets the contract to FAILED state\n * @param oldRoot Old root of the improper update\n * @param newRoot New root of the improper update\n * @param signature Signature on `oldRoot` and `newRoot\n */\n event ImproperUpdate(bytes32 oldRoot, bytes32 newRoot, bytes signature);\n\n /**\n * @notice Emitted when the Updater is slashed\n * (should be paired with ImproperUpdater or DoubleUpdate event)\n * @param updater The address of the updater\n * @param reporter The address of the entity that reported the updater misbehavior\n */\n event UpdaterSlashed(address indexed updater, address indexed reporter);\n\n /**\n * @notice Emitted when the UpdaterManager contract is changed\n * @param updaterManager The address of the new updaterManager\n */\n event NewUpdaterManager(address updaterManager);\n\n // ============ Constructor ============\n\n constructor(uint32 _localDomain) NomadBase(_localDomain) {} // solhint-disable-line no-empty-blocks\n\n // ============ Initializer ============\n\n function initialize(IUpdaterManager _updaterManager) public initializer {\n // initialize queue, set Updater Manager, and initialize\n __QueueManager_initialize();\n _setUpdaterManager(_updaterManager);\n __NomadBase_initialize(updaterManager.updater());\n }\n\n // ============ Modifiers ============\n\n /**\n * @notice Ensures that function is called by the UpdaterManager contract\n */\n modifier onlyUpdaterManager() {\n require(msg.sender == address(updaterManager), \"!updaterManager\");\n _;\n }\n\n // ============ External: Updater & UpdaterManager Configuration ============\n\n /**\n * @notice Set a new Updater\n * @param _updater the new Updater\n */\n function setUpdater(address _updater) external onlyUpdaterManager {\n _setUpdater(_updater);\n }\n\n /**\n * @notice Set a new UpdaterManager contract\n * @dev Home(s) will initially be initialized using a trusted UpdaterManager contract;\n * we will progressively decentralize by swapping the trusted contract with a new implementation\n * that implements Updater bonding & slashing, and rules for Updater selection & rotation\n * @param _updaterManager the new UpdaterManager contract\n */\n function setUpdaterManager(address _updaterManager) external onlyOwner {\n _setUpdaterManager(IUpdaterManager(_updaterManager));\n }\n\n // ============ External Functions ============\n\n /**\n * @notice Dispatch the message it to the destination domain & recipient\n * @dev Format the message, insert its hash into Merkle tree,\n * enqueue the new Merkle root, and emit `Dispatch` event with message information.\n * @param _destinationDomain Domain of destination chain\n * @param _recipientAddress Address of recipient on destination chain as bytes32\n * @param _messageBody Raw bytes content of message\n */\n function dispatch(\n uint32 _destinationDomain,\n bytes32 _recipientAddress,\n bytes memory _messageBody\n ) external notFailed {\n require(_messageBody.length <= MAX_MESSAGE_BODY_BYTES, \"msg too long\");\n // get the next nonce for the destination domain, then increment it\n uint32 _nonce = nonces[_destinationDomain];\n nonces[_destinationDomain] = _nonce + 1;\n // format the message into packed bytes\n bytes memory _message = Message.formatMessage(\n localDomain,\n bytes32(uint256(uint160(msg.sender))),\n _nonce,\n _destinationDomain,\n _recipientAddress,\n _messageBody\n );\n // insert the hashed message into the Merkle tree\n bytes32 _messageHash = keccak256(_message);\n tree.insert(_messageHash);\n // enqueue the new Merkle root after inserting the message\n queue.enqueue(root());\n // Emit Dispatch event with message information\n // note: leafIndex is count() - 1 since new leaf has already been inserted\n emit Dispatch(_messageHash, count() - 1, _destinationAndNonce(_destinationDomain, _nonce), committedRoot, _message);\n }\n\n /**\n * @notice Submit a signature from the Updater \"notarizing\" a root,\n * which updates the Home contract's `committedRoot`,\n * and publishes the signature which will be relayed to Replica contracts\n * @dev emits Update event\n * @dev If _newRoot is not contained in the queue,\n * the Update is a fraudulent Improper Update, so\n * the Updater is slashed & Home is set to FAILED state\n * @param _committedRoot Current updated merkle root which the update is building off of\n * @param _newRoot New merkle root to update the contract state to\n * @param _signature Updater signature on `_committedRoot` and `_newRoot`\n */\n function update(\n bytes32 _committedRoot,\n bytes32 _newRoot,\n bytes memory _signature\n ) external notFailed {\n // check that the update is not fraudulent;\n // if fraud is detected, Updater is slashed & Home is set to FAILED state\n if (improperUpdate(_committedRoot, _newRoot, _signature)) return;\n // clear all of the intermediate roots contained in this update from the queue\n while (true) {\n bytes32 _next = queue.dequeue();\n if (_next == _newRoot) break;\n }\n // update the Home state with the latest signed root & emit event\n committedRoot = _newRoot;\n emit Update(localDomain, _committedRoot, _newRoot, _signature);\n }\n\n /**\n * @notice Suggest an update for the Updater to sign and submit.\n * @dev If queue is empty, null bytes returned for both\n * (No update is necessary because no messages have been dispatched since the last update)\n * @return _committedRoot Latest root signed by the Updater\n * @return _new Latest enqueued Merkle root\n */\n function suggestUpdate() external view returns (bytes32 _committedRoot, bytes32 _new) {\n if (queue.length() != 0) {\n _committedRoot = committedRoot;\n _new = queue.lastItem();\n }\n }\n\n // ============ Public Functions ============\n\n /**\n * @notice Hash of Home domain concatenated with \"NOMAD\"\n */\n function homeDomainHash() public view override returns (bytes32) {\n return _homeDomainHash(localDomain);\n }\n\n /**\n * @notice Check if an Update is an Improper Update;\n * if so, slash the Updater and set the contract to FAILED state.\n *\n * An Improper Update is an update building off of the Home's `committedRoot`\n * for which the `_newRoot` does not currently exist in the Home's queue.\n * This would mean that message(s) that were not truly\n * dispatched on Home were falsely included in the signed root.\n *\n * An Improper Update will only be accepted as valid by the Replica\n * If an Improper Update is attempted on Home,\n * the Updater will be slashed immediately.\n * If an Improper Update is submitted to the Replica,\n * it should be relayed to the Home contract using this function\n * in order to slash the Updater with an Improper Update.\n *\n * An Improper Update submitted to the Replica is only valid\n * while the `_oldRoot` is still equal to the `committedRoot` on Home;\n * if the `committedRoot` on Home has already been updated with a valid Update,\n * then the Updater should be slashed with a Double Update.\n * @dev Reverts (and doesn't slash updater) if signature is invalid or\n * update not current\n * @param _oldRoot Old merkle tree root (should equal home's committedRoot)\n * @param _newRoot New merkle tree root\n * @param _signature Updater signature on `_oldRoot` and `_newRoot`\n * @return TRUE if update was an Improper Update (implying Updater was slashed)\n */\n function improperUpdate(\n bytes32 _oldRoot,\n bytes32 _newRoot,\n bytes memory _signature\n ) public notFailed returns (bool) {\n require(_isUpdaterSignature(_oldRoot, _newRoot, _signature), \"!updater sig\");\n require(_oldRoot == committedRoot, \"not a current update\");\n // if the _newRoot is not currently contained in the queue,\n // slash the Updater and set the contract to FAILED state\n if (!queue.contains(_newRoot)) {\n _fail();\n emit ImproperUpdate(_oldRoot, _newRoot, _signature);\n return true;\n }\n // if the _newRoot is contained in the queue,\n // this is not an improper update\n return false;\n }\n\n // ============ Internal Functions ============\n\n /**\n * @notice Set the UpdaterManager\n * @param _updaterManager Address of the UpdaterManager\n */\n function _setUpdaterManager(IUpdaterManager _updaterManager) internal {\n require(Address.isContract(address(_updaterManager)), \"!contract updaterManager\");\n updaterManager = IUpdaterManager(_updaterManager);\n emit NewUpdaterManager(address(_updaterManager));\n }\n\n /**\n * @notice Slash the Updater and set contract state to FAILED\n * @dev Called when fraud is proven (Improper Update or Double Update)\n */\n function _fail() internal override {\n // set contract to FAILED\n _setFailed();\n // slash Updater\n updaterManager.slashUpdater(payable(msg.sender));\n emit UpdaterSlashed(updater, msg.sender);\n }\n\n /**\n * @notice Internal utility function that combines\n * `_destination` and `_nonce`.\n * @dev Both destination and nonce should be less than 2^32 - 1\n * @param _destination Domain of destination chain\n * @param _nonce Current nonce for given destination chain\n * @return Returns (`_destination` << 32) & `_nonce`\n */\n function _destinationAndNonce(uint32 _destination, uint32 _nonce) internal pure returns (uint64) {\n return (uint64(_destination) << 32) | _nonce;\n }\n}\n"
},
"@connext/nxtp-contracts/contracts/nomad-core/contracts/Version0.sol": {
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity >=0.6.11;\n\n/**\n * @title Version0\n * @notice Version getter for contracts\n **/\ncontract Version0 {\n uint8 public constant VERSION = 0;\n}\n"
},
"@connext/nxtp-contracts/contracts/nomad-core/contracts/NomadBase.sol": {
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity >=0.6.11;\n\n// ============ Internal Imports ============\nimport {Message} from \"../libs/Message.sol\";\n// ============ External Imports ============\nimport {ECDSA} from \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport {OwnableUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\n\n/**\n * @title NomadBase\n * @author Illusory Systems Inc.\n * @notice Shared utilities between Home and Replica.\n */\nabstract contract NomadBase is Initializable, OwnableUpgradeable {\n // ============ Enums ============\n\n // States:\n // 0 - UnInitialized - before initialize function is called\n // note: the contract is initialized at deploy time, so it should never be in this state\n // 1 - Active - as long as the contract has not become fraudulent\n // 2 - Failed - after a valid fraud proof has been submitted;\n // contract will no longer accept updates or new messages\n enum States {\n UnInitialized,\n Active,\n Failed\n }\n\n // ============ Immutable Variables ============\n\n // Domain of chain on which the contract is deployed\n uint32 public immutable localDomain;\n\n // ============ Public Variables ============\n\n // Address of bonded Updater\n address public updater;\n // Current state of contract\n States public state;\n // The latest root that has been signed by the Updater\n bytes32 public committedRoot;\n\n // ============ Upgrade Gap ============\n\n // gap for upgrade safety\n uint256[47] private __GAP;\n\n // ============ Events ============\n\n /**\n * @notice Emitted when update is made on Home\n * or unconfirmed update root is submitted on Replica\n * @param homeDomain Domain of home contract\n * @param oldRoot Old merkle root\n * @param newRoot New merkle root\n * @param signature Updater's signature on `oldRoot` and `newRoot`\n */\n event Update(uint32 indexed homeDomain, bytes32 indexed oldRoot, bytes32 indexed newRoot, bytes signature);\n\n /**\n * @notice Emitted when proof of a double update is submitted,\n * which sets the contract to FAILED state\n * @param oldRoot Old root shared between two conflicting updates\n * @param newRoot Array containing two conflicting new roots\n * @param signature Signature on `oldRoot` and `newRoot`[0]\n * @param signature2 Signature on `oldRoot` and `newRoot`[1]\n */\n event DoubleUpdate(bytes32 oldRoot, bytes32[2] newRoot, bytes signature, bytes signature2);\n\n /**\n * @notice Emitted when Updater is rotated\n * @param oldUpdater The address of the old updater\n * @param newUpdater The address of the new updater\n */\n event NewUpdater(address oldUpdater, address newUpdater);\n\n // ============ Modifiers ============\n\n /**\n * @notice Ensures that contract state != FAILED when the function is called\n */\n modifier notFailed() {\n require(state != States.Failed, \"failed state\");\n _;\n }\n\n // ============ Constructor ============\n\n constructor(uint32 _localDomain) {\n localDomain = _localDomain;\n }\n\n // ============ Initializer ============\n\n function __NomadBase_initialize(address _updater) internal initializer {\n __Ownable_init();\n _setUpdater(_updater);\n state = States.Active;\n }\n\n // ============ External Functions ============\n\n /**\n * @notice Called by external agent. Checks that signatures on two sets of\n * roots are valid and that the new roots conflict with each other. If both\n * cases hold true, the contract is failed and a `DoubleUpdate` event is\n * emitted.\n * @dev When `fail()` is called on Home, updater is slashed.\n * @param _oldRoot Old root shared between two conflicting updates\n * @param _newRoot Array containing two conflicting new roots\n * @param _signature Signature on `_oldRoot` and `_newRoot`[0]\n * @param _signature2 Signature on `_oldRoot` and `_newRoot`[1]\n */\n function doubleUpdate(\n bytes32 _oldRoot,\n bytes32[2] calldata _newRoot,\n bytes calldata _signature,\n bytes calldata _signature2\n ) external notFailed {\n if (\n NomadBase._isUpdaterSignature(_oldRoot, _newRoot[0], _signature) &&\n NomadBase._isUpdaterSignature(_oldRoot, _newRoot[1], _signature2) &&\n _newRoot[0] != _newRoot[1]\n ) {\n _fail();\n emit DoubleUpdate(_oldRoot, _newRoot, _signature, _signature2);\n }\n }\n\n // ============ Public Functions ============\n\n /**\n * @notice Hash of Home domain concatenated with \"NOMAD\"\n */\n function homeDomainHash() public view virtual returns (bytes32);\n\n // ============ Internal Functions ============\n\n /**\n * @notice Hash of Home domain concatenated with \"NOMAD\"\n * @param _homeDomain the Home domain to hash\n */\n function _homeDomainHash(uint32 _homeDomain) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(_homeDomain, \"NOMAD\"));\n }\n\n /**\n * @notice Set contract state to FAILED\n * @dev Called when a valid fraud proof is submitted\n */\n function _setFailed() internal {\n state = States.Failed;\n }\n\n /**\n * @notice Moves the contract into failed state\n * @dev Called when fraud is proven\n * (Double Update is submitted on Home or Replica,\n * or Improper Update is submitted on Home)\n */\n function _fail() internal virtual;\n\n /**\n * @notice Set the Updater\n * @param _newUpdater Address of the new Updater\n */\n function _setUpdater(address _newUpdater) internal {\n address _oldUpdater = updater;\n updater = _newUpdater;\n emit NewUpdater(_oldUpdater, _newUpdater);\n }\n\n /**\n * @notice Checks that signature was signed by Updater\n * @param _oldRoot Old merkle root\n * @param _newRoot New merkle root\n * @param _signature Signature on `_oldRoot` and `_newRoot`\n * @return TRUE iff signature is valid signed by updater\n **/\n function _isUpdaterSignature(\n bytes32 _oldRoot,\n bytes32 _newRoot,\n bytes memory _signature\n ) internal view returns (bool) {\n bytes32 _digest = keccak256(abi.encodePacked(homeDomainHash(), _oldRoot, _newRoot));\n _digest = ECDSA.toEthSignedMessageHash(_digest);\n return (ECDSA.recover(_digest, _signature) == updater);\n }\n}\n"
},
"@connext/nxtp-contracts/contracts/nomad-core/libs/Queue.sol": {
"content": "// SPDX-License-Identifier: MIT OR Apache-2.0\npragma solidity >=0.6.11;\n\n/**\n * @title QueueLib\n * @author Illusory Systems Inc.\n * @notice Library containing queue struct and operations for queue used by\n * Home and Replica.\n **/\nlibrary QueueLib {\n /**\n * @notice Queue struct\n * @dev Internally keeps track of the `first` and `last` elements through\n * indices and a mapping of indices to enqueued elements.\n **/\n struct Queue {\n uint128 first;\n uint128 last;\n mapping(uint256 => bytes32) queue;\n }\n\n /**\n * @notice Initializes the queue\n * @dev Empty state denoted by _q.first > q._last. Queue initialized\n * with _q.first = 1 and _q.last = 0.\n **/\n function initialize(Queue storage _q) internal {\n if (_q.first == 0) {\n _q.first = 1;\n }\n }\n\n /**\n * @notice Enqueues a single new element\n * @param _item New element to be enqueued\n * @return _last Index of newly enqueued element\n **/\n function enqueue(Queue storage _q, bytes32 _item) internal returns (uint128 _last) {\n _last = _q.last + 1;\n _q.last = _last;\n if (_item != bytes32(0)) {\n // saves gas if we're queueing 0\n _q.queue[_last] = _item;\n }\n }\n\n /**\n * @notice Dequeues element at front of queue\n * @dev Removes dequeued element from storage\n * @return _item Dequeued element\n **/\n function dequeue(Queue storage _q) internal returns (bytes32 _item) {\n uint128 _last = _q.last;\n uint128 _first = _q.first;\n require(_length(_last, _first) != 0, \"Empty\");\n _item = _q.queue[_first];\n if (_item != bytes32(0)) {\n // saves gas if we're dequeuing 0\n delete _q.queue[_first];\n }\n _q.first = _first + 1;\n }\n\n /**\n * @notice Batch enqueues several elements\n * @param _items Array of elements to be enqueued\n * @return _last Index of last enqueued element\n **/\n function enqueue(Queue storage _q, bytes32[] memory _items) internal returns (uint128 _last) {\n _last = _q.last;\n for (uint256 i = 0; i < _items.length; i += 1) {\n _last += 1;\n bytes32 _item = _items[i];\n if (_item != bytes32(0)) {\n _q.queue[_last] = _item;\n }\n }\n _q.last = _last;\n }\n\n /**\n * @notice Batch dequeues `_number` elements\n * @dev Reverts if `_number` > queue length\n * @param _number Number of elements to dequeue\n * @return Array of dequeued elements\n **/\n function dequeue(Queue storage _q, uint256 _number) internal returns (bytes32[] memory) {\n uint128 _last = _q.last;\n uint128 _first = _q.first;\n // Cannot underflow unless state is corrupted\n require(_length(_last, _first) >= _number, \"Insufficient\");\n\n bytes32[] memory _items = new bytes32[](_number);\n\n for (uint256 i = 0; i < _number; i++) {\n _items[i] = _q.queue[_first];\n delete _q.queue[_first];\n _first++;\n }\n _q.first = _first;\n return _items;\n }\n\n /**\n * @notice Returns true if `_item` is in the queue and false if otherwise\n * @dev Linearly scans from _q.first to _q.last looking for `_item`\n * @param _item Item being searched for in queue\n * @return True if `_item` currently exists in queue, false if otherwise\n **/\n function contains(Queue storage _q, bytes32 _item) internal view returns (bool) {\n for (uint256 i = _q.first; i <= _q.last; i++) {\n if (_q.queue[i] == _item) {\n return true;\n }\n }\n return false;\n }\n\n /// @notice Returns last item in queue\n /// @dev Returns bytes32(0) if queue empty\n function lastItem(Queue storage _q) internal view returns (bytes32) {\n return _q.queue[_q.last];\n }\n\n /// @notice Returns element at front of queue without removing element\n /// @dev Reverts if queue is empty\n function peek(Queue storage _q) internal view returns (bytes32 _item) {\n require(!isEmpty(_q), \"Empty\");\n _item = _q.queue[_q.first];\n }\n\n /// @notice Returns true if queue is empty and false if otherwise\n function isEmpty(Queue storage _q) internal view returns (bool) {\n return _q.last < _q.first;\n }\n\n /// @notice Returns number of elements in queue\n function length(Queue storage _q) internal view returns (uint256) {\n uint128 _last = _q.last;\n uint128 _first = _q.first;\n // Cannot underflow unless state is corrupted\n return _length(_last, _first);\n }\n\n /// @notice Returns number of elements between `_last` and `_first` (used internally)\n function _length(uint12