Skip to content

Commit df50358

Browse files
committed
Record Base canary source verification
1 parent 328f14a commit df50358

6 files changed

Lines changed: 166 additions & 12 deletions

File tree

docs/CURRENT_STATE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Contracts foundation:
3636
- `contracts/FlowMemoryHookAdapter.sol` is a compileable V0 hook-adapter scaffold. It emits `SWAP_MEMORY_SIGNAL` FlowPulse events for the launch fixture path. It is not a production Uniswap v4 hook.
3737
- `contracts/ArtifactRegistry.sol`, `CursorRegistry.sol`, `ReceiptVerifier.sol`, `WorkerRegistry.sol`, `VerifierRegistry.sol`, `WorkReceiptRegistry.sol`, `VerifierReportRegistry.sol`, and `WorkDebtScheduler.sol` provide local/test skeleton surfaces for commitments, cursors, work receipts, verifier reports, and work state.
3838
- `contracts/FLOWPULSE_SCHEMA.md` documents event fields, receipt boundaries, and URI/log-data limitations.
39-
- `tests/RootfieldRegistry.t.sol` and `tests/LiveV0Package.t.sol` contain 36 passing Foundry tests.
39+
- `tests/RootfieldRegistry.t.sol` and `tests/LiveV0Package.t.sol` contain 38 passing Foundry tests.
4040
- `tests/README.md` documents the current test command.
4141
- `contracts/STATIC_ANALYSIS.md`, `contracts/DEPLOYMENT_BOUNDARY.md`, and `contracts/ACCESS_CONTROL_REVIEW.md` define the current hardening, deployment, and access-control boundaries.
4242
- `infra/scripts/contracts-static-analysis.ps1` and `infra/scripts/contracts-static-analysis.sh` run the contract hardening baseline. Slither is optional by default and required only when explicitly requested.
@@ -61,6 +61,7 @@ Indexer/verifier local package:
6161
- `fixtures/deployments/base-canary-v0.json`, committed canary reader output, and `npm run flowmemory:canary-dashboard` now generate a separate Base canary dashboard dataset.
6262
- The dashboard has a separate Base canary mode at `/canary` that shows live-read canary FlowPulse observations, Rootflow transitions, canary boundaries, and raw canary JSON without replacing local fixture mode.
6363
- `npm run verify:base-canary:sources` produces a dry-run source verification plan for all canary contracts and writes `fixtures/deployments/base-canary-source-verification-plan.json`; `npm run verify:base-canary:sources:submit` submits after `BASESCAN_API_KEY` is configured.
64+
- All 10 deployed Base canary contracts are verified on BaseScan. `FlowMemoryHookAdapter` was verified against deployment-source commit `11d562c` because `main` now contains the newer v4-shaped callback path.
6465
- `npm run deploy:base-sepolia` and `npm run deploy:base-sepolia:broadcast` provide Foundry deploy commands for the current V0 Base Sepolia testnet contract set. They require local env values and do not commit credentials.
6566
- A Base mainnet V0 canary deployment exists for testing only; deployed addresses and smoke transactions are recorded in `docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md`.
6667

@@ -121,7 +122,7 @@ Launch-core specifications:
121122
- Production indexer or verifier service runtime.
122123
- Production persistence layer, production live RPC reader, production APIs, or hosted services.
123124
- Broad Base mainnet reader.
124-
- Completed explorer source verification for the deployed canary contracts. Automation exists, but actual explorer acceptance requires `BASESCAN_API_KEY`.
125+
- Broad production source-verification process for future redeploys. The current Base canary addresses are verified, but future deployments must be verified again before any production claim.
125126
- Explorer or hardware console implementation.
126127
- FlowRouter firmware, manufacturing, final enclosure work, or field deployment.
127128
- Real Meshtastic or LoRa device integration.

docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,33 @@ The script uses `fixtures/deployments/base-canary-v0.json`, requires no private
164164
key, redacts the API key in generated plans, and writes a non-secret report to
165165
`fixtures/deployments/base-canary-source-verification-plan.json` by default.
166166

167+
## Source Verification Status
168+
169+
Status: complete for the current Base canary deployment.
170+
171+
- Verification date: 2026-05-13.
172+
- Status report:
173+
`fixtures/deployments/base-canary-source-verification-status.json`.
174+
- `RootfieldRegistry`, `ArtifactRegistry`, `CursorRegistry`,
175+
`ReceiptVerifier`, `WorkerRegistry`, `VerifierRegistry`,
176+
`WorkReceiptRegistry`, `VerifierReportRegistry`, and `WorkDebtScheduler`
177+
were verified against current `main` source.
178+
- `FlowMemoryHookAdapter` was verified against deployment-source commit
179+
`11d562c` because current `main` now has the newer v4-shaped callback path
180+
that was added after this canary address was deployed.
181+
182+
This means the current canary addresses are source-verified, not that the newer
183+
`FlowMemoryHookAdapter` source has been redeployed or that the adapter is a
184+
production Uniswap v4 PoolManager hook.
185+
167186
## Important Gaps Found
168187

