1 Commits
Author SHA1 Message Date
sneak 669757401e fix: TransactionDetail reads a transaction from the Blockscout of the network it was sent on (closes #623)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run
TransactionDetail read the block, network details and calldata from the
active network's Blockscout and decoded the calldata for the active network,
so after a network switch a transaction sent on the other network showed none
of them. It now reads them from the Blockscout Settings has for the network
the transaction was sent on, and decodes the calldata for that network.
endpointsByChainId() in the shared view helpers gives a network's RPC and
Blockscout URLs, picked as reviewedNetworkProvider() picked its node;
reviewedNetworkProvider() now uses it. Restore refuses a stored transaction
whose chain id names no network, as it does for WaitTx.

Model: opus-5-5
2026-10-19 23:41:40 +00:00
5 changed files with 45 additions and 23 deletions
+3 -3
View File
@@ -145,15 +145,15 @@ function isRenderablePendingTx(value) {
// The stored transaction transactionDetail.render() shows. contractAddress is
// optional on an ETH transfer, and reaches addressDotHtml() when it is there.
// `chainId` picks the network whose explorer the links open, so when it is
// there it has to name one.
// `chainId` picks the network whose explorer the links open and whose
// Blockscout the transaction is read from, so it has to name one.
function isRenderableTx(value) {
return (
isRecord(value) &&
isAddressText(value.from) &&
isAddressText(value.to) &&
(!value.contractAddress || isAddressText(value.contractAddress)) &&
(!value.chainId || SUPPORTED_CHAIN_IDS.has(value.chainId))
SUPPORTED_CHAIN_IDS.has(value.chainId)
);
}
+14 -11
View File
@@ -1,4 +1,7 @@
// Shared DOM helpers used by all views.
// Helpers the views in this directory share: showing a view and going back,
// error and flash lines and the warning banners, formatting of amounts, fees,
// balances, addresses and dates, the RPC and Blockscout URLs and node of the
// network a chain id names, explorer links, and click-to-copy.
//
// Escaping rule for every view in this directory, since they all build
// markup by concatenation: any VALUE interpolated into an innerHTML string
@@ -608,14 +611,14 @@ const EXT_ICON =
`<path d="M7 1.5h3.5V5M7 5.5L10.5 1.5"/>` +
`</svg></span>`;
// The RPC and Blockscout URLs Settings has for the network a transaction was
// reviewed for or sent on, the one its chain id names, which need not be the
// active one: the popup can reopen onto ConfirmTx, WaitTx or TransactionDetail
// after Settings switched the network. The active network's are state.rpcUrl
// and state.blockscoutUrl, any other's the ones remembered for it, or its
// defaults (see applyChainSwitchFields()).
function reviewedNetworkEndpoints(txInfo) {
const net = networkByChainId(txInfo.chainId);
// The RPC and Blockscout URLs Settings has for the network with this chain id,
// which need not be the active one: ConfirmTx, WaitTx and TransactionDetail
// use the network their transaction's chain id names, and the popup can reopen
// onto them after Settings switched the network. The active network's are
// state.rpcUrl and state.blockscoutUrl, any other's the ones remembered for it,
// or its defaults (see applyChainSwitchFields()).
function endpointsByChainId(chainId) {
const net = networkByChainId(chainId);
if (net.id === state.networkId) {
return { rpcUrl: state.rpcUrl, blockscoutUrl: state.blockscoutUrl };
}
@@ -629,7 +632,7 @@ function reviewedNetworkEndpoints(txInfo) {
// The node of the network a transaction was reviewed for.
function reviewedNetworkProvider(txInfo) {
const net = networkByChainId(txInfo.chainId);
return getProvider(reviewedNetworkEndpoints(txInfo).rpcUrl, net.id);
return getProvider(endpointsByChainId(txInfo.chainId).rpcUrl, net.id);
}
// Block-explorer URLs. The origin is a per-network constant from
@@ -787,7 +790,7 @@ module.exports = {
attachCopyHandlers,
etherscanLinkHtml,
explorerUrl,
reviewedNetworkEndpoints,
endpointsByChainId,
reviewedNetworkProvider,
EXT_ICON,
truncateMiddle,
+5 -6
View File
@@ -18,7 +18,7 @@ const {
copyableHtml,
etherscanLinkHtml,
explorerUrl,
reviewedNetworkEndpoints,
endpointsByChainId,
displaySymbol,
goBack,
} = require("./helpers");
@@ -191,7 +191,7 @@ function render() {
if (el) el.classList.add("hidden");
}
loadFullTxDetails(tx);
loadFullTxDetails(tx.hash, tx.chainId);
const isoStr = isoDate(tx.timestamp);
$("tx-detail-time").innerHTML =
@@ -303,8 +303,7 @@ function populateOnChainDetails(txData, chainId) {
}
}
async function loadFullTxDetails(tx) {
const chainId = tx.chainId;
async function loadFullTxDetails(txHash, chainId) {
const section = $("tx-detail-calldata-section");
const actionEl = $("tx-detail-calldata-action");
const detailsEl = $("tx-detail-calldata-details");
@@ -314,9 +313,9 @@ async function loadFullTxDetails(tx) {
if (!section || !actionEl || !detailsEl) return;
try {
const { blockscoutUrl } = reviewedNetworkEndpoints(tx);
const { blockscoutUrl } = endpointsByChainId(chainId);
const resp = await debugFetch(
blockscoutUrl + "/transactions/" + tx.hash,
blockscoutUrl + "/transactions/" + txHash,
);
if (!resp.ok) return;
const txData = await resp.json();
+8 -1
View File
@@ -154,7 +154,14 @@ describe("Back onto a view the reopened popup never rendered", () => {
test("Back onto the transaction detail renders it", () => {
reopenedOn("settings", ["main", "transaction"], {
viewData: { tx: { hash: "0xdead", from: ADDRESS, to: ADDRESS } },
viewData: {
tx: {
hash: "0xdead",
from: ADDRESS,
to: ADDRESS,
chainId: "0x1",
},
},
});
goBack();
expect(calls).toEqual(["transactionDetail"]);
+15 -2
View File
@@ -465,12 +465,19 @@ const CONTRACT = [
from: ADDRESS,
to: ADDRESS,
contractAddress: 42,
chainId: "0x1",
},
},
views: ["transaction"],
restored: false,
},
// A chain id naming no network has no explorer to link to.
// A transaction whose chain id is missing or names no network has
// no explorer to link to and no Blockscout to be read from.
{
value: { tx: { hash: "0x1", from: ADDRESS, to: ADDRESS } },
views: ["transaction"],
restored: false,
},
{
value: {
tx: {
@@ -836,7 +843,13 @@ const WELL_FORMED_DATA = {
message: "boom",
to: ADDRESS,
decoded: { details: [{ address: TOKEN_ADDRESS }] },
tx: { hash: "0x1", from: ADDRESS, to: ADDRESS, contractAddress: null },
tx: {
hash: "0x1",
from: ADDRESS,
to: ADDRESS,
contractAddress: null,
chainId: "0x1",
},
pendingTx: {
token: "ETH",
from: ADDRESS,