chore: the native token's label follows the active network (closes #372)
check / check (push) Failing after 2s
e2e / e2e-chrome (push) Failing after 2s
e2e / e2e-firefox (push) Failing after 2s

networks.js gives each network a nativeCurrency (ETH, SepoliaETH) and nothing
read it: every screen wrote a hardcoded ETH, so on Sepolia the balance, the
value and the fee all read ETH. A nativeCurrency() helper in the popup's
helpers.js returns the active network's, and every native balance, value, fee,
reserve, type line and history entry is labelled with it. The "ETH" that
selectedToken and txInfo.token hold is the native token's id and is unchanged.
The confirmation screen's not-enough-to-pay-the-fee sentence now names the
coin to add, so show() sets it.

Model: opus-5-5
This commit is contained in:
2026-10-04 21:53:00 +00:00
parent 6127fd9432
commit f47d17a98f
15 changed files with 407 additions and 43 deletions
+2
View File
@@ -7,6 +7,7 @@ const {
addressTitle,
escapeHtml,
displaySymbol,
nativeCurrency,
renderAddressHtml,
attachCopyHandlers,
goBack,
@@ -153,6 +154,7 @@ async function loadTransactions(address) {
const rawTxs = await fetchRecentTransactions(
address,
state.blockscoutUrl,
nativeCurrency(),
);
const result = filterTransactions(rawTxs, {
hideSpoofedSymbols: state.hideSpoofedSymbols,
+3 -1
View File
@@ -10,6 +10,7 @@ const {
addressTitle,
escapeHtml,
displaySymbol,
nativeCurrency,
balanceLine,
unknownableAmount,
renderAddressHtml,
@@ -106,7 +107,7 @@ function show() {
let symbol, amount, price;
const knownToken = TOKEN_BY_ADDRESS.get(tokenId.toLowerCase());
if (tokenId === "ETH") {
symbol = "ETH";
symbol = nativeCurrency();
amount = parseFloat(addr.balance || "0");
price = getPrice("ETH");
} else {
@@ -226,6 +227,7 @@ async function loadTransactions(address, tokenId) {
const rawTxs = await fetchRecentTransactions(
address,
state.blockscoutUrl,
nativeCurrency(),
);
const result = filterTransactions(rawTxs, {
hideSpoofedSymbols: state.hideSpoofedSymbols,
+5 -1
View File
@@ -10,6 +10,7 @@ const {
attachCopyHandlers,
onViewLeave,
formatFee,
nativeCurrency,
} = require("./helpers");
const { state, saveState } = require("../../shared/state");
const { networkByChainId } = require("../../shared/networks");
@@ -330,7 +331,10 @@ function showTxApproval(details) {
const ethUsd = ethPrice ? parseFloat(ethValueFormatted) * ethPrice : null;
const usdStr = formatUsd(ethUsd);
$("approve-tx-value").textContent =
ethValueFormatted + " ETH" + (usdStr ? " (" + usdStr + ")" : "");
ethValueFormatted +
" " +
nativeCurrency() +
(usdStr ? " (" + usdStr + ")" : "");
showTxFee(approvedTx);
+23 -8
View File
@@ -11,6 +11,7 @@ const {
addressTitle,
escapeHtml,
displaySymbol,
nativeCurrency,
renderAddressHtml,
attachCopyHandlers,
goBack,
@@ -90,7 +91,7 @@ function show(txInfo) {
// The raw symbol is the price-table key; the capped one is what the
// screen says. Truncating before the lookup would silently drop the
// price of any token whose symbol is long enough to be capped.
const rawSymbol = isErc20 ? txInfo.tokenSymbol || "?" : "ETH";
const rawSymbol = isErc20 ? txInfo.tokenSymbol || "?" : nativeCurrency();
const symbol = displaySymbol(rawSymbol);
// Transaction type
@@ -98,7 +99,7 @@ function show(txInfo) {
$("confirm-type").textContent =
"ERC-20 token transfer (" + symbol + ")";
} else {
$("confirm-type").textContent = "Native ETH transfer";
$("confirm-type").textContent = "Native " + symbol + " transfer";
}
// Token contract section (ERC-20 only)
@@ -162,7 +163,7 @@ function show(txInfo) {
const bal = txInfo.balance || "0";
const balUsd = ethPrice ? parseFloat(bal) * ethPrice : null;
$("confirm-balance").textContent = valueWithUsd(
truncateAmountNeverZero(bal) + " ETH",
truncateAmountNeverZero(bal) + " " + symbol,
balUsd,
);
}
@@ -197,6 +198,12 @@ function show(txInfo) {
// estimate landing later never moves anything.
$("confirm-amount-fee-error").classList.toggle("hidden", isErc20);
$("confirm-gas-error").classList.toggle("hidden", !isErc20);
$("confirm-gas-error").textContent =
"You do not have enough " +
nativeCurrency() +
" to pay the network fee for this transfer. Please add " +
nativeCurrency() +
" to this address and try again.";
// The fee-unknown message names its cause, which is also known here.
// Without the token's scale estimateGas() cannot encode the transfer, so
@@ -245,7 +252,9 @@ function show(txInfo) {
// touches already occupies its space, so re-running it never moves anything.
function renderValidation(txInfo) {
const isErc20 = txInfo.token !== "ETH";
const symbol = isErc20 ? displaySymbol(txInfo.tokenSymbol || "?") : "ETH";
const symbol = isErc20
? displaySymbol(txInfo.tokenSymbol || "?")
: nativeCurrency();
const { canSend, codes } = validateTransfer({
isErc20,
@@ -258,7 +267,7 @@ function renderValidation(txInfo) {
// Messages carrying the user's own numbers are built here; the fixed
// sentences live in the reserved elements in index.html, except the
// fee-unknown one, which show() sets.
// gas and fee-unknown ones, which show() sets.
const messages = [];
if (codes.includes(CODES.AMOUNT_INVALID)) {
messages.push("Please enter a valid amount to send.");
@@ -287,9 +296,13 @@ function renderValidation(txInfo) {
messages.push(
"Insufficient balance. You have " +
truncateAmountNeverZero(txInfo.balance || "0") +
" ETH but are trying to send " +
" " +
symbol +
" but are trying to send " +
txInfo.amount +
" ETH.",
" " +
symbol +
".",
);
}
@@ -382,7 +395,9 @@ async function estimateGas(txInfo) {
$("confirm-fee-reserve").textContent =
"up to " +
truncateAmountNeverZero(formatEther(gasCostWei)) +
" ETH reserved";
" " +
nativeCurrency() +
" reserved";
setVisible("confirm-fee-reserve", true);
} else {
// No spread to report: either there is no estimate, or the node
+14 -2
View File
@@ -252,6 +252,14 @@ function unknownableAmount(balance) {
return Number.isFinite(n) ? n : null;
}
// The active network's native token symbol, `ETH` on mainnet and `SepoliaETH`
// on Sepolia, as src/shared/networks.js names it. Every screen labels a native
// amount with this. The "ETH" that state.selectedToken and txInfo.token hold
// is the native token's id, not its label, and stays "ETH" on every network.
function nativeCurrency() {
return currentNetwork().nativeCurrency;
}
// A network fee in wei as the confirmation and approval screens both show it:
// the ETH figure through truncateAmountNeverZero(), then its USD value when the
// ETH price is known. The USD value is of the exact fee, not of the truncated
@@ -261,7 +269,10 @@ function formatFee(wei) {
const ethPrice = getPrice("ETH");
const usd = ethPrice ? formatUsd(parseFloat(eth) * ethPrice) : "";
return (
truncateAmountNeverZero(eth) + " ETH" + (usd ? " (" + usd + ")" : "")
truncateAmountNeverZero(eth) +
" " +
nativeCurrency() +
(usd ? " (" + usd + ")" : "")
);
}
@@ -303,7 +314,7 @@ function balanceLine(symbol, amount, price, tokenId) {
function balanceLinesForAddress(addr, trackedTokens, showZero) {
let html = balanceLine(
"ETH",
nativeCurrency(),
parseFloat(addr.balance || "0"),
getPrice("ETH"),
"ETH",
@@ -660,6 +671,7 @@ module.exports = {
balanceLinesForAddress,
addressHoldsFunds,
unknownableAmount,
nativeCurrency,
formatFee,
addressColor,
addressDotHtml,
+7 -2
View File
@@ -10,6 +10,7 @@ const {
addressTitle,
escapeHtml,
displaySymbol,
nativeCurrency,
renderAddressHtml,
attachCopyHandlers,
pushCurrentView,
@@ -68,7 +69,7 @@ function renderTotalValue() {
return;
}
const ethBal = parseFloat(addr.balance || "0");
const ethStr = ethBal.toFixed(4) + " ETH";
const ethStr = ethBal.toFixed(4) + " " + nativeCurrency();
const ethUsd = ethPrice ? " (" + formatUsd(ethBal * ethPrice) + ")" : "";
el.textContent = ethStr + ethUsd;
@@ -182,7 +183,11 @@ async function loadHomeTxs(ctx) {
try {
const fetches = allAddresses.map((addr) =>
fetchRecentTransactions(addr, state.blockscoutUrl),
fetchRecentTransactions(
addr,
state.blockscoutUrl,
nativeCurrency(),
),
);
const results = await Promise.all(fetches);
+5 -2
View File
@@ -5,6 +5,8 @@ const {
showFlash,
addressTitle,
displaySymbol,
escapeHtml,
nativeCurrency,
renderAddressHtml,
attachCopyHandlers,
goBack,
@@ -124,7 +126,7 @@ function updateToValidation() {
function renderSendTokenSelect(addr) {
const sel = $("send-token");
sel.innerHTML = '<option value="ETH">ETH</option>';
sel.innerHTML = `<option value="ETH">${escapeHtml(nativeCurrency())}</option>`;
const fraudSet = new Set(
(state.fraudContracts || []).map((a) => a.toLowerCase()),
);
@@ -204,7 +206,8 @@ function updateSendBalance() {
$("send-balance").textContent =
"Current balance: " +
truncateAmountNeverZero(addr.balance || "0") +
" ETH";
" " +
nativeCurrency();
} else {
const symbol = resolveSymbol(
token,
+6 -3
View File
@@ -18,6 +18,7 @@ const {
etherscanLinkHtml,
explorerUrl,
displaySymbol,
nativeCurrency,
goBack,
} = require("./helpers");
const { state } = require("../../shared/state");
@@ -41,8 +42,10 @@ function getTransactionType(tx) {
return "Token Approval";
return "Contract Call";
}
if (tx.symbol && tx.symbol !== "ETH") return "ERC-20 Token Transfer";
return "Native ETH Transfer";
if (tx.symbol && tx.symbol !== nativeCurrency()) {
return "ERC-20 Token Transfer";
}
return "Native " + nativeCurrency() + " Transfer";
}
function blockieHtml(address) {
@@ -227,7 +230,7 @@ function populateOnChainDetails(txData) {
showDetailField(
"tx-detail-fee-section",
"tx-detail-fee",
feeEth + " ETH",
feeEth + " " + nativeCurrency(),
);
}
+4 -3
View File
@@ -12,6 +12,7 @@ const {
etherscanLinkHtml,
explorerUrl,
displaySymbol,
nativeCurrency,
clearViewStack,
} = require("./helpers");
const { resolveTokenSymbol } = require("../../shared/approvalAmount");
@@ -88,7 +89,7 @@ function startWait(txInfo, txHash, broadcastTime, pollNow) {
const symbol =
txInfo.token === "ETH"
? "ETH"
? nativeCurrency()
: displaySymbol(txInfo.tokenSymbol || "?");
$("wait-tx-summary").textContent = txInfo.amount + " " + symbol;
$("wait-tx-to").innerHTML = toAddressHtml(txInfo.to);
@@ -223,7 +224,7 @@ function showSuccess(txInfo, txHash, blockNumber) {
const symbol =
txInfo.token === "ETH"
? "ETH"
? nativeCurrency()
: displaySymbol(txInfo.tokenSymbol || "?");
state.viewData = {
amount: txInfo.amount,
@@ -320,7 +321,7 @@ function showError(txInfo, txHash, message) {
const symbol =
txInfo.token === "ETH"
? "ETH"
? nativeCurrency()
: displaySymbol(txInfo.tokenSymbol || "?");
state.viewData = {
amount: txInfo.amount,