The uups-bank-demo wave's SDD records (ledger, six task briefs and reports, review diffs) and the brainstorm design mockups were git-ignored, so they existed only on one sandbox VM and reached no remote — this repo had no remote at all until now. Removes `.superpowers/` from .gitignore and the `*` .gitignore the superpowers plugin writes inside .superpowers/sdd/; the second blocks the directory even with the first removed. Excluded as ephemeral local-server state, and now ignored by name: .last-port, .last-token (a 64-char session token for a brainstorm server on a port that is long gone), and the per-session state/ directories. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fnwzj6McD6kSkXwjUKFKxe
7.8 KiB
Task 6: Deploy, seed, inspect, and export deterministic V1 state
Files:
- Create:
script/lib/DemoScript.sol - Create:
script/DeployV1.s.sol - Create:
script/SeedV1Demo.s.sol - Create:
script/CheckState.s.sol - Create:
test/ScriptPreflight.t.sol - Create:
tools/finalize-manifest.mjs - Create:
tools/select-manifest.mjs - Create:
tools/test-finalize-manifest.mjs - Create:
deployments/.gitkeep - Modify:
Makefile
Interfaces:
-
Consumes an RPC, chain ID, explicit broadcaster, pending manifest, and Foundry broadcast receipts.
-
Produces an active schema-versioned public manifest only after receipt/code/postcondition checks, then the exact Act 1 state.
-
Establishes local actor indexes: owner
0, Alice1, Bob2from Anvil’s standard development mnemonic. -
Write
ScriptPreflight.t.solaround a small public harness forDemoScriptand cover:- chain IDs
31337and84532accepted; - chain IDs
1,8453, and an arbitrary value rejected before broadcast; - missing/invalid manifest, wrong manifest chain, zero address, and address-without-code rejected;
- internal pending manifests may use deployment block
0, while active manifests may not; - local keys derivable only when chain ID is
31337; - serialized JSON contains public addresses but never the mnemonic, a private key,
PRIVATE_KEY, orMNEMONIC.
- chain IDs
-
Run red:
forge test --match-path test/ScriptPreflight.t.sol -vvv --force
Expected red: DemoScript does not exist.
- Implement
DemoScriptconstants and guards:
uint256 internal constant ANVIL_CHAIN_ID = 31337;
uint256 internal constant BASE_SEPOLIA_CHAIN_ID = 84532;
string internal constant ANVIL_TEST_PHRASE =
"test test test test test test test test test test test junk";
string internal constant PENDING_MANIFEST_PATH = "deployments/pending.json";
string internal constant ACTIVE_MANIFEST_PATH = "deployments/active.json";
error UnsupportedChain(uint256 chainId);
error ManifestChainMismatch(uint256 expected, uint256 actual);
error MissingCode(string label, address target);
Provide narrow helpers for chain checks, vm.deriveKey on local only, manifest read/write through vm.parseJson*/vm.serialize*, code checks, and exact-state assertions. Resolve the input/output manifest from DEPLOYMENT_MANIFEST_PATH with a narrow default appropriate to each script. Never log or serialize a derived private key.
-
Write
DeployV1.s.solred tests/behavior first, then implement:- validate chain and resolve the public
SCRIPT_SENDERvalue; - start broadcast;
- deploy
MockUSDC(sender); - call
Upgrades.deployUUPSProxy("BankV1.sol:BankV1", abi.encodeCall(...)); - stop broadcast;
- assert code, owner, asset, version
1, and implementation identity; - write
deployments/pending.jsonwith schema1, network, chain, deployment block0, display RPC/explorer metadata, token, proxy, implementation, owner, and local actor labels/addresses.
- validate chain and resolve the public
On local, derive account 0, require it equals SCRIPT_SENDER, and broadcast with that derived development key. On Base Sepolia, require SCRIPT_SENDER equals the expected owner and use the signer selected by Forge’s matching --account/--sender options; never read a raw signing secret from environment.
-
Test
finalize-manifest.mjsandselect-manifest.mjswith temporary pending/broadcast fixtures and an injected fake JSON-RPC function. Cover: a pre-broadcast deploy guard that rejects an existing target-chain canonical file, exact proxy transaction match, successful receipt, receipt-derived block, actual chain-ID match, code at token/proxy/implementation, implementation-slot match, failed/missing/ambiguous receipt, partial broadcast, wrong chain, missing code, an existing active manifest from either network remaining untouched until selection, secret-bearing content, atomic same-directory rename, and selection of only a valid confirmed chain manifest. Prove a failure never creates/changes a confirmed or active manifest, and selecting Base preserves Anvil byte-for-byte (and vice versa). -
Implement the finalizer using Node standard modules and JSON-RPC
fetch. Its network-freepreflight-deploy <network>fails before Forge runs when the target canonical file exists and prints the exact safe recovery command (make reset-localormake archive-base-manifest). Deploy finalization readsbroadcast/DeployV1.s.sol/<chainId>/run-latest.json, matches the transaction whose created address is the pending proxy, matches its transaction hash to a successful receipt, uses that receipt’s real block number, querieseth_chainId,eth_getCodefor all three contracts, and queries the EIP-1967 implementation slot. Atomically write the chain’s canonicalanvil.jsonorbase-sepolia.jsononly after all checks pass.select-manifest.mjsvalidates a named canonical file and atomically copies it toactive.json; it never deletes or overwrites the other chain. Never copy a credential-bearing terminal RPC into JSON. -
Write and implement
SeedV1Demo.s.sol, hard-guarded to chain31337. It must perform and assert this exact sequence:
owner mints Alice 2,000 mUSDC
owner mints Bob 1,000 mUSDC
Alice approves and deposits 1,000 mUSDC
Bob approves and deposits 500 mUSDC
Alice withdraws 100 mUSDC
Postconditions: Alice internal 900e6, Bob internal 500e6, liabilities 1_400e6, reserves 1_400e6, version 1, no surplus.
-
Write and implement
CheckState.s.sol. Always print network, block, token, proxy, implementation, owner, pause state, version, each configured actor balance, reserves, liabilities, and surplus. Always fail onreserves < liabilities, implementation/manifest mismatch, or manifest chain mismatch.DEMO_EXPECTED_STAGE=deployedasserts version1and empty accounting;v1asserts exact Act 1 values;invariantschecks network-independent invariants only. Task 11 adds stagev2. -
Add direct Make targets that do not start background processes yet:
RPC_LOCAL := http://127.0.0.1:8545
ANVIL_OWNER := 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
.PHONY: deploy-v1 seed-v1 check-state
deploy-v1:
@node tools/finalize-manifest.mjs preflight-deploy anvil
@SCRIPT_SENDER=$(ANVIL_OWNER) DEPLOYMENT_MANIFEST_PATH=deployments/pending.json npm_config_offline=true forge script script/DeployV1.s.sol:DeployV1 --rpc-url $(RPC_LOCAL) --sender $(ANVIL_OWNER) --broadcast --force
@DEPLOYMENT_MANIFEST_PATH=deployments/pending.json DEMO_EXPECTED_STAGE=deployed forge script script/CheckState.s.sol:CheckState --rpc-url $(RPC_LOCAL) --force
@node tools/finalize-manifest.mjs deploy --rpc-url $(RPC_LOCAL)
@node tools/select-manifest.mjs anvil
seed-v1:
@forge script script/SeedV1Demo.s.sol:SeedV1Demo --rpc-url $(RPC_LOCAL) --broadcast --force
check-state:
@DEMO_EXPECTED_STAGE=$${DEMO_EXPECTED_STAGE:-v1} forge script script/CheckState.s.sol:CheckState --rpc-url $(RPC_LOCAL) --force
- Run unit/preflight green checks:
forge fmt
forge test --match-path test/ScriptPreflight.t.sol -vvv --force
- Run the first real local smoke test in two terminals. Terminal A:
anvil --host 127.0.0.1 --port 8545 --chain-id 31337
Terminal B:
make deploy-v1
make seed-v1
DEMO_EXPECTED_STAGE=v1 make check-state
Expected: the exact Act 1 table prints and all commands exit 0. Inspect identical deployments/anvil.json and selected deployments/active.json, verify their deployment block matches the confirmed proxy receipt, and verify they contain no secret material. Simulate a failed receipt fixture and confirm the finalizer changes neither confirmed nor active files.
- Commit:
git add script test/ScriptPreflight.t.sol tools/finalize-manifest.mjs tools/select-manifest.mjs tools/test-finalize-manifest.mjs deployments/.gitkeep Makefile
git commit -m "feat: script deterministic V1 demo state"