Proxy pattern for the platform contracts: keep Transparent, or move to UUPS as we add DAO governance? #17
-
TL;DRPR #13 shipped the four platform contracts ( As we move toward DAO governance, I want to reopen the proxy-pattern choice. My recommendation: UUPS with a timelock-gated 1. Where we are todayFrom
The README's stated rationale for Transparent (§2) was: a bad V2 cannot brick the proxy (upgrade logic lives in That reasoning is sound for today's single-owner setup. The question is whether it still holds once "the owner" becomes a DAO pipeline. 2. The actual decision variableFor governance specifically, the calculus shifts from the solo-developer case. The real question isn't "which pattern is safer in the abstract" — it's:
Both patterns support DAO governance. The difference is where the upgrade authority physically lives and what failure class that exposes. 3. The case for UUPS under DAO governance
4. The case Transparent still has for a DAO
5. How governance composes (mostly pattern-independent)The governance stack is the same shape either way; the pattern only changes what sits at the bottom:
6. L1/L2 gas — same source, two cost modelsDIN's devnet is an OP-stack chain, so this is worth stating explicitly:
7. The genuinely hard part — cross-chain governance (only if we go multi-layer)If we ever deploy the same governed contracts on both L1 and L2, we must decide where governance lives. Clean pattern: governance on one layer (usually L1) sends upgrade instructions to the other through the canonical bridge ( 8. Process discipline (the part that actually decides UUPS's safety)The complexity isn't in any one layer — it's in the failure modes at the seams:
Under that discipline the UUPS bricking risk is nearly eliminated, and we keep the gas + flexibility wins. 9. RecommendationAdopt UUPS with a timelock-gated
Stay Transparent (or go hybrid — Transparent for the crown-jewel registry/stake contracts, UUPS for high-traffic peripherals) if we expect upgrade proposals from many independent authors the core team doesn't control, or if the DAO's social contract is "the upgrade path itself must be untouchable." 10. Open questions for the thread
Please react / comment. I'd like to converge before we touch the |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
I Agree with the recommendation. A few implementation-grounded notes before we commit. On migrating the current setupPROXY_KIND = "transparent" in hardhat/deploy/constants.ts exists precisely so scripts, tests, and the Hardhat Upgrades plugin always agree on the pattern — changing it to "uups" is a one-line diff, and upgrades.validateUpgrade (called in all four upgrade test suites) will enforce UUPS compliance automatically from that point. The V2 test contracts are all thin inheritors (DinCoordinatorV2 is DinCoordinator, etc.), so the structural change at the base is minimal: add UUPSUpgradeable to the inheritance chain, add a protected _authorizeUpgrade stub, and run validateUpgrade before touching any live proxy. One thing to verify before flipping: OZ's UUPSUpgradeable carries its own storage slot (__self). Inserting it into the inheritance chain needs a storage-layout pass — the __gap[50] reservations give us headroom, but the slot ordering after insertion wants an explicit check. The storage layout doc at Developer/Documentation/technical/storage_layout.md is the reference point for that audit. One non-issue worth calling out explicitly: DinCoordinator and DinValidatorStake both inherit ReentrancyGuardTransient (EIP-1153). It contributes zero persistent slots, so it doesn't complicate either the UUPS migration or __gap accounting. Answering the open questions Implementation authorship — For v1:small core team, devnet rehearsal before any mainnet proposal. That constraint is realistic and is exactly where UUPS earns its keep. If that ever changes, revisit. Bricking risk:Acceptable when upgrades.validateUpgrade is a hard CI gate before any proposal reaches the timelock. The risk is eliminated at the gate, not at execution. This is the same validation already running in the test suites promoting it to CI is the only addition needed. Timelock delay — 48h:Participants have real stake in flight during active GIs (clients, aggregators, auditors all holding staked DIN). 24h is not a meaningful exit window once task contracts are live. 48h also forces the devnet rehearsal to actually run the delay, which matters. Ossification endgame:Yes, keep it on the table. Under UUPS, shipping a final implementation with _authorizeUpgrade that reverts unconditionally is a single governance proposal — protocol maturity should have a clean path to locking the upgrade mechanism. That capability doesn't exist at all under Transparent. Multi-layer :Out of scope for v1. Noted and agreed. Hybrid :Not worth it. DINModelRegistry and DinValidatorStake are the higher-stakes contracts, but the CI gate covers the bricking risk that motivates a hybrid. Running two proxy patterns in one fleet adds maintenance surface for no clear return. Practical migration windowMigrating a live Transparent proxy to UUPS requires deploying a UUPS-compatible implementation and upgrading through the existing ProxyAdmin — the proxy bytecode itself stays put, but the ProxyAdmin lingers until explicitly discarded. If we're aligned on UUPS, doing it before any mainnet deployment keeps the ProxyAdmin lifecycle clean. Everything is still on Optimism Sepolia devnet, so the window is open now. |
Beta Was this translation helpful? Give feedback.
I Agree with the recommendation. A few implementation-grounded notes before we commit.
On migrating the current setup
PROXY_KIND = "transparent" in hardhat/deploy/constants.ts exists precisely so scripts, tests, and the Hardhat Upgrades plugin always agree on the pattern — changing it to "uups" is a one-line diff, and upgrades.validateUpgrade (called in all four upgrade test suites) will enforce UUPS compliance automatically from that point. The V2 test contracts are all thin inheritors (DinCoordinatorV2 is DinCoordinator, etc.), so the structural change at the base is minimal: add UUPSUpgradeable to the inheritance chain, add a protected _authorizeUpgrade stub, and run validateUpgrade be…