From 96294f46f5e2b32fd01bc3e47287ca6870377541 Mon Sep 17 00:00:00 2001 From: "Siyu Jiang (See-You John)" <91580504+jsy1218@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:30:32 -0800 Subject: [PATCH] chore: bump sor to 4.17.5 - fix: mixed route support v4 ETH pool to v2/v3 WETH pool new way (#814) * chore: bump sor to 4.17.5 - fix: mixed route support v4 ETH pool to v2/v3 WETH pool new way * router-sdk 1.12.2 * max hops + remove fake v4 pool * feedback * max hop only when eth-weth * remove is-wrapped-native * feedback --- package-lock.json | 54 ++-- package.json | 6 +- .../entities/route-with-valid-quote.ts | 16 +- .../functions/compute-all-routes.ts | 35 +- src/util/pools.ts | 300 ++++++++++++++++++ .../functions/compute-all-routes.test.ts | 40 +++ 6 files changed, 414 insertions(+), 37 deletions(-) create mode 100644 src/util/pools.ts diff --git a/package-lock.json b/package-lock.json index aa477857d..dfd7a7284 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,15 +13,15 @@ "@types/brotli": "^1.3.4", "@uniswap/default-token-list": "^11.13.0", "@uniswap/permit2-sdk": "^1.3.0", - "@uniswap/router-sdk": "^1.21.0", + "@uniswap/router-sdk": "^1.21.4", "@uniswap/sdk-core": "^7.5.0", "@uniswap/swap-router-contracts": "^1.3.1", "@uniswap/token-lists": "^1.0.0-beta.31", "@uniswap/universal-router": "^1.6.0", - "@uniswap/universal-router-sdk": "^4.14.0", + "@uniswap/universal-router-sdk": "^4.15.1", "@uniswap/v2-sdk": "^4.13.0", "@uniswap/v3-sdk": "^3.24.0", - "@uniswap/v4-sdk": "^1.18.0", + "@uniswap/v4-sdk": "^1.18.1", "async-retry": "^1.3.1", "await-timeout": "^1.1.1", "axios": "^0.21.1", @@ -3262,16 +3262,16 @@ } }, "node_modules/@uniswap/router-sdk": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@uniswap/router-sdk/-/router-sdk-1.21.0.tgz", - "integrity": "sha512-L7NixcJvcjkkX4WOa9mSyep1M2LMRp6gI0DKZGNuYsY+mjSJfxdlZyAKPXWHmVZGfVSE25IcbKZhA/i0KgVhKg==", + "version": "1.21.4", + "resolved": "https://registry.npmjs.org/@uniswap/router-sdk/-/router-sdk-1.21.4.tgz", + "integrity": "sha512-UQ7JAxtA5VLfiN+LgJwKWnPdRmZyW8jfdC96HDZgYlh93MfstgWQpmgBtlEq57KKPuA9LnklsD17tR0yA8WBog==", "dependencies": { "@ethersproject/abi": "^5.5.0", "@uniswap/sdk-core": "^7.5.0", "@uniswap/swap-router-contracts": "^1.3.0", "@uniswap/v2-sdk": "^4.13.0", "@uniswap/v3-sdk": "^3.24.0", - "@uniswap/v4-sdk": "^1.18.0" + "@uniswap/v4-sdk": "^1.18.1" } }, "node_modules/@uniswap/sdk-core": { @@ -3344,20 +3344,20 @@ } }, "node_modules/@uniswap/universal-router-sdk": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@uniswap/universal-router-sdk/-/universal-router-sdk-4.14.0.tgz", - "integrity": "sha512-lM8o5olNxJSgaAXUotltSuobfjiDQhkS46XkRbJEDrFjmUDCqggUgegZjU4TFDC87d5LBF47TWYF9dAVQENjxg==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@uniswap/universal-router-sdk/-/universal-router-sdk-4.15.1.tgz", + "integrity": "sha512-lj3yht0cs62E9uKsSMnuvbGMJbU8Kb6ENBFjCJ2DiVMQdNgTfW+Vl2s4BeDvGjC7Af+B4MvAvRDH/+K1gDRmjg==", "dependencies": { "@openzeppelin/contracts": "4.7.0", "@uniswap/permit2-sdk": "^1.3.0", - "@uniswap/router-sdk": "^1.21.0", + "@uniswap/router-sdk": "^1.21.4", "@uniswap/sdk-core": "^7.5.0", "@uniswap/universal-router": "2.0.0-beta.2", "@uniswap/v2-core": "^1.0.1", "@uniswap/v2-sdk": "^4.13.0", "@uniswap/v3-core": "1.0.0", "@uniswap/v3-sdk": "^3.24.0", - "@uniswap/v4-sdk": "^1.18.0", + "@uniswap/v4-sdk": "^1.18.1", "bignumber.js": "^9.0.2", "ethers": "^5.7.0" }, @@ -3531,9 +3531,9 @@ } }, "node_modules/@uniswap/v4-sdk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@uniswap/v4-sdk/-/v4-sdk-1.18.0.tgz", - "integrity": "sha512-ZcVSwgozrggfotH6D5g63W1T84bndsU1VZ+x67KM0Z8csBNzyV4GkKsMQPE4jnPEf3O0JSnGyHL2qPlpTHvGOQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@uniswap/v4-sdk/-/v4-sdk-1.18.1.tgz", + "integrity": "sha512-lBafL8CRUekzA+s0nT1y9jYzqzQ2udub5jRGsmIbLafvaGctYmId1qg8mCH2FOVLBVzH7TAjVAjdypxxvEa4XQ==", "dependencies": { "@ethersproject/solidity": "^5.0.9", "@uniswap/sdk-core": "^7.5.0", @@ -14328,16 +14328,16 @@ } }, "@uniswap/router-sdk": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@uniswap/router-sdk/-/router-sdk-1.21.0.tgz", - "integrity": "sha512-L7NixcJvcjkkX4WOa9mSyep1M2LMRp6gI0DKZGNuYsY+mjSJfxdlZyAKPXWHmVZGfVSE25IcbKZhA/i0KgVhKg==", + "version": "1.21.4", + "resolved": "https://registry.npmjs.org/@uniswap/router-sdk/-/router-sdk-1.21.4.tgz", + "integrity": "sha512-UQ7JAxtA5VLfiN+LgJwKWnPdRmZyW8jfdC96HDZgYlh93MfstgWQpmgBtlEq57KKPuA9LnklsD17tR0yA8WBog==", "requires": { "@ethersproject/abi": "^5.5.0", "@uniswap/sdk-core": "^7.5.0", "@uniswap/swap-router-contracts": "^1.3.0", "@uniswap/v2-sdk": "^4.13.0", "@uniswap/v3-sdk": "^3.24.0", - "@uniswap/v4-sdk": "^1.18.0" + "@uniswap/v4-sdk": "^1.18.1" } }, "@uniswap/sdk-core": { @@ -14409,20 +14409,20 @@ } }, "@uniswap/universal-router-sdk": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@uniswap/universal-router-sdk/-/universal-router-sdk-4.14.0.tgz", - "integrity": "sha512-lM8o5olNxJSgaAXUotltSuobfjiDQhkS46XkRbJEDrFjmUDCqggUgegZjU4TFDC87d5LBF47TWYF9dAVQENjxg==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@uniswap/universal-router-sdk/-/universal-router-sdk-4.15.1.tgz", + "integrity": "sha512-lj3yht0cs62E9uKsSMnuvbGMJbU8Kb6ENBFjCJ2DiVMQdNgTfW+Vl2s4BeDvGjC7Af+B4MvAvRDH/+K1gDRmjg==", "requires": { "@openzeppelin/contracts": "4.7.0", "@uniswap/permit2-sdk": "^1.3.0", - "@uniswap/router-sdk": "^1.21.0", + "@uniswap/router-sdk": "^1.21.4", "@uniswap/sdk-core": "^7.5.0", "@uniswap/universal-router": "2.0.0-beta.2", "@uniswap/v2-core": "^1.0.1", "@uniswap/v2-sdk": "^4.13.0", "@uniswap/v3-core": "1.0.0", "@uniswap/v3-sdk": "^3.24.0", - "@uniswap/v4-sdk": "^1.18.0", + "@uniswap/v4-sdk": "^1.18.1", "bignumber.js": "^9.0.2", "ethers": "^5.7.0" }, @@ -14553,9 +14553,9 @@ } }, "@uniswap/v4-sdk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@uniswap/v4-sdk/-/v4-sdk-1.18.0.tgz", - "integrity": "sha512-ZcVSwgozrggfotH6D5g63W1T84bndsU1VZ+x67KM0Z8csBNzyV4GkKsMQPE4jnPEf3O0JSnGyHL2qPlpTHvGOQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@uniswap/v4-sdk/-/v4-sdk-1.18.1.tgz", + "integrity": "sha512-lBafL8CRUekzA+s0nT1y9jYzqzQ2udub5jRGsmIbLafvaGctYmId1qg8mCH2FOVLBVzH7TAjVAjdypxxvEa4XQ==", "requires": { "@ethersproject/solidity": "^5.0.9", "@uniswap/sdk-core": "^7.5.0", diff --git a/package.json b/package.json index ec25f4b3f..9811e67cd 100644 --- a/package.json +++ b/package.json @@ -37,15 +37,15 @@ "@types/brotli": "^1.3.4", "@uniswap/default-token-list": "^11.13.0", "@uniswap/permit2-sdk": "^1.3.0", - "@uniswap/router-sdk": "^1.21.0", + "@uniswap/router-sdk": "^1.21.4", "@uniswap/sdk-core": "^7.5.0", "@uniswap/swap-router-contracts": "^1.3.1", "@uniswap/token-lists": "^1.0.0-beta.31", "@uniswap/universal-router": "^1.6.0", - "@uniswap/universal-router-sdk": "^4.14.0", + "@uniswap/universal-router-sdk": "^4.15.1", "@uniswap/v2-sdk": "^4.13.0", "@uniswap/v3-sdk": "^3.24.0", - "@uniswap/v4-sdk": "^1.18.0", + "@uniswap/v4-sdk": "^1.18.1", "async-retry": "^1.3.1", "await-timeout": "^1.1.1", "axios": "^0.21.1", diff --git a/src/routers/alpha-router/entities/route-with-valid-quote.ts b/src/routers/alpha-router/entities/route-with-valid-quote.ts index be5408a1d..dbfc2f676 100644 --- a/src/routers/alpha-router/entities/route-with-valid-quote.ts +++ b/src/routers/alpha-router/entities/route-with-valid-quote.ts @@ -1,15 +1,16 @@ import { BigNumber } from '@ethersproject/bignumber'; import { Protocol } from '@uniswap/router-sdk'; import { Currency, Token, TradeType } from '@uniswap/sdk-core'; +import { Pair } from '@uniswap/v2-sdk'; import { Pool as V3Pool } from '@uniswap/v3-sdk'; import { Pool as V4Pool } from '@uniswap/v4-sdk'; import _ from 'lodash'; -import { Pair } from '@uniswap/v2-sdk'; import { IV4PoolProvider } from '../../../providers'; import { IV2PoolProvider } from '../../../providers/v2/pool-provider'; import { IV3PoolProvider } from '../../../providers/v3/pool-provider'; import { CurrencyAmount } from '../../../util/amounts'; +import { FAKE_TICK_SPACING } from '../../../util/pools'; import { routeToString } from '../../../util/routes'; import { MixedRoute, @@ -430,6 +431,15 @@ export class MixedRouteWithValidQuote implements IMixedRouteWithValidQuote { v3PoolProvider, v2PoolProvider, }: MixedRouteWithValidQuoteParams) { + const poolsWithoutFakePool = route.pools.filter( + (p) => !(p instanceof V4Pool && p.tickSpacing === FAKE_TICK_SPACING) + ); + const routeWithoutEthWethFakePool = new MixedRoute( + poolsWithoutFakePool, + route.input, + route.output + ); + this.amount = amount; this.rawQuote = rawQuote; this.sqrtPriceX96AfterList = sqrtPriceX96AfterList; @@ -437,7 +447,7 @@ export class MixedRouteWithValidQuote implements IMixedRouteWithValidQuote { this.quoterGasEstimate = quoterGasEstimate; this.quote = CurrencyAmount.fromRawAmount(quoteToken, rawQuote.toString()); this.percent = percent; - this.route = route; + this.route = routeWithoutEthWethFakePool; this.gasModel = mixedRouteGasModel; this.quoteToken = quoteToken; this.tradeType = tradeType; @@ -459,7 +469,7 @@ export class MixedRouteWithValidQuote implements IMixedRouteWithValidQuote { this.quoteAdjustedForGas = quoteGasAdjusted; } - this.poolIdentifiers = _.map(route.pools, (p) => { + this.poolIdentifiers = _.map(routeWithoutEthWethFakePool.pools, (p) => { if (p instanceof V4Pool) { return v4PoolProvider.getPoolId( p.token0, diff --git a/src/routers/alpha-router/functions/compute-all-routes.ts b/src/routers/alpha-router/functions/compute-all-routes.ts index 0e9ad0cb4..7f3c3b692 100644 --- a/src/routers/alpha-router/functions/compute-all-routes.ts +++ b/src/routers/alpha-router/functions/compute-all-routes.ts @@ -1,11 +1,12 @@ import { TPool } from '@uniswap/router-sdk/dist/utils/TPool'; -import { Currency, Token } from '@uniswap/sdk-core'; +import { ChainId, Currency, Token } from '@uniswap/sdk-core'; import { Pair } from '@uniswap/v2-sdk'; import { Pool as V3Pool } from '@uniswap/v3-sdk'; import { Pool as V4Pool } from '@uniswap/v4-sdk'; -import { getAddressLowerCase } from '../../../util'; +import { getAddressLowerCase, nativeOnChain } from '../../../util'; import { log } from '../../../util/log'; +import { FAKE_TICK_SPACING, V4_ETH_WETH_FAKE_POOL } from '../../../util/pools'; import { poolToString, routeToString } from '../../../util/routes'; import { MixedRoute, @@ -75,6 +76,17 @@ export function computeAllMixedRoutes( parts: TPool[], maxHops: number ): MixedRoute[] { + // only add fake v4 pool, if we see there's a native v4 pool in the candidate pool + const containsV4NativePools = + parts.filter( + (pool) => + pool instanceof V4Pool && + pool.involvesToken(nativeOnChain(ChainId.MAINNET)) + ).length > 0; + // NOTE: we added a fake v4 pool, in order for mixed route to connect the v3 weth pool with v4 eth pool + const amendedPools = containsV4NativePools + ? parts.concat(V4_ETH_WETH_FAKE_POOL[currencyIn.chainId as ChainId]) + : parts; const routesRaw = computeAllRoutes( currencyIn, currencyOut, @@ -85,7 +97,7 @@ export function computeAllMixedRoutes( currency.isNative ? (pool as V4Pool).involvesToken(currency) : pool.involvesToken(currency), - parts, + amendedPools, maxHops ); /// filter out pure v4 and v3 and v2 routes @@ -125,7 +137,22 @@ export function computeAllRoutes< tokensVisited: Set, _previousTokenOut?: TCurrency ) => { - if (currentRoute.length > maxHops) { + const currentRouteContainsFakeV4Pool = + currentRoute.filter( + (pool) => + pool instanceof V4Pool && pool.tickSpacing === FAKE_TICK_SPACING + ).length > 0; + const amendedMaxHops = currentRouteContainsFakeV4Pool + ? maxHops + 1 + : maxHops; + + // amendedMaxHops is the maxHops + 1 if the current route contains a fake v4 pool + // b/c we want to allow the route to go through the fake v4 pool + // also gas wise, if a route goes through the fake v4 pool, mixed quoter will add the wrap/unwrap gas cost: + // https://github.com/Uniswap/mixed-quoter/pull/41/files#diff-a4d1289f264d1da22aac20cc55a9d01c8ba9cccd76ce1af8f952ec9034e7e1aaR189 + // and SOR will use the gas cost from the mixed quoter: + // https://github.com/Uniswap/smart-order-router/blob/17da523f1af050e6430afb866d96681346c8fb8b/src/routers/alpha-router/gas-models/mixedRoute/mixed-route-heuristic-gas-model.ts#L222 + if (currentRoute.length > amendedMaxHops) { return; } diff --git a/src/util/pools.ts b/src/util/pools.ts new file mode 100644 index 000000000..afc955ca3 --- /dev/null +++ b/src/util/pools.ts @@ -0,0 +1,300 @@ +import { ADDRESS_ZERO } from '@uniswap/router-sdk'; +import { ChainId } from '@uniswap/sdk-core'; +import { Pool as V4Pool } from '@uniswap/v4-sdk'; + +import { nativeOnChain } from './chains'; + +export const FAKE_TICK_SPACING = 0; + +export const V4_ETH_WETH_FAKE_POOL: { [chainId in ChainId]: V4Pool } = { + [ChainId.MAINNET]: new V4Pool( + nativeOnChain(ChainId.MAINNET), + nativeOnChain(ChainId.MAINNET).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.GOERLI]: new V4Pool( + nativeOnChain(ChainId.GOERLI), + nativeOnChain(ChainId.GOERLI).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.SEPOLIA]: new V4Pool( + nativeOnChain(ChainId.SEPOLIA), + nativeOnChain(ChainId.SEPOLIA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.OPTIMISM]: new V4Pool( + nativeOnChain(ChainId.OPTIMISM), + nativeOnChain(ChainId.OPTIMISM).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.OPTIMISM_GOERLI]: new V4Pool( + nativeOnChain(ChainId.OPTIMISM_GOERLI), + nativeOnChain(ChainId.OPTIMISM_GOERLI).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.OPTIMISM_SEPOLIA]: new V4Pool( + nativeOnChain(ChainId.OPTIMISM_SEPOLIA), + nativeOnChain(ChainId.OPTIMISM_SEPOLIA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ARBITRUM_ONE]: new V4Pool( + nativeOnChain(ChainId.ARBITRUM_ONE), + nativeOnChain(ChainId.ARBITRUM_ONE).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ARBITRUM_GOERLI]: new V4Pool( + nativeOnChain(ChainId.ARBITRUM_GOERLI), + nativeOnChain(ChainId.ARBITRUM_GOERLI).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ARBITRUM_SEPOLIA]: new V4Pool( + nativeOnChain(ChainId.ARBITRUM_SEPOLIA), + nativeOnChain(ChainId.ARBITRUM_SEPOLIA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.POLYGON]: new V4Pool( + nativeOnChain(ChainId.POLYGON), + nativeOnChain(ChainId.POLYGON).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.POLYGON_MUMBAI]: new V4Pool( + nativeOnChain(ChainId.POLYGON_MUMBAI), + nativeOnChain(ChainId.POLYGON_MUMBAI).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.CELO]: new V4Pool( + nativeOnChain(ChainId.CELO), + nativeOnChain(ChainId.CELO).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.CELO_ALFAJORES]: new V4Pool( + nativeOnChain(ChainId.CELO_ALFAJORES), + nativeOnChain(ChainId.CELO_ALFAJORES).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.GNOSIS]: new V4Pool( + nativeOnChain(ChainId.GNOSIS), + nativeOnChain(ChainId.GNOSIS).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.MOONBEAM]: new V4Pool( + nativeOnChain(ChainId.MOONBEAM), + nativeOnChain(ChainId.MOONBEAM).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.BNB]: new V4Pool( + nativeOnChain(ChainId.BNB), + nativeOnChain(ChainId.BNB).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.AVALANCHE]: new V4Pool( + nativeOnChain(ChainId.AVALANCHE), + nativeOnChain(ChainId.AVALANCHE).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.BASE_GOERLI]: new V4Pool( + nativeOnChain(ChainId.BASE_GOERLI), + nativeOnChain(ChainId.BASE_GOERLI).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.BASE_SEPOLIA]: new V4Pool( + nativeOnChain(ChainId.BASE_SEPOLIA), + nativeOnChain(ChainId.BASE_SEPOLIA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.BASE]: new V4Pool( + nativeOnChain(ChainId.BASE), + nativeOnChain(ChainId.BASE).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ZORA]: new V4Pool( + nativeOnChain(ChainId.ZORA), + nativeOnChain(ChainId.ZORA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ZORA_SEPOLIA]: new V4Pool( + nativeOnChain(ChainId.ZORA_SEPOLIA), + nativeOnChain(ChainId.ZORA_SEPOLIA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ROOTSTOCK]: new V4Pool( + nativeOnChain(ChainId.ROOTSTOCK), + nativeOnChain(ChainId.ROOTSTOCK).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.BLAST]: new V4Pool( + nativeOnChain(ChainId.BLAST), + nativeOnChain(ChainId.BLAST).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.ZKSYNC]: new V4Pool( + nativeOnChain(ChainId.ZKSYNC), + nativeOnChain(ChainId.ZKSYNC).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.WORLDCHAIN]: new V4Pool( + nativeOnChain(ChainId.WORLDCHAIN), + nativeOnChain(ChainId.WORLDCHAIN).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.UNICHAIN_SEPOLIA]: new V4Pool( + nativeOnChain(ChainId.UNICHAIN_SEPOLIA), + nativeOnChain(ChainId.UNICHAIN_SEPOLIA).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.UNICHAIN]: new V4Pool( + nativeOnChain(ChainId.UNICHAIN), + nativeOnChain(ChainId.UNICHAIN).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), + [ChainId.MONAD_TESTNET]: new V4Pool( + nativeOnChain(ChainId.MONAD_TESTNET), + nativeOnChain(ChainId.MONAD_TESTNET).wrapped, + 0, + FAKE_TICK_SPACING, + ADDRESS_ZERO, + 79228162514264337593543950336, + 0, + 0 + ), +}; diff --git a/test/unit/routers/alpha-router/functions/compute-all-routes.test.ts b/test/unit/routers/alpha-router/functions/compute-all-routes.test.ts index d255d6c18..5af951019 100644 --- a/test/unit/routers/alpha-router/functions/compute-all-routes.test.ts +++ b/test/unit/routers/alpha-router/functions/compute-all-routes.test.ts @@ -4,6 +4,7 @@ import { Pool as V4Pool } from '@uniswap/v4-sdk'; import { CurrencyAmount, DAI_MAINNET as DAI, + nativeOnChain, USDC_MAINNET as USDC, USDT_MAINNET as USDT, WBTC_MAINNET as WBTC, @@ -39,6 +40,7 @@ import { } from '../../../../test-util/mock-data'; import { ADDRESS_ZERO } from '@uniswap/router-sdk'; import { ChainId, WETH9 } from '@uniswap/sdk-core'; +import { V4_ETH_WETH_FAKE_POOL } from '../../../../../src/util/pools'; describe('compute all v4 routes', () => { test('succeeds to compute all routes', async () => { @@ -291,6 +293,44 @@ describe('compute all mixed routes', () => { expect(routes).toHaveLength(1); }); + + test('handles ETH/WETH wrapping in mixed routes', async () => { + const pools = [ + USDC_WETH_LOW, // V3 pool + ETH_USDT_V4_LOW + ]; + const routes = computeAllMixedRoutes(USDC, USDT, pools, 2); + expect(routes.length).toBeGreaterThan(0); + // Routes should not include both ETH and WETH fake pools + routes.forEach(route => { + expect(route.pools).toEqual([USDC_WETH_LOW, V4_ETH_WETH_FAKE_POOL[ChainId.MAINNET], ETH_USDT_V4_LOW]) + expect(route.path).toEqual([USDC, nativeOnChain(ChainId.MAINNET).wrapped, nativeOnChain(ChainId.MAINNET), USDT]) + expect(route.input).toEqual(USDC) + expect(route.output).toEqual(USDT) + expect(route.pathInput).toEqual(USDC) + expect(route.pathOutput).toEqual(USDT) + expect(route.chainId).toEqual(1) + }); + }); + + test('handles WETH/ETH unwrapping in mixed routes', async () => { + const pools = [ + ETH_USDT_V4_LOW, + USDC_WETH_LOW + ]; + const routes = computeAllMixedRoutes(USDT, USDC, pools, 2); + expect(routes.length).toBeGreaterThan(0); + // Routes should not include both ETH and WETH fake pools + routes.forEach(route => { + expect(route.pools).toEqual([ETH_USDT_V4_LOW, V4_ETH_WETH_FAKE_POOL[ChainId.MAINNET], USDC_WETH_LOW]) + expect(route.path).toEqual([USDT, nativeOnChain(ChainId.MAINNET), nativeOnChain(ChainId.MAINNET).wrapped, USDC]) + expect(route.input).toEqual(USDT) + expect(route.output).toEqual(USDC) + expect(route.pathInput).toEqual(USDT) + expect(route.pathOutput).toEqual(USDC) + expect(route.chainId).toEqual(1) + }); + }); }); describe('compute all v2 routes', () => {