From bc542d89f98bff34a6331a5ed6bb2c9bbe15b148 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:47:07 +0800 Subject: [PATCH 1/2] fix(protocol): fix ERC20Airdrop2.sol with an extended withdrawal window (#16596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Keszey Dániel --- packages/protocol/contracts/team/airdrop/ERC20Airdrop2.sol | 7 ++++++- packages/protocol/test/team/airdrop/ERC20Airdrop2.t.sol | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/protocol/contracts/team/airdrop/ERC20Airdrop2.sol b/packages/protocol/contracts/team/airdrop/ERC20Airdrop2.sol index 4473bf55a4..70b81ac632 100644 --- a/packages/protocol/contracts/team/airdrop/ERC20Airdrop2.sol +++ b/packages/protocol/contracts/team/airdrop/ERC20Airdrop2.sol @@ -14,6 +14,8 @@ contract ERC20Airdrop2 is MerkleClaimable { using LibMath for uint256; using SafeERC20 for IERC20; + uint256 public constant WITHDRAWAL_GRACE_PERIOD = 30 days; + /// @notice The address of the token contract. address public token; @@ -39,7 +41,10 @@ contract ERC20Airdrop2 is MerkleClaimable { error WITHDRAWALS_NOT_ONGOING(); modifier ongoingWithdrawals() { - if (claimEnd > block.timestamp || claimEnd + withdrawalWindow < block.timestamp) { + if ( + claimEnd > block.timestamp + || claimEnd + withdrawalWindow + WITHDRAWAL_GRACE_PERIOD < block.timestamp + ) { revert WITHDRAWALS_NOT_ONGOING(); } _; diff --git a/packages/protocol/test/team/airdrop/ERC20Airdrop2.t.sol b/packages/protocol/test/team/airdrop/ERC20Airdrop2.t.sol index a2b86449fa..ddd1578447 100644 --- a/packages/protocol/test/team/airdrop/ERC20Airdrop2.t.sol +++ b/packages/protocol/test/team/airdrop/ERC20Airdrop2.t.sol @@ -137,9 +137,9 @@ contract TestERC20Airdrop2 is TaikoTest { vm.expectRevert(ERC20Airdrop2.WITHDRAWALS_NOT_ONGOING.selector); airdrop2.withdraw(Alice); - // Roll 11 day after + // Roll 31 day after vm.roll(block.number + 200); - vm.warp(claimEnd + 11 days); + vm.warp(claimEnd + 10 days + 30 days + 1); // withdrawal window + grace period + 1sec (uint256 balance, uint256 withdrawable) = airdrop2.getBalance(Alice); 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 2/2] 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 b72bfa7e34..484e1ae8c1 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 e5535a1a3e..99057cc05a 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 9aeea563e0..0f76871fbb 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 8e24269e00..b6a0c37dac 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);