fix: cross-wallet-type duplicate detection for all import methods
All checks were successful
check / check (push) Successful in 22s

- Private key import now checks ALL wallets (hd, xprv, key) for address conflicts
- xprv import now checks xpub against existing xpubs and addresses across all wallet types
- Mnemonic import now checks xpub against xprv wallets and addresses across all types
- Extract findWalletByAddress() and findWalletByXpub() helpers for consistent dedup

closes #111
This commit is contained in:
user
2026-02-28 15:58:47 -08:00
parent 09c52b2519
commit ca6e9054f9

View File

@@ -11,6 +11,25 @@ const { encryptWithPassword } = require("../../shared/vault");
const { state, saveState } = require("../../shared/state"); const { state, saveState } = require("../../shared/state");
const { scanForAddresses } = require("../../shared/balances"); const { scanForAddresses } = require("../../shared/balances");
/**
* Check if an address already exists in ANY wallet (hd, xprv, or key).
* Returns the wallet object if found, or undefined.
*/
function findWalletByAddress(addr) {
const lower = addr.toLowerCase();
return state.wallets.find((w) =>
w.addresses.some((a) => a.address.toLowerCase() === lower),
);
}
/**
* Check if an xpub already exists in any HD-type wallet (hd or xprv).
* Returns the wallet object if found, or undefined.
*/
function findWalletByXpub(xpub) {
return state.wallets.find((w) => w.xpub && w.xpub === xpub);
}
let currentMode = "mnemonic"; let currentMode = "mnemonic";
const MODES = ["mnemonic", "privkey", "xprv"]; const MODES = ["mnemonic", "privkey", "xprv"];
@@ -97,18 +116,18 @@ async function importMnemonic(ctx) {
const pw = validatePassword(); const pw = validatePassword();
if (!pw) return; if (!pw) return;
const { xpub, firstAddress } = hdWalletFromMnemonic(mnemonic); const { xpub, firstAddress } = hdWalletFromMnemonic(mnemonic);
const duplicate = state.wallets.find( const xpubDup = findWalletByXpub(xpub);
(w) => if (xpubDup) {
w.type === "hd" &&
w.addresses[0] &&
w.addresses[0].address.toLowerCase() === firstAddress.toLowerCase(),
);
if (duplicate) {
showFlash( showFlash(
"This recovery phrase is already added (" + duplicate.name + ").", "This recovery phrase is already added (" + xpubDup.name + ").",
); );
return; return;
} }
const addrDup = findWalletByAddress(firstAddress);
if (addrDup) {
showFlash("Address already exists in wallet (" + addrDup.name + ").");
return;
}
const encrypted = await encryptWithPassword(mnemonic, pw); const encrypted = await encryptWithPassword(mnemonic, pw);
const walletNum = state.wallets.length + 1; const walletNum = state.wallets.length + 1;
const wallet = { const wallet = {
@@ -162,15 +181,10 @@ async function importPrivateKey(ctx) {
} }
const pw = validatePassword(); const pw = validatePassword();
if (!pw) return; if (!pw) return;
const duplicate = state.wallets.find( const duplicate = findWalletByAddress(addr);
(w) =>
w.type === "key" &&
w.addresses[0] &&
w.addresses[0].address.toLowerCase() === addr.toLowerCase(),
);
if (duplicate) { if (duplicate) {
showFlash( showFlash(
"This private key is already added (" + duplicate.name + ").", "This address already exists in wallet (" + duplicate.name + ").",
); );
return; return;
} }
@@ -208,14 +222,14 @@ async function importXprvKey(ctx) {
return; return;
} }
const { xpub, firstAddress } = result; const { xpub, firstAddress } = result;
const duplicate = state.wallets.find( const xpubDup = findWalletByXpub(xpub);
(w) => if (xpubDup) {
(w.type === "hd" || w.type === "xprv") && showFlash("This key is already added (" + xpubDup.name + ").");
w.addresses[0] && return;
w.addresses[0].address.toLowerCase() === firstAddress.toLowerCase(), }
); const addrDup = findWalletByAddress(firstAddress);
if (duplicate) { if (addrDup) {
showFlash("This key is already added (" + duplicate.name + ")."); showFlash("Address already exists in wallet (" + addrDup.name + ").");
return; return;
} }
const pw = validatePassword(); const pw = validatePassword();