169-
1. Contract source verification automation exists, but actual submission
170-
requires `BASESCAN_API_KEY` and explorer acceptance.
171-
2. `FlowMemoryHookAdapter` now exposes a dependency-light Uniswap v4-shaped
188+
1. `FlowMemoryHookAdapter` now exposes a dependency-light Uniswap v4-shaped
172189
`afterSwap` callback path, but it is not a production hook deployment wired
173190
into PoolManager permissions.
174-
3. Ownership is still direct deployer ownership where applicable. There is no
191+
2. Ownership is still direct deployer ownership where applicable. There is no
175192
multisig, governance, recovery, or operational key policy.
176-
4. Verifier and worker registry flows are deployed, but live verifier report
193+
3. Verifier and worker registry flows are deployed, but live verifier report
177194
submission, report signing, and verifier economics are not built.
178195

179196
## Notes

docs/OPERATIONS/V0_OPERATOR_POLICY.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ with production readiness.
3030

3131
Production language remains blocked until a later decision records:
3232

33-
1. source verification completed for every deployed contract;
33+
1. source verification completed for every deployed or redeployed contract;
3434
2. operational owner separation for deployer, worker admin, verifier admin, and
3535
emergency response;
3636
3. multisig or comparable account-control decision;
@@ -53,8 +53,8 @@ Production language remains blocked until a later decision records:
5353

5454
## Current Gaps
5555

56-
- Source verification automation exists, but actual submission requires
57-
`BASESCAN_API_KEY`.
56+
- Source verification is complete for the current canary addresses. Any
57+
redeploy or new canary address must be verified again.
5858
- The canary deployer is still a single account.
5959
- There is no multisig, timelock, recovery, or operator separation.
6060
- `FlowMemoryHookAdapter` remains a hook-adjacent adapter, not a production

