fix: correct swap display — output token, min received, from address, and amount #128
@@ -153,24 +153,38 @@ async function fetchRecentTransactions(address, blockscoutUrl, count = 25) {
|
||||
|
||||
// When a token transfer shares a hash with a normal tx, the normal tx
|
||||
// is the contract call (0 ETH) and the token transfer has the real
|
||||
// amount and symbol. A single transaction (e.g. a swap) can produce
|
||||
// multiple token transfers (one per token involved), so we key token
|
||||
// transfers by hash + contract address to keep all of them. We also
|
||||
// preserve contract-call metadata (direction, label, method) from the
|
||||
// matching normal tx so swaps display correctly.
|
||||
// amount and symbol. For contract calls (swaps), a single transaction
|
||||
// can produce multiple token transfers (input, intermediates, output).
|
||||
// We consolidate these into the original tx entry using the token
|
||||
// transfer where the user *receives* tokens (the swap output), so
|
||||
// the transaction list shows the final result rather than confusing
|
||||
// intermediate hops. We preserve the original tx's from/to so the
|
||||
// user sees their own address, not a router or Permit2 contract.
|
||||
for (const tt of ttJson.items || []) {
|
||||
const parsed = parseTokenTransfer(tt, addrLower);
|
||||
const existing = txsByHash.get(parsed.hash);
|
||||
if (existing && existing.direction === "contract") {
|
||||
parsed.direction = "contract";
|
||||
parsed.directionLabel = existing.directionLabel;
|
||||
parsed.isContractCall = true;
|
||||
parsed.method = existing.method;
|
||||
// Remove the bare-hash normal tx so it doesn't appear as a
|
||||
// duplicate with empty value; token transfers replace it.
|
||||
txsByHash.delete(parsed.hash);
|
||||
// For contract calls (swaps), consolidate into the original
|
||||
// tx entry. Prefer the "received" transfer (swap output)
|
||||
// for the display amount. If no received transfer exists,
|
||||
// fall back to the first "sent" transfer (swap input).
|
||||
const isReceived = parsed.direction === "received";
|
||||
const needsAmount = !existing.exactValue;
|
||||
if (isReceived || needsAmount) {
|
||||
existing.value = parsed.value;
|
||||
existing.exactValue = parsed.exactValue;
|
||||
existing.rawAmount = parsed.rawAmount;
|
||||
existing.rawUnit = parsed.rawUnit;
|
||||
existing.symbol = parsed.symbol;
|
||||
existing.contractAddress = parsed.contractAddress;
|
||||
existing.holders = parsed.holders;
|
||||
}
|
||||
// Keep the original tx's from/to (the user's address and the
|
||||
// contract they called), not the token transfer's from/to
|
||||
// which may be a router or Permit2 contract.
|
||||
continue;
|
||||
}
|
||||
// Use composite key so multiple token transfers per tx are kept.
|
||||
// Non-contract token transfers get their own entries.
|
||||
const ttKey = parsed.hash + ":" + (parsed.contractAddress || "");
|
||||
txsByHash.set(ttKey, parsed);
|
||||
}
|
||||
|
||||
@@ -359,9 +359,12 @@ function decode(data, toAddress) {
|
||||
const s = decodeV3SwapExactIn(inputs[i]);
|
||||
if (s) {
|
||||
if (!inputToken) inputToken = s.tokenIn;
|
||||
if (!outputToken) outputToken = s.tokenOut;
|
||||
if (!inputAmount) inputAmount = s.amountIn;
|
||||
if (!minOutput) minOutput = s.amountOutMin;
|
||||
// Always update output: in multi-step swaps (V3 → V4),
|
||||
// the last swap step determines the final output token
|
||||
// and minimum received amount.
|
||||
outputToken = s.tokenOut;
|
||||
minOutput = s.amountOutMin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,9 +372,9 @@ function decode(data, toAddress) {
|
||||
const s = decodeV2SwapExactIn(inputs[i]);
|
||||
if (s) {
|
||||
if (!inputToken) inputToken = s.tokenIn;
|
||||
if (!outputToken) outputToken = s.tokenOut;
|
||||
if (!inputAmount) inputAmount = s.amountIn;
|
||||
if (!minOutput) minOutput = s.amountOutMin;
|
||||
outputToken = s.tokenOut;
|
||||
minOutput = s.amountOutMin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,12 +391,11 @@ function decode(data, toAddress) {
|
||||
const v4 = decodeV4Swap(inputs[i]);
|
||||
if (v4) {
|
||||
if (!inputToken && v4.tokenIn) inputToken = v4.tokenIn;
|
||||
if (!outputToken && v4.tokenOut)
|
||||
outputToken = v4.tokenOut;
|
||||
if (!inputAmount && v4.amountIn)
|
||||
inputAmount = v4.amountIn;
|
||||
if (!minOutput && v4.amountOutMin)
|
||||
minOutput = v4.amountOutMin;
|
||||
// Always update output: last swap step wins
|
||||
if (v4.tokenOut) outputToken = v4.tokenOut;
|
||||
if (v4.amountOutMin) minOutput = v4.amountOutMin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user