- node.js v16 or v18 (later might work fine as well, but not tested)
- yarn
The repo contains bash scripts for deployment of the DAO under multiple environments:
- local node (ganache, anvil, hardhat network)
dao-local-deploy.sh - holesky testnet -
dao-holesky-deploy.sh
The protocol has a bunch of parameters to configure for the scratch deployment. The default configuration is stored in files deployed-<deploy env>-defaults.json, where <deploy env> is the target environment. Currently, there is a single default configuration, deployed-testnet-defaults.json, suitable for testnet deployments. Compared to the mainnet configuration, it has lower vote durations, more frequent oracle report cycles, etc. Part of the parameters require further specification -- they are marked with null values.
During the deployment, the "default" configuration is copied to deployed-<network name>.json, where <network name> is the name of a network configuration defined in hardhat.config.js. The file deployed-<network name>.json gets populated with the contract addresses and transaction hashes during the deployment process.
These are the deployment setups, supported currently:
- local (basically any node at http://127.0.0.1:8545);
- Holešky testnet.
Each is described in the details in the sections below.
NB: Aragon UI for Lido DAO is to be deprecated and replaced by a custom solution, thus not included in the deployment script.
A brief description of what's going on under the hood in the deploy script.
- Prepare
deployed-<network name>.jsonfile- It is copied from
deployed-testnet-defaults.json - and expended by env variables values, e. g.
DEPLOYER. - It gets filled with the deployed contracts info from step to step.
- It is copied from
- (optional) Deploy DepositContract.
- The step is skipped if the DepositContract address is specified
- (optional) Deploy ENS
- The step is skipped if the ENS Registry address is specified
- Deploy Aragon framework environment
- Deploy standard Aragon apps contracts (like
Agent,Voting) - Deploy
LidoTemplatecontract- This is an auxiliary deploy contract which performs DAO configuration
- Deploy Lido custom Aragon apps implementations (aka bases), namely for
Lido,LegacyOracle,NodeOperatorsRegistry - Registry 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,HashConsensusfor AccountingOracle,ValidatorsExitBusOracle,HashConsensusfor 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- OZ admin roles:
Burner,HashConsensusforAccountingOracle,HashConsensusforValidatorsExitBusOracle,StakingRouter,AccountingOracle,ValidatorsExitBusOracle,WithdrawalQueueERC721,OracleDaemonConfig - OssifiableProxy admins: :
LidoLocator,StakingRouter,AccountingOracle,ValidatorsExitBusOracle,WithdrawalQueueERC721 DepositSecurityModuleowner
- OZ admin roles:
Deploys the DAO to local (http://127.0.0.1:8545) dev node (anvil, hardhat, ganache).
The deployment is done from the default test account 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 derived from the default mnemonic.
Thus the node must be configured with the default test accounts derived from the mnemonic test test test test test test test test test test test junk.
- Run
yarn install(get sure repo dependencies are installed) - Run the node on default port 8545 (for the commands, see subsections below)
- Run the deploy script
bash scripts/scratch/dao-local-deploy.shfrom root repo directory - Check out the deploy artifacts in
deployed-local.json
Run the node with the command:
anvil -p 8545 --mnemonic "test test test test test test test test test test test junk"NB: Hardhat node configuration is set in
hardhat.config.jsunderhardhat: {.
To run hardhat node execute:
yarn hardhat nodeTo 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 100,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 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 to2GAS_MAX_FEE. Gas max fee. By default set to100GATE_SEAL_FACTORY. Address of the GateSeal Factory contract. Must be deployed in advance. Can be set to any0x0000000000000000000000000000000000000000to debug deployment.WITHDRAWAL_QUEUE_BASE_URI. BaseURI for WithdrawalQueueERC712. By default not set (left an empty string).
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.shDeploy artifacts information will be stored in deployed-holesky.json.
After the deployment run
NETWORK=<PUT-YOUR-VALUE> RPC_URL=<PUT-YOUR-VALUE> bash ./scripts/scratch/verify-contracts-code.shThere are some contracts deployed from other contracts for which automatic hardhat etherscan verification fails:
AppProxyUpgradeableof multiple contracts (app:lido,app:node-operators-registry,app:oracle,app:voting, ...)KernelProxy-- proxy forKernelAppProxyPinned-- proxy forEVMScriptRegistryMiniMeToken-- 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
HashConsensuscontracts forAccountingOracleandValidatorsExitBusOracle:HashConsensus.addMember; - initialize initial epoch for
HashConsensuscontracts forAccountingOracleandValidatorsExitBusOracle: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.
NB, 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_TARGET_SHARE_BP,
NOR_STAKING_MODULE_MODULE_FEE_BP,
NOR_STAKING_MODULE_TREASURY_FEE_BP,
{ 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