1 Commits
Author SHA1 Message Date
sneak f8101b559e fix: TransactionDetail reads a transaction from the Blockscout of the network it was sent on (closes #623)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
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.
reviewedNetworkEndpoints() in the shared view helpers gives that network's RPC
and Blockscout URLs, picked as reviewedNetworkProvider() picked its node;
reviewedNetworkProvider() now uses it.

Model: opus-5-5
2026-10-19 22:58:47 +00:00
5 changed files with 23 additions and 45 deletions

No files matched your search

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