Refactor popup into shared modules, wire up real ERC-20 tokens
All checks were successful
check / check (push) Successful in 13s

Split popup/index.js (784 lines) into focused modules:
- shared/state.js: state management, storage persistence
- shared/wallet.js: mnemonic gen, HD derivation, signing
- shared/prices.js: price cache (5min TTL), USD formatting,
  value aggregation (address → wallet → total)
- shared/balances.js: ETH + ERC-20 balance cache (60s TTL),
  ENS lookup, token contract metadata lookup
- shared/vault.js: unchanged (libsodium encryption)
- shared/tokens.js: unchanged (token list + CoinDesk client)
- popup/index.js: view switching and event wiring only

Token tracking is now app-wide: trackedTokens stored in state,
balances fetched for all tracked tokens across all addresses.
Add Token now calls the real contract to read name/symbol/decimals.
Total portfolio value shown in 2x type on Home screen.
This commit is contained in:
2026-02-25 18:48:44 +07:00
parent 2a8c051377
commit f50a2a0389
5 changed files with 494 additions and 333 deletions

63
src/shared/wallet.js Normal file
View File

@@ -0,0 +1,63 @@
// Wallet operations: mnemonic generation, HD derivation, signing.
// All crypto delegated to ethers.js.
const { Mnemonic, HDNodeWallet, Wallet } = require("ethers");
const BIP44_ETH_BASE = "m/44'/60'/0'/0";
const DEBUG = true;
const DEBUG_MNEMONIC =
"cube evolve unfold result inch risk jealous skill hotel bulb night wreck";
function generateMnemonic() {
if (DEBUG) return DEBUG_MNEMONIC;
const m = Mnemonic.fromEntropy(
globalThis.crypto.getRandomValues(new Uint8Array(16)),
);
return m.phrase;
}
function deriveAddressFromXpub(xpub, index) {
const node = HDNodeWallet.fromExtendedKey(xpub);
return node.deriveChild(index).address;
}
function hdWalletFromMnemonic(mnemonic) {
const node = HDNodeWallet.fromPhrase(mnemonic, "", BIP44_ETH_BASE);
const xpub = node.neuter().extendedKey;
const firstAddress = node.deriveChild(0).address;
return { xpub, firstAddress };
}
function addressFromPrivateKey(key) {
const w = new Wallet(key);
return w.address;
}
function getSignerForAddress(walletData, addrIndex, decryptedSecret) {
if (walletData.type === "hd") {
const node = HDNodeWallet.fromPhrase(
decryptedSecret,
"",
BIP44_ETH_BASE,
);
return node.deriveChild(addrIndex);
}
return new Wallet(decryptedSecret);
}
function isValidMnemonic(mnemonic) {
return Mnemonic.isValidMnemonic(mnemonic);
}
module.exports = {
BIP44_ETH_BASE,
DEBUG,
DEBUG_MNEMONIC,
generateMnemonic,
deriveAddressFromXpub,
hdWalletFromMnemonic,
addressFromPrivateKey,
getSignerForAddress,
isValidMnemonic,
};