fix: TransactionDetail reads a transaction from the Blockscout of the network it was sent on (closes #623)
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
This commit was merged in pull request #627.
This commit is contained in:
@@ -1530,9 +1530,11 @@ first screen that cannot be rendered from saved state, such as Send or AddToken,
|
|||||||
or at AddressToken for any token but the native one, because that token was
|
or at AddressToken for any token but the native one, because that token was
|
||||||
picked on the previous network. The cut stack is saved with the switch, so a
|
picked on the previous network. The cut stack is saved with the switch, so a
|
||||||
popup closed straight after it and reopened onto Settings lands in the same
|
popup closed straight after it and reopened onto Settings lands in the same
|
||||||
place. "Back" then renders the screen it lands on for the new network, except
|
place. "Back" then renders the screen it lands on for the new network, and
|
||||||
ConfirmTx, which keeps the network its transaction was reviewed on, and anything
|
anything typed there and not saved, which belonged to the previous network, is
|
||||||
typed there and not saved, which belonged to the previous network, is dropped.
|
dropped. ConfirmTx, WaitTx, SuccessTx, ErrorTx and TransactionDetail are
|
||||||
|
rendered instead for the network of the transaction they show: the network it
|
||||||
|
was reviewed on for ConfirmTx, and the network it was sent on for the others.
|
||||||
Without a wallet the stack is not cut, because it starts at Welcome, which shows
|
Without a wallet the stack is not cut, because it starts at Welcome, which shows
|
||||||
nothing from a network.
|
nothing from a network.
|
||||||
|
|
||||||
@@ -1972,8 +1974,10 @@ view would leave a wallet one click from deletion.
|
|||||||
#### TransactionDetail (`transaction`)
|
#### TransactionDetail (`transaction`)
|
||||||
|
|
||||||
- **When**: User tapped a transaction row on Home, AddressDetail, or
|
- **When**: User tapped a transaction row on Home, AddressDetail, or
|
||||||
AddressToken. Its etherscan links open the explorer of the network the
|
AddressToken. The screen describes the transaction on the network it was sent
|
||||||
transaction was sent on, whichever network is active.
|
on, whichever network is active: its block, network details, decoded details
|
||||||
|
and raw data are read from that network's Blockscout, the one Settings has for
|
||||||
|
it, and its etherscan links open that network's explorer.
|
||||||
- **Elements** (grouped into logical blocks using light well containers; field
|
- **Elements** (grouped into logical blocks using light well containers; field
|
||||||
labels are self-explanatory so groups have no headings):
|
labels are self-explanatory so groups have no headings):
|
||||||
- "Transaction" heading, "Back" button
|
- "Transaction" heading, "Back" button
|
||||||
@@ -2998,8 +3002,8 @@ Currently supported:
|
|||||||
"Swap USDT → ETH"), token addresses, amounts, execution steps, and deadline.
|
"Swap USDT → ETH"), token addresses, amounts, execution steps, and deadline.
|
||||||
The swap summary is shown only for a transaction sent to one of the Universal
|
The swap summary is shown only for a transaction sent to one of the Universal
|
||||||
Router addresses Uniswap publishes for the network: on TxApproval the network
|
Router addresses Uniswap publishes for the network: on TxApproval the network
|
||||||
the transaction is for, on TransactionDetail the active network. The same
|
the transaction is for, on TransactionDetail the network it was sent on. The
|
||||||
calldata sent to any other address is shown as raw data. Decodes Permit2,
|
same calldata sent to any other address is shown as raw data. Decodes Permit2,
|
||||||
V2/V3/V4 swaps, wrap/unwrap, and balance checks. A V4 swap action is read only
|
V2/V3/V4 swaps, wrap/unwrap, and balance checks. A V4 swap action is read only
|
||||||
in the parameter shape deployed on mainnet and Sepolia, not in the newer one
|
in the parameter shape deployed on mainnet and Sepolia, not in the newer one
|
||||||
on `v4-periphery` `main`, which adds a `minHopPriceX36` field. An action in
|
on `v4-periphery` `main`, which adds a `minHopPriceX36` field. An action in
|
||||||
|
|||||||
@@ -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, so when it is
|
// `chainId` picks the network whose explorer the links open and whose
|
||||||
// there it has to name one.
|
// Blockscout the transaction is read from, so 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)) &&
|
||||||
(!value.chainId || SUPPORTED_CHAIN_IDS.has(value.chainId))
|
SUPPORTED_CHAIN_IDS.has(value.chainId)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-10
@@ -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
|
// 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
|
||||||
@@ -608,18 +611,28 @@ 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 node of the network a transaction was reviewed for, the one its chain id
|
// The RPC and Blockscout URLs Settings has for the network with this chain id,
|
||||||
// names, which need not be the active one: the popup can reopen onto ConfirmTx
|
// which need not be the active one: ConfirmTx, WaitTx and TransactionDetail
|
||||||
// or WaitTx after Settings switched the network. Its RPC URL is the one
|
// use the network their transaction's chain id names, and the popup can reopen
|
||||||
// Settings has for that network: the active network's is state.rpcUrl, any
|
// onto them after Settings switched the network. The active network's are
|
||||||
// other's the one remembered for it (see applyChainSwitchFields()).
|
// state.rpcUrl and state.blockscoutUrl, any other's the ones remembered for it,
|
||||||
function reviewedNetworkProvider(txInfo) {
|
// or its defaults (see applyChainSwitchFields()).
|
||||||
const net = networkByChainId(txInfo.chainId);
|
function endpointsByChainId(chainId) {
|
||||||
|
const net = networkByChainId(chainId);
|
||||||
if (net.id === state.networkId) {
|
if (net.id === state.networkId) {
|
||||||
return getProvider(state.rpcUrl, net.id);
|
return { rpcUrl: state.rpcUrl, blockscoutUrl: state.blockscoutUrl };
|
||||||
}
|
}
|
||||||
const remembered = state.networkEndpoints[net.id] || {};
|
const remembered = state.networkEndpoints[net.id] || {};
|
||||||
return getProvider(remembered.rpcUrl || net.defaultRpcUrl, net.id);
|
return {
|
||||||
|
rpcUrl: remembered.rpcUrl || net.defaultRpcUrl,
|
||||||
|
blockscoutUrl: remembered.blockscoutUrl || net.defaultBlockscoutUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The node of the network a transaction was reviewed for.
|
||||||
|
function reviewedNetworkProvider(txInfo) {
|
||||||
|
const net = networkByChainId(txInfo.chainId);
|
||||||
|
return getProvider(endpointsByChainId(txInfo.chainId).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
|
||||||
@@ -777,6 +790,7 @@ module.exports = {
|
|||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
etherscanLinkHtml,
|
etherscanLinkHtml,
|
||||||
explorerUrl,
|
explorerUrl,
|
||||||
|
endpointsByChainId,
|
||||||
reviewedNetworkProvider,
|
reviewedNetworkProvider,
|
||||||
EXT_ICON,
|
EXT_ICON,
|
||||||
truncateMiddle,
|
truncateMiddle,
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ const {
|
|||||||
copyableHtml,
|
copyableHtml,
|
||||||
etherscanLinkHtml,
|
etherscanLinkHtml,
|
||||||
explorerUrl,
|
explorerUrl,
|
||||||
|
endpointsByChainId,
|
||||||
displaySymbol,
|
displaySymbol,
|
||||||
goBack,
|
goBack,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentNetwork } = require("../../shared/state");
|
const { state } = require("../../shared/state");
|
||||||
const { nativeCurrencyByChainId } = require("../../shared/networks");
|
const { nativeCurrencyByChainId } = require("../../shared/networks");
|
||||||
const { formatEther, formatUnits } = require("ethers");
|
const { formatEther, formatUnits } = require("ethers");
|
||||||
const { log, debugFetch } = require("../../shared/log");
|
const { log, debugFetch } = require("../../shared/log");
|
||||||
@@ -84,9 +85,10 @@ function show(tx) {
|
|||||||
contractAddress: tx.contractAddress || null,
|
contractAddress: tx.contractAddress || null,
|
||||||
// The network the history entry was read from. The type line and
|
// The network the history entry was read from. The type line and
|
||||||
// the fee are in its native currency, not the active network's,
|
// the fee are in its native currency, not the active network's,
|
||||||
// and the links open its explorer: the active network can change,
|
// the links open its explorer, and the block, network details and
|
||||||
// in Settings or when the user approves a site's request, before
|
// calldata are read from its Blockscout: the active network can
|
||||||
// a later popup shows this screen again.
|
// change, in Settings or when the user approves a site's request,
|
||||||
|
// before a later popup shows this screen again.
|
||||||
chainId: tx.chainId,
|
chainId: tx.chainId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -311,8 +313,9 @@ async function loadFullTxDetails(txHash, chainId) {
|
|||||||
if (!section || !actionEl || !detailsEl) return;
|
if (!section || !actionEl || !detailsEl) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const { blockscoutUrl } = endpointsByChainId(chainId);
|
||||||
const resp = await debugFetch(
|
const resp = await debugFetch(
|
||||||
state.blockscoutUrl + "/transactions/" + txHash,
|
blockscoutUrl + "/transactions/" + txHash,
|
||||||
);
|
);
|
||||||
if (!resp.ok) return;
|
if (!resp.ok) return;
|
||||||
const txData = await resp.json();
|
const txData = await resp.json();
|
||||||
@@ -326,12 +329,12 @@ async function loadFullTxDetails(txHash, chainId) {
|
|||||||
// Decoded against the address the transaction was sent to, as the
|
// Decoded against the address the transaction was sent to, as the
|
||||||
// explorer reports it, not the history entry's `to`: for a token
|
// explorer reports it, not the history entry's `to`: for a token
|
||||||
// transfer that is the transfer's recipient, not the token contract.
|
// transfer that is the transfer's recipient, not the token contract.
|
||||||
// On the active network, the one whose explorer was just asked, and
|
// On the network the transaction was sent on, whose Blockscout was
|
||||||
// from the sender it reports.
|
// just asked, and from the sender it reports.
|
||||||
const decoded = decodeCalldata(
|
const decoded = decodeCalldata(
|
||||||
inputData,
|
inputData,
|
||||||
txData.to?.hash || "",
|
txData.to?.hash || "",
|
||||||
currentNetwork().chainId,
|
chainId,
|
||||||
txData.from?.hash || "",
|
txData.from?.hash || "",
|
||||||
);
|
);
|
||||||
if (decoded) {
|
if (decoded) {
|
||||||
|
|||||||
@@ -154,7 +154,14 @@ 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: { tx: { hash: "0xdead", from: ADDRESS, to: ADDRESS } },
|
viewData: {
|
||||||
|
tx: {
|
||||||
|
hash: "0xdead",
|
||||||
|
from: ADDRESS,
|
||||||
|
to: ADDRESS,
|
||||||
|
chainId: "0x1",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
goBack();
|
goBack();
|
||||||
expect(calls).toEqual(["transactionDetail"]);
|
expect(calls).toEqual(["transactionDetail"]);
|
||||||
|
|||||||
@@ -465,12 +465,19 @@ 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 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: {
|
value: {
|
||||||
tx: {
|
tx: {
|
||||||
@@ -836,7 +843,13 @@ const WELL_FORMED_DATA = {
|
|||||||
message: "boom",
|
message: "boom",
|
||||||
to: ADDRESS,
|
to: ADDRESS,
|
||||||
decoded: { details: [{ address: TOKEN_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: {
|
pendingTx: {
|
||||||
token: "ETH",
|
token: "ETH",
|
||||||
from: ADDRESS,
|
from: ADDRESS,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ function restoringOnto(view, viewData) {
|
|||||||
test("the transaction screen's Blockscout request reaches the stub", async () => {
|
test("the transaction screen's Blockscout request reaches the stub", async () => {
|
||||||
const page = await bootPopup(
|
const page = await bootPopup(
|
||||||
restoringOnto("transaction", {
|
restoringOnto("transaction", {
|
||||||
tx: { hash: "0x1", from: ADDRESS, to: ADDRESS },
|
tx: { hash: "0x1", from: ADDRESS, to: ADDRESS, chainId: "0x1" },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
//
|
//
|
||||||
// Its links open the explorer of the network the transaction was sent on,
|
// Its links open the explorer of the network the transaction was sent on,
|
||||||
// whichever network is active
|
// whichever network is active
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/584).
|
// (https://git.eeqj.de/sneak/AutistMask/issues/584), and the transaction is
|
||||||
|
// read from that network's Blockscout, the one Settings has for it
|
||||||
|
// (https://git.eeqj.de/sneak/AutistMask/issues/623).
|
||||||
//
|
//
|
||||||
// The row is the one the real history parser makes from the explorer's answer,
|
// The row is the one the real history parser makes from the explorer's answer,
|
||||||
// opened through transactionDetail.show(), which is what a click on the row
|
// opened through transactionDetail.show(), which is what a click on the row
|
||||||
@@ -26,10 +28,10 @@ jest.mock("../src/shared/log", () => ({
|
|||||||
warnf: () => {},
|
warnf: () => {},
|
||||||
errorf: () => {},
|
errorf: () => {},
|
||||||
},
|
},
|
||||||
debugFetch: async (url) => ({
|
debugFetch: async (url) => {
|
||||||
ok: true,
|
mockAsked.push(url);
|
||||||
json: async () => mockExplorerAnswer(url),
|
return { ok: true, json: async () => mockExplorerAnswer(url) };
|
||||||
}),
|
},
|
||||||
setRuntimeDebug: () => {},
|
setRuntimeDebug: () => {},
|
||||||
isDebug: () => false,
|
isDebug: () => false,
|
||||||
}));
|
}));
|
||||||
@@ -44,6 +46,8 @@ const { fetchRecentTransactions } = require("../src/shared/transactions");
|
|||||||
const transactionDetail = require("../src/popup/views/transactionDetail");
|
const transactionDetail = require("../src/popup/views/transactionDetail");
|
||||||
|
|
||||||
const EXPLORER = "https://explorer.example/api/v2";
|
const EXPLORER = "https://explorer.example/api/v2";
|
||||||
|
// A Blockscout that knows nothing of the transaction.
|
||||||
|
const OTHER_EXPLORER = "https://other-explorer.example/api/v2";
|
||||||
const HOLDER = "0x0000000000000000000000000000000000000a11";
|
const HOLDER = "0x0000000000000000000000000000000000000a11";
|
||||||
const RECIPIENT = "0x711e691962E5c054b78071C789A0C8CE657377aD";
|
const RECIPIENT = "0x711e691962E5c054b78071C789A0C8CE657377aD";
|
||||||
// A token the wallet tracks and the bundled token list does not name.
|
// A token the wallet tracks and the bundled token list does not name.
|
||||||
@@ -56,6 +60,9 @@ const TRANSFER_DATA = new Interface([
|
|||||||
"function transfer(address to, uint256 amount)",
|
"function transfer(address to, uint256 amount)",
|
||||||
]).encodeFunctionData("transfer", [RECIPIENT, 1234500000n]);
|
]).encodeFunctionData("transfer", [RECIPIENT, 1234500000n]);
|
||||||
|
|
||||||
|
// Every URL asked of a Blockscout, in order.
|
||||||
|
const mockAsked = [];
|
||||||
|
|
||||||
// The explorer's answers: the transfer in HOLDER's token transfers, and the
|
// The explorer's answers: the transfer in HOLDER's token transfers, and the
|
||||||
// transaction itself, sent to the token contract.
|
// transaction itself, sent to the token contract.
|
||||||
function mockExplorerAnswer(url) {
|
function mockExplorerAnswer(url) {
|
||||||
@@ -149,6 +156,7 @@ beforeEach(() => {
|
|||||||
globalThis.document = makeDocument();
|
globalThis.document = makeDocument();
|
||||||
globalThis.window = { location: { search: "" } };
|
globalThis.window = { location: { search: "" } };
|
||||||
state.blockscoutUrl = EXPLORER;
|
state.blockscoutUrl = EXPLORER;
|
||||||
|
state.networkEndpoints = {};
|
||||||
state.wallets = [
|
state.wallets = [
|
||||||
{
|
{
|
||||||
name: "Main",
|
name: "Main",
|
||||||
@@ -188,11 +196,13 @@ test("a token transfer's decoded details name the token contract and the amount
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mainnet is active and the row was read from Sepolia's explorer. The stub
|
// Mainnet is active, and its Blockscout knows nothing of the transaction. The
|
||||||
// explorer answers whichever network asks, so every link on the screen is
|
// row was read from the Blockscout Settings remembers for Sepolia, and the
|
||||||
// drawn.
|
// screen reads the transaction from there too, so every link on it is drawn.
|
||||||
test("with mainnet active, a Sepolia transaction's links open the Sepolia explorer", async () => {
|
test("with mainnet active, a Sepolia transaction is read from Sepolia's Blockscout and its links open the Sepolia explorer", async () => {
|
||||||
state.networkId = "mainnet";
|
state.networkId = "mainnet";
|
||||||
|
state.blockscoutUrl = OTHER_EXPLORER;
|
||||||
|
state.networkEndpoints = { sepolia: { blockscoutUrl: EXPLORER } };
|
||||||
const [row] = await fetchRecentTransactions(
|
const [row] = await fetchRecentTransactions(
|
||||||
HOLDER,
|
HOLDER,
|
||||||
EXPLORER,
|
EXPLORER,
|
||||||
@@ -217,3 +227,21 @@ test("with mainnet active, a Sepolia transaction's links open the Sepolia explor
|
|||||||
`${sepolia}/address/${RECIPIENT}`,
|
`${sepolia}/address/${RECIPIENT}`,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("with mainnet active and no Blockscout remembered for Sepolia, a Sepolia transaction is asked of Sepolia's default Blockscout", async () => {
|
||||||
|
state.networkId = "mainnet";
|
||||||
|
state.blockscoutUrl = OTHER_EXPLORER;
|
||||||
|
const [row] = await fetchRecentTransactions(
|
||||||
|
HOLDER,
|
||||||
|
EXPLORER,
|
||||||
|
NETWORKS.sepolia.chainId,
|
||||||
|
);
|
||||||
|
mockAsked.length = 0;
|
||||||
|
|
||||||
|
transactionDetail.show(row);
|
||||||
|
for (let i = 0; i < 10; i++) await new Promise((r) => setTimeout(r, 0));
|
||||||
|
|
||||||
|
expect(mockAsked).toEqual([
|
||||||
|
NETWORKS.sepolia.defaultBlockscoutUrl + "/transactions/" + TX_HASH,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|||||||
@@ -186,14 +186,14 @@ function node(id) {
|
|||||||
return globalThis.document.getElementById(id);
|
return globalThis.document.getElementById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the transaction detail screen with Sepolia active, for the
|
// Open the transaction detail screen with `activeNetworkId` active, for the
|
||||||
// transaction the explorer reports sent to `sentTo`, and wait for its
|
// Sepolia transaction the explorer reports sent to `sentTo`, and wait for its
|
||||||
// decoded details.
|
// decoded details.
|
||||||
async function showDetailSentTo(sentTo) {
|
async function showDetailSentTo(sentTo, activeNetworkId) {
|
||||||
mockSentTo = sentTo;
|
mockSentTo = sentTo;
|
||||||
globalThis.document = makeDocument();
|
globalThis.document = makeDocument();
|
||||||
globalThis.window = { location: { search: "" } };
|
globalThis.window = { location: { search: "" } };
|
||||||
state.networkId = "sepolia";
|
state.networkId = activeNetworkId;
|
||||||
state.blockscoutUrl = "https://explorer.example/api/v2";
|
state.blockscoutUrl = "https://explorer.example/api/v2";
|
||||||
state.viewData = {};
|
state.viewData = {};
|
||||||
state.viewStack = [];
|
state.viewStack = [];
|
||||||
@@ -216,21 +216,29 @@ async function showDetailSentTo(sentTo) {
|
|||||||
|
|
||||||
describe("execute() calldata on the transaction detail screen", () => {
|
describe("execute() calldata on the transaction detail screen", () => {
|
||||||
test("sent to a router on the active network, it is a swap", async () => {
|
test("sent to a router on the active network, it is a swap", async () => {
|
||||||
await showDetailSentTo(SEPOLIA_ROUTER);
|
await showDetailSentTo(SEPOLIA_ROUTER, "sepolia");
|
||||||
|
expect(node("tx-detail-calldata-details").innerHTML).toContain(
|
||||||
|
"Swap via Uniswap Universal Router",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://git.eeqj.de/sneak/AutistMask/issues/623
|
||||||
|
test("sent to a router on its network, with the other network active, it is a swap", async () => {
|
||||||
|
await showDetailSentTo(SEPOLIA_ROUTER, "mainnet");
|
||||||
expect(node("tx-detail-calldata-details").innerHTML).toContain(
|
expect(node("tx-detail-calldata-details").innerHTML).toContain(
|
||||||
"Swap via Uniswap Universal Router",
|
"Swap via Uniswap Universal Router",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("sent to an ordinary address, it is not decoded", async () => {
|
test("sent to an ordinary address, it is not decoded", async () => {
|
||||||
await showDetailSentTo(ORDINARY);
|
await showDetailSentTo(ORDINARY, "sepolia");
|
||||||
expect(node("tx-detail-calldata-action").textContent).toBe("execute");
|
expect(node("tx-detail-calldata-action").textContent).toBe("execute");
|
||||||
expect(node("tx-detail-calldata-details").innerHTML).toBe("");
|
expect(node("tx-detail-calldata-details").innerHTML).toBe("");
|
||||||
expect(node("tx-detail-rawdata").innerHTML).toContain(SWAP_DATA);
|
expect(node("tx-detail-rawdata").innerHTML).toContain(SWAP_DATA);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("sent to a router of the other network only, it is not decoded", async () => {
|
test("sent to a router of the other network only, it is not decoded", async () => {
|
||||||
await showDetailSentTo(MAINNET_ROUTER);
|
await showDetailSentTo(MAINNET_ROUTER, "sepolia");
|
||||||
expect(node("tx-detail-calldata-action").textContent).toBe("execute");
|
expect(node("tx-detail-calldata-action").textContent).toBe("execute");
|
||||||
expect(node("tx-detail-calldata-details").innerHTML).toBe("");
|
expect(node("tx-detail-calldata-details").innerHTML).toBe("");
|
||||||
expect(node("tx-detail-rawdata").innerHTML).toContain(SWAP_DATA);
|
expect(node("tx-detail-rawdata").innerHTML).toContain(SWAP_DATA);
|
||||||
|
|||||||
Reference in New Issue
Block a user