// Send view: collect To, Amount, Token. Then go to confirmation. const { $, showFlash, addressDotHtml, addressTitle, escapeHtml, } = require("./helpers"); const { state, currentAddress } = require("../../shared/state"); let ctx; const { getProvider } = require("../../shared/balances"); const { KNOWN_SYMBOLS, resolveSymbol } = require("../../shared/tokenList"); const EXT_ICON = `` + `` + `` + `` + ``; function isSpoofedToken(t) { const upper = (t.symbol || "").toUpperCase(); if (!KNOWN_SYMBOLS.has(upper)) return false; const legit = KNOWN_SYMBOLS.get(upper); if (legit === null) return true; return t.address.toLowerCase() !== legit; } function renderSendTokenSelect(addr) { const sel = $("send-token"); sel.innerHTML = ''; const fraudSet = new Set( (state.fraudContracts || []).map((a) => a.toLowerCase()), ); for (const t of addr.tokenBalances || []) { if (isSpoofedToken(t)) continue; if (fraudSet.has(t.address.toLowerCase())) continue; if (state.hideLowHolderTokens && (t.holders || 0) < 1000) continue; const opt = document.createElement("option"); opt.value = t.address; opt.textContent = t.symbol; sel.appendChild(opt); } } function updateSendBalance() { const addr = currentAddress(); if (!addr) return; const dot = addressDotHtml(addr.address); const link = `https://etherscan.io/address/${addr.address}`; const extLink = `${EXT_ICON}`; const title = addressTitle(addr.address, state.wallets); let fromHtml = ""; if (title) { fromHtml += `
${dot}${escapeHtml(title)}
`; if (addr.ensName) { fromHtml += `
${escapeHtml(addr.ensName)}
`; } fromHtml += `
${escapeHtml(addr.address)}${extLink}
`; } else if (addr.ensName) { fromHtml += `
${dot}${escapeHtml(addr.ensName)}
`; fromHtml += `
${escapeHtml(addr.address)}${extLink}
`; } else { fromHtml += `
${dot}${escapeHtml(addr.address)}${extLink}
`; } $("send-from").innerHTML = fromHtml; const token = state.selectedToken || $("send-token").value; if (token === "ETH") { $("send-balance").textContent = "Current balance: " + (addr.balance || "0") + " ETH"; } else { const tb = (addr.tokenBalances || []).find( (t) => t.address.toLowerCase() === token.toLowerCase(), ); const symbol = resolveSymbol( token, addr.tokenBalances, state.trackedTokens, ); const bal = tb ? tb.balance || "0" : "0"; $("send-balance").textContent = "Current balance: " + bal + " " + symbol; } } function init(_ctx) { ctx = _ctx; $("send-token").addEventListener("change", updateSendBalance); $("btn-send-review").addEventListener("click", async () => { const to = $("send-to").value.trim(); const amount = $("send-amount").value.trim(); if (!to) { showFlash("Please enter a recipient address."); return; } if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) { showFlash("Please enter a valid amount."); return; } // Resolve ENS if needed let resolvedTo = to; let ensName = null; if (to.includes(".") && !to.startsWith("0x")) { try { const provider = getProvider(state.rpcUrl); const resolved = await provider.resolveName(to); if (!resolved) { showFlash("Could not resolve " + to); return; } resolvedTo = resolved; ensName = to; } catch (e) { showFlash("Failed to resolve ENS name."); return; } } const token = state.selectedToken || $("send-token").value; const addr = currentAddress(); let tokenSymbol = null; let tokenBalance = null; if (token !== "ETH") { const tb = (addr.tokenBalances || []).find( (t) => t.address.toLowerCase() === token.toLowerCase(), ); tokenSymbol = resolveSymbol( token, addr.tokenBalances, state.trackedTokens, ); tokenBalance = tb ? tb.balance || "0" : "0"; } ctx.showConfirmTx({ from: addr.address, to: resolvedTo, ensName: ensName, amount: amount, token: token, balance: addr.balance, tokenSymbol: tokenSymbol, tokenBalance: tokenBalance, }); }); $("btn-send-back").addEventListener("click", () => { $("send-token").classList.remove("hidden"); $("send-token-static").classList.add("hidden"); if (state.selectedToken) { ctx.showAddressToken(); } else { ctx.showAddressDetail(); } }); } module.exports = { init, updateSendBalance, renderSendTokenSelect };