diff --git a/.changeset/seven-fishes-relax.md b/.changeset/seven-fishes-relax.md new file mode 100644 index 0000000000000..d7a7a146f103b --- /dev/null +++ b/.changeset/seven-fishes-relax.md @@ -0,0 +1,5 @@ +--- +'@pancakeswap/routing-sdk': patch +--- + +Add support for native token pools diff --git a/packages/routing-sdk/src/findBestTrade.ts b/packages/routing-sdk/src/findBestTrade.ts index 7a3637de72376..8993f20504ad3 100644 --- a/packages/routing-sdk/src/findBestTrade.ts +++ b/packages/routing-sdk/src/findBestTrade.ts @@ -1,5 +1,5 @@ import { formatFraction } from '@pancakeswap/utils/formatFractions' -import { Currency, CurrencyAmount, Fraction, TradeType } from '@pancakeswap/swap-sdk-core' +import { Token, Currency, CurrencyAmount, Fraction, TradeType } from '@pancakeswap/swap-sdk-core' import invariant from 'tiny-invariant' import { PriceCalculator, createGraph, createPriceCalculator, getNeighbour } from './graph' @@ -190,15 +190,15 @@ async function getBestTrade({ { hops: number gasSpent?: bigint - bestAmount: CurrencyAmount - bestQuote?: CurrencyAmount + bestAmount: CurrencyAmount + bestQuote?: CurrencyAmount bestSource?: Edge } >() const processedVert = new Set() bestResult.set(start, { hops: 0, - bestAmount: amount, + bestAmount: amount.wrapped, gasSpent: 0n, }) const nextVertList: Vertice[] = [start] @@ -209,7 +209,7 @@ async function getBestTrade({ const getGasSpent = (vert: Vertice) => bestResult.get(vert)?.gasSpent || 0n const getNextVert = () => { let vert: Vertice | undefined - let bestQuote: CurrencyAmount | undefined + let bestQuote: CurrencyAmount | undefined let bestVertIndex: number | undefined for (const [i, vertice] of nextVertList.entries()) { const currentBestQuote = getBestQuote(vertice) @@ -227,17 +227,18 @@ async function getBestTrade({ } const getBestRoute = (vert: Vertice) => { const pools: Pool[] = [] - const path: Currency[] = [vert.currency] + const path: Currency[] = [quoteCurrency] for (let v: Vertice | undefined = finish; getBestSource(v); v = getNeighbour(getBestSource(v)!, v)) { const bestSource = getBestSource(v) invariant(bestSource !== undefined, 'Invalid best source') const neighbour = getNeighbour(bestSource, v) + const nextCurrency = neighbour === start ? baseCurrency : neighbour.currency if (isExactIn) { pools.unshift(bestSource.pool) - path.unshift(neighbour?.currency) + path.unshift(nextCurrency) } else { pools.push(bestSource.pool) - path.push(neighbour?.currency) + path.push(nextCurrency) } } @@ -319,7 +320,7 @@ async function getBestTrade({ const quoteResult = e.pool.getQuote({ amount: bestAmount, isExactIn, - quoteCurrency: e.vertice0.currency.wrapped.equals(bestAmount.currency.wrapped) + quoteCurrency: e.vertice0.currency.equals(bestAmount.currency.wrapped) ? e.vertice1.currency : e.vertice0.currency, }) @@ -336,7 +337,7 @@ async function getBestTrade({ `Failed to get price, base ${v2.currency.symbol}, quote ${finish.currency.symbol}`, ) const gasSpentInQuote = price.quote(gasPriceInV2.multiply(gasSpent)) - const newQuote = adjustQuoteByGas(price.quote(quote), gasSpentInQuote) + const newQuote = adjustQuoteByGas(price.quote(quote.wrapped), gasSpentInQuote) // const newQuote = price.quote(quote); const bestSource = getBestSource(v2) const v2BestQuote = getBestQuote(v2) @@ -352,9 +353,9 @@ async function getBestTrade({ bestResult.set(v2, { hops: currentHop + 1, gasSpent, - bestAmount: quote, + bestAmount: quote.wrapped, bestSource: e, - bestQuote: newQuote, + bestQuote: newQuote.wrapped, }) } } catch (_err) { diff --git a/packages/routing-sdk/src/graph/edge.ts b/packages/routing-sdk/src/graph/edge.ts index 34bab5265deb3..84f1d3f19bcf4 100644 --- a/packages/routing-sdk/src/graph/edge.ts +++ b/packages/routing-sdk/src/graph/edge.ts @@ -1,10 +1,11 @@ import type { Edge, Pool, Vertice } from '../types' +import { getVerticeKey } from './vertice' export function getNeighbour(e: Edge, v: Vertice): Vertice { return e.vertice0.currency.equals(v.currency) ? e.vertice1 : e.vertice0 } export function getEdgeKey(p: Pool, vertA: Vertice, vertB: Vertice): string { - const [vert0, vert1] = vertA.currency.wrapped.sortsBefore(vertB.currency.wrapped) ? [vertA, vertB] : [vertB, vertA] - return `${vert0.currency.chainId}-${vert0.currency.wrapped.address}-${vert1.currency.wrapped.address}-${p.getId()}` + const [vert0, vert1] = vertA.currency.sortsBefore(vertB.currency) ? [vertA, vertB] : [vertB, vertA] + return `${getVerticeKey(vert0)}-${getVerticeKey(vert1)}-${p.getId()}` } diff --git a/packages/routing-sdk/src/graph/graph.ts b/packages/routing-sdk/src/graph/graph.ts index b6db4cb436694..64467cc8ed5eb 100644 --- a/packages/routing-sdk/src/graph/graph.ts +++ b/packages/routing-sdk/src/graph/graph.ts @@ -2,8 +2,9 @@ import { Currency } from '@pancakeswap/swap-sdk-core' import memoize from 'lodash/memoize.js' import invariant from 'tiny-invariant' -import { Address, Pool, Edge, Vertice, Graph } from '../types' -import { getNeighbour } from './edge' +import { Pool, Edge, Vertice, Graph } from '../types' +import { getEdgeKey, getNeighbour } from './edge' +import { getVerticeKey, getWrappedCurrencyKey } from './vertice' type GraphParams = { pools?: Pool[] @@ -12,11 +13,6 @@ type GraphParams = { graph?: Graph } -function getEdgeKey(p: Pool, vertA: Vertice, vertB: Vertice): string { - const [vert0, vert1] = vertA.currency.wrapped.sortsBefore(vertB.currency.wrapped) ? [vertA, vertB] : [vertB, vertA] - return `${vert0.currency.chainId}-${vert0.currency.wrapped.address}-${vert1.currency.wrapped.address}-${p.getId()}` -} - function cloneGraph(graph: Graph): Graph { const pools = graph.edges.map((e) => e.pool) return createGraph({ pools }) @@ -31,7 +27,7 @@ export function createGraph({ pools, graph }: GraphParams): Graph { throw new Error('[Create graph]: Invalid pools') } - const verticeMap = new Map() + const verticeMap = new Map() const edgeMap = new Map() for (const p of pools) { @@ -46,7 +42,7 @@ export function createGraph({ pools, graph }: GraphParams): Graph { } function getVertice(c: Currency): Vertice | undefined { - return verticeMap.get(c.wrapped.address) + return verticeMap.get(getWrappedCurrencyKey(c)) } function getEdge(p: Pool, vert0: Vertice, vert1: Vertice): Edge | undefined { @@ -58,8 +54,8 @@ export function createGraph({ pools, graph }: GraphParams): Graph { if (vert) { return vert } - const vertice: Vertice = { currency: c, edges: [] } - verticeMap.set(c.wrapped.address, vertice) + const vertice: Vertice = { currency: c.wrapped, edges: [] } + verticeMap.set(getWrappedCurrencyKey(c), vertice) return vertice } @@ -101,7 +97,7 @@ export function createGraph({ pools, graph }: GraphParams): Graph { } return false }, - (v1, v2, hops) => `${v1.currency.chainId}-${v1.currency.wrapped.address}-${v2.currency.wrapped.address}-${hops}`, + (v1, v2, hops) => `${getVerticeKey(v1)}-${getVerticeKey(v2)}-${hops}`, ) return { diff --git a/packages/routing-sdk/src/graph/index.ts b/packages/routing-sdk/src/graph/index.ts index 49faace84e03c..7696e0d3fed3a 100644 --- a/packages/routing-sdk/src/graph/index.ts +++ b/packages/routing-sdk/src/graph/index.ts @@ -1,3 +1,4 @@ export * from './edge' export * from './graph' export * from './priceCalculator' +export * from './vertice' diff --git a/packages/routing-sdk/src/graph/priceCalculator.ts b/packages/routing-sdk/src/graph/priceCalculator.ts index 9fa55a68154ee..55cf1d065af6d 100644 --- a/packages/routing-sdk/src/graph/priceCalculator.ts +++ b/packages/routing-sdk/src/graph/priceCalculator.ts @@ -1,4 +1,4 @@ -import { type Currency, CurrencyAmount, Price } from '@pancakeswap/swap-sdk-core' +import { type Token, CurrencyAmount, Price } from '@pancakeswap/swap-sdk-core' import { Native } from '@pancakeswap/swap-sdk-evm' import invariant from 'tiny-invariant' @@ -16,7 +16,7 @@ export type PriceCalculator = ReturnType // Get the price reference of all tokens in the graph against the specified vertice export function createPriceCalculator({ graph, quote, gasPriceWei }: Params) { const { chainId } = quote.currency - const priceMap = new Map>() + const priceMap = new Map>() const priceSources = new Map() const processedVert = new Set() const edgeWeight = new Map() @@ -43,7 +43,7 @@ export function createPriceCalculator({ graph, quote, gasPriceWei }: Params) { const p = bestEdge.pool.getCurrentPrice(vTo.currency, vFrom.currency) const vFromQuotePrice = getQuotePrice(vFrom) invariant(vFromQuotePrice !== undefined, 'Invalid quote price') - priceMap.set(vTo, p.multiply(vFromQuotePrice)) + priceMap.set(vTo, p.wrapped.multiply(vFromQuotePrice)) priceSources.set(vTo, [...(priceSources.get(vFrom) || []), bestEdge]) nextEdges = getNextEdges(vTo) processedVert.add(vTo) @@ -53,13 +53,13 @@ export function createPriceCalculator({ graph, quote, gasPriceWei }: Params) { // ) } - const native = Native.onChain(chainId).wrapped - const nativeVertice = graph.getVertice(native) + const wnative = Native.onChain(chainId).wrapped + const nativeVertice = graph.getVertice(wnative) if (!nativeVertice) { throw new Error('No valid native currency price found') } const nativePriceInQuote = getQuotePrice(nativeVertice) - const gasPriceInQuote = nativePriceInQuote?.quote(CurrencyAmount.fromRawAmount(native, gasPriceWei)) + const gasPriceInQuote = nativePriceInQuote?.quote(CurrencyAmount.fromRawAmount(wnative, gasPriceWei)) if (!gasPriceInQuote) { throw new Error('Failed to get gas price in quote') } @@ -75,7 +75,7 @@ export function createPriceCalculator({ graph, quote, gasPriceWei }: Params) { } const tokenReserve = e.pool.getReserve(v.currency) invariant(tokenReserve !== undefined, 'Unexpected empty token reserve') - const liquidity = price.quote(tokenReserve).quotient + const liquidity = price.quote(tokenReserve.wrapped).quotient edgeWeight.set(e, liquidity) return true }) @@ -91,17 +91,17 @@ export function createPriceCalculator({ graph, quote, gasPriceWei }: Params) { return [...res, ...nextEdges, ...newEdges] } - function getPrice(base: Vertice, targetQuote: Vertice): Price | undefined { + function getPrice(base: Vertice, targetQuote: Vertice): Price | undefined { const basePrice = getQuotePrice(base) const quotePrice = getQuotePrice(targetQuote) return basePrice && quotePrice ? basePrice.multiply(quotePrice.invert()) : undefined } - function getQuotePrice(base: Vertice): Price | undefined { - return priceMap.get(base) + function getQuotePrice(base: Vertice): Price | undefined { + return priceMap.get(base)?.wrapped } - function getGasPriceInBase(base: Vertice): CurrencyAmount | undefined { + function getGasPriceInBase(base: Vertice): CurrencyAmount | undefined { const basePrice = getQuotePrice(base) return gasPriceInQuote ? basePrice?.invert().quote(gasPriceInQuote) : undefined } diff --git a/packages/routing-sdk/src/graph/vertice.ts b/packages/routing-sdk/src/graph/vertice.ts new file mode 100644 index 0000000000000..34cd6aa15dbc2 --- /dev/null +++ b/packages/routing-sdk/src/graph/vertice.ts @@ -0,0 +1,11 @@ +import { Currency, getCurrencyAddress } from '@pancakeswap/swap-sdk-core' + +import { Vertice } from '../types' + +export function getWrappedCurrencyKey(c: Currency) { + return `${c.chainId}-${getCurrencyAddress(c.wrapped)}` +} + +export function getVerticeKey(vertice: Vertice) { + return getWrappedCurrencyKey(vertice.currency) +} diff --git a/packages/routing-sdk/src/index.test.ts b/packages/routing-sdk/src/index.test.ts index ec6a89f61ffcd..52a62d0f5947d 100644 --- a/packages/routing-sdk/src/index.test.ts +++ b/packages/routing-sdk/src/index.test.ts @@ -8,6 +8,8 @@ test('exports', () => { "getEdgeKey", "createGraph", "createPriceCalculator", + "getWrappedCurrencyKey", + "getVerticeKey", "findBestTrade", "findBestTradeByStreams", "isSameRoute", diff --git a/packages/routing-sdk/src/types/graph.ts b/packages/routing-sdk/src/types/graph.ts index eccbf5a815493..976ff490ad0d3 100644 --- a/packages/routing-sdk/src/types/graph.ts +++ b/packages/routing-sdk/src/types/graph.ts @@ -1,4 +1,4 @@ -import type { Currency, CurrencyAmount } from '@pancakeswap/swap-sdk-core' +import type { Currency, Token, CurrencyAmount } from '@pancakeswap/swap-sdk-core' import type { Pool } from './pool' import type { Route } from './route' @@ -10,7 +10,7 @@ export type Edge = { } export type Vertice = { - currency: Currency + currency: Token edges: Edge[] }