You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Contract upgrades are authorised by _authorizeUpgrade, gated onlyOwner on every UUPS proxy, so a single owner key can replace any implementation and bypass every other control. Before mainnet we need to decide who holds upgrade authority. Two options below; the aim is to pick one and wire it consistently across all upgradeable contracts.
Proposal
Option 1: governance (Root), via originIsRoot().
Gate _authorizeUpgrade on ISystem(SYSTEM_ADDR).originIsRoot(). Only a Root-origin dispatch can upgrade, which on Polkadot Asset Hub means a passed OpenGov referendum. originIsRoot() (the pallet-revive System precompile) reads the transaction-level origin and holds through the UUPS delegatecall frame, unlike callerIsRoot(), which returns false from behind the proxy.
Decentralised: token holders decide, and there is no admin key.
Secure: nothing to steal or phish, and upgrades ride the Root referendum track with its confirmation period.
Robust to the Asset Hub Migration: whether OpenGov dispatches locally on Asset Hub or reaches us from the Relay as Root over XCM, the contract sees Root and the gate holds.
Option 2: wallet (keep onlyOwner).
Owner is an account, either an EOA or a multisig. This is today's model; the only change worth making is moving owner to a shared multisig.
Fast: an upgrade is a single transaction, no referendum.
Simple: no new precompile dependency, and no track to stand up.
Weaker: the owner keys are a standing target. A compromised key, or a coerced signer set, can replace any implementation and bypass every other control. A multisig raises the bar but is still a fixed keyholder set, not the token holders.
Recommendation. Option 1 for mainnet, since upgrade is the most dangerous power and it should have no single key behind it. Option 2 with a multisig is a reasonable interim on testnets, or until the Asset Hub Migration lands OpenGov on Polkadot Asset Hub.
Acceptance criteria
Upgrade authority decided and recorded.
_authorizeUpgrade updated to the chosen authority across every UUPS proxy.
If governance: gated on originIsRoot(), with a paired fork test proving a Root dispatch can upgrade and a non-root caller cannot.
If wallet: owner set to the agreed account or multisig and documented in DEPLOYMENTS.md.
Component
Other
Priority
P1
Summary
Contract upgrades are authorised by
_authorizeUpgrade, gatedonlyOwneron every UUPS proxy, so a single owner key can replace any implementation and bypass every other control. Before mainnet we need to decide who holds upgrade authority. Two options below; the aim is to pick one and wire it consistently across all upgradeable contracts.Proposal
Option 1: governance (Root), via
originIsRoot().Gate
_authorizeUpgradeonISystem(SYSTEM_ADDR).originIsRoot(). Only a Root-origin dispatch can upgrade, which on Polkadot Asset Hub means a passed OpenGov referendum.originIsRoot()(the pallet-revive System precompile) reads the transaction-level origin and holds through the UUPS delegatecall frame, unlikecallerIsRoot(), which returns false from behind the proxy.Option 2: wallet (keep
onlyOwner).Owner is an account, either an EOA or a multisig. This is today's model; the only change worth making is moving owner to a shared multisig.
Recommendation. Option 1 for mainnet, since upgrade is the most dangerous power and it should have no single key behind it. Option 2 with a multisig is a reasonable interim on testnets, or until the Asset Hub Migration lands OpenGov on Polkadot Asset Hub.
Acceptance criteria
_authorizeUpgradeupdated to the chosen authority across every UUPS proxy.originIsRoot(), with a paired fork test proving a Root dispatch can upgrade and a non-root caller cannot.