Follow-up from external review of #20 (review URL). Must land before the mainnet freeze ADR closes. Devnet-shipping is unaffected.
ADR-0044 §8 (Accepted, immutable) mandates three things for the ListEntryResolver deploy that the current implementation does not provide:
- CREATE2 with deterministic salt for the resolver address.
- Deploy-time
assert(LIST_ENTRY_SCHEMA_UID == expected) against a hardcoded constant pinned to ADR-0046's recorded UID (0xc303f1…5a86e).
- CI pin check analogous to ADR-0037's
git diff --exit-code packages/nextjs/contracts/deployedContracts.ts.
Current state in packages/hardhat/deploy/09_lists.ts:
- L62-64 use
ethers.getCreateAddress({from: deployer, nonce}) — plain CREATE, not CREATE2.
- L198-209 only verify the LIST_SCHEMA_UID baked into the resolver's immutable against the one we just computed (internal consistency); there is no comparison against a hardcoded
EXPECTED_LIST_ENTRY_SCHEMA_UID.
- No CI job verifies a fresh deploy reproduces ADR-0046's recorded bytes.
Concrete failure modes if shipped to mainnet unchanged:
- Nonce drift on persistent networks (stray deployer tx between yarn-deploy runs, parallel worktrees, faucet/manual fund) silently shifts the CREATE address → shifts the LIST_ENTRY schema UID → orphans every prior entry. The internal-consistency assert can't catch this because both sides update.
- No compile-time anchor to ADR-0046's recorded UID.
Companion finding: ADR-0044 §8 ↔ ADR-0046 ↔ code drift
ADR-0044 §8 says CREATE2. ADR-0046 §"Recorded addresses" describes the resolver as "nonce-deterministic" — implicitly walking back the CREATE2 mandate without an explicit supersession. Per docs/agent-workflow.md, accepted ADRs aren't editable in place. Reconcile via either:
- (a) Migrate the deploy to true CREATE2 (OZ
Create2.deploy or hardhat-deploy deterministicDeployment) — matches ADR-0044 §8 as written.
- (b) Write a small superseding ADR documenting why CREATE-with-nonce + the partial-deploy guard at L78-84 is sufficient, then add the EXPECTED-UID hardcoded assert + CI pin job.
Either path lands a tamper-detection CI gate before the schema UID becomes Etched.
Sub-item: registry-resolver readback assert (F5)
09_lists.ts:198-209 does not do assert(schemaRegistry.getSchema(listEntrySchemaUID).resolver == listEntryResolver.target). The address-prediction invariant at L84-91 already catches the practical drift case, but ADR-0044 §8 calls for the readback assert specifically. ~5 lines.
Suggested fix sketch
```typescript
// Pinned to ADR-0046 §"Recorded addresses"
const EXPECTED_LIST_SCHEMA_UID = "0x61363a…" as const;
const EXPECTED_LIST_ENTRY_SCHEMA_UID = "0xc303f1…5a86e" as const;
if (listSchemaUID !== EXPECTED_LIST_SCHEMA_UID)
throw new Error(`LIST_SCHEMA_UID drift: expected ${EXPECTED_LIST_SCHEMA_UID}, got ${listSchemaUID}`);
if (listEntrySchemaUID !== EXPECTED_LIST_ENTRY_SCHEMA_UID)
throw new Error(`LIST_ENTRY_SCHEMA_UID drift: expected ${EXPECTED_LIST_ENTRY_SCHEMA_UID}, got ${listEntrySchemaUID}`);
const leRecord = await schemaRegistry.getSchema(listEntrySchemaUID);
if (leRecord.resolver.toLowerCase() !== String(listEntryResolver.target).toLowerCase()) {
throw new Error(`LIST_ENTRY schema points at wrong resolver\nExpected: ${listEntryResolver.target}\nGot: ${leRecord.resolver}`);
}
```
Plus a `lists-pin-check` CI job mirroring `deploy-pin-check`: fresh deploy against the pinned fork → `git diff --exit-code` on a checked-in `packages/hardhat/lists-pin.json` with the two UIDs and two addresses.
Refs
Follow-up from external review of #20 (review URL). Must land before the mainnet freeze ADR closes. Devnet-shipping is unaffected.
ADR-0044 §8 (Accepted, immutable) mandates three things for the
ListEntryResolverdeploy that the current implementation does not provide:assert(LIST_ENTRY_SCHEMA_UID == expected)against a hardcoded constant pinned to ADR-0046's recorded UID (0xc303f1…5a86e).git diff --exit-code packages/nextjs/contracts/deployedContracts.ts.Current state in
packages/hardhat/deploy/09_lists.ts:ethers.getCreateAddress({from: deployer, nonce})— plain CREATE, not CREATE2.EXPECTED_LIST_ENTRY_SCHEMA_UID.Concrete failure modes if shipped to mainnet unchanged:
Companion finding: ADR-0044 §8 ↔ ADR-0046 ↔ code drift
ADR-0044 §8 says CREATE2. ADR-0046 §"Recorded addresses" describes the resolver as "nonce-deterministic" — implicitly walking back the CREATE2 mandate without an explicit supersession. Per
docs/agent-workflow.md, accepted ADRs aren't editable in place. Reconcile via either:Create2.deployor hardhat-deploydeterministicDeployment) — matches ADR-0044 §8 as written.Either path lands a tamper-detection CI gate before the schema UID becomes Etched.
Sub-item: registry-resolver readback assert (F5)
09_lists.ts:198-209does not doassert(schemaRegistry.getSchema(listEntrySchemaUID).resolver == listEntryResolver.target). The address-prediction invariant at L84-91 already catches the practical drift case, but ADR-0044 §8 calls for the readback assert specifically. ~5 lines.Suggested fix sketch
```typescript
// Pinned to ADR-0046 §"Recorded addresses"
const EXPECTED_LIST_SCHEMA_UID = "0x61363a…" as const;
const EXPECTED_LIST_ENTRY_SCHEMA_UID = "0xc303f1…5a86e" as const;
if (listSchemaUID !== EXPECTED_LIST_SCHEMA_UID)
throw new Error(`LIST_SCHEMA_UID drift: expected ${EXPECTED_LIST_SCHEMA_UID}, got ${listSchemaUID}`);
if (listEntrySchemaUID !== EXPECTED_LIST_ENTRY_SCHEMA_UID)
throw new Error(`LIST_ENTRY_SCHEMA_UID drift: expected ${EXPECTED_LIST_ENTRY_SCHEMA_UID}, got ${listEntrySchemaUID}`);
const leRecord = await schemaRegistry.getSchema(listEntrySchemaUID);
if (leRecord.resolver.toLowerCase() !== String(listEntryResolver.target).toLowerCase()) {
throw new Error(`LIST_ENTRY schema points at wrong resolver\nExpected: ${listEntryResolver.target}\nGot: ${leRecord.resolver}`);
}
```
Plus a `lists-pin-check` CI job mirroring `deploy-pin-check`: fresh deploy against the pinned fork → `git diff --exit-code` on a checked-in `packages/hardhat/lists-pin.json` with the two UIDs and two addresses.
Refs