Skip to content

Commit

Permalink
feat: v4 quoter support native currency quote (Uniswap#707)
Browse files Browse the repository at this point in the history
* fix test

* compute all routes native currency support

* fix compiling error

* add more compute v4 routes unit test cases
  • Loading branch information
jsy1218 authored Sep 19, 2024
1 parent 2cd0977 commit 1ea2f6f
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 93 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"@uniswap/universal-router-sdk": "^3.0.2",
"@uniswap/v2-sdk": "^4.3.2",
"@uniswap/v3-sdk": "^3.14.0",
"@uniswap/v4-sdk": "^1.0.0",
"@uniswap/v3-sdk": "^3.13.0",
"@uniswap/v4-sdk": "^1.2.0",
"async-retry": "^1.3.1",
"await-timeout": "^1.1.1",
"axios": "^0.21.1",
Expand Down
52 changes: 8 additions & 44 deletions src/providers/on-chain-quote-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import {
MixedRouteSDK,
Protocol,
} from '@uniswap/router-sdk';
import { ChainId, Currency, Token } from '@uniswap/sdk-core';
import { encodeRouteToPath } from '@uniswap/v3-sdk';
import { Pool as V4Pool } from '@uniswap/v4-sdk';
import { ChainId } from '@uniswap/sdk-core';
import { encodeRouteToPath as encodeV3RouteToPath } from '@uniswap/v3-sdk';
import {
encodeRouteToPath as encodeV4RouteToPath,
Pool as V4Pool,
} from '@uniswap/v4-sdk';
import retry, { Options as RetryOptions } from 'async-retry';
import _ from 'lodash';
import stats from 'stats-lite';
Expand Down Expand Up @@ -444,12 +447,12 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider {
>(route: TRoute, functionName: string): TPath {
switch (route.protocol) {
case Protocol.V3:
return encodeRouteToPath(
return encodeV3RouteToPath(
route,
functionName == 'quoteExactOutput' // For exactOut must be true to ensure the routes are reversed.
) as TPath;
case Protocol.V4:
return this.convertV4RouteToPathKey(
return encodeV4RouteToPath(
route,
functionName == 'quoteExactOutput'
) as TPath;
Expand All @@ -469,45 +472,6 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider {
}
}

public convertV4RouteToPathKey(route: V4Route, exactOut: boolean): PathKey[] {
const firstInputToken: Token = route.input.wrapped;

const { path } = route.pools.reduce(
(
{ inputToken, path }: { inputToken: Currency; path: PathKey[] },
pool: V4Pool,
index
): { inputToken: Currency; path: PathKey[] } => {
const outputToken = pool.token0.equals(inputToken)
? pool.token1
: pool.token0;

const pathKey: PathKey = {
intermediateCurrency: exactOut
? inputToken.wrapped.address
: outputToken.wrapped.address,
hookData: '0x',
...pool,
};

if (index === 0) {
return {
inputToken: outputToken,
path: [pathKey],
};
} else {
return {
inputToken: outputToken,
path: [...path, pathKey],
};
}
},
{ inputToken: firstInputToken, path: [] as PathKey[] }
);

return path;
}

private getContractInterface(
useMixedRouteQuoter: boolean,
mixedRouteContainsV4Pool: boolean,
Expand Down
61 changes: 36 additions & 25 deletions src/routers/alpha-router/functions/compute-all-routes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { TPool } from '@uniswap/router-sdk/dist/utils/TPool';
import { 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 { TPool } from '@uniswap/router-sdk/dist/utils/TPool';
import { getAddressLowerCase } from '../../../util';
import { log } from '../../../util/log';
import { poolToString, routeToString } from '../../../util/routes';
import {
Expand All @@ -15,18 +16,19 @@ import {
} from '../../router';

export function computeAllV4Routes(
tokenIn: Currency,
tokenOut: Currency,
currencyIn: Currency,
currencyOut: Currency,
pools: V4Pool[],
maxHops: number
): V4Route[] {
// TODO: ROUTE-217 - Support native currency routing in V4
return computeAllRoutes<V4Pool, V4Route>(
tokenIn.wrapped,
tokenOut.wrapped,
(route: V4Pool[], tokenIn: Currency, tokenOut: Currency) => {
return new V4Route(route, tokenIn, tokenOut);
return computeAllRoutes<V4Pool, V4Route, Currency>(
currencyIn,
currencyOut,
(route: V4Pool[], currencyIn: Currency, currencyOut: Currency) => {
return new V4Route(route, currencyIn, currencyOut);
},
(pool: V4Pool, currency: Currency) => pool.involvesToken(currency),
pools,
maxHops
);
Expand All @@ -38,12 +40,13 @@ export function computeAllV3Routes(
pools: V3Pool[],
maxHops: number
): V3Route[] {
return computeAllRoutes<V3Pool, V3Route>(
return computeAllRoutes<V3Pool, V3Route, Token>(
tokenIn,
tokenOut,
(route: V3Pool[], tokenIn: Token, tokenOut: Token) => {
return new V3Route(route, tokenIn, tokenOut);
},
(pool: V3Pool, token: Token) => pool.involvesToken(token),
pools,
maxHops
);
Expand All @@ -55,12 +58,13 @@ export function computeAllV2Routes(
pools: Pair[],
maxHops: number
): V2Route[] {
return computeAllRoutes<Pair, V2Route>(
return computeAllRoutes<Pair, V2Route, Token>(
tokenIn,
tokenOut,
(route: Pair[], tokenIn: Token, tokenOut: Token) => {
return new V2Route(route, tokenIn, tokenOut);
},
(pool: Pair, token: Token) => pool.involvesToken(token),
pools,
maxHops
);
Expand All @@ -72,12 +76,13 @@ export function computeAllMixedRoutes(
parts: TPool[],
maxHops: number
): MixedRoute[] {
const routesRaw = computeAllRoutes<TPool, MixedRoute>(
const routesRaw = computeAllRoutes<TPool, MixedRoute, Token>(
tokenIn,
tokenOut,
(route: TPool[], tokenIn: Token, tokenOut: Token) => {
return new MixedRoute(route, tokenIn, tokenOut);
},
(pool: TPool, token: Token) => pool.involvesToken(token),
parts,
maxHops
);
Expand All @@ -93,32 +98,38 @@ export function computeAllMixedRoutes(

export function computeAllRoutes<
TypePool extends TPool,
TRoute extends SupportedRoutes
TRoute extends SupportedRoutes,
TCurrency extends Currency
>(
tokenIn: Token,
tokenOut: Token,
buildRoute: (route: TypePool[], tokenIn: Token, tokenOut: Token) => TRoute,
tokenIn: TCurrency,
tokenOut: TCurrency,
buildRoute: (
route: TypePool[],
tokenIn: TCurrency,
tokenOut: TCurrency
) => TRoute,
involvesToken: (pool: TypePool, token: TCurrency) => boolean,
pools: TypePool[],
maxHops: number
): TRoute[] {
const poolsUsed = Array<boolean>(pools.length).fill(false);
const routes: TRoute[] = [];

const computeRoutes = (
tokenIn: Token,
tokenOut: Token,
tokenIn: TCurrency,
tokenOut: TCurrency,
currentRoute: TypePool[],
poolsUsed: boolean[],
tokensVisited: Set<string>,
_previousTokenOut?: Token
_previousTokenOut?: TCurrency
) => {
if (currentRoute.length > maxHops) {
return;
}

if (
currentRoute.length > 0 &&
currentRoute[currentRoute.length - 1]!.involvesToken(tokenOut)
involvesToken(currentRoute[currentRoute.length - 1]!, tokenOut)
) {
routes.push(buildRoute([...currentRoute], tokenIn, tokenOut));
return;
Expand All @@ -132,7 +143,7 @@ export function computeAllRoutes<
const curPool = pools[i]!;
const previousTokenOut = _previousTokenOut ? _previousTokenOut : tokenIn;

if (!curPool.involvesToken(previousTokenOut)) {
if (!involvesToken(curPool, previousTokenOut)) {
continue;
}

Expand All @@ -141,11 +152,11 @@ export function computeAllRoutes<
: curPool.token0;

// TODO: ROUTE-217 - Support native currency routing in V4
if (tokensVisited.has(currentTokenOut.wrapped.address.toLowerCase())) {
if (tokensVisited.has(getAddressLowerCase(currentTokenOut))) {
continue;
}

tokensVisited.add(currentTokenOut.wrapped.address.toLowerCase());
tokensVisited.add(getAddressLowerCase(currentTokenOut));
currentRoute.push(curPool);
poolsUsed[i] = true;
computeRoutes(
Expand All @@ -154,11 +165,11 @@ export function computeAllRoutes<
currentRoute,
poolsUsed,
tokensVisited,
currentTokenOut.wrapped
currentTokenOut as TCurrency
);
poolsUsed[i] = false;
currentRoute.pop();
tokensVisited.delete(currentTokenOut.wrapped.address.toLowerCase());
tokensVisited.delete(getAddressLowerCase(currentTokenOut));
}
};

Expand All @@ -167,7 +178,7 @@ export function computeAllRoutes<
tokenOut,
[],
poolsUsed,
new Set([tokenIn.address.toLowerCase()])
new Set([getAddressLowerCase(tokenIn)])
);

log.info(
Expand Down
2 changes: 1 addition & 1 deletion src/routers/alpha-router/functions/get-candidate-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import {
getAddressLowerCase,
getApplicableV3FeeAmounts,
unparseFeeAmount,
WRAPPED_NATIVE_CURRENCY
WRAPPED_NATIVE_CURRENCY,
} from '../../../util';
import { parseFeeAmount } from '../../../util/amounts';
import { log } from '../../../util/log';
Expand Down
20 changes: 11 additions & 9 deletions src/routers/alpha-router/quoters/v4-quoter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Protocol } from '@uniswap/router-sdk';
import { ChainId, Currency, Token, TradeType } from '@uniswap/sdk-core';
import { ChainId, Currency, TradeType } from '@uniswap/sdk-core';
import _ from 'lodash';

import {
IOnChainQuoteProvider,
ITokenListProvider,
Expand All @@ -26,6 +27,7 @@ import {
V4CandidatePools,
} from '../functions/get-candidate-pools';
import { IGasModel } from '../gas-models';

import { BaseQuoter } from './base-quoter';
import { GetQuotesResult, GetRoutesResult } from './model';

Expand Down Expand Up @@ -56,8 +58,8 @@ export class V4Quoter extends BaseQuoter<V4CandidatePools, V4Route, Currency> {
}

protected async getRoutes(
tokenIn: Currency,
tokenOut: Currency,
currencyIn: Currency,
currencyOut: Currency,
v4CandidatePools: V4CandidatePools,
_tradeType: TradeType,
routingConfig: AlphaRouterConfig
Expand Down Expand Up @@ -88,7 +90,7 @@ export class V4Quoter extends BaseQuoter<V4CandidatePools, V4Route, Currency> {
//
if (
tokenValidation == TokenValidationResult.STF &&
(token.equals(tokenIn) || token.equals(tokenOut))
(token.equals(currencyIn) || token.equals(currencyOut))
) {
return false;
}
Expand All @@ -100,11 +102,11 @@ export class V4Quoter extends BaseQuoter<V4CandidatePools, V4Route, Currency> {
}
);

// Given all our candidate pools, compute all the possible ways to route from tokenIn to tokenOut.
// Given all our candidate pools, compute all the possible ways to route from currencyIn to tokenOut.
const { maxSwapsPerPath } = routingConfig;
const routes = computeAllV4Routes(
tokenIn,
tokenOut,
currencyIn,
currencyOut,
pools,
maxSwapsPerPath
);
Expand All @@ -125,7 +127,7 @@ export class V4Quoter extends BaseQuoter<V4CandidatePools, V4Route, Currency> {
routes: V4Route[],
amounts: CurrencyAmount[],
percents: number[],
quoteToken: Token,
quoteCurrency: Currency,
tradeType: TradeType,
routingConfig: AlphaRouterConfig,
candidatePools?: CandidatePoolsBySelectionCriteria,
Expand Down Expand Up @@ -220,7 +222,7 @@ export class V4Quoter extends BaseQuoter<V4CandidatePools, V4Route, Currency> {
initializedTicksCrossedList,
quoterGasEstimate: gasEstimate,
gasModel,
quoteToken,
quoteToken: quoteCurrency.wrapped,
tradeType,
v4PoolProvider: this.v4PoolProvider,
});
Expand Down
Loading

0 comments on commit 1ea2f6f

Please sign in to comment.