--- title: GateSeal. Audit scope tags: scope, gate seal, audits authors: Azat Serikov --- # GateSeal. Audit scope 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. ## Quick summary - [Project repo](https://github.com/lidofinance/gate-seals) - Written in [Vyper 0.3.7](https://github.com/vyperlang/vyper/tree/v0.3.7) - Developed in [Ape 0.6.2](https://github.com/ApeWorX/ape/tree/v0.6.2) - Python 3.10.6 ### Scope - [GateSeal.vy](https://github.com/lidofinance/gate-seals/blob/08a57c242f902a8a3bc526a851afc32e3f6b6275/contracts/GateSeal.vy) - [GateSealFactory.vy](https://github.com/lidofinance/gate-seals/blob/08a57c242f902a8a3bc526a851afc32e3f6b6275/contracts/GateSealFactory.vy) Lines of code: < 250 (using the [`loc`](https://github.com/dgusakov/loc) method) ### Final commit to audit - [`08a57c242f902a8a3bc526a851afc32e3f6b6275`](https://github.com/lidofinance/gate-seals/tree/08a57c242f902a8a3bc526a851afc32e3f6b6275) ## Context To put such crucial components of the [Lido on Ethereum V2](https://github.com/lidofinance/lido-dao/tree/v2.0.0-beta.2) protocol as [`WithdrawalQueue`](https://github.com/lidofinance/lido-dao/blob/fix/shapella-upgrade-fixes/contracts/0.8.9/WithdrawalQueue.sol#L108..L110) and [`ValidatorExitBus`](https://github.com/lidofinance/lido-dao/blob/fix/shapella-upgrade-fixes/contracts/0.8.9/oracle/ValidatorsExitBusOracle.sol#L128..L130) on hold, the DAO must hold a vote which takes [72h](https://lido.fi/governance) at least to pass and enact. GateSeals provide a way to temporarily pause these contracts immediately if the emergency calls for a swifter response. This will give the Lido DAO the time to come up with a solution, hold a vote, implement changes, etc. Each GateSeal is operated by a committee, essentially a multisig account responsible for pulling the break 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 only be activated only once and becomes unusable immediately after, - each GateSeal can only be activated within its expiry period of 1 year maximum and becomes unusable past its expiry timestamp even if it was never triggered, - the pause duration set at costruction 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 the governance voting. With all that said, it still is undesireable for a decentralized protocol to rely on a multisig in any capacity. Which is why GateSeals are only a temporary solution; their limited lifespan and one-time use design also act as a kind of "inconvenience bomb", in that once expired, the GateSeal must be replaced and setup anew. This encourages the protocol to come with a sustainable long-term solution sooner rather than later. ## Usage The idea of GateSeals is heavily based around [PausableUntil](https://github.com/lidofinance/gate-seals/blob/08a57c242f902a8a3bc526a851afc32e3f6b6275/contracts/test_helpers/SealableMock.vy) contracts which both `WithdrawalQueue` and `ValidatorExitBus` implement. These PausableUntil contracts are similar to [Pausable](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.4/contracts/security/Pausable.sol) contracts with one important difference: the paused state is not merely a boolean value, but a timestamp from which the contract is resumed (or unpaused). 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 provide a way to pull the break on the protocol potentially within hours (or even minutes) of the incident discovery as opposed to days via the standard governance process. 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. 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. ## Important to know GateSeals are created using the `GateSealFactory`. The factory uses the blueprint pattern whereby new GateSeals are deployed using the initcode (blueprint) stored onchain. 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`](https://docs.vyperlang.org/en/stable/built-in-functions.html#chain-interaction) invokes the constructor of the contract, thus, helping avoid the initilization shenanigans. The blueprint follows the [EIP-5202](https://eips.ethereum.org/EIPS/eip-5202) format, which includes a header that makes the contract uncallable and specifies the version. ## Links - [Lido on Ethereum V2](https://github.com/lidofinance/lido-dao/releases/tag/v2.0.0-beta.2) - [EIP-5202](https://eips.ethereum.org/EIPS/eip-5202) - [Lido governance process](https://lido.fi/governance) - [`create_from_blueprint`](https://docs.vyperlang.org/en/stable/built-in-functions.html#chain-interaction)