@hyperlane-xyz/core
Version:
Core solidity contracts for Hyperlane
66 lines • 15.3 kB
JSON
{
"language": "Solidity",
"sources": {
"contracts/chugsplash/interfaces/iL1ChugSplashDeployer.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\n/**\n * @title iL1ChugSplashDeployer\n */\ninterface iL1ChugSplashDeployer {\n function isUpgrading() external view returns (bool);\n}\n"
},
"contracts/L1/deployment/ChugSplashDictator.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\nimport { L1ChugSplashProxy } from \"../../chugsplash/L1ChugSplashProxy.sol\";\nimport { iL1ChugSplashDeployer } from \"../../chugsplash/interfaces/iL1ChugSplashDeployer.sol\";\n\n/**\n * @title ChugSplashDictator\n * @dev Like the AddressDictator, but specifically for the Proxy__OVM_L1StandardBridge. We're\n * working on a generalized version of this but this is good enough for the moment.\n */\ncontract ChugSplashDictator is iL1ChugSplashDeployer {\n /*************\n * Variables *\n *************/\n\n // slither-disable-next-line constable-states\n bool public isUpgrading = true;\n L1ChugSplashProxy public target;\n address public finalOwner;\n bytes32 public codeHash;\n bytes32 public messengerSlotKey;\n bytes32 public messengerSlotVal;\n bytes32 public bridgeSlotKey;\n bytes32 public bridgeSlotVal;\n\n /***************\n * Constructor *\n ***************/\n\n constructor(\n L1ChugSplashProxy _target,\n address _finalOwner,\n bytes32 _codeHash,\n bytes32 _messengerSlotKey,\n bytes32 _messengerSlotVal,\n bytes32 _bridgeSlotKey,\n bytes32 _bridgeSlotVal\n ) {\n target = _target;\n finalOwner = _finalOwner;\n codeHash = _codeHash;\n messengerSlotKey = _messengerSlotKey;\n messengerSlotVal = _messengerSlotVal;\n bridgeSlotKey = _bridgeSlotKey;\n bridgeSlotVal = _bridgeSlotVal;\n }\n\n /********************\n * Public Functions *\n ********************/\n\n function doActions(bytes memory _code) external {\n require(keccak256(_code) == codeHash, \"ChugSplashDictator: Incorrect code hash.\");\n\n target.setCode(_code);\n target.setStorage(messengerSlotKey, messengerSlotVal);\n target.setStorage(bridgeSlotKey, bridgeSlotVal);\n target.setOwner(finalOwner);\n }\n\n /**\n * Transfers ownership of this contract to the finalOwner.\n * Only callable by the finalOwner, which is intended to be our multisig.\n * This function shouldn't be necessary, but it gives a sense of reassurance that we can\n * recover if something really surprising goes wrong.\n */\n function returnOwnership() external {\n require(msg.sender == finalOwner, \"ChugSplashDictator: only callable by finalOwner\");\n\n target.setOwner(finalOwner);\n }\n}\n"
},
"contracts/chugsplash/L1ChugSplashProxy.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\nimport { iL1ChugSplashDeployer } from \"./interfaces/iL1ChugSplashDeployer.sol\";\n\n/**\n * @title L1ChugSplashProxy\n * @dev Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added\n * functions `setCode` and `setStorage` for changing the code or storage of the contract. Nifty!\n *\n * Note for future developers: do NOT make anything in this contract 'public' unless you know what\n * you're doing. Anything public can potentially have a function signature that conflicts with a\n * signature attached to the implementation contract. Public functions SHOULD always have the\n * 'proxyCallIfNotOwner' modifier unless there's some *really* good reason not to have that\n * modifier. And there almost certainly is not a good reason to not have that modifier. Beware!\n */\ncontract L1ChugSplashProxy {\n /*************\n * Constants *\n *************/\n\n // \"Magic\" prefix. When prepended to some arbitrary bytecode and used to create a contract, the\n // appended bytecode will be deployed as given.\n bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3;\n\n // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)\n bytes32 internal constant IMPLEMENTATION_KEY =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n // bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)\n bytes32 internal constant OWNER_KEY =\n 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /***************\n * Constructor *\n ***************/\n\n /**\n * @param _owner Address of the initial contract owner.\n */\n constructor(address _owner) {\n _setOwner(_owner);\n }\n\n /**********************\n * Function Modifiers *\n **********************/\n\n /**\n * Blocks a function from being called when the parent signals that the system should be paused\n * via an isUpgrading function.\n */\n modifier onlyWhenNotPaused() {\n address owner = _getOwner();\n\n // We do a low-level call because there's no guarantee that the owner actually *is* an\n // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and\n // it turns out that it isn't the right type of contract.\n (bool success, bytes memory returndata) = owner.staticcall(\n abi.encodeWithSelector(iL1ChugSplashDeployer.isUpgrading.selector)\n );\n\n // If the call was unsuccessful then we assume that there's no \"isUpgrading\" method and we\n // can just continue as normal. We also expect that the return value is exactly 32 bytes\n // long. If this isn't the case then we can safely ignore the result.\n if (success && returndata.length == 32) {\n // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the\n // case that the isUpgrading function returned something other than 0 or 1. But we only\n // really care about the case where this value is 0 (= false).\n uint256 ret = abi.decode(returndata, (uint256));\n require(ret == 0, \"L1ChugSplashProxy: system is currently being upgraded\");\n }\n\n _;\n }\n\n /**\n * Makes a proxy call instead of triggering the given function when the caller is either the\n * owner or the zero address. Caller can only ever be the zero address if this function is\n * being called off-chain via eth_call, which is totally fine and can be convenient for\n * client-side tooling. Avoids situations where the proxy and implementation share a sighash\n * and the proxy function ends up being called instead of the implementation one.\n *\n * Note: msg.sender == address(0) can ONLY be triggered off-chain via eth_call. If there's a\n * way for someone to send a transaction with msg.sender == address(0) in any real context then\n * we have much bigger problems. Primary reason to include this additional allowed sender is\n * because the owner address can be changed dynamically and we do not want clients to have to\n * keep track of the current owner in order to make an eth_call that doesn't trigger the\n * proxied contract.\n */\n // slither-disable-next-line incorrect-modifier\n modifier proxyCallIfNotOwner() {\n if (msg.sender == _getOwner() || msg.sender == address(0)) {\n _;\n } else {\n // This WILL halt the call frame on completion.\n _doProxyCall();\n }\n }\n\n /*********************\n * Fallback Function *\n *********************/\n\n // slither-disable-next-line locked-ether\n fallback() external payable {\n // Proxy call by default.\n _doProxyCall();\n }\n\n /********************\n * Public Functions *\n ********************/\n\n /**\n * Sets the code that should be running behind this proxy. Note that this scheme is a bit\n * different from the standard proxy scheme where one would typically deploy the code\n * separately and then set the implementation address. We're doing it this way because it gives\n * us a lot more freedom on the client side. Can only be triggered by the contract owner.\n * @param _code New contract code to run inside this contract.\n */\n // slither-disable-next-line external-function\n function setCode(bytes memory _code) public proxyCallIfNotOwner {\n // Get the code hash of the current implementation.\n address implementation = _getImplementation();\n\n // If the code hash matches the new implementation then we return early.\n if (keccak256(_code) == _getAccountCodeHash(implementation)) {\n return;\n }\n\n // Create the deploycode by appending the magic prefix.\n bytes memory deploycode = abi.encodePacked(DEPLOY_CODE_PREFIX, _code);\n\n // Deploy the code and set the new implementation address.\n address newImplementation;\n assembly {\n newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode))\n }\n\n // Check that the code was actually deployed correctly. I'm not sure if you can ever\n // actually fail this check. Should only happen if the contract creation from above runs\n // out of gas but this parent execution thread does NOT run out of gas. Seems like we\n // should be doing this check anyway though.\n require(\n _getAccountCodeHash(newImplementation) == keccak256(_code),\n \"L1ChugSplashProxy: code was not correctly deployed.\"\n );\n\n _setImplementation(newImplementation);\n }\n\n /**\n * Modifies some storage slot within the proxy contract. Gives us a lot of power to perform\n * upgrades in a more transparent way. Only callable by the owner.\n * @param _key Storage key to modify.\n * @param _value New value for the storage key.\n */\n // slither-disable-next-line external-function\n function setStorage(bytes32 _key, bytes32 _value) public proxyCallIfNotOwner {\n assembly {\n sstore(_key, _value)\n }\n }\n\n /**\n * Changes the owner of the proxy contract. Only callable by the owner.\n * @param _owner New owner of the proxy contract.\n */\n // slither-disable-next-line external-function\n function setOwner(address _owner) public proxyCallIfNotOwner {\n _setOwner(_owner);\n }\n\n /**\n * Queries the owner of the proxy contract. Can only be called by the owner OR by making an\n * eth_call and setting the \"from\" address to address(0).\n * @return Owner address.\n */\n // slither-disable-next-line external-function\n function getOwner() public proxyCallIfNotOwner returns (address) {\n return _getOwner();\n }\n\n /**\n * Queries the implementation address. Can only be called by the owner OR by making an\n * eth_call and setting the \"from\" address to address(0).\n * @return Implementation address.\n */\n // slither-disable-next-line external-function\n function getImplementation() public proxyCallIfNotOwner returns (address) {\n return _getImplementation();\n }\n\n /**********************\n * Internal Functions *\n **********************/\n\n /**\n * Sets the implementation address.\n * @param _implementation New implementation address.\n */\n function _setImplementation(address _implementation) internal {\n assembly {\n sstore(IMPLEMENTATION_KEY, _implementation)\n }\n }\n\n /**\n * Queries the implementation address.\n * @return Implementation address.\n */\n function _getImplementation() internal view returns (address) {\n address implementation;\n assembly {\n implementation := sload(IMPLEMENTATION_KEY)\n }\n return implementation;\n }\n\n /**\n * Changes the owner of the proxy contract.\n * @param _owner New owner of the proxy contract.\n */\n function _setOwner(address _owner) internal {\n assembly {\n sstore(OWNER_KEY, _owner)\n }\n }\n\n /**\n * Queries the owner of the proxy contract.\n * @return Owner address.\n */\n function _getOwner() internal view returns (address) {\n address owner;\n assembly {\n owner := sload(OWNER_KEY)\n }\n return owner;\n }\n\n /**\n * Gets the code hash for a given account.\n * @param _account Address of the account to get a code hash for.\n * @return Code hash for the account.\n */\n function _getAccountCodeHash(address _account) internal view returns (bytes32) {\n bytes32 codeHash;\n assembly {\n codeHash := extcodehash(_account)\n }\n return codeHash;\n }\n\n /**\n * Performs the proxy call via a delegatecall.\n */\n function _doProxyCall() internal onlyWhenNotPaused {\n address implementation = _getImplementation();\n\n require(implementation != address(0), \"L1ChugSplashProxy: implementation is not set yet\");\n\n assembly {\n // Copy calldata into memory at 0x0....calldatasize.\n calldatacopy(0x0, 0x0, calldatasize())\n\n // Perform the delegatecall, make sure to pass all available gas.\n let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0)\n\n // Copy returndata into memory at 0x0....returndatasize. Note that this *will*\n // overwrite the calldata that we just copied into memory but that doesn't really\n // matter because we'll be returning in a second anyway.\n returndatacopy(0x0, 0x0, returndatasize())\n\n // Success == 0 means a revert. We'll revert too and pass the data up.\n if iszero(success) {\n revert(0x0, returndatasize())\n }\n\n // Otherwise we'll just return and pass the data up.\n return(0x0, returndatasize())\n }\n }\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 10000
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.gasEstimates",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc",
"devdoc",
"userdoc"
],
"": [
"ast"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}
}