Files
uupl-smart-contract/docs/PRESENTER_RUNBOOK.md
T

8.6 KiB

Presenter Runbook: Prepared V1 and Live UUPS Upgrade

Educational demo — mock token — never use real funds.

Preflight and rehearsal

Rehearse once from a clean disposable branch created at the demo-start tag. Allow 25 minutes: 3 minutes for preflight, 5 for Act 1, 10 for the Codex change and verification, 4 for Act 3, and 3 for questions. Keep two terminals visible and open the browser only after Vite reports ready.

Before the audience arrives:

make setup
make reset-local
make doctor
make verify
git diff --check
git status --short

The doctor must show Foundry 1.7.1, Node 24.18.0, npm 11.17.0, initialized dependencies, writable runtime paths, and free ports 8545/5173. Verification must exit 0. No upgrade or transfer command runs until make verify passes.

The three-act story

Act 1 — Prepared V1 establishes trust

First terminal:

make demo-local

Second terminal:

DEMO_EXPECTED_STAGE=v1 make check-state
curl --fail http://127.0.0.1:5173/

Open http://127.0.0.1:5173/; Anvil is at http://127.0.0.1:8545. Call out that every browser read targets the proxy, while the distinct implementation address is shown only to teach delegation. The exact expected state is Alice 900 mUSDC, Bob 500 mUSDC, liabilities 1,400 mUSDC, reserves 1,400 mUSDC, surplus 0, and version 1. Point to deposit, withdrawal, and upgrade/ownership events, then state that the browser is read-only.

Explain the initial flow: scripts minted 2,000 mUSDC to Alice and 1,000 to Bob; Alice deposited 1,000 and withdrew 100; Bob deposited 500. The stable proxy holds reserves and storage. Implementation code runs against that storage with delegatecall.

Act 2 — Codex changes the system live

Give Codex this approved prompt verbatim:

Add BankV2 with customer-to-customer internal transfers. Preserve the UUPS storage layout and all V1 behavior. Add unit, fuzz, invariant, and upgrade-regression tests; an owner upgrade script; a scripted Alice-to-Bob transfer; exported ABI support; and the read-only console updates needed to show V2 and its transfer event. Explain each security decision. Do not perform an upgrade until all verification passes.

Ask Codex to show the diff and explain storage compatibility, authorization, conservation, error paths, and why the browser remains read-only. The expected prepared change adds transfer-aware source, tests, scripts, ABI output, and console rendering without changing V1 storage. Run:

make verify

If any gate fails, stop and let Codex diagnose it. Do not run an upgrade merely because a partial test command passed.

Act 3 — V2 proves continuity

After Codex has implemented these targets and the full gate has passed:

make upgrade-v2
make demo-transfer
make check-state

The expected result is the same proxy and a new implementation, version 2, Alice 650 mUSDC, Bob 750 mUSDC, liabilities 1,400 mUSDC, reserves 1,400 mUSDC, and a decoded 250 mUSDC internal transfer event. No ERC-20 transfer should accompany the ledger transfer. Refresh only if the console has not observed the next block; otherwise let its live status prove the change.

Recovery without broad cleanup

  • Occupied port: make demo-local refuses to claim either port. Use curl http://127.0.0.1:8545 and curl http://127.0.0.1:5173/ plus your operating system's process inspection to identify the external owner. Stop it yourself only after proving ownership; the project never uses broad process matching.
  • Recorded stale process: run make reset-local. It validates numeric PID, process group, command signature, and Linux start tick before signaling. A mismatched live process is preserved and reset exits nonzero.
  • Stale console: confirm DEMO_EXPECTED_STAGE=v1 make check-state, inspect .demo/vite.log, and use curl --fail http://127.0.0.1:5173/. The console labels stale/disconnected state and preserves the last good snapshot rather than inventing zeros.
  • Failed test or live edit: do not upgrade. Save the diff for review, continue diagnosis on the disposable live branch, or start a new disposable branch from demo-start; never overwrite unrelated work. The prepared V1 remains the successful ending if time expires.
  • Unexpected child exit: the attached launcher stops its other validated group. Inspect .demo/anvil.log and .demo/vite.log, then run make reset-local and restart.

