Commit 83b3f43b authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #8353 from ethereum-optimism/ctb/bridge-pausable

contracts-bedrock: StandardBridge pausability
parents 8870995c 55fed2b2
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/solc" "github.com/ethereum-optimism/optimism/op-bindings/solc"
) )
const StandardBridgeStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_0_0_20\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_address\"},{\"astId\":1001,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_1_0_20\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_address\"},{\"astId\":1002,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"deposits\",\"offset\":0,\"slot\":\"2\",\"type\":\"t_mapping(t_address,t_mapping(t_address,t_uint256))\"},{\"astId\":1003,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"3\",\"type\":\"t_array(t_uint256)47_storage\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)47_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[47]\",\"numberOfBytes\":\"1504\",\"base\":\"t_uint256\"},\"t_mapping(t_address,t_mapping(t_address,t_uint256))\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e mapping(address =\u003e uint256))\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_mapping(t_address,t_uint256)\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"}}}" const StandardBridgeStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_0_2_20\",\"offset\":2,\"slot\":\"0\",\"type\":\"t_address\"},{\"astId\":1003,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"spacer_1_0_20\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"deposits\",\"offset\":0,\"slot\":\"2\",\"type\":\"t_mapping(t_address,t_mapping(t_address,t_uint256))\"},{\"astId\":1005,\"contract\":\"src/universal/StandardBridge.sol:StandardBridge\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"3\",\"type\":\"t_array(t_uint256)47_storage\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)47_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[47]\",\"numberOfBytes\":\"1504\",\"base\":\"t_uint256\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_mapping(t_address,t_mapping(t_address,t_uint256))\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e mapping(address =\u003e uint256))\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_mapping(t_address,t_uint256)\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}"
var StandardBridgeStorageLayout = new(solc.StorageLayout) var StandardBridgeStorageLayout = new(solc.StorageLayout)
......
...@@ -828,7 +828,10 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage ...@@ -828,7 +828,10 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
"xDomainMsgSender": "0x000000000000000000000000000000000000dEaD", "xDomainMsgSender": "0x000000000000000000000000000000000000dEaD",
"msgNonce": 0, "msgNonce": 0,
} }
storage["L2StandardBridge"] = state.StorageValues{} storage["L2StandardBridge"] = state.StorageValues{
"_initialized": 1,
"_initializing": false,
}
storage["L1Block"] = state.StorageValues{ storage["L1Block"] = state.StorageValues{
"number": block.Number(), "number": block.Number(),
"timestamp": block.Time(), "timestamp": block.Time(),
......
...@@ -2,9 +2,9 @@ GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352278) ...@@ -2,9 +2,9 @@ GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352278)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950440) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2950440)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 540654) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 540654)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4052847) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4052847)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 441959) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 442004)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3487708) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3487753)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 42970) GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 55446)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 86629) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 86629)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68462) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68462)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68911) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68911)
......
...@@ -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
......
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
"sourceCodeHash": "0x76ab62d44cf4efdc1db7042e75738b6d187682b0bdfb4160f5d0dc09eab55b4f" "sourceCodeHash": "0x76ab62d44cf4efdc1db7042e75738b6d187682b0bdfb4160f5d0dc09eab55b4f"
}, },
"src/L1/L1StandardBridge.sol": { "src/L1/L1StandardBridge.sol": {
"initCodeHash": "0xf925378da0e620f9ebcf8a2b6ee1989a2a527d933903398d7da53a88a550dcd6", "initCodeHash": "0x56a7fbc6807e1c4fa818a9706a12545f86499ca4269b0dc84c328ec632933eaf",
"sourceCodeHash": "0x540dbe1d8961c7b1eece7eb4bd29631b5a743b1ea9bad6983a6c6b20572bd9d7" "sourceCodeHash": "0x113420a17423d39cebb9b756fbf698ca9bdc03054ae24fe680750d979905d5a3"
}, },
"src/L1/L2OutputOracle.sol": { "src/L1/L2OutputOracle.sol": {
"initCodeHash": "0x2c79bc6708742cdf21ca8d134227dab60fe908028aee2255192950f531fa548e", "initCodeHash": "0x2c79bc6708742cdf21ca8d134227dab60fe908028aee2255192950f531fa548e",
...@@ -68,8 +68,8 @@ ...@@ -68,8 +68,8 @@
"sourceCodeHash": "0x448b01aa8e9e79782766134290d54a3557135c9d39357bfbbf1d2672687518ac" "sourceCodeHash": "0x448b01aa8e9e79782766134290d54a3557135c9d39357bfbbf1d2672687518ac"
}, },
"src/L2/L2StandardBridge.sol": { "src/L2/L2StandardBridge.sol": {
"initCodeHash": "0x6dbd834196fc887100a05aa9b730bf2a29982d6c793ae4d770d96f7c15699c17", "initCodeHash": "0x2d6b3e6fee86b9edbfe56bc4e201d1fe0cd70db424121f0059772cd9a46cdb16",
"sourceCodeHash": "0x26f070902aa1b8b421060aae222ae251515f794833c458a4ed19ef0a4472003b" "sourceCodeHash": "0xe6f70ea8495447be7aaa77c27ce9aed9c36e63c038332f3abb1550e40cb9baf9"
}, },
"src/L2/L2ToL1MessagePasser.sol": { "src/L2/L2ToL1MessagePasser.sol": {
"initCodeHash": "0x08bbede75cd6dfd076903b8f04d24f82fa7881576c135825098778632e37eebc", "initCodeHash": "0x08bbede75cd6dfd076903b8f04d24f82fa7881576c135825098778632e37eebc",
......
...@@ -306,6 +306,19 @@ ...@@ -306,6 +306,19 @@
"name": "ETHWithdrawalFinalized", "name": "ETHWithdrawalFinalized",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "version",
"type": "uint8"
}
],
"name": "Initialized",
"type": "event"
},
{ {
"inputs": [], "inputs": [],
"name": "MESSENGER", "name": "MESSENGER",
...@@ -712,6 +725,19 @@ ...@@ -712,6 +725,19 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract SuperchainConfig",
"name": "_superchainConfig",
"type": "address"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "l2TokenBridge", "name": "l2TokenBridge",
...@@ -751,6 +777,32 @@ ...@@ -751,6 +777,32 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "superchainConfig",
"outputs": [
{
"internalType": "contract SuperchainConfig",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "version", "name": "version",
......
...@@ -201,6 +201,19 @@ ...@@ -201,6 +201,19 @@
"name": "ETHBridgeInitiated", "name": "ETHBridgeInitiated",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "version",
"type": "uint8"
}
],
"name": "Initialized",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
...@@ -549,6 +562,19 @@ ...@@ -549,6 +562,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "version", "name": "version",
......
[ [
{ {
"bytes": "20", "bytes": "1",
"label": "spacer_0_0_20", "label": "_initialized",
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "20",
"label": "spacer_0_2_20",
"offset": 2,
"slot": "0",
"type": "address" "type": "address"
}, },
{ {
...@@ -26,5 +40,12 @@ ...@@ -26,5 +40,12 @@
"offset": 0, "offset": 0,
"slot": "3", "slot": "3",
"type": "uint256[47]" "type": "uint256[47]"
},
{
"bytes": "20",
"label": "superchainConfig",
"offset": 0,
"slot": "50",
"type": "contract SuperchainConfig"
} }
] ]
\ No newline at end of file
[ [
{ {
"bytes": "20", "bytes": "1",
"label": "spacer_0_0_20", "label": "_initialized",
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "20",
"label": "spacer_0_2_20",
"offset": 2,
"slot": "0",
"type": "address" "type": "address"
}, },
{ {
......
...@@ -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
...@@ -226,6 +234,7 @@ abstract contract StandardBridge { ...@@ -226,6 +234,7 @@ abstract contract StandardBridge {
payable payable
onlyOtherBridge onlyOtherBridge
{ {
require(paused() == false, "StandardBridge: paused");
require(msg.value == _amount, "StandardBridge: amount sent does not match amount required"); require(msg.value == _amount, "StandardBridge: amount sent does not match amount required");
require(_to != address(this), "StandardBridge: cannot send to self"); require(_to != address(this), "StandardBridge: cannot send to self");
require(_to != address(MESSENGER), "StandardBridge: cannot send to messenger"); require(_to != address(MESSENGER), "StandardBridge: cannot send to messenger");
...@@ -259,6 +268,7 @@ abstract contract StandardBridge { ...@@ -259,6 +268,7 @@ abstract contract StandardBridge {
public public
onlyOtherBridge onlyOtherBridge
{ {
require(paused() == false, "StandardBridge: paused");
if (_isOptimismMintableERC20(_localToken)) { if (_isOptimismMintableERC20(_localToken)) {
require( require(
_isCorrectTokenPair(_localToken, _remoteToken), _isCorrectTokenPair(_localToken, _remoteToken),
......
...@@ -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) {
......
...@@ -14,6 +14,7 @@ import { Constants } from "src/libraries/Constants.sol"; ...@@ -14,6 +14,7 @@ import { Constants } from "src/libraries/Constants.sol";
import { StandardBridge } from "src/universal/StandardBridge.sol"; import { StandardBridge } from "src/universal/StandardBridge.sol";
import { L2StandardBridge } from "src/L2/L2StandardBridge.sol"; import { L2StandardBridge } from "src/L2/L2StandardBridge.sol";
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
// Target contract // Target contract
...@@ -38,6 +39,105 @@ contract L1StandardBridge_Initialize_Test is Bridge_Initializer { ...@@ -38,6 +39,105 @@ contract L1StandardBridge_Initialize_Test is Bridge_Initializer {
} }
} }
contract L1StandardBridge_Pause_Test is Bridge_Initializer {
/// @dev Verifies that the `paused` accessor returns the same value as the `paused` function of the
/// `superchainConfig`.
function test_paused_succeeds() external {
assertEq(l1StandardBridge.paused(), superchainConfig.paused());
}
/// @dev Ensures that the `paused` function of the bridge contract actually calls the `paused` function of the
/// `superchainConfig`.
function test_pause_callsSuperchainConfig_succeeds() external {
vm.expectCall(address(superchainConfig), abi.encodeWithSelector(SuperchainConfig.paused.selector));
l1StandardBridge.paused();
}
/// @dev Checks that the `paused` state of the bridge matches the `paused` state of the `superchainConfig` after
/// it's been changed.
function test_pause_matchesSuperchainConfig_succeeds() external {
assertFalse(l1StandardBridge.paused());
assertEq(l1StandardBridge.paused(), superchainConfig.paused());
vm.prank(superchainConfig.guardian());
superchainConfig.pause("identifier");
assertTrue(l1StandardBridge.paused());
assertEq(l1StandardBridge.paused(), superchainConfig.paused());
}
}
contract L1StandardBridge_Pause_TestFail is Bridge_Initializer {
/// @dev Sets up the test by pausing the bridge, giving ether to the bridge and mocking
/// the calls to the xDomainMessageSender so that it returns the correct value.
function setUp() public override {
super.setUp();
vm.prank(superchainConfig.guardian());
superchainConfig.pause("identifier");
assertTrue(l1StandardBridge.paused());
vm.deal(address(l1StandardBridge.messenger()), 1 ether);
vm.mockCall(
address(l1StandardBridge.messenger()),
abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector),
abi.encode(address(l1StandardBridge.otherBridge()))
);
}
/// @dev Confirms that the `finalizeBridgeETH` function reverts when the bridge is paused.
function test_pause_finalizeBridgeETH_reverts() external {
vm.prank(address(l1StandardBridge.messenger()));
vm.expectRevert("StandardBridge: paused");
l1StandardBridge.finalizeBridgeETH{ value: 100 }({
_from: address(2),
_to: address(3),
_amount: 100,
_extraData: hex""
});
}
/// @dev Confirms that the `finalizeETHWithdrawal` function reverts when the bridge is paused.
function test_pause_finalizeETHWithdrawal_reverts() external {
vm.prank(address(l1StandardBridge.messenger()));
vm.expectRevert("StandardBridge: paused");
l1StandardBridge.finalizeETHWithdrawal{ value: 100 }({
_from: address(2),
_to: address(3),
_amount: 100,
_extraData: hex""
});
}
/// @dev Confirms that the `finalizeERC20Withdrawal` function reverts when the bridge is paused.
function test_pause_finalizeERC20Withdrawal_reverts() external {
vm.prank(address(l1StandardBridge.messenger()));
vm.expectRevert("StandardBridge: paused");
l1StandardBridge.finalizeERC20Withdrawal({
_l1Token: address(0),
_l2Token: address(0),
_from: address(0),
_to: address(0),
_amount: 0,
_extraData: hex""
});
}
/// @dev Confirms that the `finalizeBridgeERC20` function reverts when the bridge is paused.
function test_pause_finalizeBridgeERC20_reverts() external {
vm.prank(address(l1StandardBridge.messenger()));
vm.expectRevert("StandardBridge: paused");
l1StandardBridge.finalizeBridgeERC20({
_localToken: address(0),
_remoteToken: address(0),
_from: address(0),
_to: address(0),
_amount: 0,
_extraData: hex""
});
}
}
contract L1StandardBridge_Initialize_TestFail is Bridge_Initializer { } contract L1StandardBridge_Initialize_TestFail is Bridge_Initializer { }
contract L1StandardBridge_Receive_Test is Bridge_Initializer { contract L1StandardBridge_Receive_Test is Bridge_Initializer {
......
...@@ -28,6 +28,12 @@ contract L2StandardBridge_Test is Bridge_Initializer { ...@@ -28,6 +28,12 @@ contract L2StandardBridge_Test is Bridge_Initializer {
assertEq(address(l2StandardBridge.OTHER_BRIDGE()), address(l1StandardBridge)); assertEq(address(l2StandardBridge.OTHER_BRIDGE()), address(l1StandardBridge));
} }
/// @dev Ensures that the L2StandardBridge is always not paused. The pausability
/// happens on L1 and not L2.
function test_paused_succeeds() external {
assertFalse(l2StandardBridge.paused());
}
/// @dev Tests that the bridge receives ETH and successfully initiates a withdrawal. /// @dev Tests that the bridge receives ETH and successfully initiates a withdrawal.
function test_receive_succeeds() external { function test_receive_succeeds() external {
assertEq(address(l2ToL1MessagePasser).balance, 0); assertEq(address(l2ToL1MessagePasser).balance, 0);
......
...@@ -112,4 +112,9 @@ contract StandardBridge_Stateless_Test is CommonTest { ...@@ -112,4 +112,9 @@ contract StandardBridge_Stateless_Test is CommonTest {
vm.expectRevert(); vm.expectRevert();
bridge.isCorrectTokenPair(address(erc20), address(1)); bridge.isCorrectTokenPair(address(erc20), address(1));
} }
/// @notice The bridge by default should be unpaused.
function test_paused_succeeds() external {
assertFalse(bridge.paused());
}
} }
...@@ -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