Render Quantus addresses and chain metadata at prefix 189 #6

Open
opened 2026-09-10 10:15:19 +00:00 by grenade · 2 comments
Owner

Part of #1. Depends on the @polkadot/networks entry in quantus/common#1.

Mostly free, once the network is registered

The address path needs no structural change. components/Address.tsx does:

const publicKey = isHex(address) ? hexToU8a(address) : decodeAddress(address);
const prefix = chain ? chain.ss58Format : (settings.prefix === -1 ? 42 : settings.prefix);
formatted: encodeAddress(publicKey, prefix)

A Quantus account id is 32 bytes, so encodeAddress/decodeAddress work unchanged — the variable is named publicKey and is really an account id, which is misleading but not wrong. Worth a comment; not worth a rename that costs a rebase.

util/chains.ts builds the known-chain list from selectableNetworks, and partials/MenuSettings.tsx builds the prefix dropdown from settings.availablePrefixes. Both light up from the single @polkadot/networks entry, so the work here is verification rather than code.

What actually needs deciding

  • The default prefix. settings.prefix === -1 falls back to 42 (generic substrate). For this extension the sensible default is 189, so an account shows a Quantus address before any dapp has supplied metadata. Changing a default is a decision, not a fix — record it.
  • Identicons. @polkadot/react-identicon renders from the account id, which exists and is 32 bytes, so this works. Confirm it rather than assume: a Quantus identicon and a Substrate identicon for "the same" mnemonic will differ, because the account id is a Poseidon2 hash rather than the public key, and that difference will look like a bug to anyone comparing wallets.
  • Metadata. The extension stores metadata that dapps supply and decodes calls with it. The chain's frame-metadata is vendored unmodified from upstream, so @polkadot/types should decode it as-is — verify against a real runtime and record the metadata version in use.

Acceptance

  • an account created in the extension shows the same SS58 string that quantus-cli and the mobile wallet show
  • a balances.transfer_keep_alive from a dapp renders with the right pallet, call and arguments in the signing popup
  • the recipient address in that call renders at prefix 189, not 42
Part of #1. Depends on the `@polkadot/networks` entry in quantus/common#1. ## Mostly free, once the network is registered The address path needs no structural change. `components/Address.tsx` does: ```js const publicKey = isHex(address) ? hexToU8a(address) : decodeAddress(address); const prefix = chain ? chain.ss58Format : (settings.prefix === -1 ? 42 : settings.prefix); formatted: encodeAddress(publicKey, prefix) ``` A Quantus account id is 32 bytes, so `encodeAddress`/`decodeAddress` work unchanged — the variable is named `publicKey` and is really an account id, which is misleading but not wrong. Worth a comment; not worth a rename that costs a rebase. `util/chains.ts` builds the known-chain list from `selectableNetworks`, and `partials/MenuSettings.tsx` builds the prefix dropdown from `settings.availablePrefixes`. Both light up from the single `@polkadot/networks` entry, so the work here is verification rather than code. ## What actually needs deciding - **The default prefix.** `settings.prefix === -1` falls back to 42 (generic substrate). For this extension the sensible default is 189, so an account shows a Quantus address before any dapp has supplied metadata. Changing a default is a decision, not a fix — record it. - **Identicons.** `@polkadot/react-identicon` renders from the account id, which exists and is 32 bytes, so this works. Confirm it rather than assume: a Quantus identicon and a Substrate identicon for "the same" mnemonic will differ, because the account id is a Poseidon2 hash rather than the public key, and that difference will look like a bug to anyone comparing wallets. - **Metadata.** The extension stores metadata that dapps supply and decodes calls with it. The chain's `frame-metadata` is vendored **unmodified** from upstream, so `@polkadot/types` should decode it as-is — verify against a real runtime and record the metadata version in use. ## Acceptance - [ ] an account created in the extension shows the same SS58 string that `quantus-cli` and the mobile wallet show - [ ] a `balances.transfer_keep_alive` from a dapp renders with the right pallet, call and arguments in the signing popup - [ ] the recipient address in that call renders at prefix 189, not 42
Author
Owner

Two of three acceptance criteria met, and one of them was a regression I introduced

The recipient renders at prefix 189

