The validator ejector is a daemon service which monitors ValidatorsExitBusOracle events and sends out stored exit messages when necessary. It allows node operators to generate and sign exit messages ahead of time, which will be sent out by the ejector when the protocol requests an exit to be made.
On start, it loads exit messages from a specified folder of individual .json
files and validates their format, structure, and signature. Then, it loads events from a configurable amount of latest finalized blocks, checks if exits should be made and after that periodically fetches fresh events.
Docker & docker-compose
Node.js 16 or higher
The ejector loads and validates exit messages on start. This means that any changes to the messages folder (e.g. new exit messages) require a restart of the application to be picked up.
The ejector accepts messages in three formats:
It is highly advised that after exit messages are generated and signed, they should be encrypted for storage safety. The ejector will decrypt files on start by looking up the password in the MESSAGES_PASSWORD
environment variable.
Exit messages are encrypted and decrypted by the ejector according to the EIP-2335 specification.
The ejector is bundled with a small, easy to use encryption script.
.env
file with your encryption password or pass it to the terminal before proceeding:.json
exit message files to directory encryptor/input
yarn & yarn encrypt
encryptor/output
The ejector is bundled with an encryptor script inside, so you can run it using the same Docker image (the sha in the code must always be from the latest production release):
For platforms with a different architecture but with emulation/transpilation support e.g. macOS on M-series processors, additionally specify:
Address of the execution node.
Address of the consensus node.
Address of the LidoLocator contract (can be looked up at the respective network’s deployed contracts page).
Hoodi Testnet:
Holešky Testnet:
Mainnet:
ID of the StakingRouter contract module.
Currently, the staking router’s NodeOperatorsRegistry has three modules, the Curated one with ID 1
and Simple DVT with ID 2
, and the Community Staking Module (CSM) with ID 3
(or 4
on testnets)
You can find this on the operators dashboard (e.g. #123 on the operator card):
Hoodi Curated module,
Hoodi Simple DVT module,
Holešky Curated module,
Holešky Simple DVT module,
Mainnet Curated module,
Mainnet Simple DVT module
Location from which to load .json
exit messages.
When set, messages mode will be activated. This is not needed if you use the ejector in webhook mode.
For example, /messages
in Docker or simply messages
if running directly for local files.
External storage bucket URLs are supported for AWS S3 and Google Cloud Storage:
s3://
for S3gs://
for GCSEndpoint to fetch when an exit has to be made. Allows to implement a just in time approach by offloading exiting logic to an external service and using the ejector as a secure exit events reader.
When set, webhook mode will be activated. This is not needed if you use the ejector in messages mode.
On the endpoint, JSON will be POSTed with the following structure:
A 200 response will be counted as a successful exit, non-200 ones as failures.
JSON array of Lido Oracle addresses, from which only report transactions will be accepted.
To get the list of members you can go to the “Read Contract” section of the ValidatorsExitBusOracle’s HashConsensus on Etherscan and query the getMembers()
method: Mainnet, Holešky, Hoodi
Example .env
entry for Mainnet (last updated 2024-10-25):
Note: make sure quotes are copied correctly if copying these examples.
Example .env
entry for Holešky (last updated 2024-09-30):
1 0xcA80ee7313A315879f326105134F938676Cfd7a9
2 0xf03B8DC8762B97F13Ac82e6F94bE3Ed002FF7459
3 0x1932f53B1457a5987791a40Ba91f71c5Efd5788F
4 0xf7aE520e99ed3C41180B5E12681d31Aa7302E4e5
5 0x99B2B75F490fFC9A29E4E1f5987BE8e30E690aDF
6 0x219743f1911d84B32599BdC2Df21fC8Dba6F81a2
7 0xD3b1e36A372Ca250eefF61f90E833Ca070559970
8 0x4c75FA734a39f3a21C57e583c1c29942F021C6B7
9 0xB1cC91878c1831893D39C2Bb0988404ca5Fa7918
10 0xfe43A8B0b481Ae9fB1862d31826532047d2d538c
11 0x43C45C2455C49eed320F463fF4f1Ece3D2BF5aE2
Example .env
entry for Hoodi (last updated 2025-04-22):
Note: make sure quotes are copied correctly if copying these examples.
Password to decrypt encrypted exit messages with on application start.
Alternative to MESSAGES_PASSWORD
. Path to file containing the password to decrypt exit messages with. If used, MESSAGES_PASSWORD (not MESSAGES_PASSWORD_FILE) needs to be added to LOGGER_SECRETS to be sanitized.
Amount of blocks to load events from on application start.
Suggested to include in your .env
variables, but to be left at the default of 50000 (~7 days of blocks).
In case your ejector will be down due to an emergency, this value can be tweaked to let the ejector load a higher amount of blocks on start.
Port for serving metrics and a health check endpoint, default 8989.
Enable with true
to serve Prometheus metrics: full list
Will be served on HOST:$HTTP_PORT/metrics
.
Highly advised for monitoring and alerting.
Enabled by default, disabled with false
. Highly recommended to monitor this endpoint.
Will be served on HOST:$HTTP_PORT/health
.
Recommended to set to info
(default), can be changed to debug
in case of issues for easier debugging.
Format of logs, simple
by default, but can be set to json
to be easily parsable, e.g. by Loki.
Environment variable names or exact values which should be replaced in logs, in JSON array of strings format.
Advised to include your MESSAGES_PASSWORD, EXECUTION_NODE, and MESSAGES_PASSWORD:
Note: make sure quotes are copied correctly if copying this example.
Allows to test the application with true
without actually sending out exit messages.
Use with caution!
Make sure to set to false
or completely leave it out in production.
Please do not use unless suggested by a Lido contributor.
true
to skip security checks, for example if the ValidatorsExitBusOracle’s HashConsensus contract was changed after the ejector was unable to exit validators e.g. because it was switched offmkdir messages
cp sample.env .env
.env
filecd
into that foldermkdir messages
cp sample.env .env
.env
filedocker-compose.yml
file using this templatedocker-compose up
or docker-compose up -d
to start in detached mode (in the background)loadedMessages
count is greater than 0
Job started
and Job finished
lines in the logsAn example of a correct operation log:
Validator Ejector GitHub Repository (Open Source)
https://github.com/lidofinance/validator-ejector
What's Changing for Node Operators in Lido V2 - parts can be outdated
https://hackmd.io/@lido/Byue6SQxh
Lido Withdrawals: Automating Validator Exits - parts can be outdated
https://hackmd.io/@lido/BkxRxAr-o
Ejector Logic Spec - parts can be outdated
https://hackmd.io/@lido/r1KZ4YNdj