The Token Out line was pushed only when a symbol was known, so a swap to a token absent from the bundled list showed a Min. received figure with no statement of which token was being received. It now falls back to the address, mirroring the Token In precedent in the same file. Composes with the unknown-scale refusal from #340: the address names the token while the figure declares its scale unknown. Native ETH out, ETH out via UNWRAP_WETH and bundled tokens render exactly as before.
108 lines
4.3 KiB
JavaScript
108 lines
4.3 KiB
JavaScript
// The output token of a swap, as the dApp approval screen names it.
|
|
//
|
|
// Issue #346: the `Token Out` detail line in `src/shared/uniswap.js` was
|
|
// pushed only `if (outSymbol)`, and a token absent from the bundled list has
|
|
// no symbol. The line was therefore dropped entirely for exactly that
|
|
// population — which is every newly listed token — leaving a `Min. received`
|
|
// figure with nothing on the screen saying what is being received. The
|
|
// `Token In` line already falls back to the address; the rule asserted here is
|
|
// that the output side does too, always.
|
|
|
|
const { AbiCoder, Interface, getAddress } = require("ethers");
|
|
const uniswap = require("../src/shared/uniswap");
|
|
const { unknownDecimalsAmount } = require("../src/shared/approvalAmount");
|
|
|
|
const ROUTER = "0x66a9893cc07d91d95644aedd05d03f95e1dba8af";
|
|
const RECIPIENT = "0xC0FfEE0000000000000000000000000000c0fFEe";
|
|
// In the bundled list, at 18 decimals.
|
|
const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
|
|
// Outside it, as every newly listed token is. Checksummed, because that is
|
|
// the form the decoder gets back from the ABI decode and puts on the line.
|
|
const NOVEL_OUT = getAddress("0xd0d0000000000000000000000000000000000d0d");
|
|
|
|
const HALF_WETH = 500000000000000000n;
|
|
// 1,000.00 of a 6-decimal token.
|
|
const THOUSAND_AT_SIX = 1000000000n;
|
|
|
|
const coder = AbiCoder.defaultAbiCoder();
|
|
const routerIface = new Interface([
|
|
"function execute(bytes commands, bytes[] inputs, uint256 deadline)",
|
|
]);
|
|
|
|
// A V2_SWAP_EXACT_IN (command 0x08) execute() call.
|
|
function swapData(tokenIn, amountIn, tokenOut, amountOutMin) {
|
|
const input = coder.encode(
|
|
["address", "uint256", "uint256", "address[]", "bool"],
|
|
[RECIPIENT, amountIn, amountOutMin, [tokenIn, tokenOut], true],
|
|
);
|
|
return routerIface.encodeFunctionData("execute", [
|
|
"0x08",
|
|
[input],
|
|
9999999999n,
|
|
]);
|
|
}
|
|
|
|
// An UNWRAP_WETH (command 0x0c) execute() call: the output side is native ETH,
|
|
// which has no contract address to name.
|
|
function unwrapData() {
|
|
return routerIface.encodeFunctionData("execute", [
|
|
"0x0c",
|
|
[coder.encode(["address", "uint256"], [RECIPIENT, HALF_WETH])],
|
|
9999999999n,
|
|
]);
|
|
}
|
|
|
|
function detail(data, label, sources) {
|
|
const decoded = uniswap.decode(data, ROUTER, sources || {});
|
|
return decoded.details.find((d) => d.label === label);
|
|
}
|
|
|
|
describe("a swap to a token absent from the bundled list", () => {
|
|
const data = () => swapData(WETH, HALF_WETH, NOVEL_OUT, THOUSAND_AT_SIX);
|
|
|
|
test("still renders a Token Out line, naming the address", () => {
|
|
const out = detail(data(), "Token Out");
|
|
expect(out).toBeDefined();
|
|
expect(out.value).toBe(NOVEL_OUT);
|
|
expect(out.address).toBe(NOVEL_OUT);
|
|
expect(out.isToken).toBe(true);
|
|
});
|
|
|
|
test("names the address alongside the unknown-scale refusal", () => {
|
|
// The same population hits both: no symbol, and no scale either. The
|
|
// address says which token, the base units say how much and admit the
|
|
// scale is unknown — neither line silently means something else.
|
|
expect(detail(data(), "Token Out").value).toBe(NOVEL_OUT);
|
|
expect(detail(data(), "Min. received").value).toBe(
|
|
unknownDecimalsAmount(THOUSAND_AT_SIX),
|
|
);
|
|
});
|
|
|
|
test("names the address when the scale is known but the symbol is not", () => {
|
|
const sources = {
|
|
trackedTokens: [
|
|
{ address: NOVEL_OUT, symbol: "NOVEL", decimals: 6 },
|
|
],
|
|
};
|
|
expect(detail(data(), "Token Out", sources).value).toBe(NOVEL_OUT);
|
|
expect(detail(data(), "Min. received", sources).value).toBe(
|
|
"1000.0000",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("the output tokens that already had a line keep it unchanged", () => {
|
|
test("a bundled token is named by symbol and address", () => {
|
|
const data = swapData(NOVEL_OUT, THOUSAND_AT_SIX, WETH, HALF_WETH);
|
|
const out = detail(data, "Token Out");
|
|
expect(out.value).toBe("WETH (" + WETH + ")");
|
|
expect(out.address).toBe(WETH);
|
|
});
|
|
|
|
test("an unwrap to native ETH is named ETH, with no address", () => {
|
|
const out = detail(unwrapData(), "Token Out");
|
|
expect(out.value).toBe("ETH");
|
|
expect(out.address).toBeUndefined();
|
|
});
|
|
});
|