End the local session with Ctrl-C in the attached terminal, then make reset-local. The optional public encore is outside this prepared V1 run: local success does not depend on Base Sepolia, a faucet, an explorer, a wallet, or any external RPC.

Optional Base Sepolia encore

Run this only after the completed local demo and only when the presenter explicitly chooses the public encore. Create a named encrypted Foundry keystore before any Base deployment, upgrade, transfer, selection, or archive command:

cast wallet import uups-bank-base --interactive
cast wallet address --account uups-bank-base

Copy the displayed public address to BASE_SEPOLIA_SENDER, set BASE_SEPOLIA_ACCOUNT=uups-bank-base, and verify the two refer to the same account. Choose a different nonzero public address for BASE_SEPOLIA_RECIPIENT. Fund only the displayed Base Sepolia sender address with Base Sepolia test ETH. Never paste the key or password into Codex, shell history, or .env; Foundry requests the encrypted-keystore password through its interactive prompt.

Copy .env.example to .env and configure:

# Terminal RPC may be credentialed; never copied into browser artifacts.
BASE_SEPOLIA_RPC_URL=
# Browser RPC is intentionally public and visible to browser users.
BASE_SEPOLIA_PUBLIC_RPC_URL=https://sepolia.base.org
BASE_SEPOLIA_ACCOUNT=uups-bank-base
BASE_SEPOLIA_SENDER=
BASE_SEPOLIA_RECIPIENT=

The Base commands require HTTPS RPCs, reject browser URL credentials, validate the named account identifier and distinct nonzero actors, and bind both --account and --sender. They never accept a raw key or mnemonic. First run the offline gate and inspect the fake-value dry run:

make verify
bash tools/test-base-config.sh
make -n deploy-base-sepolia BASE_SEPOLIA_RPC_URL=https://terminal.invalid BASE_SEPOLIA_PUBLIC_RPC_URL=https://public.invalid BASE_SEPOLIA_ACCOUNT=demo BASE_SEPOLIA_SENDER=0x1111111111111111111111111111111111111111 BASE_SEPOLIA_RECIPIENT=0x2222222222222222222222222222222222222222

The dry run must contain --account, --sender, and --slow, and must not contain a raw-key option. The URLs above are deliberately fake; these checks require no keystore, faucet, or network. Then, only with explicit authorization and Base Sepolia test funds, execute:

make deploy-base-sepolia
make upgrade-base-sepolia
make transfer-base-sepolia

Deployment mints valueless mUSDC only to Presenter and deposits 1,000 mUSDC; Recipient starts at 0. The V2 transfer produces Presenter 750 mUSDC and Recipient 250 mUSDC, while reserves and liabilities stay 1,000 mUSDC. Each command runs an invariant-only state check. The confirmed browser manifest contains only the intentionally public browser RPC plus https://sepolia.basescan.org; the terminal RPC is never serialized.

Before an intentional Base redeployment, run make archive-base-manifest. It moves only deployments/base-sepolia.json to a validated timestamped sibling and leaves active.json and Anvil state untouched. A subsequent finalizer may create a new Base canonical manifest; the old active copy remains the browser fallback until make select-base-sepolia succeeds. Use make select-anvil to return the browser to the local manifest. If a faucet, RPC, or explorer fails, record the optional failure and stop—the completed local demo remains the successful outcome.

Closing trust disclosure checklist

Read these points while the matching console panel is visible:

  • 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.

Codex supplies the cross-stack implementation, tests, orchestration, and explanation. OpenZeppelin supplies reviewed contract primitives and upgrade validation; Foundry supplies compilation, tests, scripts, and the local chain. None of those tools turns this teaching artifact into an audited or regulated custody product.