# Audit report

> Review Submission20/ only (read Submission20/AUDIT_REQUEST.md first; Submission19/AUDIT_REQUEST.md is the reference for the GOLD ledger design). World source 0727200, NOT deployed, production registry empty. It fixes Swarm audit 8febfeed and makes the paid Genesis order name its payment token (the Genesis contract's matching change is public at github.com/tungweb3/ember-evo-genesis-review, Submission7). Files: server/genesis-gold.ts, genesis-gold-reader.ts, genesis-gold-service.ts, genesis-deployments.ts, world-gold-result.ts, tests/genesis-gold.test.mjs.
>
> 1. Token binding: can a hold be made for a token the registry entry does not list, at another token's GOLD price, or with a wallet consent that names a different token than the order?
> 2. The stored-order retry path (parseOrder with prices=false): can a caller use it to hold or change anything the first hold would have refused (another wallet, order, price, deployment, or no valid consent)?
> 3. orderWindow deploy branch (lo = deploy, then binary search): any off-by-one or case where the consumption block is left out? It must only ever keep a hold, never release one.
> 4. Cron page per deployment (query filtered by chain_instance, chain_id, contract; one reconcileHeldMintGold call per id): can it skip or repeat a held order across cursor wraps, or exceed the cron's CPU and subrequest limits?
> 5. Rate-limit keys (genesis:rpc for the cron, genesis:rpc:service for the service binding): does any chain read use the wrong key?
>
> Report severity, reproduction and fix. Not in scope: 3D/rendering, media, Celestao, AIMarket.

| | |
|---|---|
| Repository | https://github.com/tungweb3/imd-ember-world-review.git |
| Commit | `a28c1e7ac9c4823289ceb1e9009148783b85d014` |
| Job | `e3e9b712-4a74-44d7-83e6-58bdf61bde0c` |
| Judged | 2026-10-29 01:54 UTC |
| Findings | 3 low · 2 info |

Four agents audited the code as it is at `a28c1e7`, each in one area (math, permissions, economics, control flow),
and a judge reproduced, merged and ranked what they found, then read the code once more itself. Nothing in the repository was changed or deployed.

## Findings

### 1. Low: Cron page drops every MINT_SCOPE_MISMATCH outcome, so a wrong-chain RPC or a wrong registry codeHash leaves all of a deployment's holds stuck with nothing logged

`Submission20/source/server/genesis-gold.ts:181`

```
   out.push({deployment:d.id,outcomes:outcomes.filter(o=>o.status!=='MINT_SCOPE_MISMATCH')});
```

In 7939c37 the cron page came from the global reconcileHeldMintGold, which returned MINT_SCOPE_MISMATCH for every other deployment's held order, so filtering that code out was noise removal. In 0727200 the page is already scoped (chain_instance, chain_id, contract are bound in the SELECT at line 175, and reconcileHeldMintGold is called once per id with the deployment's own reader), so the book-side scope check in the locked world-gold.ts (line 206) and the reader's binding check (genesis-gold-reader.ts line 93) can no longer fire. The only MINT_SCOPE_MISMATCH that can reach line 181 now comes from the reader itself: eth_chainId not equal to the registry chainId (reader line 97: GENESIS_RPC_URL or the Alchemy app on another network) or keccak256(eth_getCode) not equal to the registry codeHash, or empty code (reader line 102: a metadata-hash difference between artifact and deployed bytecode, a typo in a new entry). Both mean every hold of that deployment stays held for ever (the reader fails before any settle). The filter removes those outcomes, maintainGenesisGold (genesis-gold-service.ts line 57) returns an empty list, worker/app.ts logs genesis_gold only when the list is non-empty, and the cursor is written normally, so the run leaves no trace. The service path reports the same chain state correctly (reconcileGenesisGold returns code MINT_SCOPE_MISMATCH, asserted by the test 'wrong chain, code, events or an RPC failure keep the hold'), so the two paths disagree about one chain state. No GOLD is spent or released wrongly (the hold is kept); the operator path meant to notice a stuck deployment is blind. Merged from three specialist reports of the same line (math, flow); reproduced in a harness of the submission's files over the locked world-gold.ts with a node:sqlite D1 stand-in. Fix: push outcomes unchanged (one line: out.push({deployment:d.id,outcomes})) and let maintainGenesisGold's status!=='pending' filter surface MINT_SCOPE_MISMATCH per id like MINT_PROOF_CONFLICT and MINT_CHAIN_UNAVAILABLE; or collapse repeated scope mismatches of one page into one deployment-level status, but never drop them. Add a cron-path case to the existing wrong-chain test.

