fix: sign and export with each address's own derivation index (closes #510)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

Sending, site transactions, site signatures and private key export
derived the key at the address's position in the wallet's list. After
an import whose scan passed over an unused index, or after removing an
address that was not the last, that position named another address's
key.

Each HD and xprv address record now stores the index it was derived at
(at wallet creation, by the import scan, by "+"). getSignerForAddress()
derives at that index, refuses a record without one, and refuses any key
whose address differs from the record's. The confirmation screen signs
as the From address it shows, not the selected one. The export screen
reports a refusal instead of calling the password wrong.

Model: opus-5-5
This commit was merged in pull request #536.
This commit is contained in:
2026-10-14 18:22:12 +02:00
parent 8e20a4e21c
commit c40c7432a5
13 changed files with 664 additions and 35 deletions
+14 -5
View File
@@ -567,6 +567,19 @@ function init(_ctx) {
return;
}
// Sign as the From address this screen shows. The selection is saved
// apart from this screen, so the popup can reopen onto it with another
// address selected.
const addr = wallet.addresses.find((a) => a.address === pendingTx.from);
if (!addr) {
showError(
"confirm-tx-password-error",
"The selected wallet does not hold the From address above, " +
"so nothing was sent. Please go back and send again.",
);
return;
}
const password = $("confirm-tx-password").value;
if (!password) {
showError(
@@ -597,11 +610,7 @@ function init(_ctx) {
let tx;
try {
const signer = getSignerForAddress(
wallet,
state.selectedAddress,
decryptedSecret,
);
const signer = getSignerForAddress(wallet, addr, decryptedSecret);
const provider = getProvider(state.rpcUrl, state.networkId);
const connectedSigner = signer.connect(provider);