Commit 1b84f7d1 authored by Maurelian's avatar Maurelian Committed by Mark Tyneway

contracts-bedrock: bridge pausability

Enable extended pause functionality in the standard bridge.
Includes updates to tests and the deploy script.
parent 8870995c
...@@ -39,7 +39,7 @@ library ChainAssertions { ...@@ -39,7 +39,7 @@ library ChainAssertions {
checkSystemConfig({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); checkSystemConfig({ _contracts: _prox, _cfg: _cfg, _isProxy: true });
checkL1CrossDomainMessenger({ _contracts: _prox, _vm: _vm, _isProxy: true }); checkL1CrossDomainMessenger({ _contracts: _prox, _vm: _vm, _isProxy: true });
checkL1StandardBridge(_prox); checkL1StandardBridge({ _contracts: _prox, _isProxy: true });
checkL2OutputOracle(_prox, _cfg, _l2OutputOracleStartingTimestamp, _l2OutputOracleStartingBlockNumber); checkL2OutputOracle(_prox, _cfg, _l2OutputOracleStartingTimestamp, _l2OutputOracleStartingBlockNumber);
checkOptimismMintableERC20Factory(_prox); checkOptimismMintableERC20Factory(_prox);
checkL1ERC721Bridge(_prox); checkL1ERC721Bridge(_prox);
...@@ -91,12 +91,17 @@ library ChainAssertions { ...@@ -91,12 +91,17 @@ library ChainAssertions {
} }
/// @notice Asserts that the L1StandardBridge is setup correctly /// @notice Asserts that the L1StandardBridge is setup correctly
function checkL1StandardBridge(Types.ContractSet memory _contracts) internal view { function checkL1StandardBridge(Types.ContractSet memory _contracts, bool _isProxy) internal view {
L1StandardBridge bridge = L1StandardBridge(payable(_contracts.L1StandardBridge)); L1StandardBridge bridge = L1StandardBridge(payable(_contracts.L1StandardBridge));
require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger); require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger);
require(address(bridge.messenger()) == _contracts.L1CrossDomainMessenger); require(address(bridge.messenger()) == _contracts.L1CrossDomainMessenger);
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE); require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE);
if (_isProxy) {
require(address(bridge.superchainConfig()) == _contracts.SuperchainConfig);
} else {
require(address(bridge.superchainConfig()) == address(0));
}
} }
/// @notice Asserts that the L2OutputOracle is setup correctly /// @notice Asserts that the L2OutputOracle is setup correctly
......
...@@ -671,7 +671,7 @@ contract Deploy is Deployer { ...@@ -671,7 +671,7 @@ contract Deploy is Deployer {
// are always proxies. // are always proxies.
Types.ContractSet memory contracts = _proxiesUnstrict(); Types.ContractSet memory contracts = _proxiesUnstrict();
contracts.L1StandardBridge = address(bridge); contracts.L1StandardBridge = address(bridge);
ChainAssertions.checkL1StandardBridge(contracts); ChainAssertions.checkL1StandardBridge({ _contracts: contracts, _isProxy: false });
addr_ = address(bridge); addr_ = address(bridge);
} }
...@@ -785,6 +785,7 @@ contract Deploy is Deployer { ...@@ -785,6 +785,7 @@ contract Deploy is Deployer {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin")); ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address l1StandardBridgeProxy = mustGetAddress("L1StandardBridgeProxy"); address l1StandardBridgeProxy = mustGetAddress("L1StandardBridgeProxy");
address l1StandardBridge = mustGetAddress("L1StandardBridge"); address l1StandardBridge = mustGetAddress("L1StandardBridge");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
uint256 proxyType = uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)); uint256 proxyType = uint256(proxyAdmin.proxyType(l1StandardBridgeProxy));
if (proxyType != uint256(ProxyAdmin.ProxyType.CHUGSPLASH)) { if (proxyType != uint256(ProxyAdmin.ProxyType.CHUGSPLASH)) {
...@@ -795,15 +796,16 @@ contract Deploy is Deployer { ...@@ -795,15 +796,16 @@ contract Deploy is Deployer {
} }
require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH));
_callViaSafe({ _upgradeAndCallViaSafe({
_target: address(proxyAdmin), _proxy: payable(l1StandardBridgeProxy),
_data: abi.encodeCall(ProxyAdmin.upgrade, (payable(l1StandardBridgeProxy), l1StandardBridge)) _implementation: l1StandardBridge,
_innerCallData: abi.encodeCall(L1StandardBridge.initialize, (SuperchainConfig(superchainConfigProxy)))
}); });
string memory version = L1StandardBridge(payable(l1StandardBridgeProxy)).version(); string memory version = L1StandardBridge(payable(l1StandardBridgeProxy)).version();
console.log("L1StandardBridge version: %s", version); console.log("L1StandardBridge version: %s", version);
ChainAssertions.checkL1StandardBridge(_proxies()); ChainAssertions.checkL1StandardBridge({ _contracts: _proxies(), _isProxy: true });
} }
/// @notice Initialize the L1ERC721Bridge /// @notice Initialize the L1ERC721Bridge
......
...@@ -5,6 +5,7 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; ...@@ -5,6 +5,7 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
import { StandardBridge } from "src/universal/StandardBridge.sol"; import { StandardBridge } from "src/universal/StandardBridge.sol";
import { ISemver } from "src/universal/ISemver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { Constants } from "src/libraries/Constants.sol"; import { Constants } from "src/libraries/Constants.sol";
/// @custom:proxied /// @custom:proxied
...@@ -69,12 +70,28 @@ contract L1StandardBridge is StandardBridge, ISemver { ...@@ -69,12 +70,28 @@ contract L1StandardBridge is StandardBridge, ISemver {
); );
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.5.0 /// @custom:semver 2.0.0
string public constant version = "1.5.0"; string public constant version = "2.0.0";
/// @notice Address of the SuperchainConfig contract.
SuperchainConfig public superchainConfig;
/// @notice Constructs the L1StandardBridge contract. /// @notice Constructs the L1StandardBridge contract.
/// @param _messenger Address of the L1CrossDomainMessenger. /// @param _messenger Address of the L1CrossDomainMessenger.
constructor(address payable _messenger) StandardBridge(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE)) { } constructor(address payable _messenger) StandardBridge(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE)) {
initialize({ _superchainConfig: SuperchainConfig(address(0)) });
}
/// @notice Initializes the contract.
/// @param _superchainConfig Address of the SuperchainConfig contract on this network.
function initialize(SuperchainConfig _superchainConfig) public initializer {
superchainConfig = _superchainConfig;
}
/// @inheritdoc StandardBridge
function paused() public view override returns (bool) {
return superchainConfig.paused();
}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge. /// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
receive() external payable override onlyEOA { receive() external payable override onlyEOA {
......
...@@ -52,8 +52,8 @@ contract L2StandardBridge is StandardBridge, ISemver { ...@@ -52,8 +52,8 @@ contract L2StandardBridge is StandardBridge, ISemver {
bytes extraData bytes extraData
); );
/// @custom:semver 1.5.0 /// @custom:semver 1.6.0
string public constant version = "1.5.0"; string public constant version = "1.6.0";
/// @notice Constructs the L2StandardBridge contract. /// @notice Constructs the L2StandardBridge contract.
/// @param _otherBridge Address of the L1StandardBridge. /// @param _otherBridge Address of the L1StandardBridge.
......
...@@ -16,7 +16,7 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable ...@@ -16,7 +16,7 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable
/// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles /// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles
/// the core bridging logic, including escrowing tokens that are native to the local chain /// the core bridging logic, including escrowing tokens that are native to the local chain
/// and minting/burning tokens that are native to the remote chain. /// and minting/burning tokens that are native to the remote chain.
abstract contract StandardBridge { abstract contract StandardBridge is Initializable {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
/// @notice The L2 gas limit set when eth is depoisited using the receive() function. /// @notice The L2 gas limit set when eth is depoisited using the receive() function.
...@@ -31,7 +31,7 @@ abstract contract StandardBridge { ...@@ -31,7 +31,7 @@ abstract contract StandardBridge {
/// @custom:legacy /// @custom:legacy
/// @custom:spacer messenger /// @custom:spacer messenger
/// @notice Spacer for backwards compatibility. /// @notice Spacer for backwards compatibility.
address private spacer_0_0_20; address private spacer_0_2_20;
/// @custom:legacy /// @custom:legacy
/// @custom:spacer l2TokenBridge /// @custom:spacer l2TokenBridge
...@@ -132,6 +132,14 @@ abstract contract StandardBridge { ...@@ -132,6 +132,14 @@ abstract contract StandardBridge {
return OTHER_BRIDGE; return OTHER_BRIDGE;
} }
/// @notice This function should return true if the contract is paused.
/// On L1 this function will check the SuperchainConfig for its paused status.
/// On L2 this function should be a no-op.
/// @return Whether or not the contract is paused.
function paused() public view virtual returns (bool) {
return false;
}
/// @notice Sends ETH to the sender's address on the other chain. /// @notice Sends ETH to the sender's address on the other chain.
/// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
/// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will
......
...@@ -18,14 +18,9 @@ contract ExtendedPause_Test is CommonTest { ...@@ -18,14 +18,9 @@ contract ExtendedPause_Test is CommonTest {
assertTrue(superchainConfig.paused()); assertTrue(superchainConfig.paused());
assertEq(l1CrossDomainMessenger.paused(), superchainConfig.paused()); assertEq(l1CrossDomainMessenger.paused(), superchainConfig.paused());
// The following is hacky approach which ensures that this test will fail if the paused() function is assertTrue(l1StandardBridge.paused());
// added to the L1StandardBridge or the L1ERC721Bridge. At that point this test should be updated to include assertEq(l1StandardBridge.paused(), superchainConfig.paused());
// those methods.
try SuperchainConfig(address(l1StandardBridge)).paused() {
revert("The L1StandardBridge has a paused() function, but is not tested as part of the ExtendedPause");
} catch (bytes memory) {
assertTrue(true);
}
try SuperchainConfig(address(l1ERC721Bridge)).paused() { try SuperchainConfig(address(l1ERC721Bridge)).paused() {
revert("The L1ERC721Bridge has a paused() function, but is not tested as part of the ExtendedPause"); revert("The L1ERC721Bridge has a paused() function, but is not tested as part of the ExtendedPause");
} catch (bytes memory) { } catch (bytes memory) {
......
...@@ -103,6 +103,14 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -103,6 +103,14 @@ contract Initializer_Test is Bridge_Initializer {
initializedSlotVal: deploy.loadInitializedSlot("ProtocolVersions", true) initializedSlotVal: deploy.loadInitializedSlot("ProtocolVersions", true)
}) })
); );
// L1StandardBridge
contracts.push(
InitializeableContract({
target: address(l1StandardBridge),
initCalldata: abi.encodeCall(l1StandardBridge.initialize, (superchainConfig)),
initializedSlotVal: deploy.loadInitializedSlot("L1StandardBridge", true)
})
);
// L2CrossDomainMessenger // L2CrossDomainMessenger
contracts.push( contracts.push(
InitializeableContract({ InitializeableContract({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment