Files
extension/packages
rob thijssen 34759485d3 feat: sign and decode in the extension itself via @quantus/codec
Tier 1 proved the crypto in a script. This puts the same path inside the
extension, which is what tier 2 needs: the background builds its signing payload
from the runtime's own metadata, and the approval screen decodes the call the
same way.

  - RequestExtrinsicSign takes a Runtime instead of a polkadot-js Registry and
    builds the payload with it. `ExtrinsicPayload.sign(pair)` could not survive
    either half of this: @polkadot/types cannot describe this chain (fixed arrays
    capped at 2048 bytes, ML-DSA signatures are 5261 and 7219), and it has
    nowhere to put a FIPS 204 context, which an ML-DSA pair will not sign
    without.
  - metadataExpand builds a Runtime rather than calling registry.setMetadata,
    which would throw on this chain's metadata. Chain.registry survives carrying
    chain properties only.
  - Extrinsic.tsx decodes the call through the runtime. Without this the approval
    screen falls back to raw hex for every transaction — the failure mode where a
    user approves bytes nobody read to them.
  - A chain whose metadata the extension does not have is one it now **refuses**
    to sign an extrinsic for, rather than guessing at the format. The refusal
    rejects the dapp's promise as well as the popup's, so a page is told rather
    than left waiting.

Raw-bytes signing is unchanged and still needs no metadata: there is no payload
to build and no extension to read.

The dapp-supplied `era` arrives already SCALE-encoded, and is round-tripped
against the runtime's own Era type rather than appended on trust — @quantus/codec
0.2.0 adds that, and rejects anything that does not come back identical.

Tests: upstream's `custom user extension` block was five variations on "does this
agree with @polkadot/api", each computing an expected signature with
registry.createType('ExtrinsicPayload').sign(pair). That comparison is no longer
meaningful, and the `userExtensions` mechanism it exercised — a dapp declaring in
JavaScript what an unrecognised signed extension contributes — is gone, which is
the point: the runtime declares its extensions and the wallet reads them, so
there is nothing for a dapp to assert and no way for it to be believed. Replaced
with tests for what the fork actually guarantees, against real Heisenberg
metadata committed as a fixture.

The harness now loads the keyring with type 'dilithium65' as background.ts does;
it had been making sr25519 accounts and testing the one keypair type this fork
does not target. The derivation specs name ed25519 explicitly, which is what they
were silently getting, and gain a test that an ML-DSA parent cannot derive.

Verified end to end after the rewire: included at Heisenberg block 1050581,
nonce 2762 -> 2763. 79 tests pass, lint clean, extension builds.

Refs #7, quantus/wasm#3

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 14:45:12 +03:00
..
2026-07-31 12:43:58 -03:00