**Reproduction**

State: registry entry DEP (tests/genesis-gold.test.mjs fixtures), one held order (reserveGenesisGold with a valid consent, reserved 8). Chain stand-in: chain({chainId:1}) (eth_chainId answers 0x1 for the 11155111 entry) or chain({code:'0x60aa'}) (code hash differs from DEP.codeHash). Input: reconcileGenesisDeployments(db,LIST,'sepolia',(d,hint)=>createGenesisR5Reader(d,c.rpc,hint),NOW), and maintainGenesisGold(env,{deployments:LIST,environment:'sepolia'},fetchStub,NOW) with the same answers. Expected: out[0].outcomes equals [{id:order().orderId,status:'MINT_SCOPE_MISMATCH'}] and maintainGenesisGold returns that entry, as the service path does for the same chain (reconcileGenesisGold(...) returns code 'MINT_SCOPE_MISMATCH'). Actual (measured): service code MINT_SCOPE_MISMATCH; cron outcomes []; maintainGenesisGold []; cursor written as ''; order still held (reserved 8) on every later run with the same silence. Both chain variants behave identically.

### 2. Low: Cron cursor treats an exactly full page as "more rows follow": with k*GENESIS_CRON_PAGE held orders every cycle costs one empty run, delaying release or settlement of that page by one cron interval

`Submission20/source/server/genesis-gold.ts:180`

```
    .bind(d.id,ids.length===limit?ids.at(-1)!:'',nowMs).run();
```

The cursor is advanced to the last id whenever ids.length===limit and reset to '' only when a page comes back short. The SELECT reads exactly limit rows, so a full page cannot tell whether a row follows it. When a deployment's held orders above the cursor number an exact multiple of GENESIS_CRON_PAGE (8, 16, 24, ...), the run after the last full page selects nothing (no id > cursor), makes no chain read, and only resets the cursor; the orders are visited one run later. No order is skipped or reconciled twice per wrap (id>cursor ORDER BY id; an order inserted below the cursor is reached after the wrap), and settlement is idempotent, so correctness holds; the cost is one wasted scheduled invocation per cycle and doubled per-deployment latency for the last page. The scheduled handler runs maintainGold on the '* * * * *' trigger (worker/app.ts line 262; Submission13 wrangler.jsonc lists both '*/15 * * * *' and '* * * * *'), so the delay is one minute per wrap, not the 15 minutes some specialist reports assumed; a page of 8 orders stuck in MINT_PROOF_CONFLICT (which the request says stay held) makes every other run a no-op for that deployment indefinitely. The existing test uses n = GENESIS_CRON_PAGE + 2 and never meets the exact-multiple case. Merged from three specialist reports of the same line (math, flow, economics). Fix: select limit+1 rows, reconcile only the first limit, and store ids[limit-1] as the cursor only when the extra row exists, else ''; or, after a full page, run one cheap 'SELECT 1 ... AND id>? LIMIT 1' and store '' when it is empty. Either keeps the one-page-per-run bound and the no-starvation property. Add a test with n = GENESIS_CRON_PAGE.

**Reproduction**

State: exactly 8 held orders of DEP (orderIds H(0x500..0x507), a wallet with enough GOLD), cursor row absent. Run 1: reconcileGenesisDeployments(db,[DEP],'sepolia',reader,NOW) with chain({finalTime:DEADLINE}) (every order pending). Run 2 with chain({finalTime:DEADLINE+1}) (every order releasable). Expected for run 2: 8 'released' outcomes, reserved 0 (one page per run, AUDIT_REQUEST section 3 item 4). Actual (measured): run 1 returns 8 outcomes and stores cursor 0x...0507; run 2 returns 0 outcomes, makes no RPC call, stores cursor '' and reserved stays 64; run 3 returns 8 'released' and reserved becomes 0. The pattern repeats on every wrap while the count stays a multiple of 8.

### 3. Low: Settlement never checks which token paid: a hold that names imdUSD v1 is consumed by a mint paid in v2 or any token with the same GOLD price (a Genesis-signer trust assumption, not a World check)

`Submission20/source/server/genesis-gold-reader.ts:141`

```
   if(addrTopic(c[2])!==m.wallet||BigInt(c[3])!==tokenId||wordAt(cData,0)!==BigInt(m.gold)
```

