diff --git a/src/bridge/AbsBridge.sol b/src/bridge/AbsBridge.sol index bafd56f9..24a709dd 100644 --- a/src/bridge/AbsBridge.sol +++ b/src/bridge/AbsBridge.sol @@ -14,7 +14,9 @@ import { NotSequencerInbox, NotOutbox, InvalidOutboxSet, - BadSequencerMessageNumber + BadSequencerMessageNumber, + Paused, + Unpaused } from "../libraries/Error.sol"; import "./IBridge.sol"; import "./Messages.sol"; @@ -57,6 +59,8 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { address internal constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max); + bool internal _paused; + modifier onlyRollupOrOwner() { if (msg.sender != address(rollup)) { address rollupOwner = rollup.owner(); @@ -66,6 +70,18 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { } _; } + modifier whenNotPaused() { + if (paused()) { + revert Paused(); + } + _; + } + modifier whenPaused() { + if (!paused()) { + revert Unpaused(); + } + _; + } /// @notice Allows the rollup owner to set another rollup address function updateRollupAddress(IOwnable _rollup) external onlyRollupOrOwner { @@ -105,6 +121,7 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { ) external onlySequencerInbox + whenNotPaused returns ( uint256 seqMessageIndex, bytes32 beforeAcc, @@ -135,6 +152,7 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { function submitBatchSpendingReport(address sender, bytes32 messageDataHash) external onlySequencerInbox + whenNotPaused returns (uint256) { return @@ -211,7 +229,7 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { address to, uint256 value, bytes calldata data - ) external returns (bool success, bytes memory returnData) { + ) external whenNotPaused returns (bool success, bytes memory returnData) { if (!allowedOutboxes(msg.sender)) revert NotOutbox(msg.sender); if (data.length > 0 && !to.isContract()) revert NotContract(to); address prevOutbox = _activeOutbox; @@ -283,9 +301,6 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { return sequencerInboxAccs.length; } - /// @dev For the classic -> nitro migration. TODO: remove post-migration. - function acceptFundsFromOldBridge() external payable {} - /// @dev transfer funds provided to pay for crosschain msg function _transferFunds(uint256 amount) internal virtual; @@ -299,10 +314,24 @@ abstract contract AbsBridge is Initializable, DelegateCallAware, IBridge { /// used in ArbOs to calculate the submission fee for retryable ticket function _baseFeeToReport() internal view virtual returns (uint256); + function pause() external onlyRollupOrOwner whenNotPaused { + _paused = true; + emit BridgePaused(msg.sender); + } + + function unpause() external onlyRollupOrOwner whenPaused { + _paused = false; + emit BridgeUnpaused(msg.sender); + } + + function paused() public view returns (bool) { + return _paused; + } + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[40] private __gap; + uint256[39] private __gap; } diff --git a/src/bridge/IBridge.sol b/src/bridge/IBridge.sol index 22388b4b..0f3012d7 100644 --- a/src/bridge/IBridge.sol +++ b/src/bridge/IBridge.sol @@ -34,6 +34,10 @@ interface IBridge { event RollupUpdated(address rollup); + event BridgePaused(address account); + + event BridgeUnpaused(address account); + function allowedDelayedInboxList(uint256) external returns (address); function allowedOutboxList(uint256) external returns (address); @@ -101,4 +105,8 @@ interface IBridge { function setOutbox(address inbox, bool enabled) external; function updateRollupAddress(IOwnable _rollup) external; + + function pause() external; + + function unpause() external; } diff --git a/src/libraries/Error.sol b/src/libraries/Error.sol index fd6f3255..8f8b1365 100644 --- a/src/libraries/Error.sol +++ b/src/libraries/Error.sol @@ -86,6 +86,9 @@ error AlreadyUnpaused(); /// @dev The contract is paused error Paused(); +/// @dev The contract is unpaused +error Unpaused(); + /// @dev msg.value sent to the inbox isn't high enough error InsufficientValue(uint256 expected, uint256 actual); diff --git a/src/mocks/BridgeStub.sol b/src/mocks/BridgeStub.sol index 2e2dee05..e9dedab8 100644 --- a/src/mocks/BridgeStub.sol +++ b/src/mocks/BridgeStub.sol @@ -179,9 +179,11 @@ contract BridgeStub is IBridge, IEthBridge { revert("NOT_IMPLEMENTED"); } - function acceptFundsFromOldBridge() external payable {} - function initialize(IOwnable) external pure { revert("NOT_IMPLEMENTED"); } + + function pause() external {} + + function unpause() external {} } diff --git a/src/test-helpers/BridgeTester.sol b/src/test-helpers/BridgeTester.sol index 0bcbe00f..ec7c4657 100644 --- a/src/test-helpers/BridgeTester.sol +++ b/src/test-helpers/BridgeTester.sol @@ -240,5 +240,7 @@ contract BridgeTester is Initializable, DelegateCallAware, IBridge, IEthBridge { receive() external payable {} - function acceptFundsFromOldBridge() external payable {} + function pause() external {} + + function unpause() external {} } diff --git a/test/storage/Bridge b/test/storage/Bridge index 9ed7f48a..aa4a98d6 100644 --- a/test/storage/Bridge +++ b/test/storage/Bridge @@ -12,4 +12,5 @@ | rollup | contract IOwnable | 8 | 0 | 20 | src/bridge/Bridge.sol:Bridge | | sequencerInbox | address | 9 | 0 | 20 | src/bridge/Bridge.sol:Bridge | | sequencerReportedSubMessageCount | uint256 | 10 | 0 | 32 | src/bridge/Bridge.sol:Bridge | -| __gap | uint256[40] | 11 | 0 | 1280 | src/bridge/Bridge.sol:Bridge | +| _paused | bool | 11 | 0 | 1 | src/bridge/Bridge.sol:Bridge | +| __gap | uint256[39] | 12 | 0 | 1248 | src/bridge/Bridge.sol:Bridge | diff --git a/test/storage/ERC20Bridge b/test/storage/ERC20Bridge index 1b0d838b..d9444f70 100644 --- a/test/storage/ERC20Bridge +++ b/test/storage/ERC20Bridge @@ -12,5 +12,6 @@ | rollup | contract IOwnable | 8 | 0 | 20 | src/bridge/ERC20Bridge.sol:ERC20Bridge | | sequencerInbox | address | 9 | 0 | 20 | src/bridge/ERC20Bridge.sol:ERC20Bridge | | sequencerReportedSubMessageCount | uint256 | 10 | 0 | 32 | src/bridge/ERC20Bridge.sol:ERC20Bridge | -| __gap | uint256[40] | 11 | 0 | 1280 | src/bridge/ERC20Bridge.sol:ERC20Bridge | +| _paused | bool | 11 | 0 | 1 | src/bridge/ERC20Bridge.sol:ERC20Bridge | +| __gap | uint256[39] | 12 | 0 | 1248 | src/bridge/ERC20Bridge.sol:ERC20Bridge | | nativeToken | address | 51 | 0 | 20 | src/bridge/ERC20Bridge.sol:ERC20Bridge |