harden: state an undetermined V4 output token as undetermined, not as ETH (closes #353)
A V4 swap could set minOutput while leaving outputToken null, and tokenInfo(null) returned ETH at 18 decimals, so the approval screen named an asset the calldata never stated. Established from the V4 encoding that this is wrong rather than protocol semantics: Currency is a user-defined value type over address, so native ETH arrives as the truthy zero-address string and is already mapped to ETH, and an UNWRAP_WETH output is caught earlier — both genuine native-ETH routes are covered without null, so null can only mean no sub-action yielded a currency. A genuine native-ETH V4 swap still renders as ETH, pinned by test against the real mainnet fixture. The same gate also let a later step overwrite minOutput while an earlier hop's outputToken stayed on screen, showing a figure against the wrong token. Fixed together as the same two lines and the same defect.
This commit was merged in pull request #356.
This commit is contained in:
@@ -429,8 +429,16 @@ function decode(data, toAddress, sources) {
|
||||
if (!inputToken && v4.tokenIn) inputToken = v4.tokenIn;
|
||||
if (!inputAmount && v4.amountIn)
|
||||
inputAmount = v4.amountIn;
|
||||
// Always update output: last swap step wins
|
||||
if (v4.tokenOut) outputToken = v4.tokenOut;
|
||||
// Always update output: last swap step wins. A step
|
||||
// that carries the Min. received figure but decoded no
|
||||
// output currency makes the output *undetermined* — it
|
||||
// is neither ETH nor whatever an earlier step named,
|
||||
// and that figure is no longer counted in that token.
|
||||
if (v4.tokenOut) {
|
||||
outputToken = v4.tokenOut;
|
||||
} else if (v4.amountOutMin) {
|
||||
outputToken = null;
|
||||
}
|
||||
if (v4.amountOutMin) minOutput = v4.amountOutMin;
|
||||
}
|
||||
}
|
||||
@@ -443,11 +451,26 @@ function decode(data, toAddress, sources) {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve token info
|
||||
// Resolve token info.
|
||||
//
|
||||
// A null `outputToken` means undetermined, not native ETH, so it is
|
||||
// not handed to tokenInfo() — which maps null to ETH at 18 decimals
|
||||
// for the input side's benefit. Uniswap V4 spells native ETH as
|
||||
// `Currency.wrap(address(0))` (v4-core `type Currency is address`),
|
||||
// and a Currency is ABI-encoded as a plain address word, so every
|
||||
// decode site here gets back the truthy string
|
||||
// "0x0000000000000000000000000000000000000000" for it — never null.
|
||||
// tokenInfo() already names that ETH, and an UNWRAP_WETH output is
|
||||
// caught above, so nothing that genuinely outputs ETH arrives null.
|
||||
// Only a step whose output currency did not decode does, and naming
|
||||
// that ETH states the wrong asset and formats Min. received at the
|
||||
// wrong scale.
|
||||
const inInfo = tokenInfo(inputToken, sources);
|
||||
const outInfo = hasUnwrapWeth
|
||||
? { symbol: "ETH", decimals: 18, address: null }
|
||||
: tokenInfo(outputToken, sources);
|
||||
: outputToken
|
||||
? tokenInfo(outputToken, sources)
|
||||
: { symbol: null, decimals: null, address: null };
|
||||
|
||||
const inSymbol = inInfo.symbol;
|
||||
const outSymbol = outInfo.symbol;
|
||||
@@ -513,6 +536,16 @@ function decode(data, toAddress, sources) {
|
||||
});
|
||||
} else if (outSymbol) {
|
||||
details.push({ label: "Token Out", value: outSymbol });
|
||||
} else {
|
||||
// Nothing established the output token, so the line says that
|
||||
// rather than going missing or naming a token by default. It reads
|
||||
// as a refusal, the same stance unknownDecimalsAmount() takes on a
|
||||
// scale, so a Min. received figure below it is never attached to a
|
||||
// token the calldata did not state.
|
||||
details.push({
|
||||
label: "Token Out",
|
||||
value: "Unknown (not named in the calldata)",
|
||||
});
|
||||
}
|
||||
|
||||
if (minOutput !== null && minOutput !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user