Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 4 additions & 0 deletions sdks/v4-sdk/src/utils/v4BaseActionsParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ describe('Command Parser', () => {
{
currencyIn: DAI.address,
path: encodeRouteToPath(new Route([DAI_USDC, USDC_WETH], DAI, WETH9[1])),
maxHopSlippage: [],
amountIn: amount,
amountOutMinimum: amount,
},
Expand Down Expand Up @@ -237,6 +238,7 @@ describe('Command Parser', () => {
hookData: '0x',
},
],
maxHopSlippage: [],
amountIn: amount,
amountOutMinimum: amount,
},
Expand All @@ -251,6 +253,7 @@ describe('Command Parser', () => {
{
currencyOut: DAI.address,
path: encodeRouteToPath(new Route([DAI_USDC, USDC_WETH], DAI, WETH9[1])),
maxHopSlippage: [],
amountOut: amount,
amountInMaximum: amount,
},
Expand Down Expand Up @@ -281,6 +284,7 @@ describe('Command Parser', () => {
hookData: '0x',
},
],
maxHopSlippage: [],
amountOut: amount,
amountInMaximum: amount,
},
Expand Down
8 changes: 6 additions & 2 deletions sdks/v4-sdk/src/utils/v4BaseActionsParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type SwapExactInSingle = {
export type SwapExactIn = {
readonly currencyIn: string
readonly path: readonly PathKey[]
readonly maxHopSlippage: readonly string[]
readonly amountIn: string
readonly amountOutMinimum: string
}
Expand All @@ -44,6 +45,7 @@ export type SwapExactOutSingle = {
export type SwapExactOut = {
readonly currencyOut: string
readonly path: readonly PathKey[]
readonly maxHopSlippage: readonly string[]
readonly amountOut: string
readonly amountInMaximum: string
}
Expand Down Expand Up @@ -162,12 +164,13 @@ function parseV4ExactInSingle(data: any[]): SwapExactInSingle {
}

function parseV4ExactIn(data: any[]): SwapExactIn {
const [currencyIn, path, amountIn, amountOutMinimum] = data
const [currencyIn, path, maxHopSlippage, amountIn, amountOutMinimum] = data
const paths: readonly PathKey[] = path.map((pathKey: string) => parsePathKey(pathKey))

return {
path: paths,
currencyIn,
maxHopSlippage,
amountIn,
amountOutMinimum,
}
Expand All @@ -193,12 +196,13 @@ function parseV4ExactOutSingle(data: any[]): SwapExactOutSingle {
}

function parseV4ExactOut(data: any[]): SwapExactOut {
const [currencyOut, path, amountOut, amountInMaximum] = data
const [currencyOut, path, maxHopSlippage, amountOut, amountInMaximum] = data
const paths: readonly PathKey[] = path.map((pathKey: string) => parsePathKey(pathKey))

return {
path: paths,
currencyOut,
maxHopSlippage,
amountOut,
amountInMaximum,
}
Expand Down
121 changes: 115 additions & 6 deletions sdks/v4-sdk/src/utils/v4Planner.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BigNumber } from 'ethers'
import { BigNumber, utils } from 'ethers'
import JSBI from 'jsbi'
import { CurrencyAmount, Ether, Percent, TradeType, Token, WETH9 } from '@uniswap/sdk-core'
import { encodeSqrtRatioX96, nearestUsableTick, TickMath } from '@uniswap/v3-sdk'
Expand All @@ -8,7 +8,9 @@ import { Route } from '../entities/route'
import { encodeRouteToPath } from './encodeRouteToPath'
import { ADDRESS_ZERO, FEE_AMOUNT_MEDIUM, TICK_SPACING_TEN, ONE_ETHER, NEGATIVE_ONE } from '../internalConstants'

import { Actions, V4Planner } from './v4Planner'
import { Actions, V4Planner, V4_BASE_ACTIONS_ABI_DEFINITION } from './v4Planner'

const { defaultAbiCoder } = utils

const ONE_ETHER_BN = BigNumber.from(1).mul(10).pow(18)
const TICKLIST = [
Expand Down Expand Up @@ -96,6 +98,7 @@ describe('RouterPlanner', () => {
{
currencyIn: DAI.address,
path: encodeRouteToPath(route),
maxHopSlippage: [],
amountIn: ONE_ETHER_BN.toString(),
amountOutMinimum: 0,
},
Expand All @@ -112,7 +115,7 @@ describe('RouterPlanner', () => {

expect(planner.actions).toEqual('0x07')
expect(planner.params[0]).toEqual(
'0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000'
'0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
)

expect(planner.actions).toEqual(tradePlanner.actions)
Expand All @@ -131,7 +134,7 @@ describe('RouterPlanner', () => {

expect(planner.actions).toEqual('0x09')
expect(planner.params[0]).toEqual(
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000'
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
)
})

Expand All @@ -147,7 +150,7 @@ describe('RouterPlanner', () => {

expect(planner.actions).toEqual('0x09')
expect(planner.params[0]).toEqual(
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000'
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
)
})

Expand All @@ -163,7 +166,7 @@ describe('RouterPlanner', () => {

expect(planner.actions).toEqual('0x07')
expect(planner.params[0]).toEqual(
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000'
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
)
})

Expand Down Expand Up @@ -193,6 +196,112 @@ describe('RouterPlanner', () => {
'Only accepts Trades with 1 swap (must break swaps into individual trades)'
)
})

it('completes a v4 exactIn 2 hop swap with per-hop slippage limits', async () => {
const route = new Route([DAI_USDC, USDC_WETH], DAI, WETH9[1])
const trade = await Trade.fromRoute(
route,
CurrencyAmount.fromRawAmount(DAI, ONE_ETHER.toString()),
TradeType.EXACT_INPUT
)

// Set per-hop slippage limits: 10000 for first hop, 20000 for second hop
const maxHopSlippage = [BigNumber.from('10000'), BigNumber.from('20000')]

planner.addTrade(trade, undefined, maxHopSlippage)

expect(planner.actions).toEqual('0x07')

// Decode the params to verify the maxHopSlippage values
const decoded = defaultAbiCoder.decode(
V4_BASE_ACTIONS_ABI_DEFINITION[Actions.SWAP_EXACT_IN].map((v) => v.type),
planner.params[0]
)

expect(decoded[0].currencyIn).toEqual(DAI.address)
expect(decoded[0].maxHopSlippage).toHaveLength(2)
expect(decoded[0].maxHopSlippage[0].toString()).toEqual('10000')
expect(decoded[0].maxHopSlippage[1].toString()).toEqual('20000')
})

it('completes a v4 exactOut 2 hop swap with per-hop slippage limits', async () => {
const route = new Route([DAI_USDC, USDC_WETH], DAI, WETH9[1])
const slippageTolerance = new Percent('5')
const trade = await Trade.fromRoute(
route,
CurrencyAmount.fromRawAmount(WETH9[1], ONE_ETHER.toString()),
TradeType.EXACT_OUTPUT
)

// Set per-hop slippage limits: 10000 for first hop, 20000 for second hop
const maxHopSlippage = [BigNumber.from('10000'), BigNumber.from('20000')]

planner.addTrade(trade, slippageTolerance, maxHopSlippage)

expect(planner.actions).toEqual('0x09')

// Decode the params to verify the maxHopSlippage values
const decoded = defaultAbiCoder.decode(
V4_BASE_ACTIONS_ABI_DEFINITION[Actions.SWAP_EXACT_OUT].map((v) => v.type),
planner.params[0]
)

expect(decoded[0].currencyOut).toEqual(WETH9[1].address)
expect(decoded[0].maxHopSlippage).toHaveLength(2)
expect(decoded[0].maxHopSlippage[0].toString()).toEqual('10000')
expect(decoded[0].maxHopSlippage[1].toString()).toEqual('20000')
})

it('completes a v4 exactIn 2 hop swap using addAction with per-hop slippage limits', async () => {
const route = new Route([DAI_USDC, USDC_WETH], DAI, WETH9[1])
const maxHopSlippage = [BigNumber.from('10000').toString(), BigNumber.from('20000').toString()]

planner.addAction(Actions.SWAP_EXACT_IN, [
{
currencyIn: DAI.address,
path: encodeRouteToPath(route),
maxHopSlippage: maxHopSlippage,
amountIn: ONE_ETHER_BN.toString(),
amountOutMinimum: 0,
},
])

expect(planner.actions).toEqual('0x07')

// Decode the params to verify the maxHopSlippage values
const decoded = defaultAbiCoder.decode(
V4_BASE_ACTIONS_ABI_DEFINITION[Actions.SWAP_EXACT_IN].map((v) => v.type),
planner.params[0]
)

expect(decoded[0].currencyIn).toEqual(DAI.address)
expect(decoded[0].maxHopSlippage).toHaveLength(2)
expect(decoded[0].maxHopSlippage[0].toString()).toEqual('10000')
expect(decoded[0].maxHopSlippage[1].toString()).toEqual('20000')
expect(decoded[0].amountIn.toString()).toEqual(ONE_ETHER_BN.toString())
})

it('completes a v4 exactIn swap with empty maxHopSlippage when not provided', async () => {
const route = new Route([DAI_USDC, USDC_WETH], DAI, WETH9[1])
const trade = await Trade.fromRoute(
route,
CurrencyAmount.fromRawAmount(DAI, ONE_ETHER.toString()),
TradeType.EXACT_INPUT
)

planner.addTrade(trade)

expect(planner.actions).toEqual('0x07')

// Decode the params to verify the maxHopSlippage is empty array
const decoded = defaultAbiCoder.decode(
V4_BASE_ACTIONS_ABI_DEFINITION[Actions.SWAP_EXACT_IN].map((v) => v.type),
planner.params[0]
)

expect(decoded[0].currencyIn).toEqual(DAI.address)
expect(decoded[0].maxHopSlippage).toHaveLength(0)
})
})

describe('addSettle', () => {
Expand Down
Loading
Loading