Skip to content

fix: a dMint contract's cap is not the token's supply - #611

Merged
Zyrtnin merged 4 commits into
mainfrom
fix/dmint-supply-label-is-per-contract
Sep 4, 2026
Merged

fix: a dMint contract's cap is not the token's supply#611
Zyrtnin merged 4 commits into
mainfrom
fix/dmint-supply-label-is-per-contract

Conversation

@Zyrtnin

@Zyrtnin Zyrtnin commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Stacked on #605. The CLI printed total supply: N for a dMint contract, where N is max_height × reward. That is this contract's cap.

A dMint token commonly deploys several parallel contracts against one token_ref, so the token's supply is the sum across them — and the browser tool computes reward × max_height × N for the same token. Two surfaces of one tool answered "what is this token's supply" with figures differing by the parallel-contract count, and the CLI's carried no qualifier.

The "if all mints succeed" hedge existed — in a code comment, where no reader sees it.

This renderer is handed one contract script and can never see the others, so the fix is to name the quantity rather than attempt a total it has no way to know:

  this contract's cap: 1,050,000,000 photons (21,000 mints x 50,000)
  (the cap IF every mint succeeds, and for THIS contract only —
   a token may deploy several contracts against one token_ref,
   and its supply is the sum across them)

The arithmetic is unchanged and still shown — it is right for the one contract, which was never the defect.

An existing CLI test pinned the old label and is updated rather than deleted, with the reasoning in place. Its docstring said "the derived total supply line" and is aligned too: the sentence that names the thing is part of the claim.

Planted the bare label back — 3 failures. Full suite: 11,021 passed, 192 skipped, 1 xfailed.

🤖 Generated with Claude Code

Mudwood Labs and others added 4 commits September 3, 2026 21:47
`docs/concepts/glyph-inspect-tool.md` listed this under the tool's threat model:

  Token names and tickers are run through the TR39 confusables skeleton check
  (`looks_confusable_with_latin`) — a Cyrillic-spoofed "USDC" is flagged with a
  warning banner before the user sees the rendered metadata.

`looks_confusable_with_latin` had NO PRODUCTION CALLER. Repo-wide it was its own
definition, a facade re-export, and its tests. The CLI ran no confusables check
at all; the browser page used a weaker script-mixing heuristic that fires on any
wholly non-Latin name.

Wired rather than downgraded, because the doc describes a real protection and the
function already exists and is tested. Computed once in `_inspect_core` so both
surfaces read one verdict, and the CLI prints it ABOVE the field it applies to —
a look-alike warning printed after the name is one the reader has already acted
on.

IT REPORTS MIMICRY, NOT FOREIGNNESS. "トークン" and "中文" are in the checker's
own not-flagged examples; "USDС" with a Cyrillic С, "раypal" and small-caps
"ᴜꜱᴅᴄ" are flagged. A warning firing on every legitimate Japanese token is the
false positive that trains a reader to ignore the real one, which this repo names
as a hazard in as many words elsewhere.

A COLLISION FOUND BY LOOKING. `glue.py` produces warnings under the same
`display_warnings` key from its heuristic and ASSIGNED it, so on the browser path
the stronger check's findings would have been silently replaced. Two producers,
one field name, last writer wins. It now seeds from the classifier and uses
setdefault, so the more specific TR39 reason survives where both fire.

MY OWN TEST SUITE FAILED THE RULE IT WAS WRITTEN FOR. The first version called
`_confusable_warnings` directly and nothing else: unwiring it from the classifier
— restoring the exact defect, a capability with no production caller — left all
16 tests green. A suite about a reachability bug that could not detect one.
Reachability tests now build a real reveal transaction and read the warning out
of `_classify_raw_tx`; that plant fails 1.

Three plants, each caught: unwiring the classifier, dropping the CLI rendering,
and restoring the bridge overwrite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two claims the CLI made that the code does not support.

1. THE TX-LISTING ROW DROPPED EVERY QUALIFIER. It printed
   `variant=... (non-transferable at consensus)` and nothing else, while the
   classifier attaches a `note` saying the verdict "does NOT verify that ref names
   a live Glyph singleton, that the singleton is actually held here, or that the
   covenant is free of defects - the covenant is a pre-external-audit prototype".
   The standalone script card prints that in full; the tx listing is the path most
   people meet the tool on, and "non-transferable at consensus" is exactly the
   sentence a credential or swap gate would over-trust. The sibling
   container-legacy branch already points the reader onward; soulbound did not.

2. BOTH VARIANTS WERE CALLED "a byte-identical self-clone". The fixed-index
   builder does compare whole scripts (OP_OUTPUTBYTECODE / OP_UTXOBYTECODE). The
   COMPOSABLE one compares CODE-SCRIPT HASHES - `OP_CODESCRIPTBYTECODE_UTXO
   OP_HASH256` + `OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS` - and its own docstring
   says "code-identical clone", not byte-identical.

   The two coincide only because neither builder emits OP_STATESEPARATOR, so the
   code script IS the whole script. That is not a detail to paper over: code-script
   equality WITH a state prefix is precisely the shape that lets the owner change
   between hops, which `classify_soulbound` now reports as MUTABLE_STATE_COVENANT.
   The composable card now names the weaker constraint AND says why it is
   sufficient here, so the distinction is visible rather than assumed.

No exploit is claimed for (2) - with no state separator a clone must still be
byte-identical in practice. The defect is that the renderer asserts a stronger
property than the variant's bytes impose, on a credential primitive, in the
surface a human reads.

Verified by planting both: calling composable byte-identical again fails 2, and
dropping the tx-listing qualifier fails 1.

Noted for the next restore: the first attempt at that second restore was eaten by
the shell - backticks in the replacement string became command substitution and
the plant stayed live while the command reported an error that looked like a test
result. Restored via a heredoc and confirmed with git diff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The CLI printed `total supply: N` for a dMint contract, where N is
`max_height * reward`. That is THIS CONTRACT's cap. A dMint token commonly
deploys several parallel contracts against ONE token_ref, so the token's supply
is the sum across them - and the browser tool computes `reward x max_height x N`
for the same token. Two surfaces of one tool answered "what is this token's
supply" with figures differing by the parallel-contract count, and the CLI's
carried no qualifier at all.

The "if all mints succeed" hedge existed, in a code comment, where no reader
sees it.

This renderer is handed one contract script and can never see the others, so the
fix is to NAME the quantity rather than attempt a total it has no way to know:

  this contract's cap: 1,050,000,000 photons (21,000 mints x 50,000)
  (the cap IF every mint succeeds, and for THIS contract only -
   a token may deploy several contracts against one token_ref,
   and its supply is the sum across them)

The arithmetic is unchanged and still shown, because it is right for the one
contract - that was never the defect.

An existing CLI test pinned the old label and is updated rather than deleted,
with the reasoning in place: `assert "total supply: 41,850,000,000"` becomes the
per-contract label plus an assertion that the overstating phrase is gone. Its
docstring said "the derived total supply line" and is aligned too - the sentence
that names the thing is part of the claim.

Verified by planting the bare label back: 3 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This branch also predates the conditional `display_warnings` form; main's wins,
and this branch's dMint cap tests are kept.
@Zyrtnin
Zyrtnin merged commit e0877ad into main Sep 4, 2026
15 checks passed
@Zyrtnin
Zyrtnin deleted the fix/dmint-supply-label-is-per-contract branch September 4, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant