diff --git a/api/_utils.ts b/api/_utils.ts index eb8242800..9e53b3ad8 100644 --- a/api/_utils.ts +++ b/api/_utils.ts @@ -562,6 +562,15 @@ export const getTokenByAddress = ( } } + // For some chains, the same address is associated with both the ETH and WETH symbols in the constants file. + // See: https://www.npmjs.com/package/@across-protocol/constants + // This can cause issues when resolving the token. + // To fix this, we will check if there is a WETH match and prioritize it over the ETH match. + const wethMatch = matches.find(([symbol]) => symbol === "WETH"); + if (wethMatch) { + return wethMatch[1]; + } + return matches[0][1]; } catch (error) { return undefined; @@ -2543,12 +2552,7 @@ export async function getTokenInfo({ chainId, address }: TokenOptions): Promise< }); } - // Resolve token info statically - const token = Object.values(TOKEN_SYMBOLS_MAP).find((token) => - Boolean( - token.addresses?.[chainId]?.toLowerCase() === address.toLowerCase() - ) - ); + const token = getTokenByAddress(address, chainId); if (token) { return { diff --git a/e2e-api/swap/fetch-approval.test.ts b/e2e-api/swap/fetch-approval.test.ts index 274f84e3b..8851a1f0c 100644 --- a/e2e-api/swap/fetch-approval.test.ts +++ b/e2e-api/swap/fetch-approval.test.ts @@ -165,4 +165,24 @@ describe("GET /swap/approval", () => { ); }); }); + + test("should return WETH for Base and Linea", async () => { + const params = { + tradeType: "exactInput", + amount: "10000000000000000", + inputToken: "0x4200000000000000000000000000000000000006", + outputToken: "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f", + originChainId: 8453, + destinationChainId: 59144, + depositor: "0xB8034521BB1a343D556e5005680B3F17FFc74BeD", + recipient: "0xB8034521BB1a343D556e5005680B3F17FFc74BeD", + includeSources: "uniswap_v3", + }; + const response = await axios.get(SWAP_API_URL, { + params, + }); + expect(response.status).toBe(200); + expect(response.data.inputToken.symbol).toBe("WETH"); + expect(response.data.outputToken.symbol).toBe("WETH"); + }); }); diff --git a/test/api/_utils.test.ts b/test/api/_utils.test.ts index 6b2b43b42..67ef9913b 100644 --- a/test/api/_utils.test.ts +++ b/test/api/_utils.test.ts @@ -7,6 +7,7 @@ import { validEvmAddress, validSvmAddress, validAddress, + getTokenByAddress, } from "../../api/_utils"; import { is } from "superstruct"; @@ -109,6 +110,39 @@ describe("_utils", () => { }); }); + describe("#getTokenByAddress()", () => { + // Iterate over all chain IDs to test the token resolution for each chain. + for (const chainId of Object.values(CHAIN_IDs)) { + if (typeof chainId !== "number") continue; + + const weth = TOKEN_SYMBOLS_MAP.WETH.addresses[chainId]; + const eth = TOKEN_SYMBOLS_MAP.ETH.addresses[chainId]; + + // Test case where WETH and ETH have the same address. + // In this case, we want to ensure that WETH is always returned to avoid ambiguity. + if (weth && eth && weth.toLowerCase() === eth.toLowerCase()) { + test(`should return WETH for chain ${chainId} when both ETH and WETH have the same address`, () => { + const token = getTokenByAddress(weth, chainId); + expect(token?.symbol).toBe("WETH"); + }); + } else { + // Test case where WETH and ETH have different addresses. + if (weth) { + test(`should return WETH for chain ${chainId}`, () => { + const token = getTokenByAddress(weth, chainId); + expect(token?.symbol).toBe("WETH"); + }); + } + if (eth) { + test(`should return ETH for chain ${chainId}`, () => { + const token = getTokenByAddress(eth, chainId); + expect(token?.symbol).toBe("ETH"); + }); + } + } + } + }); + describe("#validateChainAndTokenParams()", () => { test("throw if 'destinationChainId' is not provided", () => { expect(() => validateChainAndTokenParams({})).toThrowError(