Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDK: all quotes, Query modifications #1716

Merged
merged 10 commits into from
Dec 27, 2023
308 changes: 308 additions & 0 deletions packages/sdk-router/src/module/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
reduceToQuery,
narrowToRouterQuery,
narrowToCCTPRouterQuery,
modifyDeadline,
applySlippage,
applySlippageInBips,
} from './query'

describe('#query', () => {
Expand Down Expand Up @@ -72,4 +75,309 @@ describe('#query', () => {
)
})
})

describe('modifyDeadline', () => {
describe('RouterQuery', () => {
it('modifies the deadline', () => {
const query = modifyDeadline(routerQuery, BigNumber.from(42))
expect(query).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(3),
deadline: BigNumber.from(42),
rawParams: '5',
})
})

it('does not modify the original query', () => {
modifyDeadline(routerQuery, BigNumber.from(42))
expect(routerQuery).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(3),
deadline: BigNumber.from(4),
rawParams: '5',
})
})
})

describe('CCTPRouterQuery', () => {
it('modifies the deadline', () => {
const query = modifyDeadline(cctpRouterQuery, BigNumber.from(42))
expect(query).toEqual({
routerAdapter: '6',
tokenOut: '7',
minAmountOut: BigNumber.from(8),
deadline: BigNumber.from(42),
rawParams: '10',
})
})

it('does not modify the original query', () => {
modifyDeadline(cctpRouterQuery, BigNumber.from(42))
expect(cctpRouterQuery).toEqual({
routerAdapter: '6',
tokenOut: '7',
minAmountOut: BigNumber.from(8),
deadline: BigNumber.from(9),
rawParams: '10',
})
})
})
})

