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 --

820
src/shared/tokens.js Normal file
View File

@@ -0,0 +1,820 @@
// Default ERC-20 token list for Ethereum mainnet, ordered by market cap.
// Users can add custom tokens by contract address. This list provides
// convenient defaults so users don't have to look up addresses manually.
//
// Source: Etherscan, CoinGecko, CoinMarketCap as of 2026-02-25.
// Ordered by approximate market cap descending.
const COINDESK_API = "https://data-api.coindesk.com/index/cc/v1/latest/tick";
const DEFAULT_TOKENS = [
// 1-10
{
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
symbol: "USDT",
decimals: 6,
},
{
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
symbol: "USDC",
decimals: 6,
},
{
address: "0xB8c77482e45F1F44dE1745F52C74426C631bDD52",
symbol: "BNB",
decimals: 18,
},
{
address: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
symbol: "LINK",
decimals: 18,
},
{
address: "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE",
symbol: "SHIB",
decimals: 18,
},
{
address: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84",
symbol: "stETH",
decimals: 18,
},
{
address: "0x2AF5D2aD76741191D15Dfe7bF6aC92d4Bd912Ca3",
symbol: "LEO",
decimals: 18,
},
{
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
symbol: "WETH",
decimals: 18,
},
{
address: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
symbol: "WBTC",
decimals: 8,
},
{
address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
symbol: "UNI",
decimals: 18,
},
// 11-20
{
address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
symbol: "DAI",
decimals: 18,
},
{
address: "0x6De037ef9aD2725EB40118Bb1702EBb27e4Aeb24",
symbol: "RNDR",
decimals: 18,
},
{
address: "0x6982508145454Ce325dDbE47a25d4ec3d2311933",
symbol: "PEPE",
decimals: 18,
},
{
address: "0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85",
symbol: "FET",
decimals: 18,
},
{
address: "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
symbol: "AAVE",
decimals: 18,
},
{
address: "0x75231F58b43240C9718Dd58B4967c5114342a86c",
symbol: "OKB",
decimals: 18,
},
{
address: "0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1",
symbol: "ARB",
decimals: 18,
},
{
address: "0x455e53CBB86018Ac2B8092FdCd39d8444aFFC3F6",
symbol: "POL",
decimals: 18,
},
{
address: "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2",
symbol: "MKR",
decimals: 18,
},
{
address: "0xF57e7e7C23978C3cAEC3C3548E3D615c346e79fF",
symbol: "IMX",
decimals: 18,
},
// 21-30
{
address: "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
symbol: "INJ",
decimals: 18,
},
{
address: "0xc944E90C64B2c07662A292be6244BDf05Cda44a7",
symbol: "GRT",
decimals: 18,
},
{
address: "0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3",
symbol: "ONDO",
decimals: 18,
},
{
address: "0x4a220E6096B25EADb88358cb44068A3248254675",
symbol: "QNT",
decimals: 18,
},
{
address: "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32",
symbol: "LDO",
decimals: 18,
},
{
address: "0x3883f5e181fccaF8410FA61e12b59BAd963fb645",
symbol: "THETA",
decimals: 18,
},
{
address: "0x4e15361fd6b4BB609Fa63C81A2be19d873717870",
symbol: "FTM",
decimals: 18,
},
{
address: "0xec53bF9167f50cDEB3Ae105f56099aaaB9061F83",
symbol: "EIGEN",
decimals: 18,
},
{
address: "0x808507121B80c02388fAd14726482e061B8da827",
symbol: "PENDLE",
decimals: 18,
},
{
address: "0x163f8C2467924be0ae7B5347228CABF260318753",
symbol: "WLD",
decimals: 18,
},
// 31-40
{
address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
symbol: "wstETH",
decimals: 18,
},
{
address: "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee",
symbol: "weETH",
decimals: 18,
},
{
address: "0x57e114B691Db790C35207b2e685D4A43181e6061",
symbol: "ENA",
decimals: 18,
},
{
address: "0x5283D291DBCF85356A21bA090E6db59121208b44",
symbol: "BLUR",
decimals: 18,
},
{
address: "0xBB0E17EF65F82Ab018d8EDd776e8DD940327B28b",
symbol: "AXS",
decimals: 18,
},
{
address: "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F",
symbol: "SNX",
decimals: 18,
},
{
address: "0xD533a949740bb3306d119CC777fa900bA034cd52",
symbol: "CRV",
decimals: 18,
},
{
address: "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72",
symbol: "ENS",
decimals: 18,
},
{
address: "0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
symbol: "BAT",
decimals: 18,
},
{
address: "0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD",
symbol: "LRC",
decimals: 18,
},
// 41-50
{
address: "0x3506424F91fD33084466F402d5D97f05F8e3b4AF",
symbol: "CHZ",
decimals: 18,
},
{
address: "0xc00e94Cb662C3520282E6f5717214004A7f26888",
symbol: "COMP",
decimals: 18,
},
{
address: "0x4d224452801ACEd8B2F0aebE155379bb5D594381",
symbol: "APE",
decimals: 18,
},
{
address: "0xae78736Cd615f374D3085123A210448E74Fc6393",
symbol: "rETH",
decimals: 18,
},
{
address: "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942",
symbol: "MANA",
decimals: 18,
},
{
address: "0x3845badAde8e6dFF049820680d1F14bD3903a5d0",
symbol: "SAND",
decimals: 18,
},
{
address: "0x111111111117dC0aa78b770fA6A738034120C302",
symbol: "1INCH",
decimals: 18,
},
{
address: "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
symbol: "YFI",
decimals: 18,
},
{
address: "0x15D4c048F83bd7e37d49eA4C83a07267Ec4203dA",
symbol: "GALA",
decimals: 8,
},
{
address: "0x853d955aCEf822Db058eb8505911ED77F175b99e",
symbol: "FRAX",
decimals: 18,
},
// 51-60
{
address: "0xE41d2489571d322189246DaFA5ebDe1F4699F498",
symbol: "ZRX",
decimals: 18,
},
{
address: "0x6810e776880C02933D47DB1b9fc05908e5386b96",
symbol: "GNO",
decimals: 18,
},
{
address: "0x56072C95FAA701256059aa122697B133aDEd9279",
symbol: "SKY",
decimals: 18,
},
{
address: "0x9994E35Db50125E0DF82e4c2dde62496CE330999",
symbol: "MORPHO",
decimals: 18,
},
{
address: "0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074",
symbol: "MASK",
decimals: 18,
},
{
address: "0xD33526068D116cE69F19A9ee46F0bd304F21A51f",
symbol: "RPL",
decimals: 18,
},
{
address: "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2",
symbol: "SUSHI",
decimals: 18,
},
{
address: "0xba100000625a3754423978a60c9317c58a424e3D",
symbol: "BAL",
decimals: 18,
},
{
address: "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B",
symbol: "CVX",
decimals: 18,
},
{
address: "0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b",
symbol: "CRO",
decimals: 8,
},
// 61-70
{
address: "0x4691937a7508860F876c9c0a2a617E7d9E945D4B",
symbol: "WOO",
decimals: 18,
},
{
address: "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07",
symbol: "OMG",
decimals: 18,
},
{
address: "0xdd974D5C2e2928deA5F71b9825b8b646686BD200",
symbol: "KNC",
decimals: 18,
},
{
address: "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671",
symbol: "NMR",
decimals: 18,
},
{
address: "0x58b6A8A3302369DAEc383334672404Ee733aB239",
symbol: "LPT",
decimals: 18,
},
{
address: "0x767FE9EDC9E0dF98E07454847909338D505F78a4",
symbol: "ILV",
decimals: 18,
},
{
address: "0x7DD9c5Cba05E151C895FDe1CF355C9A1D5DA6429",
symbol: "GLM",
decimals: 18,
},
{
address: "0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D",
symbol: "LQTY",
decimals: 18,
},
{
address: "0x320623b8E4fF03373931769A31Fc52A4E78B5d70",
symbol: "RSR",
decimals: 18,
},
{
address: "0x8290333ceF9e6D528dD5618Fb97a76f268f3EDD4",
symbol: "ANKR",
decimals: 18,
},
// 71-80
{
address: "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828",
symbol: "UMA",
decimals: 18,
},
{
address: "0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0",
symbol: "FXS",
decimals: 18,
},
{
address: "0xDE30da39c46104798bB5aA3fe8B9e0e1F348163F",
symbol: "GTC",
decimals: 18,
},
{
address: "0x408e41876cCCDC0F92210600ef50372656052a38",
symbol: "REN",
decimals: 18,
},
{
address: "0x3472A5A71965499acd81997a54BBA8D852C6E53d",
symbol: "BADGER",
decimals: 18,
},
{
address: "0xbC396689893D065F41bc2C6EcbEE5e0085233447",
symbol: "PERP",
decimals: 18,
},
{
address: "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2",
symbol: "HOT",
decimals: 18,
},
{
address: "0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6",
symbol: "STG",
decimals: 18,
},
{
address: "0x6985884C4392D348587B19cb9eAAf157F13271cd",
symbol: "ZRO",
decimals: 18,
},
{
address: "0xFD09911130e6930Bf87F2B0554c44F400bD80D3e",
symbol: "ETHFI",
decimals: 18,
},
// 81-90
{
address: "0x5aFE3855358E112B5647B952709E6165e1c1eEEe",
symbol: "SAFE",
decimals: 18,
},
{
address: "0x0b38210ea11411557c13457D4dA7dC6ea731B88a",
symbol: "API3",
decimals: 18,
},
{
address: "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b",
symbol: "DPI",
decimals: 18,
},
{
address: "0x7a58c0Be72BE218B41C608b7Fe7C5bB630736C71",
symbol: "PEOPLE",
decimals: 18,
},
{
address: "0xCdF7028ceAB81fA0C6971208e83fa7872994beE5",
symbol: "T",
decimals: 18,
},
{
address: "0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998",
symbol: "AUDIO",
decimals: 18,
},
{
address: "0x4F9254C83EB525f9FCf346490bbb3ed28a81C667",
symbol: "CELR",
decimals: 18,
},
{
address: "0x0391D2021f89DC339F60Fff84546EA23E337750f",
symbol: "BOND",
decimals: 18,
},
{
address: "0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd",
symbol: "DODO",
decimals: 18,
},
{
address: "0x3F382DbD960E3a9bbCeaE22651E88158d2791550",
symbol: "GHST",
decimals: 18,
},
// 91-100
{
address: "0xf4d2888d29D722226FafA5d9B24F9164c092421E",
symbol: "LOOKS",
decimals: 18,
},
{
address: "0x32353A6C91143bfd6C7d363B546e62a9A2489A20",
symbol: "AGLD",
decimals: 18,
},
{
address: "0x090185f2135308BaD17527004364eBcC2D37e5F6",
symbol: "SPELL",
decimals: 18,
},
{
address: "0xc5102fE9359FD9a28f877a67E36B0F050d81a3CC",
symbol: "HOP",
decimals: 18,
},
{
address: "0x0F2D719407FdBeFF09D87557AbB7232601FD9F29",
symbol: "SYN",
decimals: 18,
},
{
address: "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb",
symbol: "INST",
decimals: 18,
},
{
address: "0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5",
symbol: "PSP",
decimals: 18,
},
{
address: "0x31c8EAcBFFdD875c74b94b077895Bd78CF1E64A3",
symbol: "RAD",
decimals: 18,
},
{
address: "0xDBDB4d16EdA451D0503b854CF79D55697F90c8DF",
symbol: "ALCX",
decimals: 18,
},
{
address: "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39",
symbol: "HEX",
decimals: 8,
},
// 101-110 (stablecoins continued)
{
address: "0x6c3ea9036406852006290770BEdFcAbA0e23A0e8",
symbol: "PYUSD",
decimals: 6,
},
{
address: "0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f",
symbol: "GHO",
decimals: 18,
},
{
address: "0xdC035D45d973E3EC169d2276DDab16f1e407384F",
symbol: "USDS",
decimals: 18,
},
{
address: "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3",
symbol: "USDe",
decimals: 18,
},
{
address: "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0",
symbol: "LUSD",
decimals: 18,
},
{
address: "0x8E870D67F660D95d5be530380D0eC0bd388289E1",
symbol: "USDP",
decimals: 18,
},
{
address: "0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd",
symbol: "GUSD",
decimals: 2,
},
{
address: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E",
symbol: "crvUSD",
decimals: 18,
},
{
address: "0x99D8a9C45b2ecA8864373A26D1459e3Dff1e17F3",
symbol: "MIM",
decimals: 18,
},
{
address: "0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919",
symbol: "RAI",
decimals: 18,
},
// 111-120 (wrapped / liquid staking continued)
{
address: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
symbol: "cbBTC",
decimals: 8,
},
{
address: "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704",
symbol: "cbETH",
decimals: 18,
},
{
address: "0xa2E3356610840701BDf5611a53974510Ae27E2e1",
symbol: "wBETH",
decimals: 18,
},
{
address: "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110",
symbol: "ezETH",
decimals: 18,
},
{
address: "0xD9A442856C234a39a81a089C06451EBAa4306a72",
symbol: "pufETH",
decimals: 18,
},
{
address: "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7",
symbol: "rsETH",
decimals: 18,
},
{
address: "0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38",
symbol: "osETH",
decimals: 18,
},
{
address: "0x5E8422345238F34275888049021821E8E08CAa1f",
symbol: "frxETH",
decimals: 18,
},
{
address: "0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa",
symbol: "mETH",
decimals: 18,
},
{
address: "0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa",
symbol: "tBTC",
decimals: 18,
},
// 121-130
{
address: "0x4da27a545c0c5B758a6BA100e3a049001de870f5",
symbol: "stkAAVE",
decimals: 18,
},
{
address: "0x62B9c7356A2Dc64a1969e19C23e4f579F9810Aa7",
symbol: "cvxCRV",
decimals: 18,
},
{
address: "0x0d438F3b5175Bebc262bF23753C1E53d03432bDE",
symbol: "wNXM",
decimals: 18,
},
{
address: "0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44",
symbol: "KP3R",
decimals: 18,
},
{
address: "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e",
symbol: "POOL",
decimals: 18,
},
{
address: "0xba5BDe662c17e2aDFF1075610382B9B691296350",
symbol: "RARE",
decimals: 18,
},
{
address: "0x44108f0223A3C3028F5Fe7AEC7f9bb2E66beF82F",
symbol: "ACX",
decimals: 18,
},
{
address: "0x33349B282065b0284d756F0577FB39c158F935e6",
symbol: "MPL",
decimals: 18,
},
{
address: "0x6243d8CEA23066d098a15582d81a598b4e8391F4",
symbol: "FLX",
decimals: 18,
},
{
address: "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C",
symbol: "TORN",
decimals: 18,
},
// 131-140
{
address: "0xaaeE1A9723aaDB7afA2810263653A34bA2C21C7a",
symbol: "MOG",
decimals: 18,
},
{
address: "0xb131f4A55907B10d1F0A50d8ab8FA09EC342cd74",
symbol: "MEME",
decimals: 18,
},
{
address: "0x761D38e5ddf6ccf6Cf7c55759d5210750B5D60F3",
symbol: "ELON",
decimals: 18,
},
{
address: "0x8f8221aFbB33998d8584A2B05749bA73c37a938a",
symbol: "REQ",
decimals: 18,
},
{
address: "0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9",
symbol: "SXP",
decimals: 18,
},
{
address: "0x5732046A883704404F284Ce41FfADd5b007FD668",
symbol: "BLZ",
decimals: 18,
},
{
address: "0xDDB3422497E61e13543BeA06989C0789117555c5",
symbol: "COTI",
decimals: 18,
},
{
address: "0x9E32b13ce7f2e80A01932B42553652E053D6ed8e",
symbol: "Metis",
decimals: 18,
},
{
address: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
symbol: "EUROC",
decimals: 6,
},
{
address: "0xC581b735A1688071A1746c968e0798D642EDE491",
symbol: "EURT",
decimals: 6,
},
// 141-150
{
address: "0x6e1A19F235bE7ED8E3369eF73b196C07257494DE",
symbol: "FIL",
decimals: 18,
},
{
address: "0xC669928185DbCE49d2230CC9B0979BE6DC797957",
symbol: "BTT",
decimals: 18,
},
{
address: "0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69",
symbol: "IOTX",
decimals: 18,
},
{
address: "0x41e5560054824eA6B0732E656E3Ad64E20e94E45",
symbol: "CVC",
decimals: 8,
},
{
address: "0x27054b13b1B798B345b591a4d22e6562d47eA75a",
symbol: "AST",
decimals: 4,
},
{
address: "0x2e9d63788249371f1DFC918a52f8d799F4a38C94",
symbol: "TOKE",
decimals: 18,
},
{
address: "0x9813037ee2218799597d83D4a5B6F3b6778218d9",
symbol: "BONE",
decimals: 18,
},
{
address: "0xCa14007Eff0dB1f8135f4C25B34De49AB0d42766",
symbol: "STRK",
decimals: 18,
},
{
address: "0x0C10bF8FcB7Bf5412187A595ab97a3609160b5c6",
symbol: "USDD",
decimals: 18,
},
{
address: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
symbol: "MATIC",
decimals: 18,
},
];
// Deduplicate by address (case-insensitive), preserving order
const seen = new Set();
const TOKENS = DEFAULT_TOKENS.filter((t) => {
const key = t.address.toLowerCase();
if (seen.has(key)) return false;
seen.add(key);
return true;
});
// Return the symbols of the top N tokens (by position in the list).
function getTopTokenSymbols(n) {
return TOKENS.slice(0, n).map((t) => t.symbol);
}
// Fetch USD prices for the top N tokens from CoinDesk.
// Also always includes ETH. Returns { ETH: 1234.56, LINK: 8.60, ... }.
// n must be <= 30 (API and sanity limit).
async function getTopTokenPrices(n) {
if (n > 30) {
throw new Error("getTopTokenPrices: n must be <= 30, got " + n);
}
const symbols = ["ETH", ...getTopTokenSymbols(n)];
// Deduplicate (ETH might be in the token list as WETH but we want ETH-USD)
const unique = [...new Set(symbols)];
const instruments = unique.map((s) => s + "-USD").join(",");
const url =
COINDESK_API +
"?market=cadli&instruments=" +
instruments +
"&apply_mapping=true";
const resp = await fetch(url);
const json = await resp.json();
const prices = {};
for (const [instrument, data] of Object.entries(json.Data || {})) {
const symbol = instrument.split("-")[0];
if (data.VALUE !== undefined) {
prices[symbol] = data.VALUE;
}
}
return prices;
}
module.exports = { TOKENS, getTopTokenSymbols, getTopTokenPrices };