fix: a V4 swap action not in the deployed shape is shown as not decoded (closes #358)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

The four V4 swap actions are read only as the structs deployed on mainnet
and Sepolia. ethers read the newer v4-periphery structs, which add
minHopPriceX36, as the deployed ones, and the newer multi-hop exact-in
struct showed the offset of its hop-price array as the swap amount. A
decode now counts only when encoding it again gives back the same bytes.
A swap action that fails that marks its step "V4 Swap (could not be
decoded)", and a side its SETTLE or TAKE did not name reads "Unknown
(could not be decoded)". The wait and error screens no longer show the
ETH value as the amount of a swap whose amount was not read, unless the
swap is paid in native ETH.

Model: opus-5-5
This commit was merged in pull request #537.
This commit is contained in:
2026-10-16 03:00:32 +02:00
parent f4551dcb67
commit bad6494e43
7 changed files with 756 additions and 49 deletions
+17
View File
@@ -298,6 +298,23 @@ function showTxApproval(details) {
sources,
);
}
// A swap the decoder read no amount for, such as one whose V4 swap
// action could not be read or a V4 exact-out swap. The wait and error
// screens say the amount could not be decoded, with no currency after
// it, rather than show the transaction's ETH value as the amount. A
// swap paid in native ETH keeps that value: it is the ETH the swap
// pays (for an exact-out swap, the most it pays), not a figure that
// had to be read from the calldata.
const tokenIn = decoded.details.find((d) => d.label === "Token In");
if (
tokenIn &&
tokenIn.value !== "ETH (native)" &&
!decoded.details.some((d) => d.label === "Amount")
) {
pendingTxDetails.amount = "Unknown (could not be decoded)";
pendingTxDetails.token = null;
pendingTxDetails.tokenSymbol = null;
}
}
// Carry decoded calldata info through to success/error views
+29 -16
View File
@@ -91,12 +91,17 @@ function startWait(txInfo, txHash, broadcastTime, pollNow) {
// native currency of txInfo.chainId, the network the transaction was sent
// on, not the active network's: the active network can change, in
// Settings or when the user approves a site's request, while this screen
// is open or before a later popup resumes it.
const symbol =
txInfo.token === "ETH"
? nativeCurrencyByChainId(txInfo.chainId)
: displaySymbol(txInfo.tokenSymbol || "?");
$("wait-tx-summary").textContent = txInfo.amount + " " + symbol;
// is open or before a later popup resumes it. A null token is a swap whose
// amount was not decoded (see showTxApproval() in approval.js): the amount
// says so, and no symbol follows it.
let symbol = "";
if (txInfo.token === "ETH") {
symbol = nativeCurrencyByChainId(txInfo.chainId);
} else if (txInfo.token !== null) {
symbol = displaySymbol(txInfo.tokenSymbol || "?");
}
$("wait-tx-summary").textContent =
txInfo.amount + (symbol ? " " + symbol : "");
$("wait-tx-to").innerHTML = toAddressHtml(txInfo.to);
$("wait-tx-hash").innerHTML = txHashHtml(txHash);
attachCopyHandlers("view-wait-tx");
@@ -231,10 +236,13 @@ function restoreWait() {
function showSuccess(txInfo, txHash, blockNumber) {
endWait();
const symbol =
txInfo.token === "ETH"
? nativeCurrencyByChainId(txInfo.chainId)
: displaySymbol(txInfo.tokenSymbol || "?");
// As on the wait screen, a null token has no symbol.
let symbol = "";
if (txInfo.token === "ETH") {
symbol = nativeCurrencyByChainId(txInfo.chainId);
} else if (txInfo.token !== null) {
symbol = displaySymbol(txInfo.tokenSymbol || "?");
}
state.viewData = {
amount: txInfo.amount,
symbol: symbol,
@@ -294,7 +302,8 @@ function renderSuccess() {
} else {
summarySection.classList.remove("hidden");
toSection.classList.remove("hidden");
$("success-tx-summary").textContent = d.amount + " " + d.symbol;
$("success-tx-summary").textContent =
d.amount + (d.symbol ? " " + d.symbol : "");
$("success-tx-to").innerHTML = toAddressHtml(d.to);
}
@@ -317,10 +326,13 @@ function renderSuccess() {
function showError(txInfo, txHash, message) {
endWait();
const symbol =
txInfo.token === "ETH"
? nativeCurrencyByChainId(txInfo.chainId)
: displaySymbol(txInfo.tokenSymbol || "?");
// As on the wait screen, a null token has no symbol.
let symbol = "";
if (txInfo.token === "ETH") {
symbol = nativeCurrencyByChainId(txInfo.chainId);
} else if (txInfo.token !== null) {
symbol = displaySymbol(txInfo.tokenSymbol || "?");
}
state.viewData = {
amount: txInfo.amount,
symbol: symbol,
@@ -334,7 +346,8 @@ function showError(txInfo, txHash, message) {
function renderError() {
const d = state.viewData;
if (!d || !d.message) return;
$("error-tx-summary").textContent = d.amount + " " + d.symbol;
$("error-tx-summary").textContent =
d.amount + (d.symbol ? " " + d.symbol : "");
$("error-tx-to").innerHTML = toAddressHtml(d.to);
$("error-tx-message").textContent = d.message;