Skip to content

Commit

Permalink
feat: implement best swap route with v4 routes (#680)
Browse files Browse the repository at this point in the history
- **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
feature

- **What is the current behavior?** (You can also link to an open issue here)
best swap route does not account for v4 routes

- **What is the new behavior (if this is a feature change)?**
best swap route is now implemented to account for v4 routes.

- **Other information**:
I added v4 routing integ-test against alpha router, in [alpha-router.integration.test.ts](https://github.com/Uniswap/smart-order-router/pull/680/files#diff-b1a09e5a7156f19225eba6ca4e2e78d0942580ff1118bab472bf0ea3b1294904). I can see the alpha router can quote 1 OP -> 997404.407521 USDC against pool id 0xa40318dea5fabf21971f683f641b54d6d7d86f5b083cd6f0af9332c5c7a9ec06. Tenderly simulation does not work yet, because universal router doesn't support v4 swap commands yet.
  • Loading branch information
jsy1218 authored Aug 29, 2024
1 parent a38fded commit fc5f8a1
Show file tree
Hide file tree
Showing 15 changed files with 405 additions and 122 deletions.
45 changes: 8 additions & 37 deletions src/providers/subgraph-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,7 @@ export abstract class SubgraphProvider<
: undefined;

const query = gql`
query getPools($pageSize: Int!, $id: String) {
pools(
first: $pageSize
${blockNumber ? `block: { number: ${blockNumber} }` : ``}
where: { id_gt: $id }
) {
id
token0 {
symbol
id
}
token1 {
symbol
id
}
feeTier
liquidity
totalValueLockedUSD
totalValueLockedETH
totalValueLockedUSDUntracked
}
}
${this.subgraphQuery(blockNumber)}
`;

let pools: TRawSubgraphPool[] = [];
Expand Down Expand Up @@ -244,21 +223,7 @@ export abstract class SubgraphProvider<
parseFloat(pool.totalValueLockedETH) > this.trackedEthThreshold
)
.map((pool) => {
const { totalValueLockedETH, totalValueLockedUSD } = pool;

return {
id: pool.id.toLowerCase(),
feeTier: pool.feeTier,
token0: {
id: pool.token0.id.toLowerCase(),
},
token1: {
id: pool.token1.id.toLowerCase(),
},
liquidity: pool.liquidity,
tvlETH: parseFloat(totalValueLockedETH),
tvlUSD: parseFloat(totalValueLockedUSD),
} as TSubgraphPool;
return this.mapSubgraphPool(pool);
});

metric.putMetric(
Expand Down Expand Up @@ -288,4 +253,10 @@ export abstract class SubgraphProvider<

return poolsSanitized;
}

protected abstract subgraphQuery(blockNumber?: number): string;

protected abstract mapSubgraphPool(
rawSubgraphPool: TRawSubgraphPool
): TSubgraphPool;
}
16 changes: 16 additions & 0 deletions src/providers/token-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -977,3 +977,19 @@ export const USDC_ON = (chainId: ChainId): Token => {
export const WNATIVE_ON = (chainId: ChainId): Token => {
return WRAPPED_NATIVE_CURRENCY[chainId];
};

export const V4_SEPOLIA_TEST_OP = new Token(
ChainId.SEPOLIA,
'0xc268035619873d85461525f5fdb792dd95982161',
18,
'OP',
'Optimism'
);

export const V4_SEPOLIA_TEST_USDC = new Token(
ChainId.SEPOLIA,
'0xbe2a7f5acecdc293bf34445a0021f229dd2edd49',
6,
'USDC',
'USD'
);
45 changes: 45 additions & 0 deletions src/providers/v3/subgraph-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,49 @@ export class V3SubgraphProvider
subgraphUrlOverride ?? SUBGRAPH_URL_BY_CHAIN[chainId]
);
}

protected override subgraphQuery(blockNumber?: number): string {
return `
query getPools($pageSize: Int!, $id: String) {
pools(
first: $pageSize
${blockNumber ? `block: { number: ${blockNumber} }` : ``}
where: { id_gt: $id }
) {
id
token0 {
symbol
id
}
token1 {
symbol
id
}
feeTier
liquidity
totalValueLockedUSD
totalValueLockedETH
totalValueLockedUSDUntracked
}
}
`;
}