0727200 adds token to the order, to the wallet's GenesisGoldHold consent and to the stored document, and the registry comment (genesis-gold.ts lines 30-32) says an order 'is payable only in' its token. On the World side that binding ends at the hold. createGenesisR5Reader.read() settles from PaidOrderConsumed(orderId, wallet, tokenId; goldCost) and GenesisMinted(wallet, tokenId, lookKey; choice, skin, paid, cost): neither event carries the payment token, the `own` filter at line 130 keeps only logs the Genesis contract emitted, so the ERC-20 Transfer that shows which token actually paid is never read, and order.token is never compared with anything on chain (lines 95-96 and 141-143 compare orderId, wallet, goldCost, deadline, lookKey, choice, embeoCost, schema, deploymentId and codeHash only). For two listed tokens with the same GOLD price (imdUSD v1 and v2, both 1,200 GOLD, the case the audit request names) the proof is identical whichever token paid. Reachability: the contract's PaidAuthorization binds the token the Genesis signer signs, so this needs the signer to sign a different token than the hold it reserved (a signer defect, a later signer change, or a compromise); Submission19 section 2 records the signer as trusted. Impact today: GOLD held and GOLD charged are equal and the wallet consented to that amount, but world_gold_mints.auth_json (and the review record built from it) names a token other than the one that paid, which a reconciliation of the treasury Safe's receipts against the book cannot explain; if the other token has a different price, the hold stays MINT_PROOF_CONFLICT (safe). Reported so the 'payable only in it' claim is read as a signer property, and because a later addPaymentToken at another price makes the equal-price assumption load-bearing. Merged from three specialist reports of the same line (permissions, flow, economics). Minimal fix that preserves the design: in the consumed branch require, in the same receipt, exactly one Transfer(address,address,uint256) log emitted by order.token with topics[1]==wallet and topics[2]==order.recipient (the contract pulls the payment with transferFrom to paymentRecipient; a Safe or 4337 wallet is the order wallet itself), MINT_PROOF_CONFLICT otherwise; optionally compare the amount with a per-token amount in the registry. Alternatively have the Genesis contract include the token in PaidOrderConsumed and compare it here. Add a test where an IMD hold meets a receipt whose Transfer comes from V2, expecting MINT_PROOF_CONFLICT.

**Reproduction**

State: registry entry listing paymentToken IMD (goldCost '8') and addedTokens [{USDC,'9'},{V2,'8'}]; a held order with token IMD, goldCost '8', consent signed over token IMD. Chain reader input: paidOrderTokenId(orderId)=7, a finalized receipt with status 1 whose logs are (a) Transfer(wallet -> recipient, 20e18) emitted by V2 (0xd3d3...d3d3) and none by IMD, (b) GenesisMinted for the wallet and token 7 with the order's lookKey, choice, paid=1, embeoCost, (c) PaidOrderConsumed(orderId, wallet, 7; goldCost 8). Call reconcileGenesisGold(db,LIST,'sepolia',reader,{deploymentId,orderId},NOW). Expected if the token binding were enforced end to end: state held, code MINT_PROOF_CONFLICT. Actual (measured): state 'consumed', reserved 0, auth_json order.token still 0xa1a1...a1a1 (IMD). Same with the existing harness: settle(f,chain({tokenId:7n})) settles an IMD hold although the receipt contains no ERC-20 Transfer log at all.

### 4. Info: GENESIS_CRON_PAGE=8 counts orders, not RPC cost: the cron releases at most 8 abandoned holds per deployment per minute while a cheap release costs about 5 RPC calls

`Submission20/source/server/genesis-gold.ts:163`

```
export const GENESIS_CRON_PAGE=8;
```

