All checks were successful
check / check (push) Successful in 22s
- Tracked Tokens well in settings with [x] remove buttons - New settings-addtoken view with: - Top-10 quick-pick buttons (tracked ones dimmed+disabled) - Top-100 dropdown showing "Token Name (SYMBOL)", tracked disabled - Manual contract address entry with RPC lookup - View comment in helpers.js about keeping README in sync
160 lines
5.5 KiB
JavaScript
160 lines
5.5 KiB
JavaScript
const { $, showView, showFlash } = require("./helpers");
|
|
const { getTopTokens } = require("../../shared/tokenList");
|
|
const { state, saveState } = require("../../shared/state");
|
|
const { lookupTokenInfo } = require("../../shared/balances");
|
|
const { isScamAddress } = require("../../shared/scamlist");
|
|
const { log } = require("../../shared/log");
|
|
|
|
let ctx;
|
|
|
|
function isTracked(address) {
|
|
const lower = address.toLowerCase();
|
|
return state.trackedTokens.some((t) => t.address.toLowerCase() === lower);
|
|
}
|
|
|
|
function tokenLabel(t) {
|
|
return t.name ? t.name + " (" + t.symbol + ")" : t.symbol;
|
|
}
|
|
|
|
function renderTop10() {
|
|
const el = $("settings-addtoken-top10");
|
|
el.innerHTML = getTopTokens(10)
|
|
.map((t) => {
|
|
const tracked = isTracked(t.address);
|
|
const cls = tracked
|
|
? "border border-border px-1 text-xs opacity-40 cursor-default"
|
|
: "border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer text-xs";
|
|
return (
|
|
`<button class="settings-addtoken-quick ${cls}"` +
|
|
` data-address="${t.address}"` +
|
|
` data-symbol="${t.symbol}"` +
|
|
` data-decimals="${t.decimals}"` +
|
|
` data-name="${(t.name || "").replace(/"/g, """)}"` +
|
|
`${tracked ? " disabled" : ""}>${t.symbol}</button>`
|
|
);
|
|
})
|
|
.join("");
|
|
el.querySelectorAll(".settings-addtoken-quick:not([disabled])").forEach(
|
|
(btn) => {
|
|
btn.addEventListener("click", async () => {
|
|
const token = {
|
|
address: btn.dataset.address,
|
|
symbol: btn.dataset.symbol,
|
|
decimals: parseInt(btn.dataset.decimals, 10),
|
|
name: btn.dataset.name || btn.dataset.symbol,
|
|
};
|
|
state.trackedTokens.push(token);
|
|
await saveState();
|
|
showFlash("Added " + token.symbol);
|
|
renderTop10();
|
|
renderDropdown();
|
|
ctx.doRefreshAndRender();
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
function renderDropdown() {
|
|
const sel = $("settings-addtoken-select");
|
|
const tokens = getTopTokens(100);
|
|
let html = '<option value="">-- select --</option>';
|
|
for (const t of tokens) {
|
|
const tracked = isTracked(t.address);
|
|
const label = tokenLabel(t) + (tracked ? " (tracked)" : "");
|
|
html +=
|
|
`<option value="${t.address}"` +
|
|
` data-symbol="${t.symbol}"` +
|
|
` data-decimals="${t.decimals}"` +
|
|
` data-name="${(t.name || "").replace(/"/g, """)}"` +
|
|
`${tracked ? " disabled" : ""}>${label}</option>`;
|
|
}
|
|
sel.innerHTML = html;
|
|
}
|
|
|
|
function show() {
|
|
$("settings-addtoken-address").value = "";
|
|
$("settings-addtoken-info").classList.add("hidden");
|
|
renderTop10();
|
|
renderDropdown();
|
|
showView("settings-addtoken");
|
|
}
|
|
|
|
function init(_ctx) {
|
|
ctx = _ctx;
|
|
|
|
$("btn-settings-addtoken-back").addEventListener("click", () => {
|
|
ctx.showSettingsView();
|
|
});
|
|
|
|
$("btn-settings-addtoken-select").addEventListener("click", async () => {
|
|
const sel = $("settings-addtoken-select");
|
|
const opt = sel.options[sel.selectedIndex];
|
|
if (!opt || !opt.value) {
|
|
showFlash("Please select a token.");
|
|
return;
|
|
}
|
|
if (isTracked(opt.value)) {
|
|
showFlash("Already tracked.");
|
|
return;
|
|
}
|
|
const token = {
|
|
address: opt.value,
|
|
symbol: opt.dataset.symbol,
|
|
decimals: parseInt(opt.dataset.decimals, 10),
|
|
name: opt.dataset.name || opt.dataset.symbol,
|
|
};
|
|
state.trackedTokens.push(token);
|
|
await saveState();
|
|
showFlash("Added " + token.symbol);
|
|
renderTop10();
|
|
renderDropdown();
|
|
ctx.doRefreshAndRender();
|
|
});
|
|
|
|
$("btn-settings-addtoken-manual").addEventListener("click", async () => {
|
|
const addr = $("settings-addtoken-address").value.trim();
|
|
if (!addr || !addr.startsWith("0x")) {
|
|
showFlash(
|
|
"Please enter a valid contract address starting with 0x.",
|
|
);
|
|
return;
|
|
}
|
|
if (isTracked(addr)) {
|
|
showFlash("Already tracked.");
|
|
return;
|
|
}
|
|
if (isScamAddress(addr)) {
|
|
showFlash("This address is on a known scam/fraud list.");
|
|
return;
|
|
}
|
|
const infoEl = $("settings-addtoken-info");
|
|
infoEl.textContent = "Looking up token...";
|
|
infoEl.classList.remove("hidden");
|
|
log.debugf("Looking up token contract", addr);
|
|
try {
|
|
const info = await lookupTokenInfo(addr, state.rpcUrl);
|
|
log.infof("Adding token", info.symbol, addr);
|
|
state.trackedTokens.push({
|
|
address: addr,
|
|
symbol: info.symbol,
|
|
decimals: info.decimals,
|
|
name: info.name,
|
|
});
|
|
await saveState();
|
|
showFlash("Added " + info.symbol);
|
|
$("settings-addtoken-address").value = "";
|
|
infoEl.classList.add("hidden");
|
|
renderTop10();
|
|
renderDropdown();
|
|
ctx.doRefreshAndRender();
|
|
} catch (e) {
|
|
const detail = e.shortMessage || e.message || String(e);
|
|
log.errorf("Token lookup failed for", addr, detail);
|
|
showFlash(detail);
|
|
infoEl.classList.add("hidden");
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = { init, show };
|