Skip to content

Commit

Permalink
feat(router-sdk): support mixed route with v4 route (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsy1218 authored Sep 13, 2024
1 parent 45d05b8 commit 0e336db
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 35 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@

# dependencies
node_modules

# Intellij IDEA artifacts
.idea/
13 changes: 11 additions & 2 deletions sdks/router-sdk/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ export const ADDRESS_THIS = '0x0000000000000000000000000000000000000002'
export const ZERO = JSBI.BigInt(0)
export const ONE = JSBI.BigInt(1)

// = 1 << 23 or 100000000000000000000000
export const V2_FEE_PATH_PLACEHOLDER = 8388608
// = 1 << 23 or 0b0100000000000000000000000
export const MIXED_QUOTER_V1_V2_FEE_PATH_PLACEHOLDER = 1 << 23

// = 10 << 4 or 0b00100000
export const MIXED_QUOTER_V2_V2_FEE_PATH_PLACEHOLDER = 2 << 4

// = 11 << 20 or 0b001100000000000000000000
export const MIXED_QUOTER_V2_V3_FEE_PATH_PLACEHOLDER = 3 << 20

// = 100 << 20 or 0b010000000000000000000000
export const MIXED_QUOTER_V2_V4_FEE_PATH_PLACEHOLDER = 4 << 20

export const ZERO_PERCENT = new Percent(ZERO)
export const ONE_HUNDRED_PERCENT = new Percent(100, 100)
37 changes: 33 additions & 4 deletions sdks/router-sdk/src/utils/encodeMixedRouteToPath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ describe('#encodeMixedRouteToPath', () => {
const pool_V3_1_weth = new V3Pool(token1, weth, FeeAmount.MEDIUM, encodeSqrtRatioX96(1, 1), 0, 0, [])

const pool_V4_0_1 = new V4Pool(token0, token1, FeeAmount.MEDIUM, 30, ADDRESS_ZERO, encodeSqrtRatioX96(1, 1), 0, 0, [])
const pool_V4_0_eth = new V4Pool(
token0,
ETHER,
FeeAmount.MEDIUM,
30,
ADDRESS_ZERO,
encodeSqrtRatioX96(1, 1),
0,
0,
[]
)

const pair_0_1 = new Pair(CurrencyAmount.fromRawAmount(token0, '100'), CurrencyAmount.fromRawAmount(token1, '200'))
const pair_1_2 = new Pair(CurrencyAmount.fromRawAmount(token1, '150'), CurrencyAmount.fromRawAmount(token2, '150'))
Expand Down Expand Up @@ -47,6 +58,8 @@ describe('#encodeMixedRouteToPath', () => {
const route_0_V3_weth_V2_1_V2_2 = new MixedRouteSDK([pool_V3_0_weth, pair_1_weth, pair_1_2], token0, token2)
const route_0_V3_1_v3_weth_V2_2 = new MixedRouteSDK([pool_V3_0_1_medium, pool_V3_1_weth, pair_2_weth], token0, token2)
const route_0_V3_weth_V4_1 = new MixedRouteSDK([pool_V3_0_weth, pool_V4_0_1], ETHER, token1)
const route_eth_V4_0_V3_1 = new MixedRouteSDK([pool_V4_0_eth, pool_V3_0_1_medium], ETHER, token1)
const route_eth_V3_0_V4_1 = new MixedRouteSDK([pool_V3_0_weth, pool_V4_0_1], ETHER, token1)

describe('pure V3', () => {
it('packs them for exact input single hop', () => {
Expand Down Expand Up @@ -87,8 +100,10 @@ describe('#encodeMixedRouteToPath', () => {
})

describe('pure v4', () => {
it('throws if MixedRouteSDK is a pure v4 route', () => {
expect(() => encodeMixedRouteToPath(route_0_V4_1)).toThrow('Encoding mixed routes with V4 not supported')
it('packs them for exact input single hop', () => {
expect(encodeMixedRouteToPath(route_0_V4_1)).toEqual(
'0x0000000000000000000000000000000000000001400bb800001e00000000000000000000000000000000000000000000000000000000000000000000000000000002'
)
})
})

Expand Down Expand Up @@ -149,8 +164,22 @@ describe('#encodeMixedRouteToPath', () => {
)
})

it('throws if it contains a v4 pool', () => {
expect(() => encodeMixedRouteToPath(route_0_V3_weth_V4_1)).toThrow('Encoding mixed routes with V4 not supported')
it('packs them for exact input v3 -> v4', () => {
expect(encodeMixedRouteToPath(route_0_V3_weth_V4_1)).toEqual(
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2300bb80000000000000000000000000000000000000001400bb800001e00000000000000000000000000000000000000000000000000000000000000000000000000000002'
)
})

it('packs them for exact input native eth v4 -> v3', () => {
expect(encodeMixedRouteToPath(route_eth_V4_0_V3_1)).toEqual(
'0x0000000000000000000000000000000000000000400bb800001e00000000000000000000000000000000000000000000000000000000000000000000000000000001300bb80000000000000000000000000000000000000002'
)
})

it('packs them for exact input native eth v3 -> v4', () => {
expect(encodeMixedRouteToPath(route_eth_V3_0_V4_1)).toEqual(
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2300bb80000000000000000000000000000000000000001400bb800001e00000000000000000000000000000000000000000000000000000000000000000000000000000002'
)
})
})
})
110 changes: 81 additions & 29 deletions sdks/router-sdk/src/utils/encodeMixedRouteToPath.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { pack } from '@ethersproject/solidity'
import { Currency } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { Pool as V3Pool } from '@uniswap/v3-sdk'
import { Pool as V4Pool } from '@uniswap/v4-sdk'
import {
ADDRESS_ZERO,
MIXED_QUOTER_V2_V2_FEE_PATH_PLACEHOLDER,
MIXED_QUOTER_V2_V3_FEE_PATH_PLACEHOLDER,
MIXED_QUOTER_V2_V4_FEE_PATH_PLACEHOLDER,
MIXED_QUOTER_V1_V2_FEE_PATH_PLACEHOLDER,
} from '../constants'
import { MixedRouteSDK } from '../entities/mixedRoute/route'
import { V2_FEE_PATH_PLACEHOLDER } from '../constants'
import { TPool } from './TPool'

/**
Expand All @@ -13,36 +20,81 @@ import { TPool } from './TPool'
* @returns the exactIn encoded path
*/
export function encodeMixedRouteToPath(route: MixedRouteSDK<Currency, Currency>): string {
const firstInputToken: Currency = route.input.wrapped

const { path, types } = route.pools.reduce(
(
{ inputToken, path, types }: { inputToken: Currency; path: (string | number)[]; types: string[] },
pool: TPool,
index
): { inputToken: Currency; path: (string | number)[]; types: string[] } => {
if (pool instanceof V4Pool) throw 'Encoding mixed routes with V4 not supported'
const outputToken: Currency = pool.token0.equals(inputToken) ? pool.token1 : pool.token0
if (index === 0) {
return {
inputToken: outputToken,
types: ['address', 'uint24', 'address'],
path: [
inputToken.wrapped.address,
pool instanceof V3Pool ? pool.fee : V2_FEE_PATH_PLACEHOLDER,
outputToken.wrapped.address,
],
}
const containsV4Pool = route.pools.some((pool) => pool instanceof V4Pool)

let path: (string | number)[]
let types: string[]

if (containsV4Pool) {
path = [route.adjustedInput.isNative ? ADDRESS_ZERO : route.adjustedInput.address]
types = ['address']
let currencyIn = route.adjustedInput

for (const pool of route.pools) {
const currencyOut = currencyIn.equals(pool.token0) ? pool.token1 : pool.token0

if (pool instanceof V4Pool) {
const v4Fee = pool.fee + MIXED_QUOTER_V2_V4_FEE_PATH_PLACEHOLDER
path.push(
v4Fee,
pool.tickSpacing,
pool.hooks,
currencyOut.isNative ? ADDRESS_ZERO : currencyOut.wrapped.address
)
types.push('uint24', 'uint24', 'address', 'address')
} else if (pool instanceof V3Pool) {
const v3Fee = pool.fee + MIXED_QUOTER_V2_V3_FEE_PATH_PLACEHOLDER
path.push(v3Fee, currencyOut.wrapped.address)
types.push('uint24', 'address')
} else if (pool instanceof Pair) {
const v2Fee = MIXED_QUOTER_V2_V2_FEE_PATH_PLACEHOLDER
path.push(v2Fee, currencyOut.wrapped.address)
types.push('uint8', 'address')
} else {
return {
inputToken: outputToken,
types: [...types, 'uint24', 'address'],
path: [...path, pool instanceof V3Pool ? pool.fee : V2_FEE_PATH_PLACEHOLDER, outputToken.wrapped.address],
}
throw new Error(`Unsupported pool type ${JSON.stringify(pool)}`)
}
},
{ inputToken: firstInputToken, path: [], types: [] }
)

currencyIn = currencyOut
}
} else {
// TODO: ROUTE-276 - delete this else block
// We introduced this else block as a safety measure to prevent non-v4 mixed routes from potentially regressing
// We'd like to gain more confidence in the new implementation before removing this block
const result = route.pools.reduce(
(
{ inputToken, path, types }: { inputToken: Currency; path: (string | number)[]; types: string[] },
pool: TPool,
index
): { inputToken: Currency; path: (string | number)[]; types: string[] } => {
const outputToken: Currency = pool.token0.equals(inputToken) ? pool.token1 : pool.token0
if (index === 0) {
return {
inputToken: outputToken,
types: ['address', 'uint24', 'address'],
path: [
inputToken.wrapped.address,
pool instanceof V3Pool ? pool.fee : MIXED_QUOTER_V1_V2_FEE_PATH_PLACEHOLDER,
outputToken.wrapped.address,
],
}
} else {
return {
inputToken: outputToken,
types: [...types, 'uint24', 'address'],
path: [
...path,
pool instanceof V3Pool ? pool.fee : MIXED_QUOTER_V1_V2_FEE_PATH_PLACEHOLDER,
outputToken.wrapped.address,
],
}
}
},
{ inputToken: route.input, path: [], types: [] }
)

path = result.path
types = result.types
}

return pack(types, path)
}

0 comments on commit 0e336db

Please sign in to comment.