The page size is sized for the worst case per order (a consumed order without a transaction hint: about 35-41 RPC subrequests including the binary search and up to 11 eth_getLogs of 10 blocks), but the common cron case is cheap: an order that was never paid costs eth_chainId, finalized block, eth_getCode (once per block), two eth_call and the final block check, about 5-6 calls and no log search. Because the page counts orders, a run releases at most 8 abandoned orders per deployment, and each such wallet's held GOLD counts against its available balance (world_gold_holds SUM(amount)) until the cron reaches it; a wallet whose book is below twice the price cannot place a second order meanwhile (GOLD_INSUFFICIENT). The scheduled handler runs the GOLD step on the '* * * * *' trigger (worker/app.ts line 262, Submission13 wrangler.jsonc), so the bound is about 8 releases per minute per deployment, roughly 480 per hour; 200 holds abandoned in one hour of a sale clear within about 25-50 minutes of the last deadline, not the 6-12 hours one specialist computed from a 15-minute interval. With ECONOMY_CHAIN_LIMITER at 600 per minute per location (Submission13 wrangler.jsonc) and the Paid plan's 1,000 subrequests, a worst-case page of 8 (about 330 calls) fits; the limits are not exceeded. This is a documented design bound (Submission19 section 1 item 4) rather than a defect, listed so the throughput is known. If it matters for the sale: page by cost instead of count (let the loop continue past 8 while outcomes are 'pending' or 'released', up to reconcileHeldMintGold's cap of 32, or read a larger page of orders whose deadline is already past), and document the per-order call counts next to GENESIS_CRON_PAGE.

**Reproduction**

State: 24 held orders of DEP (orderIds H(0x500..0x517)) with the same deadline, chain({finalTime:DEADLINE+1}) so every order is releasable. Input: reconcileGenesisDeployments(db,LIST,'sepolia',reader,NOW) once. Expected (what the RPC budget allows): 24 released in one run. Actual (measured): 8 released, 41 RPC calls in total, reserved stays 128 (16 holds of 8 GOLD); two more runs are needed. A new order of the same wallet succeeds only because the fixture book (1000) exceeds reserved + price; with a book below 2x the price it fails GOLD_INSUFFICIENT until the cron reaches the stale hold.

### 5. Info: Stored-order retry path (prices=false) is correct but untested for another account's or an invalid consent and for a changed token, goldCost, wallet or recipient

`Submission20/source/tests/genesis-gold.test.mjs:392`

```
test('World audit 8febfeed finding 4: an identical retry is answered from the stored order after the registry changes; another order still conflicts',async()=>{
```

The stored-order branch of reserveGenesisGold (genesis-gold.ts lines 114-124) requires the same deployment scope, a byte-identical document (order including token and goldCost, domain, codeHash, recipient), and then verifies the wallet's consent over the stored order at line 122, so it cannot hold or change anything the first hold would refuse; measured: a retry with another key's consent or a garbage signature returns INVALID_GOLD_CONSENT, and a retry with changed token, goldCost, recipient or wallet returns MINT_ORDER_CONFLICT, reserved unchanged. The suite does not cover those guards: 'the same orderId with any other content conflicts' (line 47) changes only choice, lookKey, uriHash, deadline and signerEpoch; 'World audit 8febfeed finding 4' (line 392) retries only the identical request; 'consent must be the wallet's own signature' (line 55) uses orders that were never held, so it takes the first-hold branch. Mutation check: with line 122 (await verifyConsent(d,o,sig as string)) deleted, a retry of a held order with a consent signed by any other key returns the held view (state 'held', the wallet's book/reserved/available), and no test in tests/genesis-gold.test.mjs would fail, because every retry test sends the original valid consent or expects MINT_ORDER_CONFLICT before line 122 runs. The same holds for dropping token or goldCost from the byte comparison: no retry test patches them. Fix: add retry cases after a successful hold: (a) identical order, consent by another account -> INVALID_GOLD_CONSENT; (b) consentSignature '0x'+'1'.repeat(130) -> INVALID_GOLD_CONSENT; (c) same orderId with {token:USDC,goldCost:'9'}, {token:V2}, {goldCost:'9'}, another wallet with its own valid consent, another recipient, each under a registry that lists that price or recipient -> MINT_ORDER_CONFLICT; reserved unchanged in all cases. (The private fixture world-gold-audit5-fixture.mjs is not in the public tree; the checks above ran on a stand-in over the submission's files and the locked world-gold.ts.)

**Reproduction**

Copy Submission20/source/server/genesis-gold.ts and delete line 122 (the verifyConsent call on the stored-order path). Hold order() with the wallet's consent, then call reserveGenesisGold with the same order and consentSignature signed by privateKeyToAccount('0x'+'22'.repeat(32)). Expected: INVALID_GOLD_CONSENT (as the unmutated code returns, measured). Actual on the mutant (measured): returns {state:'held',...} with the wallet's balances. Every test in tests/genesis-gold.test.mjs that reaches the stored-order branch (lines 39, 349, 398) sends the original consent, so none fails on the mutant.

---

Judge's submission `39340cc459ea762d6d12cae73f7b647704c6ca2225ef59dc180317eef7f790be`, accepted on the IdentityMD network. Acceptance means the report met the job's checks;
it is not a guarantee that the code has no other defects.
