From 381f8b8b180958091187f26f32c514932bc1f7fe Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:53:56 +0800 Subject: [PATCH] fix(protocol): fix bridge unpause will delay execution (#16612) --- .../protocol/contracts/L1/libs/LibUtils.sol | 6 ++++-- packages/protocol/contracts/bridge/Bridge.sol | 19 +++++++++++++++++-- .../contracts/common/EssentialContract.sol | 3 +++ .../contracts/team/TimelockTokenPool.sol | 12 ++++++++++-- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/protocol/contracts/L1/libs/LibUtils.sol b/packages/protocol/contracts/L1/libs/LibUtils.sol index b72bfa7e34d..484e1ae8c15 100644 --- a/packages/protocol/contracts/L1/libs/LibUtils.sol +++ b/packages/protocol/contracts/L1/libs/LibUtils.sol @@ -98,7 +98,9 @@ library LibUtils { view returns (bool) { - uint256 deadline = _tsTimestamp.max(_lastUnpausedAt) + _windowMinutes * 60; - return block.timestamp >= deadline; + unchecked { + uint256 deadline = _tsTimestamp.max(_lastUnpausedAt) + _windowMinutes * 60; + return block.timestamp >= deadline; + } } } diff --git a/packages/protocol/contracts/bridge/Bridge.sol b/packages/protocol/contracts/bridge/Bridge.sol index e5535a1a3e5..99057cc05ae 100644 --- a/packages/protocol/contracts/bridge/Bridge.sol +++ b/packages/protocol/contracts/bridge/Bridge.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.24; import "../common/EssentialContract.sol"; import "../libs/LibAddress.sol"; +import "../libs/LibMath.sol"; import "../signal/ISignalService.sol"; import "./IBridge.sol"; @@ -13,6 +14,7 @@ import "./IBridge.sol"; /// @custom:security-contact security@taiko.xyz contract Bridge is EssentialContract, IBridge { using Address for address; + using LibMath for uint256; using LibAddress for address; using LibAddress for address payable; @@ -190,7 +192,7 @@ contract Bridge is EssentialContract, IBridge { } } - if (block.timestamp >= invocationDelay + receivedAt) { + if (_isPostInvocationDelay(receivedAt, invocationDelay)) { delete proofReceipt[msgHash]; messageStatus[msgHash] = Status.RECALLED; @@ -262,7 +264,7 @@ contract Bridge is EssentialContract, IBridge { } } - if (block.timestamp >= invocationDelay + receivedAt) { + if (_isPostInvocationDelay(receivedAt, invocationDelay)) { // If the gas limit is set to zero, only the owner can process the message. if (_message.gasLimit == 0 && msg.sender != _message.destOwner) { revert B_PERMISSION_DENIED(); @@ -646,4 +648,17 @@ contract Bridge is EssentialContract, IBridge { return false; } } + + function _isPostInvocationDelay( + uint256 _receivedAt, + uint256 _invocationDelay + ) + private + view + returns (bool) + { + unchecked { + return block.timestamp >= _receivedAt.max(lastUnpausedAt) + _invocationDelay; + } + } } diff --git a/packages/protocol/contracts/common/EssentialContract.sol b/packages/protocol/contracts/common/EssentialContract.sol index 9aeea563e07..0f76871fbb9 100644 --- a/packages/protocol/contracts/common/EssentialContract.sol +++ b/packages/protocol/contracts/common/EssentialContract.sol @@ -26,6 +26,8 @@ abstract contract EssentialContract is UUPSUpgradeable, Ownable2StepUpgradeable, uint8 private __paused; + uint64 public lastUnpausedAt; + uint256[49] private __gap; /// @notice Emitted when the contract is paused. @@ -81,6 +83,7 @@ abstract contract EssentialContract is UUPSUpgradeable, Ownable2StepUpgradeable, /// @notice Unpauses the contract. function unpause() public virtual whenPaused { __paused = _FALSE; + lastUnpausedAt = uint64(block.timestamp); emit Unpaused(msg.sender); // We call the authorize function here to avoid: // Warning (5740): Unreachable code. diff --git a/packages/protocol/contracts/team/TimelockTokenPool.sol b/packages/protocol/contracts/team/TimelockTokenPool.sol index 8e24269e00c..b6a0c37daca 100644 --- a/packages/protocol/contracts/team/TimelockTokenPool.sol +++ b/packages/protocol/contracts/team/TimelockTokenPool.sol @@ -168,7 +168,7 @@ contract TimelockTokenPool is EssentialContract, EIP712Upgradeable { } /// @notice Withdraws all withdrawable tokens. - function withdraw() external nonReentrant { + function withdraw() external whenNotPaused nonReentrant { _withdraw(msg.sender, msg.sender); } @@ -176,7 +176,15 @@ contract TimelockTokenPool is EssentialContract, EIP712Upgradeable { /// @param _to The address where the granted and unlocked tokens shall be sent to. /// @param _nonce The nonce to be used. /// @param _sig Signature provided by the grant recipient. - function withdraw(address _to, uint256 _nonce, bytes memory _sig) external nonReentrant { + function withdraw( + address _to, + uint256 _nonce, + bytes calldata _sig + ) + external + whenNotPaused + nonReentrant + { if (_to == address(0)) revert INVALID_PARAM(); address account = ECDSA.recover(getWithdrawalHash(_to, _nonce), _sig);