fixtures/deployments/base-canary-source-verification-plan.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"productionReady": false,
1212
"submit": false,
1313
"checkBytecode": true,
14+
"continueOnError": false,
15+
"delayMs": 0,
1416
"apiKeyEnv": "BASESCAN_API_KEY",
1517
"contractCount": 10,
1618
"commands": [
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{
2+
"schema": "flowmemory.base_canary_source_verification_status.v0",
3+
"verifiedAt": "2026-05-13T21:09:00Z",
4+
"network": {
5+
"name": "Base mainnet",
6+
"chainId": "8453",
7+
"environment": "mainnet"
8+
},
9+
"productionReady": false,
10+
"notes": [
11+
"All current Base canary addresses are source-verified on BaseScan.",
12+
"FlowMemoryHookAdapter was verified against deployment-source commit 11d562c because main now contains a newer v4-shaped callback path added after deployment.",
13+
"This report is not a production-readiness claim and does not make the adapter a production Uniswap v4 PoolManager hook."
14+
],
15+
"contracts": [
16+
{
17+
"name": "RootfieldRegistry",
18+
"address": "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C",
19+
"sourceName": "contracts/RootfieldRegistry.sol:RootfieldRegistry",
20+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
21+
"verified": true,
22+
"url": "https://basescan.org/address/0x2a7add68a1d45c3251e2f92ffe4926124654a97c"
23+
},
24+
{
25+
"name": "FlowMemoryHookAdapter",
26+
"address": "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245",
27+
"sourceName": "contracts/FlowMemoryHookAdapter.sol:FlowMemoryHookAdapter",
28+
"sourceCommit": "11d562c90b8bf5d2bb7d860fa45701fd0c4fb89f",
29+
"verified": true,
30+
"url": "https://basescan.org/address/0x179df6d52e9def5d02704583a2e4e5a9ff427245"
31+
},
32+
{
33+
"name": "ArtifactRegistry",
34+
"address": "0x8F074d0F4e66975b740A4b7a316330c9660a485E",
35+
"sourceName": "contracts/ArtifactRegistry.sol:ArtifactRegistry",
36+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
37+
"verified": true,
38+
"url": "https://basescan.org/address/0x8f074d0f4e66975b740a4b7a316330c9660a485e"
39+
},
40+
{
41+
"name": "CursorRegistry",
42+
"address": "0x3360689009685eade15c876855D24161b05829C1",
43+
"sourceName": "contracts/CursorRegistry.sol:CursorRegistry",
44+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
45+
"verified": true,
46+
"url": "https://basescan.org/address/0x3360689009685eade15c876855d24161b05829c1"
47+
},
48+
{
49+
"name": "ReceiptVerifier",
50+
"address": "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B",
51+
"sourceName": "contracts/ReceiptVerifier.sol:ReceiptVerifier",
52+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
53+
"verified": true,
54+
"url": "https://basescan.org/address/0x94ba7aa4562f8f8528c327378f6352350f6ddb5b"
55+
},
56+
{
57+
"name": "WorkerRegistry",
58+
"address": "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29",
59+
"sourceName": "contracts/WorkerRegistry.sol:WorkerRegistry",
60+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
61+
"verified": true,
62+
"url": "https://basescan.org/address/0xa8c07ef53eeb4e57297ee35025a9cd5303fccd29"
63+
},
64+
{
65+
"name": "VerifierRegistry",
66+
"address": "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd",
67+
"sourceName": "contracts/VerifierRegistry.sol:VerifierRegistry",
68+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
69+
"verified": true,
70+
"url": "https://basescan.org/address/0xaf920ca7436bb72172e27c96e0b716f01dcc5dbd"
71+
},
72+
{
73+
"name": "WorkReceiptRegistry",
74+
"address": "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3",
75+
"sourceName": "contracts/WorkReceiptRegistry.sol:WorkReceiptRegistry",
76+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
77+
"verified": true,
78+
"url": "https://basescan.org/address/0x2874cee0d581e4562ac9015bfcf330f1ea58a1f3"
79+
},
80+
{
81+
"name": "VerifierReportRegistry",
82+
"address": "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996",
83+
"sourceName": "contracts/VerifierReportRegistry.sol:VerifierReportRegistry",
84+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
85+
"verified": true,
86+
"url": "https://basescan.org/address/0x95bc7455adfd60e1b908ba455c25ae732c1ef996"
87+
},
88+
{
89+
"name": "WorkDebtScheduler",
90+
"address": "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34",
91+
"sourceName": "contracts/WorkDebtScheduler.sol:WorkDebtScheduler",
92+
"sourceCommit": "328f14a7ae65c38494d373b485fa400927ee7920",
93+
"verified": true,
94+
"url": "https://basescan.org/address/0xa752e9bc7faf39f659110d8cf408e7707db94e34"
95+
}
96+
]
97+
}

infra/scripts/verify-base-canary-sources.mjs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const submit = flags.has("--submit");
2323
const json = flags.has("--json");
2424
const watch = flags.has("--watch");
2525
const checkBytecode = flags.has("--check-bytecode");
26+
const continueOnError = flags.has("--continue-on-error");
27+
const delayMs = Number(valueAfter("--delay-ms", "0"));
2628
const artifact = JSON.parse(readFileSync(resolve(deploymentPath), "utf8"));
2729

2830
if (artifact.schema !== "flowmemory.deployment_artifact.v0") {
@@ -64,13 +66,27 @@ function verificationArgs(contract, apiKeyValue) {
6466
return result;
6567
}
6668

69+
function redact(value) {
70+
if (!apiKey) return value;
71+
return value.replaceAll(apiKey, `<${apiKeyEnv}>`);
72+
}
73+
74+
function redactArgs(commandArgs) {
75+
return commandArgs.map((arg) => redact(arg));
76+
}
77+
78+
function sleep(milliseconds) {
79+
if (milliseconds <= 0) return;
80+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
81+
}
82+
6783
function run(command, commandArgs) {
6884
const result = spawnSync(command, commandArgs, {
6985
cwd: process.cwd(),
7086
stdio: "inherit",
7187
});
7288
if (result.status !== 0) {
73-
throw new Error(`${command} ${commandArgs.join(" ")} failed with exit code ${result.status ?? "unknown"}`);
89+
throw new Error(`${command} ${redactArgs(commandArgs).join(" ")} failed with exit code ${result.status ?? "unknown"}`);
7490
}
7591
}
7692

@@ -102,6 +118,8 @@ const plan = {
102118
productionReady: false,
103119
submit,
104120
checkBytecode,
121+
continueOnError,
122+
delayMs,
105123
apiKeyEnv,
106124
contractCount: artifact.contracts.length,
107125
commands: artifact.contracts.map((contract) => ({
@@ -119,7 +137,26 @@ if (json || !submit) {
119137
}
120138

121139
if (submit) {
140+
const failures = [];
122141
for (const contract of artifact.contracts) {
123-
run("forge", verificationArgs(contract, apiKey));
142+
try {
143+
run("forge", verificationArgs(contract, apiKey));
144+
} catch (error) {
145+
failures.push({
146+
name: contract.name,
147+
address: contract.address,
148+
error: redact(error instanceof Error ? error.message : String(error)),
149+
});
150+
if (!continueOnError) {
151+
throw new Error(failures.at(-1).error);
152+
}
153+
}
154+
sleep(delayMs);
155+
}
156+
if (failures.length > 0) {
157+
const failure = new Error(`source verification completed with ${failures.length} failure(s)`);
158+
failure.failures = failures;
159+
console.error(JSON.stringify({ schema: "flowmemory.base_canary_source_verification_failures.v0", failures }, null, 2));
160+
process.exitCode = 1;
124161
}
125162
}

0 commit comments

Comments
 (0)