describe('applySlippage', () => {
describe('RouterQuery', () => {
// 1M in 18 decimals
const query: RouterQuery = {
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
}

it('applies 0% slippage', () => {
const newQuery = applySlippage(query, 0, 10000)
expect(newQuery).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('applies 0.5% slippage', () => {
// 50 bips
const newQuery = applySlippage(query, 50, 10000)
expect(newQuery).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(995_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('applies 10% slippage', () => {
const newQuery = applySlippage(query, 10, 100)
expect(newQuery).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(900_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('applies 100% slippage', () => {
const newQuery = applySlippage(query, 1, 1)
expect(newQuery).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(0),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('rounds down', () => {
const queryPlusOne = {
...query,
minAmountOut: query.minAmountOut.add(1),
}
const newQuery = applySlippage(queryPlusOne, 50, 10000)
expect(newQuery).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(995_000).add(1),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('does not modify the original query', () => {
applySlippage(query, 50, 10000)
expect(query).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})
})

describe('CCTPRouterQuery', () => {
// 1M in 6 decimals
const query: CCTPRouterQuery = {
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(6).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
}

it('applies 0% slippage', () => {
const newQuery = applySlippage(query, 0, 10000)
expect(newQuery).toEqual({
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(6).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('applies 0.5% slippage', () => {
// 50 bips
const newQuery = applySlippage(query, 50, 10000)
expect(newQuery).toEqual({
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(6).mul(995_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('applies 10% slippage', () => {
const newQuery = applySlippage(query, 10, 100)
expect(newQuery).toEqual({
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(6).mul(900_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('applies 100% slippage', () => {
const newQuery = applySlippage(query, 1, 1)
expect(newQuery).toEqual({
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(0),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('rounds down', () => {
const queryPlusOne = {
...query,
minAmountOut: query.minAmountOut.add(1),
}
const newQuery = applySlippage(queryPlusOne, 50, 10000)
expect(newQuery).toEqual({
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(6).mul(995_000).add(1),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('does not modify the original query', () => {
applySlippage(query, 50, 10000)
expect(query).toEqual({
routerAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(6).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})
})

describe('errors', () => {
it('throws if slippage denominator is zero', () => {
expect(() => applySlippage(routerQuery, 1, 0)).toThrow(
'Slippage denominator cannot be zero'
)
})

it('throws if slippage numerator is negative', () => {
expect(() => applySlippage(routerQuery, -1, 1)).toThrow(
'Slippage numerator cannot be negative'
)
})

it('throws if slippage numerator is greater than denominator', () => {
expect(() => applySlippage(routerQuery, 2, 1)).toThrow(
'Slippage cannot be greater than 1'
)
})
})
})

describe('applySlippageInBips parity', () => {
// 1M in 18 decimals
const query: RouterQuery = {
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
}

it('applies 0% slippage', () => {
const newQuery = applySlippage(query, 0, 10000)
const newQueryInBips = applySlippageInBips(query, 0)
expect(newQuery).toEqual(newQueryInBips)
})

it('applies 0.5% slippage', () => {
// 50 bips
const newQuery = applySlippage(query, 50, 10000)
const newQueryInBips = applySlippageInBips(query, 50)
expect(newQuery).toEqual(newQueryInBips)
})

it('applies 10% slippage', () => {
const newQuery = applySlippage(query, 10, 100)
const newQueryInBips = applySlippageInBips(query, 1000)
expect(newQuery).toEqual(newQueryInBips)
})

it('applies 100% slippage', () => {
const newQuery = applySlippage(query, 1, 1)
const newQueryInBips = applySlippageInBips(query, 10000)
expect(newQuery).toEqual(newQueryInBips)
})

it('rounds down', () => {
const queryPlusOne = {
...query,
minAmountOut: query.minAmountOut.add(1),
}
const newQuery = applySlippage(queryPlusOne, 50, 10000)
const newQueryInBips = applySlippageInBips(queryPlusOne, 50)
expect(newQuery).toEqual(newQueryInBips)
})

it('does not modify the original query', () => {
applySlippageInBips(query, 50)
expect(query).toEqual({
swapAdapter: '1',
tokenOut: '2',
minAmountOut: BigNumber.from(10).pow(18).mul(1_000_000),
deadline: BigNumber.from(4),
rawParams: '5',
})
})

it('throws if basis points are negative', () => {
expect(() => applySlippageInBips(routerQuery, -1)).toThrow(
'Slippage numerator cannot be negative'
)
})

it('throws if basis points are greater than 10000', () => {
expect(() => applySlippageInBips(routerQuery, 10001)).toThrow(
'Slippage cannot be greater than 1'
)
})
})
})
64 changes: 64 additions & 0 deletions packages/sdk-router/src/module/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,67 @@ export const hasComplexBridgeAction = (destQuery: Query): boolean => {
destQuery.tokenOut !== ETH_NATIVE_TOKEN_ADDRESS
)
}

/**
* Modifies the deadline of the query and returns the modified query.
* Note: the original query is preserved unchanged.
*
* @param query - The query to modify.
* @param deadline - The new deadline.
* @returns The modified query with the new deadline.
*/
export const modifyDeadline = (query: Query, deadline: BigNumber): Query => {
return {
...query,
deadline,
}
}

/**
* Applies the slippage to the query's minAmountOut (rounded down), and returns the modified query
* with the reduced minAmountOut.
* Note: the original query is preserved unchanged.
*
* @param query - The query to modify.
* @param slipNumerator - The numerator of the slippage.
* @param slipDenominator - The denominator of the slippage.
* @returns The modified query with the reduced minAmountOut.
* @throws If the slippage fraction is invalid (<0, >1, or NaN)
*/
export const applySlippage = (
query: Query,
slipNumerator: number,
slipDenominator: number
): Query => {
invariant(slipDenominator > 0, 'Slippage denominator cannot be zero')
invariant(slipNumerator >= 0, 'Slippage numerator cannot be negative')
invariant(
slipNumerator <= slipDenominator,
'Slippage cannot be greater than 1'
)
const slippageAmount = query.minAmountOut
.mul(slipNumerator)
.div(slipDenominator)
return {
...query,
minAmountOut: query.minAmountOut.sub(slippageAmount),
}
}

/**
* Applies the slippage (in basis points) to the query's minAmountOut (rounded down), and returns the modified query
* with the reduced minAmountOut.
* Note: the original query is preserved unchanged.
* Note: the slippage is applied as a fraction of 10000, e.g. 100 bips = 1%.
*
* @param query - The query to modify.
* @param slipBasisPoints - The slippage in basis points.
* @returns The modified query with the reduced minAmountOut.
* @throws If the basis points are invalid (<0, >10000)
*/
export const applySlippageInBips = (
query: Query,
slipBasisPoints: number
): Query => {
return applySlippage(query, slipBasisPoints, 10000)
}
Loading
Loading