Upgrades & timelock
Every GENESIS contract is an upgradeable UUPS proxy, except the Genesis Beacons NFT, which is immutable. Its art, supply and reveal can never change.
| Upgradeable (proxy) | Immutable |
|---|---|
| VRF Coordinator (incl. subscriptions), Fee Manager, Staking, Automation Registry, Data Oracle and feed proxies, Randomness Beacon, Commit–Reveal, Fair Selection, Beacon Rewards, DiceRoller, RaffleGame, AutomatedCounter | Genesis Beacons NFT |
A contract's address, balances and state stay the same across upgrades. Only its code is replaced.
Two separate powers
| Role | Can do | Who holds it |
|---|---|---|
| owner | Day-to-day settings: prices, tiers, signers, proving keys, holder share. Takes effect immediately. | Deployer wallet (move to a multisig with transferOwnership) |
| upgradeAdmin | Replace a contract's code. | GenesisTimelock: every upgrade is scheduled on-chain and can only run after the delay |
The owner cannot upgrade. On Elysium the timelock delay is 48 hours. A pending upgrade is public for two days before it can run, so users can review it or exit first.
Upgrading a contract
Write the new version. It must keep the existing storage layout and only append new variables. The plugin rejects incompatible layouts. If you add state, set it up in a
reinitializer(2)function.Propose. This validates the layout, deploys the new code and schedules the upgrade:
shUPGRADE_ACTION=propose UPGRADE_TARGET=vrfCoordinator UPGRADE_TO=GenesisVRFCoordinatorV2 \ npx hardhat run scripts/upgrade.ts --network elysiumSet
UPGRADE_CALLto calldata (e.g. forinitializeV2()) to run it atomically with the upgrade.Wait for the delay.
UPGRADE_ACTION=statusshows what's pending and when it becomes executable.Execute:
shUPGRADE_ACTION=execute UPGRADE_TARGET=vrfCoordinator npx hardhat run scripts/upgrade.ts --network elysium
To abort, run UPGRADE_ACTION=cancel before it executes.
UPGRADE_TARGET is a key of contracts in deployments/<network>.json. The signer must be the timelock proposer: GENESIS_UPGRADE_PROPOSER at deploy time, or the deployer by default. Use a multisig for production.
Deploy settings
| Variable | Default | Meaning |
|---|---|---|
TIMELOCK_DELAY | 172800 (48h) on real networks, 0 locally | Minimum wait between scheduling and executing an upgrade |
GENESIS_UPGRADE_PROPOSER | deployer | Account allowed to schedule, execute and cancel upgrades |
Keep the .openzeppelin/ folder the deploy creates. It records each proxy's storage layout and is needed to validate future upgrades.