DelayedWETH
Table of Contents
Overview
DelayedWETH extends the standard WETH contract to provide time-delayed withdrawals with emergency recovery mechanisms. It serves as the bond holding contract for the Fault Dispute Game system, ensuring that dispute participants cannot immediately withdraw their bonds and providing a safety window for governance intervention in case of incorrect bond distributions.
Definitions
Withdrawal Delay
The minimum time period (in seconds) that must elapse between unlocking a withdrawal and executing it. This delay provides a window for the contract owner to intervene if the Fault Dispute Game incorrectly distributes bonds.
The Withdrawal Delay is configured per chain. It may be changed by the L1 ProxyAdmin owner, but only
within a lower and upper bound that cannot be changed without an upgrade. The value in effect is the
one configured at the time a withdrawal is executed, so a change applies to
Withdrawal Requests that were already unlocked, in either direction. Upgrades
carry the configured value forward; see
i01-003. All chains and game types that share a
DelayedWETH share the same delay.
Sub-Account
An address parameter used to segregate withdrawal requests within a single caller's account. This allows the Fault Dispute Game to track individual user withdrawals separately, enabling precise accounting and selective intervention by the owner if bugs occur in bond distribution logic.
Withdrawal Request
A record tracking the amount of WETH unlocked for withdrawal and the timestamp when it was unlocked. Each withdrawal request is uniquely identified by the combination of the caller address and the Sub-Account address.
Assumptions
a01-001: Owner operates within governance constraints
The contract owner (typically the System Owner multisig) acts honestly and only uses emergency functions
(recover, hold) to correct errors in dispute game bond distributions, not to arbitrarily confiscate user
funds.
Mitigations
- Owner is expected to be a multisig with multiple signers across different timezones
- Withdrawal Delay of at least 12 hours and up to 7 days provides time for community oversight before withdrawals complete
- Owner actions are transparent on-chain and subject to community monitoring
a01-002: SuperchainConfig provides accurate pause state
The ETHLockbox contract correctly references a SuperchainConfig contract that accurately reflects the intended pause state of the system.
Mitigations
- SuperchainConfig is a well-audited core protocol contract
- Pause functionality is controlled by the Guardian role with established governance processes
Invariants
i01-001: Withdrawal Delay enforcement
Withdrawals cannot be executed immediately after unlocking. A time delay must elapse to ensure the owner has sufficient time to intervene if the Fault Dispute Game has incorrectly distributed bonds.
Impact
Severity: High
If this invariant is violated, malicious actors could immediately withdraw incorrectly awarded bonds before governance can intervene, leading to loss of funds that should have been redistributed to honest participants. The severity is High rather than Critical because violation requires the Owner operates within governance constraints assumption to also fail for actual fund loss to occur.
i01-002: Owner emergency intervention capability
The contract owner can always recover ETH from the contract balance via recover() and can always transfer
WETH from any account to themselves via hold(), regardless of Withdrawal Request states. This
provides an
emergency backstop if the Fault Dispute Game distributes bonds incorrectly.
Impact
Severity: High
If this invariant is violated, the owner cannot correct erroneous bond distributions from buggy dispute games, potentially resulting in permanent loss of funds that should have been awarded to honest participants. The severity is High because it depends on the Owner operates within governance constraints assumption - the owner must act correctly to utilize this capability for its intended purpose.
Function Specification
constructor
Sets the immutable bounds of the Withdrawal Delay and disables initializers for the implementation contract.
Parameters:
_minDelay: The lower bound of the Withdrawal Delay in seconds_maxDelay: The upper bound of the Withdrawal Delay in seconds
Behavior:
- MUST revert if
_minDelayis zero or greater than_maxDelay - MUST set the immutable
MIN_DELAY_SECONDSto_minDelayandMAX_DELAY_SECONDSto_maxDelay - MUST call
_disableInitializers()to prevent initialization of the implementation contract
initialize
Initializes the proxy contract with the ETHLockbox address and the Withdrawal Delay.
Parameters:
_ethLockbox: Address of the ETHLockbox contract_delay: The Withdrawal Delay in seconds
Behavior:
- MUST revert if caller is not the ProxyAdmin or ProxyAdmin owner
- MUST revert if
_delayis less thanMIN_DELAY_SECONDSor greater thanMAX_DELAY_SECONDS - MUST set
ethLockboxto_ethLockbox - MUST set the Withdrawal Delay to
_delayand emit aDelaySetevent with the new value - MUST only be callable once per initialization version via the
reinitializermodifier
delay
Returns the Withdrawal Delay in seconds.
Behavior:
- MUST return the current value of the Withdrawal Delay
minDelay
Returns the lower bound of the Withdrawal Delay in seconds.
Behavior:
- MUST return the value of
MIN_DELAY_SECONDS
maxDelay
Returns the upper bound of the Withdrawal Delay in seconds.
Behavior:
- MUST return the value of
MAX_DELAY_SECONDS
setDelay
Allows the L1 ProxyAdmin owner to change the Withdrawal Delay.
Parameters:
_delay: The new Withdrawal Delay in seconds
Behavior:
- MUST revert if
msg.senderis not the ProxyAdmin owner - MUST revert if
_delayis less thanMIN_DELAY_SECONDSor greater thanMAX_DELAY_SECONDS - MUST set the Withdrawal Delay to
_delay - MUST emit a
DelaySetevent with the new value
config
Returns the SuperchainConfig contract address.
Behavior:
- MUST return the result of calling
ethLockbox.superchainConfig()
unlock
Creates or updates a Withdrawal Request for a Sub-Account, setting the unlock timestamp to the current block timestamp and increasing the unlocked amount.
Parameters:
_guy: The Sub-Account address for which to unlock withdrawals_wad: The amount of WETH to unlock
Behavior:
- MUST set
withdrawals[msg.sender][_guy].timestamptoblock.timestamp - MUST increase
withdrawals[msg.sender][_guy].amountby_wad - MUST be callable by any address
withdraw
Withdraws ETH to msg.sender after the Withdrawal Delay has elapsed.
Parameters:
_guy: The Sub-Account address to withdraw from_wad: The amount of WETH to withdraw
Behavior:
- MUST revert if
ethLockbox.paused()returns true - MUST revert if
withdrawals[msg.sender][_guy].amountis less than_wad - MUST revert if
withdrawals[msg.sender][_guy].timestampis 0 - MUST revert if
delay()is 0 - MUST revert if
withdrawals[msg.sender][_guy].timestamp + delay()is greater thanblock.timestamp - MUST decrease
withdrawals[msg.sender][_guy].amountby_wad - MUST call the parent
WETH98.withdraw(_wad)function to transfer ETH tomsg.sender
withdraw (single parameter)
Convenience function that withdraws from the caller's own Sub-Account.
Parameters:
_wad: The amount of WETH to withdraw
Behavior:
- MUST call
withdraw(msg.sender, _wad)
recover
Allows the owner to recover ETH from the contract balance in emergency situations.
Parameters:
_wad: The requested amount of ETH to recover
Behavior:
- MUST revert if
msg.senderis not the ProxyAdmin owner - MUST calculate the actual recovery amount as the minimum of
_wadandaddress(this).balance - MUST transfer the calculated amount to
msg.sendervia a low-level call - MUST revert if the transfer fails
hold (single parameter)
Allows the owner to transfer all WETH from a specific account to themselves.
Parameters:
_guy: The address to transfer WETH from
Behavior:
- MUST call
hold(_guy, balanceOf(_guy))
hold (two parameters)
Allows the owner to transfer a specific amount of WETH from any account to themselves by setting an allowance and immediately executing a transfer.
Parameters:
_guy: The address to transfer WETH from_wad: The amount of WETH to transfer
Behavior:
- MUST revert if
msg.senderis not the ProxyAdmin owner - MUST set
_allowance[_guy][msg.sender]to_wad - MUST emit an
Approvalevent with parameters(_guy, msg.sender, _wad) - MUST call
transferFrom(_guy, msg.sender, _wad)to execute the transfer