Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: properly use global cache + mixed routes fix #820

Merged
merged 10 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@uniswap/smart-order-router",
"version": "4.17.11",
"version": "4.18.0",
"description": "Uniswap Smart Order Router",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",
Expand Down
71 changes: 51 additions & 20 deletions src/routers/alpha-router/alpha-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1427,10 +1427,46 @@ export class AlphaRouter
// Fetch CachedRoutes
let cachedRoutes: CachedRoutes | undefined;

// Decide whether to use cached routes or not - If |enabledAndRequestedProtocolsMatch| is true we are good to use cached routes.
// In order to use cached routes, we need to have all enabled protocols specified in the request.
// By default, all protocols are enabled but for UniversalRouterVersion.V1_2, V4 is not.
// - ref: https://github.com/Uniswap/routing-api/blob/663b607d80d9249f85e7ab0925a611ec3701da2a/lib/util/supportedProtocolVersions.ts#L15
// So we take this into account when deciding whether to use cached routes or not.
// We only want to use cache if all enabled protocols are specified (V2,V3,V4? + MIXED). In any other case, use onchain path.
// - Cache is optimized for global search, not for specific protocol(s) search.
// For legacy systems (SWAP_ROUTER_02) or missing swapConfig, follow UniversalRouterVersion.V1_2 logic.
const availableProtocolsSet = new Set(Object.values(Protocol));
const requestedProtocolsSet = new Set(protocols);
if (
!swapConfig ||
swapConfig.type === SwapType.SWAP_ROUTER_02 ||
(swapConfig.type === SwapType.UNIVERSAL_ROUTER &&
swapConfig.version === UniversalRouterVersion.V1_2)
) {
availableProtocolsSet.delete(Protocol.V4);
if (requestedProtocolsSet.has(Protocol.V4)) {
requestedProtocolsSet.delete(Protocol.V4);
}
}
const enabledAndRequestedProtocolsMatch =
availableProtocolsSet.size === requestedProtocolsSet.size &&
[...availableProtocolsSet].every((protocol) =>
requestedProtocolsSet.has(protocol)
);

log.debug('UniversalRouterVersion_CacheGate_Check', {
availableProtocolsSet: Array.from(availableProtocolsSet),
requestedProtocolsSet: Array.from(requestedProtocolsSet),
enabledAndRequestedProtocolsMatch,
swapConfigType: swapConfig?.type,
swapConfigUniversalRouterVersion:
swapConfig?.type === SwapType.UNIVERSAL_ROUTER
? swapConfig?.version
: 'N/A',
});

