fix: a confirmation screen drawn again keeps the network it was reviewed for (closes #586)
ConfirmTx set the transaction's chain id to the active network's every time it drew the screen, so a popup reopened onto a saved confirmation after a network switch in Settings described the reviewed transfer as the new network's and sent it there. show() now sets the chain id only on a transaction Send hands over. One drawn again from the saved screen keeps its own, so Sign & Send refuses it while another network is active. The screen labels its amounts in that network's native currency and asks that network's RPC endpoint for the fee and the recipient checks. Model: opus-5-5
This commit is contained in:
@@ -11,7 +11,6 @@ const {
|
||||
addressTitle,
|
||||
escapeHtml,
|
||||
displaySymbol,
|
||||
nativeCurrency,
|
||||
renderAddressHtml,
|
||||
blockieHtml,
|
||||
attachCopyHandlers,
|
||||
@@ -20,6 +19,10 @@ const {
|
||||
formatFee,
|
||||
} = require("./helpers");
|
||||
const { state, currentNetwork } = require("../../shared/state");
|
||||
const {
|
||||
networkByChainId,
|
||||
nativeCurrencyByChainId,
|
||||
} = require("../../shared/networks");
|
||||
const { getSignerForAddress } = require("../../shared/wallet");
|
||||
const { walletDefect } = require("../../shared/walletDefects");
|
||||
const { decryptWithPassword } = require("../../shared/vault");
|
||||
@@ -87,7 +90,9 @@ function valueWithUsd(text, usdAmount) {
|
||||
// once its amount is re-derived from the fee estimate.
|
||||
function renderAmount(txInfo) {
|
||||
const isErc20 = txInfo.token !== "ETH";
|
||||
const rawSymbol = isErc20 ? txInfo.tokenSymbol || "?" : nativeCurrency();
|
||||
const rawSymbol = isErc20
|
||||
? txInfo.tokenSymbol || "?"
|
||||
: nativeCurrencyByChainId(txInfo.chainId);
|
||||
const price = isErc20 ? getPrice(rawSymbol) : getPrice("ETH");
|
||||
const amountUsd = price ? parseFloat(txInfo.amount) * price : null;
|
||||
$("confirm-amount").textContent = valueWithUsd(
|
||||
@@ -101,16 +106,21 @@ function show(txInfo) {
|
||||
feeStatus = FEE_PENDING;
|
||||
feeWei = null;
|
||||
maxSendFees = null;
|
||||
// The network this screen is drawn for, and the only one it sends on:
|
||||
// Settings can switch the active network while the screen is open. The
|
||||
// wait, success and error screens label the amount by it too.
|
||||
txInfo.chainId = currentNetwork().chainId;
|
||||
// The network this screen is drawn for, and the only one it sends on: the
|
||||
// one active when Send handed the transaction over. Drawn again from the
|
||||
// saved transaction, as when the popup reopens onto this screen, it keeps
|
||||
// that network, so Sign & Send refuses once Settings has switched to
|
||||
// another. This screen and the wait, success and error screens label
|
||||
// amounts by it.
|
||||
if (!txInfo.chainId) txInfo.chainId = currentNetwork().chainId;
|
||||
|
||||
const isErc20 = txInfo.token !== "ETH";
|
||||
// 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 || "?" : nativeCurrency();
|
||||
const rawSymbol = isErc20
|
||||
? txInfo.tokenSymbol || "?"
|
||||
: nativeCurrencyByChainId(txInfo.chainId);
|
||||
const symbol = displaySymbol(rawSymbol);
|
||||
|
||||
// Transaction type
|
||||
@@ -212,9 +222,9 @@ function show(txInfo) {
|
||||
$("confirm-gas-error").classList.toggle("hidden", !isErc20);
|
||||
$("confirm-gas-error").textContent =
|
||||
"You do not have enough " +
|
||||
nativeCurrency() +
|
||||
nativeCurrencyByChainId(txInfo.chainId) +
|
||||
" to pay the network fee for this transfer. Please add " +
|
||||
nativeCurrency() +
|
||||
nativeCurrencyByChainId(txInfo.chainId) +
|
||||
" to this address and try again.";
|
||||
|
||||
// Both shown later, by checkRecipientHistory(). The first sentence says
|
||||
@@ -222,12 +232,12 @@ function show(txInfo) {
|
||||
$("confirm-recipient-warning").textContent =
|
||||
"WARNING: The recipient address has never sent a transaction and" +
|
||||
" holds no " +
|
||||
nativeCurrency() +
|
||||
nativeCurrencyByChainId(txInfo.chainId) +
|
||||
". This may indicate a fresh or unused address. Double-check the" +
|
||||
" address before sending.";
|
||||
$("confirm-contract-warning").textContent =
|
||||
"WARNING: The recipient is a smart contract. Sending " +
|
||||
nativeCurrency() +
|
||||
nativeCurrencyByChainId(txInfo.chainId) +
|
||||
" or tokens directly to a contract may result in permanent loss of" +
|
||||
" funds.";
|
||||
|
||||
@@ -280,7 +290,7 @@ function renderValidation(txInfo) {
|
||||
const isErc20 = txInfo.token !== "ETH";
|
||||
const symbol = isErc20
|
||||
? displaySymbol(txInfo.tokenSymbol || "?")
|
||||
: nativeCurrency();
|
||||
: nativeCurrencyByChainId(txInfo.chainId);
|
||||
|
||||
const { canSend, codes } = validateTransfer({
|
||||
isErc20,
|
||||
@@ -369,9 +379,23 @@ function setVisible(id, visible) {
|
||||
$(id).style.visibility = visible ? "visible" : "hidden";
|
||||
}
|
||||
|
||||
// The node of the network this transaction was reviewed for, which need not be
|
||||
// the active one: the popup can reopen onto this screen after Settings switched
|
||||
// the network. Its RPC URL is the one Settings has for that network: the
|
||||
// active network's is state.rpcUrl, any other's the one remembered for it (see
|
||||
// applyChainSwitchFields()).
|
||||
function reviewedNetworkProvider(txInfo) {
|
||||
const net = networkByChainId(txInfo.chainId);
|
||||
if (net.id === state.networkId) {
|
||||
return getProvider(state.rpcUrl, net.id);
|
||||
}
|
||||
const remembered = state.networkEndpoints[net.id] || {};
|
||||
return getProvider(remembered.rpcUrl || net.defaultRpcUrl, net.id);
|
||||
}
|
||||
|
||||
async function estimateGas(txInfo) {
|
||||
try {
|
||||
const provider = getProvider(state.rpcUrl, state.networkId);
|
||||
const provider = reviewedNetworkProvider(txInfo);
|
||||
const feeData = await provider.getFeeData();
|
||||
let gasLimit;
|
||||
|
||||
@@ -419,12 +443,13 @@ async function estimateGas(txInfo) {
|
||||
// does, so the same fee reads the same on both.
|
||||
if (estimateWei !== null && estimateWei < gasCostWei) {
|
||||
$("confirm-fee-amount").textContent =
|
||||
"~" + formatFee(estimateWei, nativeCurrency());
|
||||
"~" +
|
||||
formatFee(estimateWei, nativeCurrencyByChainId(txInfo.chainId));
|
||||
$("confirm-fee-reserve").textContent =
|
||||
"up to " +
|
||||
truncateAmountNeverZero(formatEther(gasCostWei)) +
|
||||
" " +
|
||||
nativeCurrency() +
|
||||
nativeCurrencyByChainId(txInfo.chainId) +
|
||||
" reserved";
|
||||
setVisible("confirm-fee-reserve", true);
|
||||
} else {
|
||||
@@ -433,7 +458,7 @@ async function estimateGas(txInfo) {
|
||||
// cost is not below the reserve. Show the reserve alone.
|
||||
$("confirm-fee-amount").textContent = formatFee(
|
||||
gasCostWei,
|
||||
nativeCurrency(),
|
||||
nativeCurrencyByChainId(txInfo.chainId),
|
||||
);
|
||||
setVisible("confirm-fee-reserve", false);
|
||||
}
|
||||
@@ -523,7 +548,7 @@ function showSendError(message) {
|
||||
|
||||
async function checkRecipientHistory(txInfo) {
|
||||
try {
|
||||
const provider = getProvider(state.rpcUrl, state.networkId);
|
||||
const provider = reviewedNetworkProvider(txInfo);
|
||||
const asyncWarnings = await getFullWarnings(txInfo.to, provider, {
|
||||
fromAddress: txInfo.from,
|
||||
});
|
||||
@@ -564,8 +589,10 @@ function init(_ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Back from Settings only unhides this screen, still drawn for the
|
||||
// network that was active when it was shown.
|
||||
// The screen describes the network the transaction was reviewed for,
|
||||
// which Settings may have switched away from since: with this screen
|
||||
// open, where Back only unhides it, or before the popup reopened onto
|
||||
// it.
|
||||
if (currentNetwork().chainId !== pendingTx.chainId) {
|
||||
showSendError(
|
||||
"The active network changed after this transaction was" +
|
||||
|
||||
Reference in New Issue
Block a user