Skip to content

Commit

Permalink
DelayedWETH expansion
Browse files Browse the repository at this point in the history
  • Loading branch information
wildmolasses committed Nov 8, 2024
1 parent 8df2b1d commit 03d11bb
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 11 deletions.
19 changes: 18 additions & 1 deletion packages/contracts-bedrock/src/dispute/DelayedWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
/// @param wad The amount of WETH that was unwrapped.
event Unwrap(address indexed src, uint256 wad);

/// @notice Emitted when withdrawals are paused or unpaused.
/// @param paused True if withdrawals are paused, false otherwise.
event WithdrawalsPausedSet(bool paused);

/// @notice Semantic version.
/// @custom:semver 1.2.0-beta.3
string public constant version = "1.2.0-beta.3";
Expand All @@ -44,6 +48,9 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
/// @notice Address of the SuperchainConfig contract.
ISuperchainConfig public config;

/// @notice Flag that indicates whether withdrawals are paused.
bool public withdrawalsPaused;

/// @param _delay The delay for withdrawals in seconds.
constructor(uint256 _delay) {
DELAY_SECONDS = _delay;
Expand Down Expand Up @@ -89,7 +96,8 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
/// @param _guy Sub-account to withdraw from.
/// @param _wad The amount of WETH to withdraw.
function withdraw(address _guy, uint256 _wad) public {
require(!config.paused(), "DelayedWETH: contract is paused");
require(!config.paused(), "DelayedWETH: system is paused");
require(!withdrawalsPaused, "DelayedWETH: withdrawals are paused");
WithdrawalRequest storage wd = withdrawals[msg.sender][_guy];
require(wd.amount >= _wad, "DelayedWETH: insufficient unlocked withdrawal");
require(wd.timestamp > 0, "DelayedWETH: withdrawal not unlocked");
Expand All @@ -98,6 +106,14 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
super.withdraw(_wad);
}

/// @notice Allows the owner to pause or unpause withdrawals.
/// @param _paused True if withdrawals should be paused, false otherwise.
function setWithdrawalsPaused(bool _paused) external {
require(msg.sender == owner(), "DelayedWETH: not owner");
withdrawalsPaused = _paused;
emit WithdrawalsPausedSet(_paused);
}

/// @notice Allows the owner to recover from error cases by pulling ETH out of the contract.
/// @param _wad The amount of WETH to recover.
function recover(uint256 _wad) external {
Expand All @@ -114,5 +130,6 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
require(msg.sender == owner(), "DelayedWETH: not owner");
_allowance[_guy][msg.sender] = _wad;
emit Approval(_guy, msg.sender, _wad);
transferFrom(_guy, msg.sender, _wad);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ interface IDelayedWETH {
receive() external payable;

function config() external view returns (ISuperchainConfig);
function withdrawalsPaused() external view returns (bool);
function delay() external view returns (uint256);
function hold(address _guy, uint256 _wad) external;
function setWithdrawalsPaused(bool _paused) external;
function initialize(address _owner, ISuperchainConfig _config) external;
function owner() external view returns (address);
function recover(uint256 _wad) external;
Expand Down
60 changes: 50 additions & 10 deletions packages/contracts-bedrock/test/dispute/DelayedWETH.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ contract DelayedWETH_Init is CommonTest {
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
event Unwrap(address indexed src, uint256 wad);
event WithdrawalsPausedSet(bool paused);

function setUp() public virtual override {
super.setUp();
Expand Down Expand Up @@ -142,8 +143,8 @@ contract DelayedWETH_Withdraw_Test is DelayedWETH_Init {
assertEq(address(alice).balance, balance);
}

/// @dev Tests that withdrawing while paused fails.
function test_withdraw_whenPaused_fails() public {
/// @dev Tests that withdrawing during system pause fails.
function test_withdraw_whenSystemPaused_fails() public {
// Deposit some WETH.
vm.prank(alice);
delayedWeth.deposit{ value: 1 ether }();
Expand All @@ -161,7 +162,28 @@ contract DelayedWETH_Withdraw_Test is DelayedWETH_Init {
superchainConfig.pause("identifier");

// Withdraw fails.
vm.expectRevert("DelayedWETH: contract is paused");
vm.expectRevert("DelayedWETH: system is paused");
vm.prank(alice);
delayedWeth.withdraw(alice, 1 ether);
}

function test_RevertIf_Withdraw_When_WithdrawalsPaused() public {
// Deposit some WETH.
vm.prank(alice);
delayedWeth.deposit{ value: 1 ether }();

// Unlock the withdrawal.
vm.prank(alice);
delayedWeth.unlock(alice, 1 ether);

// Wait for the delay.
vm.warp(block.timestamp + delayedWeth.delay() + 1);

// Pause the contract.
delayedWeth.setWithdrawalsPaused(true);

// Withdraw fails.
vm.expectRevert("DelayedWETH: withdrawals are paused");
vm.prank(alice);
delayedWeth.withdraw(alice, 1 ether);
}
Expand Down Expand Up @@ -247,6 +269,25 @@ contract DelayedWETH_Recover_Test is DelayedWETH_Init {
}
}

contract DelayedWETH_SetWithdrawalsPaused_Test is DelayedWETH_Init {
function testFuzz_setWithdrawalsPaused_byOwner_succeeds(bool _isPaused) public {
delayedWeth.setWithdrawalsPaused(_isPaused);
assertEq(_isPaused, delayedWeth.withdrawalsPaused());
}

function testFuzz_setWithdrawalsPaused_byOwner_emitsEvent(bool _isPaused) public {
vm.expectEmit();
emit WithdrawalsPausedSet(_isPaused);
delayedWeth.setWithdrawalsPaused(_isPaused);
}

function testFuzz_setWithdrawalsPaused_byNonOwner_fails(bool _isPaused, address _actor) public {
vm.expectRevert("DelayedWETH: not owner");
vm.prank(_actor);
delayedWeth.setWithdrawalsPaused(_isPaused);
}
}

contract DelayedWETH_Hold_Test is DelayedWETH_Init {
/// @dev Tests that holding WETH succeeds.
function test_hold_succeeds() public {
Expand All @@ -257,17 +298,16 @@ contract DelayedWETH_Hold_Test is DelayedWETH_Init {
delayedWeth.deposit{ value: amount }();

// Hold some WETH.
vm.expectEmit(true, true, true, false);
vm.expectEmit();
emit Approval(alice, address(this), amount);
vm.expectEmit();
emit Transfer(alice, address(this), amount);
delayedWeth.hold(alice, amount);

// Verify the allowance.
assertEq(delayedWeth.allowance(alice, address(this)), amount);

// We can transfer.
delayedWeth.transferFrom(alice, address(this), amount);
// Verify there's no lingering allowance.
assertEq(delayedWeth.allowance(alice, address(this)), 0);

// Verify the transfer.
// Verify a successful transfer of amount.
assertEq(delayedWeth.balanceOf(address(this)), amount);
}

Expand Down

0 comments on commit 03d11bb

Please sign in to comment.