if (routingConfig.useCachedRoutes && cacheMode !== CacheMode.Darkmode) {
// Only use cache if 0 or more than 1 protocol is specified.
// - Cache is optimized for global search, not for specific protocol search
if (protocols.length != 1) {
if (enabledAndRequestedProtocolsMatch) {
if (
protocols.includes(Protocol.V4) &&
(currencyIn.isNative || currencyOut.isNative)
Expand Down Expand Up @@ -2314,10 +2350,7 @@ export class AlphaRouter
Promise.resolve(undefined);

// we are explicitly requiring people to specify v4 for now
if (
(v4SupportedInChain && (v4ProtocolSpecified || noProtocolsSpecified)) ||
(shouldQueryMixedProtocol && mixedProtocolAllowed)
) {
if (v4SupportedInChain && (v4ProtocolSpecified || noProtocolsSpecified)) {
// if (v4ProtocolSpecified || noProtocolsSpecified) {
v4CandidatePoolsPromise = getV4CandidatePools({
currencyIn: currencyIn,
Expand All @@ -2343,11 +2376,7 @@ export class AlphaRouter
let v3CandidatePoolsPromise: Promise<V3CandidatePools | undefined> =
Promise.resolve(undefined);
if (!fotInDirectSwap) {
if (
v3ProtocolSpecified ||
noProtocolsSpecified ||
(shouldQueryMixedProtocol && mixedProtocolAllowed)
) {
if (v3ProtocolSpecified || noProtocolsSpecified) {
const tokenIn = currencyIn.wrapped;
const tokenOut = currencyOut.wrapped;

Expand All @@ -2374,10 +2403,7 @@ export class AlphaRouter

let v2CandidatePoolsPromise: Promise<V2CandidatePools | undefined> =
Promise.resolve(undefined);
if (
(v2SupportedInChain && (v2ProtocolSpecified || noProtocolsSpecified)) ||
(shouldQueryMixedProtocol && mixedProtocolAllowed)
) {
if (v2SupportedInChain && (v2ProtocolSpecified || noProtocolsSpecified)) {
const tokenIn = currencyIn.wrapped;
const tokenOut = currencyOut.wrapped;

Expand Down Expand Up @@ -2534,7 +2560,12 @@ export class AlphaRouter
// Maybe Quote mixed routes
// if MixedProtocol is specified or no protocol is specified and v2 is supported AND tradeType is ExactIn
// AND is Mainnet or Gorli
if (shouldQueryMixedProtocol && mixedProtocolAllowed) {
// Also make sure there are at least 2 protocols provided besides MIXED, before entering mixed quoter
if (
shouldQueryMixedProtocol &&
mixedProtocolAllowed &&
protocols.filter((protocol) => protocol !== Protocol.MIXED).length >= 2
) {
log.info({ protocols, tradeType }, 'Routing across MixedRoutes');

metric.putMetric(
Expand Down Expand Up @@ -2575,9 +2606,9 @@ export class AlphaRouter
percents,
quoteCurrency.wrapped,
[
v4CandidatePools!,
v3CandidatePools!,
v2CandidatePools!,
v4CandidatePools,
v3CandidatePools,
v2CandidatePools,
crossLiquidityPools,
],
tradeType,
Expand Down
71 changes: 63 additions & 8 deletions src/routers/alpha-router/functions/get-candidate-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ export type V2GetCandidatePoolsParams = {
};

export type MixedRouteGetCandidatePoolsParams = {
v4CandidatePools: V4CandidatePools;
v3CandidatePools: V3CandidatePools;
v2CandidatePools: V2CandidatePools;
v4CandidatePools: V4CandidatePools | undefined;
v3CandidatePools: V3CandidatePools | undefined;
v2CandidatePools: V2CandidatePools | undefined;
crossLiquidityPools: CrossLiquidityCandidatePools;
routingConfig: AlphaRouterConfig;
tokenProvider: ITokenProvider;
Expand Down Expand Up @@ -1929,11 +1929,66 @@ export async function getMixedRouteCandidatePools({
chainId,
}: MixedRouteGetCandidatePoolsParams): Promise<MixedCandidatePools> {
const beforeSubgraphPools = Date.now();
const [
{ subgraphPools: V4subgraphPools, candidatePools: V4candidatePools },
{ subgraphPools: V3subgraphPools, candidatePools: V3candidatePools },
{ subgraphPools: V2subgraphPools, candidatePools: V2candidatePools },
] = [v4CandidatePools, v3CandidatePools, v2CandidatePools];
const [v4Results, v3Results, v2Results] = [
v4CandidatePools,
v3CandidatePools,
v2CandidatePools,
];

// Create empty defaults for undefined results
const {
subgraphPools: V4subgraphPools = [],
candidatePools: V4candidatePools = {
protocol: Protocol.V4,
selections: {
topByBaseWithTokenIn: [],
topByBaseWithTokenOut: [],
topByDirectSwapPool: [],
topByEthQuoteTokenPool: [],
topByTVL: [],
topByTVLUsingTokenIn: [],
topByTVLUsingTokenOut: [],
topByTVLUsingTokenInSecondHops: [],
topByTVLUsingTokenOutSecondHops: [],
},
},
} = v4Results || {};

const {
subgraphPools: V3subgraphPools = [],
candidatePools: V3candidatePools = {
protocol: Protocol.V3,
selections: {
topByBaseWithTokenIn: [],
topByBaseWithTokenOut: [],
topByDirectSwapPool: [],
topByEthQuoteTokenPool: [],
topByTVL: [],
topByTVLUsingTokenIn: [],
topByTVLUsingTokenOut: [],
topByTVLUsingTokenInSecondHops: [],
topByTVLUsingTokenOutSecondHops: [],
},
},
} = v3Results || {};

const {
subgraphPools: V2subgraphPools = [],
candidatePools: V2candidatePools = {
protocol: Protocol.V2,
selections: {
topByBaseWithTokenIn: [],
topByBaseWithTokenOut: [],
topByDirectSwapPool: [],
topByEthQuoteTokenPool: [],
topByTVL: [],
topByTVLUsingTokenIn: [],
topByTVLUsingTokenOut: [],
topByTVLUsingTokenInSecondHops: [],
topByTVLUsingTokenOutSecondHops: [],
},
},
} = v2Results || {};

// Injects the liquidity pools found by the getMixedCrossLiquidityCandidatePools function
V2subgraphPools.push(...crossLiquidityPools.v2Pools);
Expand Down
6 changes: 3 additions & 3 deletions src/routers/alpha-router/quoters/base-quoter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ export abstract class BaseQuoter<
CandidatePools extends
| SupportedCandidatePools
| [
V4CandidatePools,
V3CandidatePools,
V2CandidatePools,
V4CandidatePools | undefined,
V3CandidatePools | undefined,
V2CandidatePools | undefined,
CrossLiquidityCandidatePools
],
Route extends SupportedRoutes,
Expand Down
12 changes: 6 additions & 6 deletions src/routers/alpha-router/quoters/mixed-quoter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ import { GetQuotesResult, GetRoutesResult } from './model';

export class MixedQuoter extends BaseQuoter<
[
V4CandidatePools,
V3CandidatePools,
V2CandidatePools,
V4CandidatePools | undefined,
V3CandidatePools | undefined,
V2CandidatePools | undefined,
CrossLiquidityCandidatePools
],
MixedRoute,
Expand Down Expand Up @@ -90,9 +90,9 @@ export class MixedQuoter extends BaseQuoter<
currencyIn: Currency,
currencyOut: Currency,
v4v3v2candidatePools: [
V4CandidatePools,
V3CandidatePools,
V2CandidatePools,
V4CandidatePools | undefined,
V3CandidatePools | undefined,
V2CandidatePools | undefined,
CrossLiquidityCandidatePools
],
tradeType: TradeType,
Expand Down
Loading
Loading