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

79
src/shared/prices.js Normal file
View File

@@ -0,0 +1,79 @@
// Price fetching with 5-minute cache, USD formatting, value aggregation.
const { getTopTokenPrices } = require("./tokens");
const PRICE_CACHE_TTL = 300000; // 5 minutes
const prices = {};
let lastFetchedAt = 0;
async function refreshPrices() {
const now = Date.now();
if (now - lastFetchedAt < PRICE_CACHE_TTL) return;
try {
const fetched = await getTopTokenPrices(25);
Object.assign(prices, fetched);
lastFetchedAt = now;
} catch (e) {
// prices stay stale on error
}
}
function getPrice(symbol) {
return prices[symbol] || null;
}
function formatUsd(amount) {
if (amount === null || amount === undefined || isNaN(amount)) return "";
if (amount === 0) return "$0.00";
if (amount < 0.01) return "< $0.01";
return (
"$" +
amount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
);
}
function getAddressValueUsd(addr) {
let total = 0;
const ethBal = parseFloat(addr.balance || "0");
const ethPrice = prices.ETH;
if (ethPrice) {
total += ethBal * ethPrice;
}
for (const token of addr.tokenBalances || []) {
const tokenBal = parseFloat(token.balance || "0");
if (tokenBal > 0 && prices[token.symbol]) {
total += tokenBal * prices[token.symbol];
}
}
return total;
}
function getWalletValueUsd(wallet) {
let total = 0;
for (const addr of wallet.addresses) {
total += getAddressValueUsd(addr);
}
return total;
}
function getTotalValueUsd(wallets) {
let total = 0;
for (const wallet of wallets) {
total += getWalletValueUsd(wallet);
}
return total;
}
module.exports = {
prices,
refreshPrices,
getPrice,
formatUsd,
getAddressValueUsd,
getWalletValueUsd,
getTotalValueUsd,
};