Skip to content

Commit c2d8b8b

Browse files
committed
docs: two more empty union members, and a guard that was too careful
Thirty-two became thirty-four. Both are the placeholder shape, and together they make `IntentKind` the single worst offender in this codebase: four of its seven members were shipped with nothing behind them, three of those on money paths. The last two are the worst instance because of WHEN they were found. The creator-fee builder was written in the same session, for exactly this defect, and nobody — including whoever wrote it — went back to ask whether the holder half had the same gap. It did. The API served a holder their claimable amount and the proof to spend it with, and the product had no way to spend either. The check that finds this is one grep per union member. A NEW SECTION FOR A NEAR-MISS The empty-proof guard never shipped, but it is the only failure here that came from being too careful, and that earns its own heading. A single-leaf Merkle tree has no siblings, so its proof is legitimately empty — and a market with one holder is not an edge case, it is every market on its first day. The guard cost the smallest markets their claims to avoid one confusing revert. The question it generalises to: every validation refuses a set of states, and the useful one to ask is not "is this input suspicious" but "what legitimate state does this reject, and how common is it". Here the answer was "every market that has just launched". The deliberately-not-implemented list is down to one item, and it is no longer object storage. Neither metadata nor the share card needs a file store: one went on-chain with an IPFS CID, the other derives its mark from the token address. What remains is PNG rasterisation, which is a native dependency and a §434 deployment question.
1 parent c7a385a commit c2d8b8b

3 files changed

Lines changed: 97 additions & 10 deletions

File tree

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ asserted:
196196

197197
## Where the bugs have actually been
198198

