Files
self/contracts/tasks/upgrade
Evi Nova bc4e52bb1e Refactor/multitiered multisig roles (#1483)
* refactor: switch to multitiered governance with multisigs

* feat: add scripts for assisting with upgrading contracts and

* test: add tests for governance upgrade

* chore: install Foundry with Hardhat compatability

* fix: add separate intializeGovernance function for upgrading

Uses reinitializer modifier for proper security around function call

* feat: migrate new function to AccessControl governance

* test: full end to end upgrade typescript test

* chore: add hardhat-upgrade

* chore: add foundry outputs to gitignore

* test: add Foundry upgrade script and test for deployed contracts

* refactor: update PCR0 inputs to be 32 bytes for GCP image hashes

Still pad to 48 bytes to ensure compatibility with mobile app.

* feat: add PCR0 migration script + test file

* fix: use custom natspec to prevent constructor warnings on upgrade

* test: cleanup tests and add role transfer to upgrade script

* test: add deployed libraries to foundry.toml for proper library linking

* chore: add /contracts/broadcast to gitignore for foundry deployments

* fix: set variable in initializer instead of defining in declaration

* test: improve upgrade test script to check all state variables

* docs: better explain safety behind using unsafeSkipStorageCheck

* doc: add guide for upgrading to AccessControl governance

* style: change multisig role names

CRITICAL_ROLE -> SECURITY_ROLE (3/5)
STANDARD_ROLE -> OPERATIONRS_ROLE (2/5)

* refactor: change OFAC + CSCA root update functions to 2/5 multisig

* fix: package version clashes + outdated code from old ver of packages

OpenZeppelin v5.5.0 no longer requires __UUPS_Upgradeable_Init, new OZ version requires opcodes that need cancun evmVersion, hard defining @noble/hashes led to clashes with other dependencies

* fix: fix PCR0 tests broken from change in byte size

* feat: add contract upgrade tooling with Safe multisig integration

- Add unified 'upgrade' Hardhat task with automatic safety checks
- Add deployment registry for version tracking
- Add Safe SDK integration for auto-proposing upgrades
- Update UPGRADE_GUIDE.md with new workflow documentation
- Validate version increments, reinitializer, and storage layout

* fix: revert fix on Hub V1 contract that is not supported

* style: update upgraded contracts to not use custom:version-history

* fix: V1 test requires old style as well

* fix: correct registry currentVersion to reflect actual deployed versions

On-chain verification confirmed all contracts are using OLD Ownable2StepUpgradeable:
- Hub: 2.11.0 (was incorrectly 2.12.0)
- Registry: 1.1.0 (was incorrectly 1.2.0)
- IdCard: 1.1.0 (was incorrectly 1.2.0)
- Aadhaar: 1.1.0 (was incorrectly 1.2.0)

Owner address: 0xcaee7aaf115f04d836e2d362a7c07f04db436bd0

* fix: upgrade script now correctly handles pre-defined versions in registry

When upgrading to a version that already exists in registry.json (like 2.12.0),
the script now uses that version's initializerVersion instead of incrementing
from the latest version. This fixes the reinitializer validation for the
governance upgrade.

* fix: upgrade script handles Ownable contracts and outputs transaction data

- Detect Ownable pattern before creating Safe proposals
- Output transaction data for owner direct execution in --prepare-only mode
- Use initializerFunction from registry (initializeGovernance) instead of constructing names
- Skip Safe proposal creation for initial Ownable → AccessControl upgrade
- After upgrade, owner grants SECURITY_ROLE to Safe for future upgrades

* feat: IdentityVerificationHub v2.12.0 deployed on Celo

- Implementation: 0x05FB9D7830889cc389E88198f6A224eA87F01151
- Changelog: Governance upgrade

* feat: IdentityRegistryIdCard v1.2.0 deployed on Celo

- Implementation: 0x7d5e4b7D4c3029aF134D50642674Af8F875118a4
- Changelog: Governance upgrade

* feat: IdentityRegistryAadhaar v1.2.0 deployed on Celo

- Implementation: 0xbD861A9cecf7B0A9631029d55A8CE1155e50697c
- Changelog: Governance upgrade

* feat: IdentityRegistry v1.2.0 deployed on Celo

- Implementation: 0x81E7F74560FAF7eE8DE3a36A5a68B6cbc429Cd36
- Changelog: Governance upgrade

* feat: add multisig addresses to registry

* feat: PCR0Manager v1.2.0 deployed on Celo

- Implementation: 0x9743fe2C1c3D2b068c56dE314e9B10DA9c904717
- Changelog: Governance upgrade

* refactor: cleanup old scripts

* chore: yarn prettier formatting
2025-12-10 17:30:50 +10:00
..

Upgrade Tooling

A comprehensive toolset for safely upgrading UUPS proxy contracts in the Self Protocol.

Overview

The upgrade tooling provides:

  • Safety checks - Storage layout validation, version validation, reinitializer verification
  • Safe multisig integration - Creates proposals for SECURITY_ROLE approval
  • Version tracking - Automatic registry updates and git tagging
  • Audit trail - Complete deployment history with changelogs

Quick Start

# Single command to validate, deploy, and propose
npx hardhat upgrade --contract IdentityVerificationHub --network celo --changelog "Added feature X"

The upgrade Command

Validates, deploys, and creates a Safe multisig proposal in one step.

npx hardhat upgrade \
  --contract <ContractId> \
  --network <network> \
  [--changelog <message>] \
  [--prepare-only]

Options:

  • --contract - Contract to upgrade (IdentityVerificationHub, IdentityRegistry, etc.)
  • --network - Target network (celo, sepolia, localhost)
  • --changelog - Description of changes
  • --prepare-only - Deploy implementation without creating Safe proposal

What it does:

  1. Validates @custom:version increment
  2. Checks reinitializer(N) matches expected version
  3. Validates storage layout compatibility
  4. Clears cache and compiles fresh
  5. Compares bytecode (warns if unchanged)
  6. Deploys new implementation
  7. Updates deployment registry
  8. Creates git commit and tag
  9. Creates Safe proposal (or outputs manual instructions)

Utility Commands

# Check current deployment status
npx hardhat upgrade:status --contract IdentityVerificationHub --network celo

# View version history
npx hardhat upgrade:history --contract IdentityVerificationHub

Workflow

For Developers

┌─────────────────────────────────────────────────────────────────────┐
│ 1. UPDATE CONTRACT CODE                                             │
│    - Make your changes                                              │
│    - Update @custom:version in NatSpec                              │
│    - Increment reinitializer(N) modifier                            │
│    - Add new storage fields at END of struct only                   │
├─────────────────────────────────────────────────────────────────────┤
│ 2. RUN: npx hardhat upgrade --contract X --network Y --changelog Z  │
│    - Validates all safety checks                                    │
│    - Deploys new implementation                                     │
│    - Updates registry.json                                          │
│    - Creates git commit + tag                                       │
│    - Creates Safe proposal                                          │
├─────────────────────────────────────────────────────────────────────┤
│ 3. MULTISIG APPROVAL                                                │
│    - Signers review in Safe UI                                      │
│    - Once threshold met, click Execute                              │
└─────────────────────────────────────────────────────────────────────┘

Contract Update Pattern

/**
 * @title MyContract
 * @custom:version 2.13.0  // <-- Update this
 */
contract MyContract is ImplRoot {

    struct MyStorage {
        uint256 existingField;
        uint256 newField;  // <-- Add new fields at end only
    }

    // Increment reinitializer(N) for each upgrade
    function initialize(...) external reinitializer(13) {
        // Initialize new fields if needed
        MyStorage storage $ = _getMyStorage();
        if ($.newField == 0) {
            $.newField = defaultValue;
        }
    }
}

Configuration

Deployment Registry

The registry (deployments/registry.json) tracks:

  • Proxy addresses per network
  • Current versions
  • Implementation history
  • Git commits and tags

Governance Configuration

Multisig addresses are configured in deployments/registry.json:

{
  "networks": {
    "celo": {
      "governance": {
        "securityMultisig": "0x...",
        "operationsMultisig": "0x...",
        "securityThreshold": "3/5",
        "operationsThreshold": "2/5"
      }
    }
  }
}

Environment Variables

Required for deployments:

PRIVATE_KEY=0x...          # Deployer private key
CELO_RPC_URL=https://...   # RPC endpoint

Supported Contracts

Contract ID Contract Name Type
IdentityVerificationHub IdentityVerificationHubImplV2 UUPS Proxy
IdentityRegistry IdentityRegistryImplV1 UUPS Proxy
IdentityRegistryIdCard IdentityRegistryIdCardImplV1 UUPS Proxy
IdentityRegistryAadhaar IdentityRegistryAadhaarImplV1 UUPS Proxy

Safety Checks

Check What it Does Failure Behavior
Version validation Ensures semantic version increment Blocks upgrade
Reinitializer check Verifies reinitializer(N) matches version Blocks upgrade
Storage layout Detects breaking storage changes Blocks upgrade
Bytecode comparison Warns if code unchanged Prompts confirmation
Safe role verification Confirms Safe has SECURITY_ROLE Blocks upgrade
Constructor check Flags _disableInitializers() Prompts confirmation

Troubleshooting

Issue Solution
"Version matches current" Update @custom:version in contract
"Reinitializer mismatch" Update reinitializer(N) to next version
"Storage layout incompatible" Don't remove/reorder storage variables
"Safe not indexed" Submit manually via Safe UI
"Bytecode unchanged" Ensure you saved contract changes