Add token list module with CoinDesk price client
All checks were successful
check / check (push) Successful in 12s

tokens.js: ~150 ERC-20 tokens ordered by market cap with
getTopTokenSymbols(n) and getTopTokenPrices(n) (errors if n>30).
Price fetching uses CoinDesk CADLI API. Popup now shows USD
values next to ETH balances in wallet list and address detail.
Prices and balances fetched in parallel on popup open.
This commit is contained in:
2026-02-25 17:35:27 +07:00
parent 933c13ad1a
commit 097f90d7f8
2 changed files with 860 additions and 7 deletions

View File

@@ -6,6 +6,7 @@ const {
JsonRpcProvider,
formatEther,
} = require("ethers");
const { getTopTokenPrices } = require("../shared/tokens");
const DEBUG = true;
const DEBUG_MNEMONIC =
@@ -119,6 +120,31 @@ function addressFromPrivateKey(key) {
return w.address;
}
// -- price fetching --
// { "ETH": 1234.56, "LINK": 8.60, ... }
const prices = {};
async function refreshPrices() {
try {
const fetched = await getTopTokenPrices(25);
Object.assign(prices, fetched);
} catch (e) {
// prices stay empty on error
}
}
function formatUsd(amount) {
if (amount === null || amount === undefined || isNaN(amount)) return "";
if (amount < 0.01) return "< $0.01";
return (
"$" +
amount.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
);
}
// -- balance fetching --
function getProvider() {
return new JsonRpcProvider(state.rpcUrl);
@@ -188,9 +214,15 @@ function renderWalletList() {
if (addr.ensName) {
html += `<div class="text-xs font-bold">${addr.ensName}</div>`;
}
html += `<div class="text-xs break-all">${addr.address}</div>`;
html += `<div class="flex justify-between items-center">`;
html += `<span class="text-xs break-all">${addr.address}</span>`;
html += `<span class="text-xs ml-1 whitespace-nowrap">${addr.balance} ETH</span>`;
html += `<span class="text-xs">${addr.balance} ETH</span>`;
const ethUsd = prices.ETH
? parseFloat(addr.balance) * prices.ETH
: null;
if (ethUsd !== null) {
html += `<span class="text-xs text-muted">${formatUsd(ethUsd)}</span>`;
}
html += `</div>`;
html += `</div>`;
});
@@ -235,7 +267,9 @@ function showAddressDetail() {
$("address-full").textContent = addr.address;
$("address-copied-msg").textContent = "";
$("address-eth-balance").textContent = addr.balance;
$("address-usd-value").textContent = "";
const ethUsd = prices.ETH ? parseFloat(addr.balance) * prices.ETH : null;
$("address-usd-value").textContent =
ethUsd !== null ? formatUsd(ethUsd) : "";
const ensEl = $("address-ens");
if (addr.ensName) {
ensEl.textContent = addr.ensName;
@@ -331,10 +365,9 @@ async function init() {
} else {
renderWalletList();
showView("main");
// Fetch balances in the background, re-render when done
refreshBalances().then(() => {
renderWalletList();
});
// Fetch prices and balances in parallel, re-render as each completes
refreshPrices().then(() => renderWalletList());
refreshBalances().then(() => renderWalletList());
}
// -- Welcome --