# Start a local Ethereum node
anvil -p 8555 --base-fee 0 --gas-price 0
# In a separate terminal, run the deployment script
bash scripts/dao-local-deploy.sh
Same as for the rest of the repo, see CONTRIBUTING.md.
The repository contains bash scripts for deploying the DAO across various environments:
- Local Node Deployment -
scripts/dao-local-deploy.sh
(Supports Ganache, Anvil, Hardhat Network, and other local Ethereum nodes) - Holešky Testnet Deployment –
scripts/dao-holesky-deploy.sh
The protocol requires configuration of numerous parameters for a scratch deployment. The default configurations are
stored in JSON files named deployed-<deploy env>-defaults.json
, where <deploy env>
represents the target
environment. Currently, a single default configuration file exists: deployed-testnet-defaults.json
, which is tailored
for testnet deployments. This configuration differs from the mainnet setup, featuring shorter vote durations and more
frequent oracle report cycles, among other adjustments.
Note
Some parameters in the default configuration file are intentionally set to null
, indicating that they require
further specification during the deployment process.
The deployment script performs the following steps regarding configuration:
-
Copies the appropriate default configuration file (e.g.,
deployed-testnet-defaults.json
) to a new file nameddeployed-<network name>.json
, where<network name>
corresponds to a network configuration defined inhardhat.config.js
. -
Populates the
deployed-<network name>.json
file with specific contract addresses and transaction hashes as the deployment progresses.
Detailed information for each setup is provided in the sections below.
Note
Aragon UI for Lido DAO is to be deprecated and replaced by a custom solution, thus not included in the deployment script, see https://research.lido.fi/t/discontinuation-of-aragon-ui-use/7992.
A detailed overview of the deployment script's process:
- Prepare
deployed-<network name>.json
file- Copied from
deployed-testnet-defaults.json
- Enhanced with environment variable values, e.g.,
DEPLOYER
- Progressively updated with deployed contract information
- Copied from
- (optional) Deploy DepositContract
- Skipped if DepositContract address is pre-specified
- (optional) Deploy ENS
- Skipped if ENS Registry address is pre-specified
- Deploy Aragon framework environment
- Deploy standard Aragon apps contracts (e.g.,
Agent
,Voting
) - Deploy
LidoTemplate
contract- Auxiliary contract for DAO configuration
- Deploy Lido custom Aragon apps implementations (bases) for
Lido
,LegacyOracle
,NodeOperatorsRegistry
- Register Lido APM name in ENS
- Deploy Aragon package manager contract
APMRegistry
(viaLidoTemplate
) - Deploy Lido custom Aragon apps repo contracts (via
LidoTemplate
) - Deploy Lido DAO (via
LidoTemplate
) - Issue DAO tokens (via
LidoTemplate
) - Deploy non-Aragon Lido contracts:
OracleDaemonConfig
,LidoLocator
,OracleReportSanityChecker
,EIP712StETH
,WstETH
,WithdrawalQueueERC721
,WithdrawalVault
,LidoExecutionLayerRewardsVault
,StakingRouter
,DepositSecurityModule
,AccountingOracle
,HashConsensus
for AccountingOracle,ValidatorsExitBusOracle
,HashConsensus
for ValidatorsExitBusOracle,Burner
- Finalize Lido DAO deployment: issue unvested LDO tokens, set Aragon permissions, register Lido DAO name in Aragon ID
(via
LidoTemplate
) - Initialize non-Aragon Lido contracts
- Set parameters of
OracleDaemonConfig
- Setup non-Aragon permissions
- Plug NodeOperatorsRegistry as Curated staking module
- Transfer all admin roles from deployer to
Agent
- OpenZeppelin admin roles:
Burner
,HashConsensus
forAccountingOracle
,HashConsensus
forValidatorsExitBusOracle
,StakingRouter
,AccountingOracle
,ValidatorsExitBusOracle
,WithdrawalQueueERC721
,OracleDaemonConfig
- OssifiableProxy admin roles:
LidoLocator
,StakingRouter
,AccountingOracle
,ValidatorsExitBusOracle
,WithdrawalQueueERC721
DepositSecurityModule
owner
- OpenZeppelin admin roles:
This section describes how to deploy the DAO to a local development node (such as Anvil, Hardhat, or Ganache) running at http://127.0.0.1:8555.
The deployment process utilizes the default test account 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
, which is derived
from the standard mnemonic phrase: test test test test test test test test test test test junk
To ensure a successful deployment, configure your local node with the default test accounts associated with this mnemonic.
Follow these steps for local deployment:
- Run
yarn install
(get sure repo dependencies are installed) - Run the node on port 8555 (for the commands, see subsections below)
- Run the deploy script
bash scripts/dao-local-deploy.sh
from root repo directory - Check out the deploy artifacts in
deployed-local.json
anvil -p 8555 --mnemonic "test test test test test test test test test test test junk" --base-fee 0 --gas-price 0
yarn hardhat node
To do Holešky deployment, the following parameters must be set up via env variables:
DEPLOYER
. The deployer address. The deployer must own its private key. To ensure proper operation, it should have an adequate amount of ether. The total deployment gas cost is approximately 120,000,000 gas, and this cost can vary based on whether specific components of the environment, such as the DepositContract, are deployed or not.RPC_URL
. Address of the Ethereum RPC node to use. E.g. for Infura it ishttps://holesky.infura.io/v3/<yourProjectId>
GAS_PRIORITY_FEE
. Gas priority fee. By default set to2
GAS_MAX_FEE
. Gas max fee. By default set to100
GATE_SEAL_FACTORY
. Address of the GateSeal Factory contract. Must be deployed in advance. Can be set to any0x0000000000000000000000000000000000000000
to debug deploymentWITHDRAWAL_QUEUE_BASE_URI
. BaseURI for WithdrawalQueueERC712. By default not set (left an empty string)DSM_PREDEFINED_ADDRESS
. Address to use instead of deployingDepositSecurityModule
ornull
otherwise. If used, the deposits can be made by callingLido.deposit
from the address.
Also you need to specify DEPLOYER
private key in accounts.json
under /eth/holesky
like "holesky": ["<key>"]
. See
accounts.sample.json
for an example.
To start the deployment, run (the env variables must already defined) from the root repo directory:
bash scripts/scratch/dao-holesky-deploy.sh
Deploy artifacts information will be stored in deployed-holesky.json
.
NETWORK=<PUT-YOUR-VALUE> RPC_URL=<PUT-YOUR-VALUE> bash ./scripts/verify-contracts-code.sh
There are some contracts deployed from other contracts for which automatic hardhat etherscan verification fails:
AppProxyUpgradeable
of multiple contracts (app:lido
,app:node-operators-registry
,app:oracle
,app:voting
, ...)KernelProxy
-- proxy forKernel
AppProxyPinned
-- proxy forEVMScriptRegistry
MiniMeToken
-- LDO tokenCallsScript
-- Aragon internal contractEVMScriptRegistry
-- Aragon internal contract
The workaround used during Holešky deployment is to deploy auxiliary instances of these contracts standalone and verify them via hardhat Etherscan plugin. After this Etherscan will mark the target contracts as verified by "Similar Match Source Code".
NB, that some contracts require additional auxiliary contract to be deployed. Namely, the constructor of
AppProxyPinned
depends on proxy implementation ("base" in Aragon terms) contract with initialize()
function and
Kernel
contract, which must return the implementation by call kernel().getApp(KERNEL_APP_BASES_NAMESPACE, _appId)
.
See @aragon/os/contracts/apps/AppProxyBase.sol
for the details.
In order to make the protocol fully operational, the additional steps are required:
- add oracle committee members to
HashConsensus
contracts forAccountingOracle
andValidatorsExitBusOracle
:HashConsensus.addMember
; - initialize initial epoch for
HashConsensus
contracts forAccountingOracle
andValidatorsExitBusOracle
:HashConsensus.updateInitialEpoch
; - add guardians to
DepositSecurityModule
:DepositSecurityModule.addGuardians
; - resume protocol:
Lido.resume
; - resume WithdrawalQueue:
WithdrawalQueueERC721.resume
; - add at least one Node Operator:
NodeOperatorsRegistry.addNodeOperator
; - add validator keys to the Node Operators:
NodeOperatorsRegistry.addSigningKeys
; - set staking limits for the Node Operators:
NodeOperatorsRegistry.setNodeOperatorStakingLimit
.
Note
That part of the actions require prior granting of the required roles, e.g. STAKING_MODULE_MANAGE_ROLE
for
StakingRouter.addStakingModule
:
await stakingRouter.grantRole(STAKING_MODULE_MANAGE_ROLE, agent.address, { from: agent.address });
await stakingRouter.addStakingModule(
state.nodeOperatorsRegistry.deployParameters.stakingModuleTypeId,
nodeOperatorsRegistry.address,
NOR_STAKING_MODULE_STAKE_SHARE_LIMIT_BP,
NOR_STAKING_MODULE_PRIORITY_EXIT_SHARE_THRESHOLD_BP,
NOR_STAKING_MODULE_MODULE_FEE_BP,
NOR_STAKING_MODULE_TREASURY_FEE_BP,
NOR_STAKING_MODULE_MAX_DEPOSITS_PER_BLOCK,
NOR_STAKING_MODULE_MIN_DEPOSIT_BLOCK_DISTANCE,
{ from: agent.address },
);
await stakingRouter.renounceRole(STAKING_MODULE_MANAGE_ROLE, agent.address, { from: agent.address });
This section describes part of the parameters and their values used at the deployment. The values are specified in
deployed-testnet-defaults.json
.
# Parameters related to "bunker mode"
# See https://research.lido.fi/t/withdrawals-for-lido-on-ethereum-bunker-mode-design-and-implementation/3890/4
# and https://snapshot.org/#/lido-snapshot.eth/proposal/0xa4eb1220a15d46a1825d5a0f44de1b34644d4aa6bb95f910b86b29bb7654e330
# NB: BASE_REWARD_FACTOR: https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#rewards-and-penalties
NORMALIZED_CL_REWARD_PER_EPOCH=64
NORMALIZED_CL_REWARD_MISTAKE_RATE_BP=1000 # 10%
REBASE_CHECK_NEAREST_EPOCH_DISTANCE=1
REBASE_CHECK_DISTANT_EPOCH_DISTANCE=23 # 10% of AO 225 epochs frame
VALIDATOR_DELAYED_TIMEOUT_IN_SLOTS=7200 # 1 day
# See https://snapshot.org/#/lido-snapshot.eth/proposal/0xa4eb1220a15d46a1825d5a0f44de1b34644d4aa6bb95f910b86b29bb7654e330 for "Requirement not be considered Delinquent"
VALIDATOR_DELINQUENT_TIMEOUT_IN_SLOTS=28800 # 4 days
# See "B.3.I" of https://snapshot.org/#/lido-snapshot.eth/proposal/0xa4eb1220a15d46a1825d5a0f44de1b34644d4aa6bb95f910b86b29bb7654e330
NODE_OPERATOR_NETWORK_PENETRATION_THRESHOLD_BP=100 # 1% network penetration for a single NO
# Time period of historical observations used for prediction of the rewards amount
# see https://research.lido.fi/t/withdrawals-for-lido-on-ethereum-bunker-mode-design-and-implementation/3890/4
PREDICTION_DURATION_IN_SLOTS=50400 # 7 days
# Max period of delay for requests finalization in case of bunker due to negative rebase
# twice min governance response time - 3 days voting duration
FINALIZATION_MAX_NEGATIVE_REBASE_EPOCH_SHIFT=1350 # 6 days