133 lines
8.6 KiB
Markdown
133 lines
8.6 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
make demo-local
|
|
```
|
|
|
|
Second terminal:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```dotenv
|
|
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|