protected override mapSubgraphPool(
rawPool: V3RawSubgraphPool
): V3SubgraphPool {
return {
id: rawPool.id,
feeTier: rawPool.feeTier,
liquidity: rawPool.liquidity,
token0: {
id: rawPool.token0.id,
},
token1: {
id: rawPool.token1.id,
},
tvlETH: parseFloat(rawPool.totalValueLockedETH),
tvlUSD: parseFloat(rawPool.totalValueLockedUSD),
};
}
}
49 changes: 49 additions & 0 deletions src/providers/v4/subgraph-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,53 @@ export class V4SubgraphProvider
subgraphUrlOverride ?? SUBGRAPH_URL_BY_CHAIN[chainId]
);
}

protected override subgraphQuery(blockNumber?: number): string {
return `
query getPools($pageSize: Int!, $id: String) {
pools(
first: $pageSize
${blockNumber ? `block: { number: ${blockNumber} }` : ``}
where: { id_gt: $id }
) {
id
token0 {
symbol
id
}
token1 {
symbol
id
}
feeTier
tickSpacing
hooks
liquidity
totalValueLockedUSD
totalValueLockedETH
totalValueLockedUSDUntracked
}
}
`;
}

protected override mapSubgraphPool(
rawPool: V4RawSubgraphPool
): V4SubgraphPool {
return {
id: rawPool.id,
feeTier: rawPool.feeTier,
tickSpacing: rawPool.tickSpacing,
hooks: rawPool.hooks,
liquidity: rawPool.liquidity,
token0: {
id: rawPool.token0.id,
},
token1: {
id: rawPool.token1.id,
},
tvlETH: parseFloat(rawPool.totalValueLockedETH),
tvlUSD: parseFloat(rawPool.totalValueLockedUSD),
};
}
}
41 changes: 30 additions & 11 deletions src/routers/alpha-router/alpha-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ import {
} from '../../providers/v3/pool-provider';
import { IV3SubgraphProvider } from '../../providers/v3/subgraph-provider';
import { Erc20__factory } from '../../types/other/factories/Erc20__factory';
import { SWAP_ROUTER_02_ADDRESSES, WRAPPED_NATIVE_CURRENCY } from '../../util';
import {
SWAP_ROUTER_02_ADDRESSES,
V4_SUPPORTED,
WRAPPED_NATIVE_CURRENCY
} from '../../util';
import { CurrencyAmount } from '../../util/amounts';
import {
ID_TO_CHAIN_ID,
Expand Down Expand Up @@ -147,7 +151,8 @@ import {
MixedRouteWithValidQuote,
RouteWithValidQuote,
V2RouteWithValidQuote,
V3RouteWithValidQuote, V4RouteWithValidQuote
V3RouteWithValidQuote,
V4RouteWithValidQuote,
} from './entities/route-with-valid-quote';
import { BestSwapRoute, getBestSwapRoute } from './functions/best-swap-route';
import { calculateRatioAmountIn } from './functions/calculate-ratio-amount-in';
Expand All @@ -162,6 +167,7 @@ import {
V3CandidatePools,
V4CandidatePools,
} from './functions/get-candidate-pools';
import { NATIVE_OVERHEAD } from './gas-models/gas-costs';
import {
GasModelProviderConfig,
GasModelType,
Expand All @@ -172,7 +178,6 @@ import {
} from './gas-models/gas-model';
import { MixedRouteHeuristicGasModelFactory } from './gas-models/mixedRoute/mixed-route-heuristic-gas-model';
import { V2HeuristicGasModelFactory } from './gas-models/v2/v2-heuristic-gas-model';
import { NATIVE_OVERHEAD } from './gas-models/gas-costs';
import { V3HeuristicGasModelFactory } from './gas-models/v3/v3-heuristic-gas-model';
import { V4HeuristicGasModelFactory } from './gas-models/v4/v4-heuristic-gas-model';
import { GetQuotesResult, MixedQuoter, V2Quoter, V3Quoter } from './quoters';
Expand Down Expand Up @@ -302,6 +307,11 @@ export type AlphaRouterParams = {
* All the supported v2 chains configuration
*/
v2Supported?: ChainId[];

/**
* All the supported v4 chains configuration
*/
v4Supported?: ChainId[];
};

export class MapWithLowerCaseKey<V> extends Map<string, V> {
Expand Down Expand Up @@ -508,6 +518,7 @@ export class AlphaRouter
protected tokenPropertiesProvider: ITokenPropertiesProvider;
protected portionProvider: IPortionProvider;
protected v2Supported?: ChainId[];
protected v4Supported?: ChainId[];

constructor({
chainId,
Expand Down Expand Up @@ -536,6 +547,7 @@ export class AlphaRouter
tokenPropertiesProvider,
portionProvider,
v2Supported,
v4Supported,
}: AlphaRouterParams) {
this.chainId = chainId;
this.provider = provider;
Expand Down Expand Up @@ -961,6 +973,7 @@ export class AlphaRouter
);

this.v2Supported = v2Supported ?? V2_SUPPORTED;
this.v4Supported = v4Supported ?? V4_SUPPORTED;
}

public async routeToRatio(
Expand Down Expand Up @@ -1438,6 +1451,7 @@ export class AlphaRouter
tradeType,
routingConfig,
v3GasModel,
v4GasModel,
mixedRouteGasModel,
gasPriceWei,
v2GasModel,
Expand Down Expand Up @@ -1978,6 +1992,7 @@ export class AlphaRouter
this.portionProvider,
v2GasModel,
v3GasModel,
v4GasModel,
swapConfig,
providerConfig
);
Expand All @@ -1992,6 +2007,7 @@ export class AlphaRouter
tradeType: TradeType,
routingConfig: AlphaRouterConfig,
v3GasModel: IGasModel<V3RouteWithValidQuote>,
v4GasModel: IGasModel<V4RouteWithValidQuote>,
mixedRouteGasModel: IGasModel<MixedRouteWithValidQuote>,
gasPriceWei: BigNumber,
v2GasModel?: IGasModel<V2RouteWithValidQuote>,
Expand Down Expand Up @@ -2027,6 +2043,7 @@ export class AlphaRouter
const v3ProtocolSpecified = protocols.includes(Protocol.V3);
const v2ProtocolSpecified = protocols.includes(Protocol.V2);
const v2SupportedInChain = this.v2Supported?.includes(this.chainId);
const v4SupportedInChain = this.v4Supported?.includes(this.chainId);
const shouldQueryMixedProtocol =
protocols.includes(Protocol.MIXED) ||
(noProtocolsSpecified && v2SupportedInChain);
Expand All @@ -2040,7 +2057,7 @@ export class AlphaRouter
Promise.resolve(undefined);

// we are explicitly requiring people to specify v4 for now
if (v4ProtocolSpecified) {
if (v4ProtocolSpecified && v4SupportedInChain) {
// if (v4ProtocolSpecified || noProtocolsSpecified) {
v4CandidatePoolsPromise = getV4CandidatePools({
tokenIn,
Expand Down Expand Up @@ -2123,7 +2140,7 @@ export class AlphaRouter
const quotePromises: Promise<GetQuotesResult>[] = [];

// for v4, for now we explicitly require people to specify
if (v4ProtocolSpecified) {
if (v4SupportedInChain && v4ProtocolSpecified) {
log.info({ protocols, tradeType }, 'Routing across V4');

metric.putMetric(
Expand Down Expand Up @@ -2325,6 +2342,7 @@ export class AlphaRouter
this.portionProvider,
v2GasModel,
v3GasModel,
v4GasModel,
swapConfig,
providerConfig
);
Expand Down Expand Up @@ -2498,12 +2516,13 @@ export class AlphaRouter
providerConfig: providerConfig,
});

const [v2GasModel, v3GasModel, V4GasModel, mixedRouteGasModel] = await Promise.all([
v2GasModelPromise,
v3GasModelPromise,
v4GasModelPromise,
mixedRouteGasModelPromise,
]);
const [v2GasModel, v3GasModel, V4GasModel, mixedRouteGasModel] =
await Promise.all([
v2GasModelPromise,
v3GasModelPromise,
v4GasModelPromise,
mixedRouteGasModelPromise,
]);

metric.putMetric(
'GasModelCreation',
Expand Down
Loading

0 comments on commit fc5f8a1

Please sign in to comment.