199-
Thirty-two defects were found and fixed during the build, and none of them was
199+
Thirty-four defects were found and fixed during the build, and none of them was
200200
caught by a unit test. They fell into five shapes — a seam between two
201201
components that were each individually correct, a placeholder that outlived the
202202
reason it was written, code that was correct but quadratic, and a failed read
@@ -210,6 +210,11 @@ The most expensive single one was an argument nobody was checking: the launch
210210
price arrived as plain calldata, verified only against zero, and it fixes a
211211
market'''s starting price permanently.
212212

213+
The most instructive is smaller. Four members of one TypeScript union were
214+
shipped with no implementation behind them, three of them money paths — and the
215+
last two were found only because somebody asked why something else had not been
216+
done.
217+
213218
[`docs/REVIEW-NOTES.md`](docs/REVIEW-NOTES.md) lists every one of them and what
214219
the pattern implies for reviewing this codebase. It is the first thing worth
215220
reading before auditing anything here.

docs/API.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,31 @@ what.
189189
name (`UNSUPPORTED_INTERVAL`) rather than clamped to the nearest — a client bug
190190
should surface, not silently receive a different timeframe.
191191

192+
### `GET /v1/markets/:token/preview.svg`
193+
194+
The social preview (§117), 1200×630, as `image/svg+xml`. Point `og:image` at
195+
it.
196+
197+
**The creator's image is deliberately not embedded.** Fetching a
198+
creator-supplied IPFS CID from a public, unauthenticated endpoint would be an
199+
outbound request from this service to a URL a stranger chose — request forgery
200+
with a public trigger — and would make render time depend on somebody else's
201+
gateway. The mark on the card is derived from the token address instead:
202+
deterministic, unique per market, always available. The real logo is served as
203+
`metadata.imageCid`, for a client to fetch in a browser where the request is the
204+
user's.
205+
206+
Cached for five minutes with an hour of `stale-while-revalidate`. A preview that
207+
500s during a slow database renders as a bare URL, and a slightly old card beats
208+
no card by a wide margin.
209+
210+
An unknown token returns a **JSON 404**, not an SVG saying "not found" — a
211+
crawler would cache and display the second.
212+
213+
Most crawlers want PNG for `og:image`. Rasterising needs a native dependency
214+
whose choice depends on the deployment runtime (§434), so it sits behind a
215+
documented seam rather than being guessed at.
216+
192217
### `GET /v1/markets/:token/epochs`
193218

194219
§333's public distribution dataset and §367's status.

docs/REVIEW-NOTES.md

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Where the defects in this codebase have actually been, written down so the next
44
person reviewing it looks in the right places rather than the obvious ones.
55

6-
Thirty-two defects were found and fixed during the build. **None of them was
6+
Thirty-four defects were found and fixed during the build. **None of them was
77
found by a unit test.** Every one came from running real components against each
88
other — with one exception, which was found by being asked why something had not
99
been done and checking instead of answering. That is not an argument against the unit tests — they are what makes the
@@ -51,7 +51,7 @@ ever handed it real output from the thing upstream".
5151

5252
## Shape 2 — the placeholder
5353

54-
Ten defects, all the same:
54+
Twelve defects, all the same:
5555

5656
```
5757
priceAfter: 0n // every trade priced at zero; a flat tape and a flat chart
@@ -64,8 +64,26 @@ CLAIM_CREATOR_FEES // an IntentKind with no builder: earnings shown, no way
6464
StockbackMessage // a realtime message type nothing ever published
6565
referenceMarketCapUsd // declared in the realtime schema, produced by nobody
6666
VOLUME: trade_count // a sort labelled "volume" that ranked by number of trades
67+
CLAIM_STOCKBACK // an IntentKind with no builder — on a HOLDER's money path
68+
LAUNCH // ↑ and the primary creator action, same union
6769
```
6870

71+
**`IntentKind` alone accounts for four of these.** `APPROVE_QUOTE`,
72+
`CLAIM_CREATOR_FEES`, `CLAIM_STOCKBACK` and `LAUNCH` were all shipped as union
73+
members with nothing behind them, and each read as implemented until somebody
74+
tried to use it. Three of the four were money paths.
75+
76+
The last two are the worst instance, because of when they were found. The
77+
creator-fee builder was written *in the same session*, for exactly this defect,
78+
and nobody — including whoever wrote it — went back to ask whether the holder
79+
half had the same gap. It did. The API served a holder their claimable amount
80+
and the Merkle proof to spend it with, and the product had no way to spend
81+
either.
82+
83+
**A union member is a claim that something exists.** The check that finds this
84+
takes one grep per member and was worth doing three commits earlier than it
85+
was.
86+
6987
Each was defensible when written. Each typechecked forever. Each outlived the
7088
reason it existed, silently, and produced a value indistinguishable from a real
7189
one once it reached the database.
@@ -187,6 +205,41 @@ them, rather than asserting that a query succeeds.
187205

188206
---
189207

208+
## The near-miss worth keeping: a guard that refused a real state
209+
210+
Not a shipped defect — it was caught by the e2e on its first run — but it is
211+
the only failure in this document that came from being *too* careful, and that
212+
makes it worth writing down.
213+
214+
The Stockback claim builder refused an empty Merkle proof:
215+
216+
```ts
217+
if (proof.length === 0 && cumulative > 0n) {
218+
throw new Error("no proof supplied");
219+
}
220+
```
221+
222+
The reasoning was sound as far as it went. A caller who forgot to fetch a proof
223+
passes an empty array, and that is worth catching before a transaction is
224+
signed.
225+
226+
What it missed is that **a single-leaf tree has no siblings**, so its proof is
227+
legitimately empty — and a market with one holder is not an edge case, it is
228+
every market on its first day. The guard cost the smallest markets their claims
229+
entirely, to avoid one confusing revert. The vault verifies the proof against
230+
the active root regardless, so the authority was never in doubt.
231+
232+
**What to do about it.** Every validation refuses a set of states. The question
233+
worth asking is not "is this input suspicious" but "what legitimate state does
234+
this reject, and how common is it" — and here the answer was "every market that
235+
has just launched", which is most of them.
236+
237+
It failed on the first real run against a single-holder market. Nothing in the
238+
unit tests would have caught it, because the fixtures all had several holders —
239+
which is the same reason `tests/e2e/stack.ts` exists.
240+
241+
---
242+
190243
## Shape 5 — the argument nobody was checking
191244

192245
One, and it is the most expensive defect in this document.
@@ -264,13 +317,17 @@ verification item blocking it:
264317
- The xStock allowlist, empty — V-02, V-03, V-05
265318
- Platform accounts, unset — C-08
266319
- `Logo.tsx` geometry, pending the official SVG export
267-
- Share-card generation (§95.25). Needs object storage, which §427 leaves as a
268-
CHOOSE decision with no deployment target fixed (§434).
269-
270-
Token metadata is no longer here: it went on-chain (D-013), and the image is
271-
an IPFS CID rather than a file this platform stores. That sidesteps §427
272-
rather than answering it, which is the right outcome for a dependency nothing
273-
actually needs.
320+
- PNG rasterisation of the share card. The card itself is built and tested
321+
(§117, §95.25) and served as SVG; most crawlers want PNG for `og:image`, and
322+
rasterising needs a native dependency whose choice depends on a runtime §434
323+
has not fixed. Everything carrying product knowledge is in `preview.ts`; what
324+
is left is a mechanical transform behind a documented seam.
325+
326+
Neither token metadata nor the share card needs object storage any more.
327+
Metadata went on-chain (D-013) with the image as an IPFS CID, and the card
328+
derives its mark from the token address rather than fetching anything — which
329+
sidesteps §427 rather than answering it, and is the right outcome for a
330+
dependency nothing actually needs.
274331
- `liveMarketCapUsd` (§403). Needs the live xStock/USD DISPLAY feed, V-12. The
275332
field is ABSENT from the response rather than zero: an absent field renders as
276333
nothing, a zero renders as a market worth nothing.

0 commit comments

Comments
 (0)