Skip to content

Commit

Permalink
Avoid routing through AMPL on second hop (Uniswap#435)
Browse files Browse the repository at this point in the history
* Avoid routing through AMPL on second hop

* tokenOutAddress

---------

Co-authored-by: jsy1218 <[email protected]>
  • Loading branch information
mikeki and jsy1218 authored Nov 13, 2023
1 parent 5821f76 commit 6dc9c84
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
74 changes: 41 additions & 33 deletions src/routers/alpha-router/alpha-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ import {
} from '../../providers';
import { CachingTokenListProvider, ITokenListProvider } from '../../providers/caching-token-list-provider';
import { GasPrice, IGasPriceProvider } from '../../providers/gas-price-provider';
import {
IPortionProvider,
PortionProvider
} from '../../providers/portion-provider';
import { IPortionProvider, PortionProvider } from '../../providers/portion-provider';
import { ProviderConfig } from '../../providers/provider';
import { OnChainTokenFeeFetcher } from '../../providers/token-fee-fetcher';
import { ITokenProvider, TokenProvider } from '../../providers/token-provider';
Expand Down Expand Up @@ -87,10 +84,7 @@ import {
V3Route,
} from '../router';

import {
DEFAULT_ROUTING_CONFIG_BY_CHAIN,
ETH_GAS_STATION_API_URL,
} from './config';
import { DEFAULT_ROUTING_CONFIG_BY_CHAIN, ETH_GAS_STATION_API_URL } from './config';
import {
MixedRouteWithValidQuote,
RouteWithValidQuote,
Expand Down Expand Up @@ -237,6 +231,13 @@ export class MapWithLowerCaseKey<V> extends Map<string, V> {
}
}

export class LowerCaseStringArray extends Array<string> {
constructor(...items: string[]) {
// Convert all items to lowercase before calling the parent constructor
super(...items.map(item => item.toLowerCase()));
}
}

/**
* Determines the pools that the algorithm will consider when finding the optimal swap.
*
Expand Down Expand Up @@ -274,6 +275,13 @@ export type ProtocolPoolSelection = {
* it would find the top 4 pools that involve USDT, and find the topNSecondHop pools that involve DAI
*/
topNSecondHopForTokenAddress?: MapWithLowerCaseKey<number>;
/**
* List of token addresses to avoid using as a second hop.
* There might be multiple reasons why we would like to avoid a specific token,
* but the specific reason that we are trying to solve is when the pool is not synced properly
* e.g. when the pool has a rebasing token that isn't syncing the pool on every rebase.
*/
tokensToAvoidOnSecondHops?: LowerCaseStringArray;
/**
* The top N pools for token in and token out that involve a token from a list of
* hardcoded 'base tokens'. These are standard tokens such as WETH, USDC, DAI, etc.
Expand Down Expand Up @@ -612,7 +620,7 @@ export class AlphaRouter
this.chainId,
new NodeJSCache(new NodeCache({ stdTTL: 86400, useClones: false })),
new OnChainTokenFeeFetcher(this.chainId, provider)
)
);
}
this.v2PoolProvider =
v2PoolProvider ??
Expand Down Expand Up @@ -1363,7 +1371,7 @@ export class AlphaRouter
this.l2GasDataProvider
? await this.l2GasDataProvider!.getGasData()
: undefined,
providerConfig
providerConfig
);
metric.putMetric(
'SimulateTransaction',
Expand Down Expand Up @@ -1636,29 +1644,29 @@ export class AlphaRouter
const beforeGetRoutesThenQuotes = Date.now();

quotePromises.push(
v2CandidatePoolsPromise.then((v2CandidatePools) =>
this.v2Quoter.getRoutesThenQuotes(
tokenIn,
tokenOut,
amount,
amounts,
percents,
quoteToken,
v2CandidatePools!,
tradeType,
routingConfig,
undefined,
gasPriceWei
).then((result) => {
metric.putMetric(
`SwapRouteFromChain_V2_GetRoutesThenQuotes_Load`,
Date.now() - beforeGetRoutesThenQuotes,
MetricLoggerUnit.Milliseconds
);

return result;
})
)
v2CandidatePoolsPromise.then((v2CandidatePools) =>
this.v2Quoter.getRoutesThenQuotes(
tokenIn,
tokenOut,
amount,
amounts,
percents,
quoteToken,
v2CandidatePools!,
tradeType,
routingConfig,
undefined,
gasPriceWei
).then((result) => {
metric.putMetric(
`SwapRouteFromChain_V2_GetRoutesThenQuotes_Load`,
Date.now() - beforeGetRoutesThenQuotes,
MetricLoggerUnit.Milliseconds
);

return result;
})
)
);
}

Expand Down
5 changes: 4 additions & 1 deletion src/routers/alpha-router/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId } from '@uniswap/sdk-core';

import { AlphaRouterConfig } from './alpha-router';
import { AlphaRouterConfig, LowerCaseStringArray } from './alpha-router';

export const DEFAULT_ROUTING_CONFIG_BY_CHAIN = (
chainId: ChainId
Expand Down Expand Up @@ -71,6 +71,9 @@ export const DEFAULT_ROUTING_CONFIG_BY_CHAIN = (
topNDirectSwaps: 1,
topNTokenInOut: 5,
topNSecondHop: 2,
tokensToAvoidOnSecondHops: new LowerCaseStringArray(
'0xd46ba6d942050d489dbd938a2c909a5d5039a161' // AMPL on Mainnet
),
topNWithEachBaseToken: 2,
topNWithBaseToken: 6,
},
Expand Down
26 changes: 22 additions & 4 deletions src/routers/alpha-router/functions/get-candidate-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ export async function getV3CandidatePools({
topNTokenInOut,
topNSecondHop,
topNSecondHopForTokenAddress,
tokensToAvoidOnSecondHops,
topNWithEachBaseToken,
topNWithBaseToken,
},
Expand Down Expand Up @@ -446,6 +447,7 @@ export async function getV3CandidatePools({
.filter((subgraphPool) => {
return (
!poolAddressesSoFar.has(subgraphPool.id) &&
!tokensToAvoidOnSecondHops?.includes(secondHopId.toLowerCase()) &&
(subgraphPool.token0.id == secondHopId ||
subgraphPool.token1.id == secondHopId)
);
Expand All @@ -469,6 +471,7 @@ export async function getV3CandidatePools({
.filter((subgraphPool) => {
return (
!poolAddressesSoFar.has(subgraphPool.id) &&
!tokensToAvoidOnSecondHops?.includes(secondHopId.toLowerCase()) &&
(subgraphPool.token0.id == secondHopId ||
subgraphPool.token1.id == secondHopId)
);
Expand Down Expand Up @@ -625,6 +628,7 @@ export async function getV2CandidatePools({
topNDirectSwaps,
topNTokenInOut,
topNSecondHop,
tokensToAvoidOnSecondHops,
topNWithEachBaseToken,
topNWithBaseToken,
},
Expand Down Expand Up @@ -899,11 +903,25 @@ export async function getV2CandidatePools({
// Filtering step for second hops
const topByTVLUsingTokenInSecondHopsMap: Map<string, SubcategorySelectionPools<V2SubgraphPool>> = new Map();
const topByTVLUsingTokenOutSecondHopsMap: Map<string, SubcategorySelectionPools<V2SubgraphPool>> = new Map();
const tokenInSecondHopAddresses = topByTVLUsingTokenIn.map((pool) =>
tokenInAddress == pool.token0.id ? pool.token1.id : pool.token0.id
const tokenInSecondHopAddresses = topByTVLUsingTokenIn.filter((pool) => {
// filtering second hops
if (tokenInAddress === pool.token0.id) {
return !tokensToAvoidOnSecondHops?.includes(pool.token1.id.toLowerCase());
} else {
return !tokensToAvoidOnSecondHops?.includes(pool.token0.id.toLowerCase());
}
}).map((pool) =>
tokenInAddress === pool.token0.id ? pool.token1.id : pool.token0.id
);
const tokenOutSecondHopAddresses = topByTVLUsingTokenOut.map((pool) =>
tokenOutAddress == pool.token0.id ? pool.token1.id : pool.token0.id
const tokenOutSecondHopAddresses = topByTVLUsingTokenOut.filter((pool) => {
// filtering second hops
if (tokenOutAddress === pool.token0.id) {
return !tokensToAvoidOnSecondHops?.includes(pool.token1.id.toLowerCase());
} else {
return !tokensToAvoidOnSecondHops?.includes(pool.token0.id.toLowerCase());
}
}).map((pool) =>
tokenOutAddress === pool.token0.id ? pool.token1.id : pool.token0.id
);

for (const secondHopId of tokenInSecondHopAddresses) {
Expand Down

0 comments on commit 6dc9c84

Please sign in to comment.