This was the interesting one, and it got worse before it got better. Moving call decoding onto @quantus/codec (#7) left the approval screen printing

{ "dest": { "Id": "0x300bb607ba60e89461d2f9005668231ceb30237b33db53a614164b8590965519" } }

— a correct description of the value and the one form nobody reads. Upstream got SS58 for free from toHuman(); the codec deliberately does not, because an SS58 prefix is a property of the chain a caller is talking to rather than of the metadata, and inferring one would put a plausible, wrong address in front of the person being asked to approve a transfer.

So @quantus/codec gained setSs58Format, off until set, and metadataExpand passes the one the chain's own metadata definition carries. The screen now shows qzkYEQv8tQsmniZYdame3Cku18RL5g9bGK9Pdydq5TMPdpE3y.

Account ids are found by their registry path, not by length — scale_value carries each value's type id as its context, so the check is on what the runtime declared. A block hash is 32 bytes too, and rendering one as an address would be a lie a reader cannot catch; there is a test that System::BlockHash stays hex with a prefix set.

The SS58 vector is crystal_bob taken from the chain rather than computed, which also pins the two-byte prefix form. 189 needs it, and getting that arithmetic wrong yields an address that looks right and belongs to nobody.

The call renders with the right pallet, call and arguments

decodeMethod moved out of Extrinsic.tsx into util/ with tests, because yarn test skips .spec.tsx and as a component method it had no coverage. They pin three things that matter more than the happy path: metadata from a different spec version is refused rather than shown (it decodes a call into something plausible and wrong); a chain with no runtime renders hex; and bytes that are not a call must not throw, since an exception there lands between somebody and their funds.

The default prefix is now 189

Recording it as the decision this issue asked for. Upstream falls back to 42, the generic Substrate prefix, because it is a wallet for every Substrate chain and has no reason to prefer one. This one does: every account it can hold is a Quantus account, so 42 would show a correct address in a form no Quantus tool displays — same account id, same funds, unfamiliar string. Somebody comparing the extension against quantus-cli or the mobile wallet would reasonably conclude they had created the wrong account.

A chain's own ss58Format still wins where one is known, and the user setting still overrides. DEFAULT_PREFIX in extension-ui/src/util/ carries the reasoning.

Checklist

  • an account created in the extension shows the same SS58 string that quantus-cli and the mobile wallet show — settled by the CLI fixtures in quantus/common#3, and independently by tier 1: the seed 0x0000…00 resolves to qzk1Nxai…2vSn7, which is the dev-genesis account actually holding funds on Heisenberg, so the chain agrees too
  • a balances.transfer_keep_alive from a dapp renders with the right pallet, call and arguments in the signing popup
  • the recipient address in that call renders at prefix 189, not 42

Still open, and honestly

  • Identicons are unverified. The issue asks to confirm rather than assume, and I have not: driving the extension's own UI needs a privileged browsing context this tooling cannot script (see #7). The prediction stands and is worth writing down before somebody reports it as a bug — a Quantus identicon and a Substrate identicon for "the same" mnemonic will differ, because the account id is a Poseidon2 hash of the public key rather than the public key.
  • Metadata version. Recorded for the record: Heisenberg at spec 148 emits v14, 101,493 bytes, declaring extrinsic version 4 and twelve signed extensions. @quantus/codec reads v14 and refuses anything else loudly rather than mis-reading it. The premise in this issue — that @polkadot/types would decode it as-is — turned out to be false, and quantus/api#1 has why.
  • Balances are not displayed at all yet, by this extension. The codec can now read them (quantus/wasm#4), so that is UI work; it wants its own issue rather than living here.
## Two of three acceptance criteria met, and one of them was a regression I introduced ### The recipient renders at prefix 189 This was the interesting one, and it got worse before it got better. Moving call decoding onto `@quantus/codec` (#7) left the approval screen printing ```json { "dest": { "Id": "0x300bb607ba60e89461d2f9005668231ceb30237b33db53a614164b8590965519" } } ``` — a correct description of the value and the one form nobody reads. Upstream got SS58 for free from `toHuman()`; the codec deliberately does not, because an SS58 prefix is a property of the chain a caller is talking to rather than of the metadata, and inferring one would put a plausible, *wrong* address in front of the person being asked to approve a transfer. So `@quantus/codec` gained `setSs58Format`, off until set, and `metadataExpand` passes the one the chain's own metadata definition carries. The screen now shows `qzkYEQv8tQsmniZYdame3Cku18RL5g9bGK9Pdydq5TMPdpE3y`. Account ids are found by their **registry path**, not by length — `scale_value` carries each value's type id as its context, so the check is on what the runtime declared. A block hash is 32 bytes too, and rendering one as an address would be a lie a reader cannot catch; there is a test that `System::BlockHash` stays hex with a prefix set. The SS58 vector is crystal_bob taken from the chain rather than computed, which also pins the **two-byte prefix form**. 189 needs it, and getting that arithmetic wrong yields an address that looks right and belongs to nobody. ### The call renders with the right pallet, call and arguments `decodeMethod` moved out of `Extrinsic.tsx` into `util/` with tests, because `yarn test` skips `.spec.tsx` and as a component method it had no coverage. They pin three things that matter more than the happy path: metadata from a **different spec version** is refused rather than shown (it decodes a call into something plausible and wrong); a chain with no runtime renders hex; and bytes that are not a call must not throw, since an exception there lands between somebody and their funds. ### The default prefix is now 189 Recording it as the decision this issue asked for. Upstream falls back to 42, the generic Substrate prefix, because it is a wallet for every Substrate chain and has no reason to prefer one. This one does: every account it can hold is a Quantus account, so 42 would show a correct address in a form no Quantus tool displays — same account id, same funds, unfamiliar string. Somebody comparing the extension against `quantus-cli` or the mobile wallet would reasonably conclude they had created the wrong account. A chain's own `ss58Format` still wins where one is known, and the user setting still overrides. `DEFAULT_PREFIX` in `extension-ui/src/util/` carries the reasoning. ### Checklist - [x] an account created in the extension shows the same SS58 string that `quantus-cli` and the mobile wallet show — settled by the CLI fixtures in quantus/common#3, and independently by tier 1: the seed `0x0000…00` resolves to `qzk1Nxai…2vSn7`, which is the dev-genesis account actually holding funds on Heisenberg, so the chain agrees too - [x] a `balances.transfer_keep_alive` from a dapp renders with the right pallet, call and arguments in the signing popup - [x] the recipient address in that call renders at prefix 189, not 42 ### Still open, and honestly - **Identicons are unverified.** The issue asks to confirm rather than assume, and I have not: driving the extension's own UI needs a privileged browsing context this tooling cannot script (see #7). The prediction stands and is worth writing down before somebody reports it as a bug — a Quantus identicon and a Substrate identicon for "the same" mnemonic **will** differ, because the account id is a Poseidon2 hash of the public key rather than the public key. - **Metadata version.** Recorded for the record: Heisenberg at spec 148 emits **v14**, 101,493 bytes, declaring extrinsic version 4 and twelve signed extensions. `@quantus/codec` reads v14 and refuses anything else loudly rather than mis-reading it. The premise in this issue — that `@polkadot/types` would decode it as-is — turned out to be false, and quantus/api#1 has why. - **Balances are not displayed at all** yet, by this extension. The codec can now read them (quantus/wasm#4), so that is UI work; it wants its own issue rather than living here.
Author
Owner

Checked against main. Everything except identicons is met.

Met:

  • SS58 matches quantus-cli: common's CLI vectors, and crystal_alice against the chain.
  • Calls render correctly: pallet, call and arguments (util/decodeMethod.spec.ts).
  • Recipients at prefix 189: extension-chains/src/bundle.ts sets the format, and the spec includes a Bob SS58 vector.
  • Default prefix 189, with the reasoning written down: util/defaultPrefix.ts.
  • Metadata version recorded: v14.

Remaining: nobody has looked at identicons. Check one in the running extension, and note whether it is the polkadot identicon of the 32-byte account id and whether that is acceptable. That is a look-and-decide, not code; close once someone has looked.

Checked against `main`. Everything except identicons is met. **Met:** - **SS58 matches `quantus-cli`:** common's CLI vectors, and crystal_alice against the chain. - **Calls render correctly:** pallet, call and arguments (`util/decodeMethod.spec.ts`). - **Recipients at prefix 189:** `extension-chains/src/bundle.ts` sets the format, and the spec includes a Bob SS58 vector. - **Default prefix 189, with the reasoning written down:** `util/defaultPrefix.ts`. - **Metadata version recorded:** v14. **Remaining:** nobody has looked at identicons. Check one in the running extension, and note whether it is the polkadot identicon of the 32-byte account id and whether that is acceptable. That is a look-and-decide, not code; close once someone has looked.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: quantus/extension#6