Skip to content

Commit

Permalink
feat: Routing sdk add support for native token pools (#10882)
Browse files Browse the repository at this point in the history
<!--
Before opening a pull request, please read the [contributing
guidelines](https://github.com/pancakeswap/pancake-frontend/blob/develop/CONTRIBUTING.md)
first
-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces support for native token pools in the
`@pancakeswap/routing-sdk`. It includes updates to types, functions, and
data handling to accommodate the new native token structure in the
routing logic.

### Detailed summary
- Added support for native token pools.
- Updated `Vertice` type to use `Token` instead of `Currency`.
- Introduced `getWrappedCurrencyKey` and `getVerticeKey` functions.
- Modified `getEdgeKey` to utilize the new key functions.
- Adjusted various functions to handle `Token` instead of `Currency`.
- Updated price calculations to work with wrapped tokens.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your
question}`

<!-- end pr-codex -->
  • Loading branch information
chefjackson authored Oct 29, 2024
1 parent 176eb10 commit ffa96b3
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/seven-fishes-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pancakeswap/routing-sdk': patch
---

Add support for native token pools
25 changes: 13 additions & 12 deletions packages/routing-sdk/src/findBestTrade.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -190,15 +190,15 @@ async function getBestTrade({
{
hops: number
gasSpent?: bigint
bestAmount: CurrencyAmount<Currency>
bestQuote?: CurrencyAmount<Currency>
bestAmount: CurrencyAmount<Token>
bestQuote?: CurrencyAmount<Token>
bestSource?: Edge
}
>()
const processedVert = new Set<Vertice>()
bestResult.set(start, {
hops: 0,
bestAmount: amount,
bestAmount: amount.wrapped,
gasSpent: 0n,
})
const nextVertList: Vertice[] = [start]
Expand All @@ -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<Currency> | undefined
let bestQuote: CurrencyAmount<Token> | undefined
let bestVertIndex: number | undefined
for (const [i, vertice] of nextVertList.entries()) {
const currentBestQuote = getBestQuote(vertice)
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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,
})
Expand All @@ -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)
Expand All @@ -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) {
Expand Down
5 changes: 3 additions & 2 deletions packages/routing-sdk/src/graph/edge.ts
Original file line number Diff line number Diff line change
@@ -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()}`
}
20 changes: 8 additions & 12 deletions packages/routing-sdk/src/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand All @@ -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 })
Expand All @@ -31,7 +27,7 @@ export function createGraph({ pools, graph }: GraphParams): Graph {
throw new Error('[Create graph]: Invalid pools')
}

const verticeMap = new Map<Address, Vertice>()
const verticeMap = new Map<string, Vertice>()
const edgeMap = new Map<string, Edge>()

for (const p of pools) {
Expand All @@ -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 {
Expand All @@ -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
}

Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions packages/routing-sdk/src/graph/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './edge'
export * from './graph'
export * from './priceCalculator'
export * from './vertice'
22 changes: 11 additions & 11 deletions packages/routing-sdk/src/graph/priceCalculator.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -16,7 +16,7 @@ export type PriceCalculator = ReturnType<typeof createPriceCalculator>
// 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<Vertice, Price<Currency, Currency>>()
const priceMap = new Map<Vertice, Price<Token, Token>>()
const priceSources = new Map<Vertice, Edge[]>()
const processedVert = new Set<Vertice>()
const edgeWeight = new Map<Edge, bigint>()
Expand All @@ -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)
Expand All @@ -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')
}
Expand All @@ -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
})
Expand All @@ -91,17 +91,17 @@ export function createPriceCalculator({ graph, quote, gasPriceWei }: Params) {
return [...res, ...nextEdges, ...newEdges]
}

function getPrice(base: Vertice, targetQuote: Vertice): Price<Currency, Currency> | undefined {
function getPrice(base: Vertice, targetQuote: Vertice): Price<Token, Token> | undefined {
const basePrice = getQuotePrice(base)
const quotePrice = getQuotePrice(targetQuote)
return basePrice && quotePrice ? basePrice.multiply(quotePrice.invert()) : undefined
}

function getQuotePrice(base: Vertice): Price<Currency, Currency> | undefined {
return priceMap.get(base)
function getQuotePrice(base: Vertice): Price<Token, Token> | undefined {
return priceMap.get(base)?.wrapped
}

function getGasPriceInBase(base: Vertice): CurrencyAmount<Currency> | undefined {
function getGasPriceInBase(base: Vertice): CurrencyAmount<Token> | undefined {
const basePrice = getQuotePrice(base)
return gasPriceInQuote ? basePrice?.invert().quote(gasPriceInQuote) : undefined
}
Expand Down
11 changes: 11 additions & 0 deletions packages/routing-sdk/src/graph/vertice.ts
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 2 additions & 0 deletions packages/routing-sdk/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ test('exports', () => {
"getEdgeKey",
"createGraph",
"createPriceCalculator",
"getWrappedCurrencyKey",
"getVerticeKey",
"findBestTrade",
"findBestTradeByStreams",
"isSameRoute",
Expand Down
4 changes: 2 additions & 2 deletions packages/routing-sdk/src/types/graph.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -10,7 +10,7 @@ export type Edge = {
}

export type Vertice = {
currency: Currency
currency: Token
edges: Edge[]
}

Expand Down

0 comments on commit ffa96b3

Please sign in to comment.