Skip to content

DHRUVCHARNE/foundry-upgradable-smart-contract

Repository files navigation

Upgradable Box (UUPS) — Foundry

Live on Sepolia testnet

  • BoxV1 (implementation v1): 0x93c4c2bdecd10809b7aa4dabab52fc068d7c0fa6
  • BoxV2 (implementation v2): 0x1dfe668525caecdde986c8395adfb609f12db89d
  • Proxy (ERC1967Proxy): 0x9081f5e9dc38ecc02ffd2916a4b0c51bd4437a79

Overview

This project demonstrates a UUPS (Universal Upgradeable Proxy Standard) smart contract workflow using Foundry and OpenZeppelin. The system consists of a single ERC1967 proxy whose logic can be upgraded from BoxV1 to BoxV2 without changing the proxy address or losing state.

The contracts are deployed and upgraded using Foundry scripts, and the upgrade flow is validated with Foundry-based tests.


Architecture

  • ERC1967Proxy

    • Holds all contract state
    • Delegates calls to the current implementation
  • BoxV1 (Initial Implementation)

    • UUPS-enabled
    • Uses initializer pattern instead of constructor
    • Exposes read-only functionality
  • BoxV2 (Upgraded Implementation)

    • Extends functionality by allowing state mutation
    • Preserves storage layout to ensure safe upgrades
  • Upgrade Pattern

    • UUPS-based upgrade via the proxy
    • Proxy calls upgrade logic defined in the implementation

Deployment Flow

  1. BoxV1 is deployed as an implementation contract.
  2. An ERC1967Proxy is deployed pointing to BoxV1.
  3. The proxy becomes the primary contract users interact with.

The proxy address remains constant across upgrades.


Upgrade Flow (V1 → V2)

  1. A new implementation (BoxV2) is deployed.
  2. The proxy calls the UUPS upgrade function.
  3. The proxy’s implementation pointer is updated.
  4. All existing state is preserved.

After the upgrade, calls to the proxy execute BoxV2 logic.


Testing Strategy

The test suite validates:

  • The proxy initially behaves like BoxV1
  • Functions introduced in BoxV2 are unavailable before upgrade
  • The upgrade successfully switches logic to BoxV2
  • State can be written and read correctly after upgrade

Security Considerations

  • Upgrade Authorization

    • The _authorizeUpgrade hook must be restricted (e.g. owner or multisig)
    • Leaving it unrestricted allows anyone to upgrade the contract
  • Initializer Safety

    • Initializers are disabled on implementation contracts to prevent misuse
  • Storage Layout

    • New versions must only append state variables
    • Reordering or removing variables can corrupt storage

Verification Notes

For UUPS proxies:

  • Verify implementation contracts on Etherscan (BoxV1, BoxV2)
  • Mark the proxy as a proxy contract on Etherscan
  • Once linked, Etherscan will display the ABI and allow interaction via the proxy address

Tooling

  • Foundry (forge, cast)
  • OpenZeppelin Contracts
  • OpenZeppelin Upgradeable Contracts
  • ERC1967 + UUPS standard

Why UUPS?

  • Cheaper upgrades than Transparent Proxy
  • Upgrade logic lives in the implementation
  • Clear separation of state (proxy) and logic (implementation)
  • Widely used in production protocols

License

MIT


Author

Dhruv Charne

This repository is intended for learning, experimentation, and as a reference for production-grade upgradeable smart contract patterns using Foundry.