GateSeal
- Source code
- Factory source code
- (proposed) GateSeal (VaultHub and PredepositGuarantee (PDG))
- GateSeal (ValidatorExitBus (VEB) and TriggerableWithdrawalsGateway (TWG))
- GateSeal (WithdrawalQueue)
- GateSeal (CSM)
- Deployed factory contract
- Deployed blueprint contract
A one-time panic button for pausable contracts.
What is a GateSeal?
A GateSeal is a contract that allows the designated account to instantly put a set of contracts on pause (i.e., seal) for a limited duration. GateSeals are meant to be used as a panic button for crucial contracts in case of an emergency. Each GateSeal is one-time use only and immediately becomes unusable once activated. If the seal is never triggered, the GateSeal will still eventually expire after a set period.
Why use a GateSeal?
To put such crucial components of the Lido protocol as WithdrawalQueue, ValidatorsExitBusOracle, and TriggerableWithdrawalsGateway on hold, the DAO must hold a vote that may take several days to pass. GateSeals provide a way to temporarily pause these contracts immediately if the emergency calls for a swifter response. This gives the Lido DAO time to investigate, propose a fix, vote, and implement changes. There are two GateSeal instances in production today: one that pauses the WithdrawalQueue, and another that pauses the ValidatorsExitBusOracle and the TriggerableWithdrawalsGateway.
Each GateSeal is operated by a committee, essentially a multisig account responsible for pulling the brake in case things go awry. However, authorizing a committee to pause/resume the protocol withdrawals would be utterly reckless, which is why GateSeals have a number of safeguards in place:
- each GateSeal can be activated only once and becomes unusable immediately after,
- each GateSeal can be activated only within its expiry period (up to 1 year) and becomes unusable past its expiry timestamp even if it was never triggered,
- the pause duration set at construction time is limited to 14 days.
Thus, the biggest damage a compromised GateSeal multisig can inflict is to pause withdrawals for 14 days, given the DAO does not resume withdrawals sooner via governance voting.
With all that said, it is still undesirable for a decentralized protocol to rely on a multisig in any capacity. This is why GateSeals are only a temporary solution; their limited lifespan and one-time-use design also act as an "inconvenience bomb": once expired, the GateSeal must be replaced and set up anew.
How does it work?
The idea of GateSeals is heavily based on PausableUntil contracts, which are implemented by WithdrawalQueue, ValidatorsExitBusOracle, and TriggerableWithdrawalsGateway. These PausableUntil contracts are similar to Pausable contracts with one important difference: the paused state is not merely a boolean value, but a timestamp at which the contract resumes (or unpauses). This allows the user to pause the contract for a certain period, and after this period the contract will resume itself without an explicit call. Thus, the PausableUntil pattern in conjunction with a GateSeal provides a way to pull the brake on the protocol in a critical situation.
A GateSeal is set up with an immutable configuration at the time of construction:
- the sealing committee, an account responsible for triggering the seal,
- the seal duration, a period for which the contracts will be sealed,
- the sealables, a list of contracts to be sealed,
- the expiry period, a period after which the GateSeal becomes unusable.
It is important to note that GateSeals do not bypass the access control settings for pausable contracts, which is why GateSeals must be given the appropriate permissions beforehand. If and when an emergency arises, the sealing committee simply calls the seal function and puts the contracts on pause for the set duration.
How are GateSeals created?
GateSeals are created using the GateSealFactory. The factory uses the blueprint pattern whereby new GateSeals are deployed using the initcode (blueprint) stored on-chain. The blueprint is essentially a broken GateSeal that can only be used to create new GateSeals.
While Vyper offers other ways to create new contracts, we opted to use the blueprint pattern because it creates a fully autonomous contract without any dependencies. Unlike other contract-creating functions, create_from_blueprint invokes the constructor of the contract, thus helping avoid initialization shenanigans.
The blueprint follows the EIP-5202 format, which includes a header that makes the contract uncallable and specifies the version.