fix(aegis): 0.9.7 — chipnet WizardConnect advertised the wrong key tree

A chipnet BCH wallet derives from m/44'/1'/0', but the WizardConnect
registration fell back to a hardcoded m/44'/145'/0' whenever the entry
had no explicit accountPath — which is the normal case for a wallet
created through the UI. Mainnet's default happens to be that same
literal, so only chipnet was affected.

The consequence was worse than a failed pairing. Pairing SUCCEEDED, the
handshake carried xpubs for an unrelated key tree, and the dapp then
derived addresses this wallet does not own:

  wallet's real chipnet address : bchtest:qpezx8qkwpjd4e6pd5aang0ve6fctpjvg5ckp2lwu7
  address from the WC xpub      : bchtest:qzvpe3w9rqnszk6mntnef6zv6v94zmfvpc49qkxml4

So the dapp saw an empty stranger's wallet, and anything it built spent
inputs the wallet could not match — signing would fail with "no path for
input". Silent, and only reachable on testnet.

Both registration sites (vault-derived and imported) now take the path
from adapter.snapshot().accountPath, which is by construction the tree
the wallet actually derives its addresses from.

Two wrong turns worth recording. defaultAccountPathFor() takes the coin
CONFIG object, not a chain string, so passing entry.chain returned null
and would have stopped WizardConnect registering at all — strictly worse
than the bug being fixed. chainMeta().defaultAccountPath was no better:
BCH has no coinType in COINS, so it is null for every BCH network. The
adapter is the only component that resolves this correctly, which is why
it is now the source.
This commit is contained in:
Local Dev 2026-09-24 04:56:12 +02:00
parent f07df2b39e
commit 36012e7c26
2 changed files with 12 additions and 3 deletions

View file

@ -1,7 +1,7 @@
{ {
"id": "aegis", "id": "aegis",
"name": "Aegis Wallet", "name": "Aegis Wallet",
"version": "0.9.6", "version": "0.9.7",
"category": "plugin", "category": "plugin",
"description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.", "description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.",
"author": "Silent Mode", "author": "Silent Mode",

View file

@ -535,7 +535,9 @@ async function mountWallet(entry) {
const root = new Uint8Array(seedHex.match(/../g).map((x) => parseInt(x, 16))); const root = new Uint8Array(seedHex.match(/../g).map((x) => parseInt(x, 16)));
return c.wc.startForWallet({ return c.wc.startForWallet({
walletId: entry.id, label: entry.label, walletId: entry.id, label: entry.label,
root32: root, accountPath: entry.accountPath || "m/44'/145'/0'", // Same network-aware default as the vault-derived branch —
// an imported chipnet wallet had the identical mismatch.
root32: root, accountPath: entry.accountPath || adapter.snapshot?.()?.accountPath || "m/44'/145'/0'",
}).finally(() => { try { root.fill(0); } catch {} }); }).finally(() => { try { root.fill(0); } catch {} });
}).catch((e) => { }).catch((e) => {
c.api.log(`[${entry.id}] wc start (imported):`, e?.message || e); c.api.log(`[${entry.id}] wc start (imported):`, e?.message || e);
@ -696,7 +698,14 @@ async function mountWallet(entry) {
if (entry.chain === "bch" && c.wc) { if (entry.chain === "bch" && c.wc) {
c.wc.startForWallet({ c.wc.startForWallet({
walletId: entry.id, label: entry.label, walletId: entry.id, label: entry.label,
root32: root, accountPath: entry.accountPath || "m/44'/145'/0'", // Resolve the default from the wallet's OWN network. This used to
// fall back to a hardcoded m/44'/145'/0' (mainnet), so a chipnet
// wallet — which derives from m/44'/1'/0' — advertised xpubs for a
// completely different key tree. Pairing succeeded and then the
// dapp saw an unrelated, empty wallet; anything it built spent
// from addresses this wallet does not own, so signing failed with
// "no path for input". Silent, and only visible on testnet.
root32: root, accountPath: entry.accountPath || adapter.snapshot?.()?.accountPath || "m/44'/145'/0'",
}).catch((e) => c.api.log(`[${entry.id}] wc start:`, e?.message || e)); }).catch((e) => c.api.log(`[${entry.id}] wc start:`, e?.message || e));
} }
emitStateForWallet(entry.id); emitStateForWallet(entry.id);