Abstract
In a particular way, Dual Governance rollout is a one-way decision: any mistake made in tweaking the upgradability of the protocol risks causing irreparable damage. To make sure the rollout is safe to propose, Contributors have made specific preparations in order to make sure that 1) the deployed code functions as expected; 2) the rollout itself is doubly and triply checked.
Beyond code audits: preparing the deployment
It’s worth mentioning that the spec, code and parameters going to the Dual Governance proposal had been thoroughly audited by a bunch of third parties. Mechanism specification had seen the review by Certora and RV; code was formally verified by Certora and RV and audited by StateMind and OpenZeppelin, and parameters chosen had been challenged and tested by 20 and CollectifLabs
What’s usually left to check is:
- If the code deployed matches the audited version.
- If the protocol roles and parameters required are set correctly (and what specifically “set correctly” means).
- If the vote for switching the mechanism on is correct.
Smoke test
Usually, Lido Contributors strive to have as ready-for-vote deployment as possible. What that means is: contracts are deployed, potentially have a handful of calls made to set everything up — and left as-is for the DAO vote.
In the case of Dual Governance, though, a more thorough mainnet smoke test was performed. Dual Governance makes use of Emergency Committee: there are methods allowing a dedicated multisig to pause Dual Governance pipeline or even reset it fully in case of unforeseen technical vulnerability getting detected. In order to ultimately check the emergency brake works, it simply had been triggered after the mainnet contracts deployment. This smoke test served two purposes:
- Check the brakes work on the specific bytecode at specific Ethereum addresses — as an ultimate functional test.
- Make sure Emergency Committee members know and understand how to act in case their action is required.
After Emergency reset had been triggered, the temporary governance (in this case, a Contributors multisig) had set the DG parameters to the final state and had renounced any access or roles in Dual Governance it had.
Roles, rights, and ownership
In order for Dual Governance to be useful, 1) it must “guard” DAO’s access to high-leverage roles and rights; 2) with no way to circumvent the guard. Contributors have compiled the list of roles, rights, and permissions among Lido-related smart contracts: dual-governance/docs/permissions-transition/permissions-transition-mainnet.md at feat/deploy-plan · lidofinance/dual-governance · GitHub. On a high-level:
- Aragon Voting passes all the roles (except treasury management, insurance fund, and all roles related to Voting and TokenManager contracts) and ownerships to Aragon Agent.
- Aragon Agent:
- Grants a role for
Agent.forward
to Dual Governance Executor contract. - Revokes a role for
Agent.forward
from Voting.
- Grants a role for
- EVMScriptExecutor/EasyTrack remains as-is (Voting is owner of EVMScriptExecutor and holder of
DEFAULT_ADMIN_ROLE
for EasyTrack, but given Voting isn’t role manager / admin on things which need to be under DG — Voting can’t grant permissions necessary to breach “DG protection”). - Committees remain as-is to maintain business continuity.
- Immutable variables remain as-is until future updates.
- All EasyTrack factories remain as-is until future updates.
The document referenced has a full-blown list of actions and role changes required for the implementation. Need to note that even for a “do as little as possible” markup 50 different vote actions are required only to make minimal viable roles shift.
DAO Vote markup
50+ action items in the vote are tough to manage for anyone. In order to make sure no operational error has slipped in, a couple of ideas were implemented:
- Vote actions are stored in a “read-only” smart contract onchain (DGLaunchOmnibusMainnet | Address 0x1DB8a9313785b78f7d0a201C5E0BE007f1eb63b4 | Etherscan). That allows for auditability, as well as has a method to check if the vote number X match the expected plan.
- There’s an on-chain contract (DGRolesValidatorMainnet | Address 0x31534e3aFE219B609da3715a00a1479D2A2d7981 | Etherscan) to make an acceptance test on roles, rights, and ownership transferred during the vote. It’s called by the end of the vote, reverting the vote execution transaction in case anything unexpected is detected.
- There’s an on-chain contract (DGLaunchStateVerifier | Address 0xd48c2fc419569537Bb069BAD2165dC0cEB160CEC | Etherscan) to check the Dual Governance state during the vote execution. In case any part of the setup ends up in other-than-expected state, the vote execution would get reverted as well.
- Given the dynamic nature of the Dual Governance timelock, operational smart contract (TimeConstraints | Address 0x2a30F5aC03187674553024296bed35Aa49749DDa | Etherscan) preventing vote execution at random times during the day are implemented. Long story short — that way one can enforce specific operational concerns on timing without making the core mechanics more complex.
All contracts used in the vote had been reviewed by auditors, providing third party verification not only for mechanics implementation, but to the rollout plan as well.
Switching Dual Governance on
The onchain vote to switch Dual Governance on is scheduled for the 25th of June. Feel free to ask any questions here, and if you hold LDO — please, make sure to send your vote =)