Commit Graph

1869 Commits

Author SHA1 Message Date
8e767c0332 fix: remove the Asset Hub migration notice, and the strings left by other removals
Upstream shows an Asset Hub migration notice ahead of everything else on first
open. It is about balances moving from the Polkadot Relay Chain to Asset Hub,
and neither chain exists here — but it is worse than irrelevant, because it told
every user of this extension that their balances had been migrated somewhere and
warned them in red capitals not to teleport to a chain it cannot reach. A wallet
that opens by describing something that did not happen to money the user does
hold is not a cosmetic problem.

Gone with it: the component, the route gate, the `asset_hub_migration_read`
flag and its nine translation strings.

Also regenerates the translation file from source, which dropped 52 more strings
and added none. The scanner adds keys it finds but never prunes ones it does not,
so every UI removal so far has left its text behind — the Ledger path (#5) and
the Parity Signer QR flow account for most of it, along with camera and
network-selection strings from paths that no longer exist. Verified the survivors
rather than assuming: nothing dropped appears as a `t()` call anywhere under
`packages/extension-ui/src`, which is the whole of what the scanner reads.

Refs #2

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 18:02:45 +03:00
b32b24348f feat: show account balances, from a node the user chooses
Upstream shows none: polkadot-js's extension is a signer, and a balance is the
dapp's business. That reasoning does not survive contact with this wallet. A
Quantus account id is a Poseidon2 hash of the public key, not the public key, so
somebody cannot paste an address into a block explorer they already trust and
satisfy themselves it is the account they made — the extension is the only thing
that knows. It is also the only way the account list can tell the truth: without
it, an account nobody has funded and an account holding a thousand QTC look
alike.

`Balances` in the background holds one connection and reads `System::Account`
through @quantus/codec's storage addressing, then watches it with
`state_subscribeStorage`. Only changed keys arrive, so updates merge rather than
replace — a block that moves one account must not blank the rest of the list.
`System::Account` is a **Default** entry, so a node returning nothing means a
zero balance, not a failure; conflating those would show an error for every
account at the moment it is created.

Verified against Heisenberg: four accounts read correctly including one nobody
has ever funded (0, not an error), and a live transfer moved the recipient by
exactly 1000000000 through the subscription while the other three stayed put.

The endpoint is a setting, with the known Quantus endpoints offered and a free
text field beside them. Asking a node for balances tells that node which accounts
belong to one person; anyone who doubts a default should point this at their own
node, and "off" is in the list, after which nothing connects at all. Nothing
connects until the popup asks, and the connection closes with the last
subscriber — a signer holding a socket open to somebody's node for the life of
the browser would report far more than the feature needs.

Kept under its own localStorage key rather than @polkadot/ui-settings' `apiUrl`:
that field means the endpoint polkadot-js *apps* talks to and ships a default of
`ws://127.0.0.1:9944/`, so reusing it would have pointed the extension at a local
node nobody is running and shown no balances at all, with nothing on screen to
say why.

Formatting is string arithmetic throughout. At 12 decimal places `2^53` smallest
units is about 9 007 tokens, so any balance above that loses digits to a Number —
quietly, on the one screen whose whole job is saying how much money somebody has.
The fraction truncates rather than rounds, so a displayed amount is never more
than the account holds.

Closes #12

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 15:50:04 +03:00
7fbcae31e4 feat: show addresses, not account ids, and default the prefix to 189
Two halves of #6.

**The approval screen names its recipient.** Moving call decoding onto
@quantus/codec left `dest` rendering as 32 bytes of hex, because the codec has no
business guessing an SS58 prefix. `metadataExpand` now tells it the one the
chain's own metadata definition carries, so a transfer shows
`qzkYEQv8tQsmniZYdame3Cku18RL5g9bGK9Pdydq5TMPdpE3y` rather than
`0x300bb607…`. Somebody approving a transfer has to be able to check the
recipient against what they meant to send to, and hex is the form nobody checks.

Account ids are identified by registry path, not by length, so a 32-byte block
hash still renders as hex — quantus/wasm has the test.

**The fallback prefix is 189, not 42.** Upstream falls back to 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 —
the same account id, the same funds, an 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 setting still overrides.

Refs #6, 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 15:26:26 +03:00
4f759c4094 test: the tier-1 cases that prove the plumbing, not the happy path
`cases.mjs`, against Heisenberg at spec 148. All five pass:

  PASS  the empty signing context is rejected — 1010: bad signature
  PASS  a payload over 256 bytes is hashed before signing — 279 -> blake2 32
  PASS  a batch_all over 256 bytes dispatches — block 1050692, funded 50 HEI
  PASS  an ML-DSA-65 account signs and is accepted — block 1050693
  PASS  an account exported to JSON and re-imported still signs — block 1050694

The first is the one worth having. Every other failure mode here surfaces as a
decode error somewhere; a signature under the wrong FIPS 204 context is
well-formed, verifies against its own key, and is refused only by the runtime —
so nothing local can tell it from a correct one.

**`submitAndInclude` does not watch the nonce**, and the reason is a mistake this
harness made first. `system_accountNextIndex` counts pending pool transactions,
so it advances the moment a transaction is *accepted* — before it has done
anything. Waiting on it and then reading a balance shows the state from before
the dispatch. Two `batch_all` calls were recorded as reverted on that basis when
both had funded their target with exactly what they were asked for; the accounts
were sitting there holding 50 HEI each while the run said they held nothing. It
now looks for the extrinsic's own bytes in the blocks that arrive, which also
exercises the decoder against real chain data — every Quantus block opens with a
bare-v5 timestamp inherent that @polkadot/api cannot read.

`submit.mjs` moves onto the same shared `chain.mjs` and now *asserts* the
recipient's balance moved rather than printing it, which it could not do before:
reading a balance needs storage addressing, which @quantus/codec only gained for
this (quantus/wasm#4).

Refs #7

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 15:15:51 +03:00
a29a68d48a feat: add the tier 2 harness dapp
quantus/extension#7 tier 2: a page that asks the *extension* to sign, then
assembles and submits the extrinsic itself. `yarn tier2` bundles it; the README
has the run steps and where to get a funded dev account.

Every intermediate value is printed — the call, the extra, the payload that was
signed, the signature that came back, the assembled extrinsic, and the round trip
decoded back out of it. That display is the debugger when a node rejects
something, and it is exactly what a real app hides behind a success toast.

Nothing in it uses @polkadot/api. @quantus/codec produces every byte and the
WebSocket is a JSON-RPC client that decodes nothing.

Also extracts decodeMethod out of Extrinsic.tsx into util/, with tests. It moved
from `registry.createType('Call', data)` to the runtime in the previous commit and
was the one changed path with no coverage — `yarn test` skips .spec.tsx, so as a
component method it would have stayed that way. The tests pin the three cases
that matter more than the happy path: metadata from a different spec version
decodes a call into something plausible and wrong and must be refused; 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.

Verified so far in a browser: the page loads under Firefox with the extension
installed, and @quantus/codec parses Heisenberg's 101,493-byte metadata client
side and reports extrinsic v4 with all twelve signed extensions. The injected
signing round trip is **not** yet verified end to end — driving the extension's
own UI needs a privileged browsing context this tooling cannot script — so the
popup half of tier 2 still wants a human at a keyboard. The background half is
covered by Extension.spec.ts, which runs the full pub(extrinsic.sign) ->
pri(signing.approve.password) path.

Refs #7

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 14:57:53 +03:00
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
be69149957 feat: build and decode extrinsics with @quantus/codec, and prove tier 1
The tier-1 harness now signs a real transfer and gets it into a real block:

    accepted    0xd25bb081e4a87da6a2af79f038f6991e914d732322393096128894c0d644e806
    included    block 1050475 index 1
    decoded     {"Balances":{"transfer_keep_alive":{…,"value":"1000000000"}}}
    nonce       2761 -> 2762

on Heisenberg at spec 148, signed by the forked keyring under QUANTUS_EXTRINSIC.
First end-to-end proof that the fork can spend.

It got there by dropping @polkadot/api entirely. Every byte on the wire is now
produced by @quantus/codec from metadata the node generated by running
Metadata_metadata against the runtime WASM; WsProvider appears only as a
JSON-RPC transport. The api route failed three times over — a 2048-byte cap on
fixed arrays, a preamble byte read as a version so that no Quantus block decodes
at all, and signed extensions *assumed* to be empty rather than read — and
quantus/api#1 has the evidence. The first submission through it was rejected as
`1010: Invalid Transaction: Transaction has a bad signature`; through the codec
the same transfer is accepted.

The harness reads the block back with the same runtime that built the extrinsic,
which also exercises what @polkadot/api cannot do at all: index 0 of every
Quantus block is a timestamp inherent whose preamble byte is 0x05 — bare,
version 5 — beside this signed extrinsic's 0x84, while the metadata declares
version 4.

Also moves the post-quantum chain gate here from @polkadot/networks. Gating the
library list broke @polkadot/api at import, because @polkadot/types-known throws
for a chain it holds upgrade history for but cannot find. What a wallet offers
is the wallet's decision, and this is the wallet.

Refs #7, quantus/api#1, 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:16:34 +03:00
rob thijssen
7fa26252f0 feat: choose the signing context from specVersion, and return sig||pk for raw bytes
Extrinsic signing no longer goes through ExtrinsicPayload.sign(pair). That helper
calls pair.sign(encoded, { withType: true }) and has nowhere to put a signing
context — and an ML-DSA pair refuses to sign without one, so before this the
extension simply threw.

The two lines it would have run are reproduced here with the context added:
encode the payload, BLAKE2b-256 it if it exceeds 256 bytes (Substrate's own rule
from unchecked_extrinsic.rs, which signer and runtime must apply identically),
then sign. Forking @polkadot/types to thread an option through was the
alternative, and is a far larger commitment for the same result.

The context comes from the payload's specVersion, because that is the only place
it can come from: spec >= 148 verifies under QUANTUS_EXTRINSIC, earlier specs
under the empty context, and only the caller knows which runtime it is talking
to. Getting it wrong produces a signature that is cryptographically valid,
rejected by the chain, and indistinguishable from a correct one without asking a
node — which is why both directions are pinned by tests rather than assumed.

Raw bytes use the **empty** context, deliberately not the extrinsic one. A
signature made for a dapp login must never be replayable as a transfer, and the
context is bound into the signature itself. u8aWrapBytes stays, but it is not
what provides that guarantee — it is polkadot-js's own <Bytes> wrapping, kept so
a verifier written against that convention still sees what it expects. For ML-DSA
the context is authoritative: it cannot be stripped or forgotten by a verifier
the way an in-band wrapper can.

No Quantus-specific bytes context is invented. Nothing else in the ecosystem
defines one — the chain and the SDK name only QUANTUS_EXTRINSIC — and making one
up would produce signatures no other Quantus tool could verify.

Raw signing returns sig||pk, which is not a convenience: the account id is a
one-way Poseidon2 hash of the public key, so a verifier holding only an address
cannot recover the key. The key has to travel with the signature. Pinned by a
test that verifies against the signer's address and nothing else.

79 tests pass; typecheck, lint and build:chrome clean.

Refs quantus/extension#4, quantus/common#5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 13:23:58 +03:00
rob thijssen
ac08676ea7 feat: default new accounts to ML-DSA-65, and stop conflating derivable with signable
New accounts are dilithium65. ML-DSA-87 stays importable — accounts predating the
recorded scheme, and the dev-genesis ones — but is never offered as a choice.

The important part of this change is not the default, it is splitting canDerive.

Upstream used canDerive in two unrelated places: gating the derive-from-parent
UI, and filtering which accounts are handed to dapps. That worked only because
every type it knew about happened to be derivable, so the second use was really
asking "is the type known". The coincidence ends with ML-DSA. Making canDerive
honest — false for lattice keys, which are not derivable from one another —
would have silently hidden **every Quantus account from every dapp**, leaving an
extension that looked empty to the entire ecosystem while holding perfectly good
accounts. It types and builds and tests clean either way, which is what makes it
worth a comment and a test rather than a one-line edit.

So: canInject for dapp injection, canDerive for actual derivation. Tabs.ts uses
the former.

canDerive returning false for ML-DSA removes the derive-from-parent flow on its
own — `master` in the account context is found with it, and the menu item is
gated on `master`. That is the right outcome and a permanent one: a pair holds
no material a child could come from, and pair.derive() refuses outright
(quantus/common#4).

Adding a second account is therefore importing the same phrase with an index,
which ImportSeed already supports — it builds `suri = seed + path` and the suri
rules accept `//1`. Its advanced field is relabelled from "derivation path" to an
account index, with a note saying each Quantus account derives from the phrase
independently, because "derivation path" invites a junction chain that will be
rejected.

getSuri needs no change: it only special-cases ethereum, and returns the phrase
untouched for everything else, which is what the Quantus path wants.

Verified rather than assumed — the new spec checks the default type actually
produces the address quantus-cli derives for the public dev phrase, that //1
gives a different account, and that ML-DSA is injectable but not derivable.

70 tests pass; typecheck, lint and build:chrome clean.

Refs quantus/extension#3, quantus/extension#8

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 13:19:43 +03:00
rob thijssen
6dfd109991 feat: replace the Parity Signer QR flow with "track an address"
Keeps watch-only accounts, drops the protocol that cannot work.

ImportQr scanned a Parity Signer QR — SUBSTRATE_ID 0x53, CRYPTO_SR25519 0x01, a
crypto-type byte with no ML-DSA value — so no Quantus device could ever answer
it, and one branch hardcoded createAccountSuri(..., 'sr25519', ...). But it was
also the *only* caller of createAccountExternal, so deleting it would have taken
watch-only accounts with it. Those are wanted independently of how the address
arrives, and are what the account list is used for.

TrackAddress takes a pasted SS58 string instead, which for Quantus is the common
case anyway: the address is on screen in another wallet, not on a signing device.
It round-trips through decodeAddress/encodeAddress, which rejects a mistyped
address by its checksum rather than storing one that can never receive anything,
and normalises the prefix so an address pasted from a tool using a different one
displays the way the rest of the extension displays it.

The signing path had to change with it, and this is the part that would have been
a bug. `isExternal && !isHardware` previously rendered the QR signer — and a
tracked address satisfies exactly that condition, so leaving it would have shown
a Parity QR that nothing can scan, for an account that can never sign. External
accounts now show the decoded call and say plainly that the extension holds no
key for them.

That makes Signing/Qr.tsx unreachable, along with the CMD_MORTAL and
CMD_SIGN_MESSAGE Parity command bytes and the _onSignature callback, whose whole
job was accepting a signature produced outside the extension. All removed.

approveSignSignature in the background is deliberately left. Unlike Ledger, which
is gone for good, external signing returns when quantus/extension#10 ports the
flow to multipart UR — the message it carries is the right shape for that, and
deleting it would only mean writing it again.

Two specs rewritten rather than deleted: the one asserting a QR scanner appears
for external accounts now asserts the extrinsic and the cannot-sign warning
appear instead.

Typecheck, lint and 65 tests clean; build:chrome completes.

Refs quantus/extension#10, quantus/extension#5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 12:19:08 +03:00
rob thijssen
87a0b3926c feat: remove the Ledger account path
No hardware wallet speaks ML-DSA, and @polkadot/hw-ledger talks ed25519 over the
Ledger Substrate app, so every one of these screens was unreachable for a Quantus
account. Removed rather than hidden behind a flag: this fork is not trying to stay
mergeable with upstream, so unreachable UI costs more than it saves. It is also
work that quantus/common#6 turns into a type error regardless.

Gone: ImportLedger.tsx, useLedger.ts, LedgerSign.tsx, legerChains.ts (upstream's
typo), the route and its ALLOWED_PATH entry, the add-menu item, the Ledger App
setting, and the @polkadot/hw-ledger dependency.

Also gone is the background handler. accountsCreateHardware and its message type
were reachable only from ImportLedger, so they are now dead code — and dead code
that mints accounts is worth deleting rather than leaving for someone to wire up
again.

Two things deliberately stay:

isHardware remains on AccountJson. Nothing creates such an account any more, but
a restored JSON can still carry the flag, and the display branches that read it
are correct for that case — an imported hardware account has no key here and
cannot sign. Ripping the flag out would touch unrelated display code to remove a
branch that is right.

canEditGenesis becomes unconditional. It was `!isHardware || ledgerApp ===
'generic'`, because the chain-specific Ledger app could not sign for an arbitrary
genesis hash. With no hardware path and no such setting, nothing restricts it.
The two spec cases that asserted the old behaviour are rewritten into one that
asserts the new rule, rather than deleted.

Not done, contrary to what the issue asked for: no "hardware wallets are not
supported" note in the add menu. The reasoning there was that a missing item
reads as broken — but that only holds for someone looking for it, and in a
Quantus-only wallet nobody is. A permanent dead entry needing translation into
every locale is a worse wart than its absence. It belongs in the README when
branding lands (#2).

65 tests pass; typecheck clean.

Refs quantus/extension#5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 12:02:19 +03:00
rob thijssen
bff8eec1e6 build: consume the Quantus forks from the Gitea registry
Wires this repo to the four forked @polkadot packages and @quantus/crypto, and
the extension now builds with post-quantum crypto in it.

Two mechanisms, for the reason established in quantus/ui: @quantus is routed as a
whole scope in .yarnrc.yml, which is safe because we own every name in it, while
the forked @polkadot packages are pinned individually to tarball URLs in
resolutions. Routing the @polkadot scope would send yarn hunting for the dozens
of packages in it that are not forked — api, types, rpc-provider, dev, x-*,
hw-ledger and more — all of which must keep coming from npm.

The forks carry -quantus.N versions, so a resolution left out fails with "No
candidates found" rather than being quietly satisfied from npm by the unforked
package.

buildHierarchy.spec.ts is rewritten rather than deleted. compareByNetwork
resolves a genesis hash through getNetworkMap(), which is built from
selectableNetworks — and this fork only offers chains whose accounts are
post-quantum, so Polkadot and Kusama are no longer in it. Both fixtures resolved
to '' and compared equal, meaning the network-ordering tests passed without
ordering anything. They now use Quantus and Heisenberg, whose display names sort
in the same direction the originals did, so the fixtures keep their shape.

Verified in the built artifacts rather than assumed: background.js carries
QUANTUS_EXTRINSIC, the dilithium keypair types, ext_poseidon_hash, ext_mldsa_sign
and the base64 wasm payload; extension.js carries the Quantus network entry, QTC
and the mainnet genesis hash. The split is right — signing lives in the service
worker, the UI only needs to render addresses.

65 tests pass; `yarn build:chrome` completes.

Refs quantus/extension#2

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 11:53:36 +03:00
Alberto Nicolas Penayo
d7c9ce2145 0.64.0 (#1625)
Some checks failed
Lock Threads / lock (push) Has been cancelled
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:43:58 -03:00
github-actions[bot]
91f0b7194e [CI Skip] bump/beta 0.63.2-1-x
skip-checks: true
2026-07-31 15:14:13 +00:00
Patricio
e66446edc3 fix: derive raw vs extrinsic signing from the request channel (#1624)
The signing popup decided whether to show the message view or the
transaction view by inspecting fields on the payload (`data`,
`blockNumber`), while the background picks the signing behaviour from the
message channel the request arrived on. Those two are independent and the
payload comes from the dapp, so they can disagree.

`RequestSign` becomes a discriminated union on `channel`, set by the
background from the message type. Correlating the discriminant with the
payload type means consumers narrow through `isExtrinsicRequest` rather
than casting, so the display and the signing path can no longer drift
apart. `Tabs.extrinsicSign` also rejects a payload carrying a truthy
`data`, since that field has no meaning on that channel.

Also branches the signing render on the request rather than on decoded
state. Paging between two queued requests of different shapes could
otherwise commit one frame using the previous request's decoded payload.

Tests added under `signer channel` in `Extension.spec.ts`.
2026-07-31 12:11:11 -03:00
github-actions[bot]
58093e3a50 [CI Skip] bump/beta 0.63.2-0-x
skip-checks: true
2026-07-30 18:24:37 +00:00
Alberto Nicolas Penayo
d98cc677fd chore: fix docs typos (#1623) 2026-07-30 15:21:10 -03:00
github-actions[bot]
987784c81e [CI Skip] release/stable 0.63.1
skip-checks: true
2026-03-31 01:56:50 +00:00
Tarik Gul
b54ff921b4 0.63.1 (#1615)
* 0.63.1

* Update Changelog
2026-03-31 04:54:01 +03:00
github-actions[bot]
77a7f59489 [CI Skip] bump/beta 0.62.7-5-x
skip-checks: true
2026-03-30 20:47:28 +00:00
Francis O'Brien
bc320ec762 Allow Ledger generic app accounts to sign on any chain (#1613)
* feat(ui): allow generic Ledger app to derive from any chain via polkadot slip44

* feat: allow Ledger generic app accounts to sign on any chain

* fix(extension-ui): guard generic ledger init and stabilize i18n deps
2026-03-30 16:44:34 -04:00
github-actions[bot]
2198396137 [CI Skip] bump/beta 0.62.7-4-x
skip-checks: true
2026-03-26 22:20:12 +00:00
Tarik Gul
ec26b80ab1 Bump pjs deps (#1614) 2026-03-27 00:17:18 +02:00
github-actions[bot]
81714b2cf5 [CI Skip] bump/beta 0.62.7-3-x
skip-checks: true
2026-03-26 19:48:35 +00:00
Francis O'Brien
11929ea24e feat(extension-chains): support rawMetadata in metadataExpand alongside metaCalls (#1612) 2026-03-26 15:45:32 -04:00
github-actions[bot]
a9bbe3efb2 [CI Skip] bump/beta 0.62.7-2-x
skip-checks: true
2026-03-26 19:33:03 +00:00
Francis O'Brien
62b7395023 Improve Ledger reliability and signing (WebHID, lifecycle, mismatch guard) (#1611)
* fix: prefer WebHID over WebUSB for Ledger transport to fix Flex/Nano X connectivity

* fix(ui): release Ledger transport with explicit disconnect lifecycle

* fix(ui): align Ledger app mode with reactive settings context

* refactor(ui): keep useLedger render phase pure for init errors

* fix(ui): apply popup-aware routing to Ledger connection links

* fix(ui): harden Ledger signing error handling and mismatch UX

- Remove chain.name being set as specName from merkleizeMetadata payload
- Use reactive SettingsContext ledgerApp instead of settings.get() snapshot
- Guard no-metadata signing path with setIsBusy(false) + early return
- Detect signer mismatch by normalizing addresses and block signing when mismatched
2026-03-26 15:30:08 -04:00
github-actions[bot]
73fef6ffbf [CI Skip] bump/beta 0.62.7-1-x
skip-checks: true
2026-03-12 18:51:34 +00:00
Francis O'Brien
ff42d99624 fix: set chainSpecific account type and disable Ethereum switch (#1608) 2026-03-12 14:48:38 -04:00
github-actions[bot]
67faeb9f69 [CI Skip] bump/beta 0.62.7-0-x
skip-checks: true
2026-03-12 17:30:41 +00:00
Tarik Gul
a1c87fa383 Set headers to 2026 (#1609) 2026-03-12 19:27:39 +02:00
github-actions[bot]
9288eb12b1 [CI Skip] release/stable 0.62.6
skip-checks: true
2025-11-26 07:49:46 +00:00
Arjun Porwal
4622b6c13d 0.62.6 (#1604) 2025-11-26 13:16:54 +05:30
github-actions[bot]
d1fb181e27 [CI Skip] bump/beta 0.62.6-0-x
skip-checks: true
2025-11-26 07:38:34 +00:00
Arjun Porwal
d6473a5c7a chore: bump deps (#1603) 2025-11-26 13:05:38 +05:30
github-actions[bot]
eeeb336417 [CI Skip] release/stable 0.62.5
skip-checks: true
2025-11-17 13:38:00 +00:00
Arjun Porwal
3f5fa9b043 0.62.5 (#1602) 2025-11-17 08:35:06 -05:00
github-actions[bot]
f64d18828d [CI Skip] bump/beta 0.62.5-0-x
skip-checks: true
2025-11-14 16:42:44 +00:00
Tarik Gul
48ef4046ef Add AHM warning page (#1601) 2025-11-14 11:39:34 -05:00
github-actions[bot]
f32f39e371 [CI Skip] release/stable 0.62.4
skip-checks: true
2025-11-13 04:48:02 +00:00
Arjun Porwal
81347b8894 0.63.4 (#1600) 2025-11-13 10:15:10 +05:30
github-actions[bot]
e17b36371b [CI Skip] bump/beta 0.62.4-0-x
skip-checks: true
2025-11-13 04:33:12 +00:00
Arjun Porwal
7103f3e809 chore: bump dependecies (#1599) 2025-11-13 10:00:11 +05:30
github-actions[bot]
132381008e [CI Skip] release/stable 0.62.3
skip-checks: true
2025-10-23 12:48:59 +00:00
Arjun Porwal
3822c6d2d5 0.62.3 (#1597) 2025-10-23 18:15:27 +05:30
github-actions[bot]
ed97de2f85 [CI Skip] bump/beta 0.62.3-0-x
skip-checks: true
2025-10-23 12:08:01 +00:00
Arjun Porwal
461c7d7dbe chore: bump deps (#1596) 2025-10-23 09:04:23 -03:00
github-actions[bot]
0cfe2d5c9d [CI Skip] release/stable 0.62.2
skip-checks: true
2025-10-09 04:12:53 +00:00
Arjun Porwal
87544c7864 0.62.2 (#1594) 2025-10-09 09:39:29 +05:30
github-actions[bot]
211fee8528 [CI Skip] bump/beta 0.62.2-0-x
skip-checks: true
2025-10-08 14:33:42 +00:00