2026-08-17 16:01:42 -06:00
2026-08-21 14:58:24 -06:00
2026-08-17 16:01:42 -06:00
2026-08-17 16:01:42 -06:00
2026-08-17 16:01:42 -06:00
2026-08-17 16:01:42 -06:00
2026-08-21 16:01:35 -06:00
2026-08-17 16:01:42 -06:00
2026-08-22 21:00:47 -06:00
2026-08-17 16:01:42 -06:00

UUPS Bank V1 → V2 Demo

Educational demo — mock token — never use real funds.

This repository is a local-first upgradeability lesson. It deploys a six-decimal mock ERC-20 and a V1 custody ledger behind an ERC-1967 proxy, upgrades the proxy to V2 without losing state, performs a customer-to-customer ledger transfer, and presents the result in a read-only operations console.

Trust boundary: MockUSDC has no value. These contracts are educational and unaudited; real deposits must never be sent here. The owner can pause customer actions and install arbitrary future logic. UUPS mistakes can corrupt state or permanently brick upgradeability. A real custody product requires professional audits, operational key controls, multisig or timelocked governance, incident procedures, legal advice, and jurisdiction-specific compliance work.

Prerequisites

Use a Linux-like Bash environment with Git, GNU Make, curl, Foundry 1.7.1 (forge, anvil, and cast), Node 24.18.0, and npm 11.17.0. The exact package graph is committed in the lockfiles. Installation documentation: Git, Foundry, Node, npm, Bash, Make, and curl.

make setup
make doctor

make setup initializes recursive Git submodules and installs both pinned npm dependency trees. make doctor is read-only: it verifies versions, dependencies, writable runtime locations, and that local ports 8545 and 5173 are available.

Ten-minute local quick start

This complete path is local. It needs no Base Sepolia service, browser wallet, faucet, explorer, public RPC, keystore, secret, or real funds.

In the first terminal:

make demo-local

The command performs a scoped reset, starts deterministic Anvil at http://127.0.0.1:8545, deploys and seeds V1, checks its exact state, exports public artifacts, and starts the console at http://127.0.0.1:5173/. It remains attached so Ctrl-C safely stops only the recorded project process groups.

In a second terminal:

DEMO_EXPECTED_STAGE=v1 make check-state
make upgrade-v2
make demo-transfer
DEMO_EXPECTED_STAGE=v2 make check-state
curl --fail http://127.0.0.1:5173/

Act 1 proves Alice has 900 mUSDC, Bob has 500 mUSDC, liabilities and reserves are both 1,400 mUSDC, and version is 1. The upgrade keeps the proxy, owner, asset, pause state, customer balances, liabilities, reserves, surplus, and deployment block unchanged while changing the implementation and version to 2. The internal 250 mUSDC transfer then leaves Alice with 650 mUSDC and Bob with 750 mUSDC; liabilities and reserves remain 1,400 mUSDC, and the console decodes BalanceTransferred without an ERC-20 reserve transfer.

Stop the attached first terminal with Ctrl-C, then remove only reproducible local state:

make reset-local

Architecture and guarantees

Foundry scripts are the state-changing control plane; the browser has no connector, signer, transaction client, or write control. MockUSDC holds no value. An ERC-1967 proxy keeps the bank address and storage stable while delegating calls to BankV1 or BankV2. The proxy holds token reserves and the internal ledger records liabilities. Confirmed public manifests and generated ABIs connect that system to the React/Vite console through viem and wagmi.

OpenZeppelin supplies the UUPS and access-control primitives plus storage/upgrade validation. Foundry supplies compilation, tests, scripts, and the local chain. The project supplies the application logic, tests, manifests, lifecycle tooling, and UI. Passing those checks does not make the demo audited, decentralized, or suitable for custody.

Command reference

  • make doctor — check prerequisites, dependencies, runtime locations, configuration, and local ports without changing chain state.
  • make setup — initialize pinned submodules and npm dependencies.
  • make demo-local — run the attached local V1 experience.
  • make verify — run Solidity format/build/unit/fuzz/invariant/upgrade gates, artifact and shell-safety tests, the project scanner, and web lint/type/tests/build.
  • make check-state — print and validate the selected stage; use DEMO_EXPECTED_STAGE=v1 or v2 for the exact local acts.
  • make upgrade-v2 — validate and perform the owner-authorized local V1-to-V2 upgrade, finalize its manifest, and refresh exported artifacts.
  • make demo-transfer — perform Alice's local 250 mUSDC internal transfer to Bob and validate Act 3.
  • make reset-local — validate recorded process identity, stop only owned groups, and remove only known local artifacts.
  • make deploy-v1 and make seed-v1 — run the lower-level guarded V1 deployment and deterministic Act 1 setup used by make demo-local.
  • make sync-artifacts, make sync-artifacts-check, make sync-abis, and make publish-web-manifest — regenerate or validate the ABI bridge and publish only the confirmed active manifest.
  • make test-finalize-manifest — exercise manifest finalization in isolation.
  • make select-anvil and make select-base-sepolia — explicitly select and republish an existing confirmed manifest.
  • make archive-base-manifest — safely archive the Base canonical manifest before an intentional redeployment.
  • make deploy-base-sepolia, make upgrade-base-sepolia, and make transfer-base-sepolia — explicit optional-testnet commands with chain, actor, account, and RPC guards.

Optional Base Sepolia encore

The local lesson above is complete without a wallet, faucet, explorer, or public RPC. The optional Base Sepolia encore is separate and runs only after the local V1→V2 demo and make verify succeed. It uses a named encrypted Foundry keystore through --account and the same public address through --sender; no supported command accepts a raw private key or mnemonic.

Copy .env.example to .env and fill only its public configuration. BASE_SEPOLIA_RPC_URL is the terminal endpoint and may be credentialed; BASE_SEPOLIA_PUBLIC_RPC_URL is intentionally public and is the only RPC serialized for the browser. Foundry requests the keystore password interactively, and that password never belongs in .env.

Use make archive-base-manifest before an intentional Base redeployment. It moves only the Base canonical manifest to a timestamped sibling while preserving the active browser fallback and Anvil state. See the presenter runbook for the exact optional onboarding, funding boundary, execution sequence, and recovery rules.

Continue with the learning guide or rehearse from the presenter runbook.

S
Description
No description provided
Readme
378 KiB
Languages
Solidity 34.6%
JavaScript 21.4%
TypeScript 20.2%
Shell 20%
CSS 2.2%
Other 1.5%