Skip to content
This repository has been archived by the owner on Apr 25, 2024. It is now read-only.

Commit

Permalink
Add error handling tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zhongeric committed Mar 28, 2024
1 parent 1c2b538 commit c65b595
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 90 deletions.
12 changes: 1 addition & 11 deletions src/utils/routerTradeAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { MixedRouteSDK, Trade as RouterTrade } from '@uniswap/router-sdk'
import {
Currency,
CurrencyAmount,
Ether,
Token,
TradeType,
} from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Ether, Token, TradeType } from '@uniswap/sdk-core'
import { Pair, Route as V2Route } from '@uniswap/v2-sdk'
import { Pool, Route as V3Route, FeeAmount } from '@uniswap/v3-sdk'
import { BigNumber } from 'ethers'
Expand Down Expand Up @@ -85,7 +79,6 @@ export class RouterTradeAdapter {
if (!route.length) throw new Error('Expected there to be at least one route')
if (route.some((r) => !r.length)) throw new Error('Expected all routes to have at least one pool')
const firstRoute = route[0]
if (!firstRoute.length) throw new Error('Expected route to have at least one pool')

const tokenInData = firstRoute[0].tokenIn
const tokenOutData = firstRoute[firstRoute.length - 1].tokenOut
Expand All @@ -98,9 +91,6 @@ export class RouterTradeAdapter {
const parsedCurrencyOut = RouterTradeAdapter.toCurrency(isNativeCurrency(tokenOut), tokenOutData)

const typedRoutes: RouteResult[] = route.map((subRoute) => {
if (subRoute.length === 0) {
throw new Error('Expected route to have at least one pair or pool')
}
const rawAmountIn = subRoute[0].amountIn
const rawAmountOut = subRoute[subRoute.length - 1].amountOut

Expand Down
2 changes: 1 addition & 1 deletion test/forge/interop.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,4 @@
"calldata": "0x24856bc30000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030b000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000003eb3459f0ce6ae000b00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb8a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f46b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000000000000000000000000000",
"value": "1000000000000000000"
}
}
}
249 changes: 171 additions & 78 deletions test/uniswapTrades.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,84 @@ describe('Uniswap', () => {
})
})

const mockV2PoolInRoute = (
pair: Pair,
tokenIn: Token,
tokenOut: Token,
amountIn: string,
amountOut: string
): V2PoolInRoute => {
// get token0 and token1
const token0 = tokenIn.sortsBefore(tokenOut) ? tokenIn : tokenOut
const token1 = tokenIn.sortsBefore(tokenOut) ? tokenOut : tokenIn

return {
type: PoolType.V2Pool,
tokenIn: {
address: tokenIn.address,
chainId: 1,
symbol: tokenIn.symbol!,
decimals: String(tokenIn.decimals),
},
tokenOut: {
address: tokenOut.address,
chainId: 1,
symbol: tokenOut.symbol!,
decimals: String(tokenOut.decimals),
},
reserve0: {
token: {
address: token0.address,
chainId: 1,
symbol: token0.symbol!,
decimals: String(token0.decimals),
},
quotient: pair.reserve0.quotient.toString(),
},
reserve1: {
token: {
address: token1.address,
chainId: 1,
symbol: token1.symbol!,
decimals: String(token1.decimals),
},
quotient: pair.reserve1.quotient.toString(),
},
amountIn,
amountOut,
}
}

const mockV3PoolInRoute = (
pool: Pool,
tokenIn: Token,
tokenOut: Token,
amountIn: string,
amountOut: string
): V3PoolInRoute => {
return {
type: PoolType.V3Pool,
tokenIn: {
address: tokenIn.address,
chainId: 1,
symbol: tokenIn.symbol!,
decimals: String(tokenIn.decimals),
},
tokenOut: {
address: tokenOut.address,
chainId: 1,
symbol: tokenOut.symbol!,
decimals: String(tokenOut.decimals),
},
sqrtRatioX96: pool.sqrtRatioX96.toString(),
liquidity: pool.liquidity.toString(),
tickCurrent: pool.tickCurrent.toString(),
fee: pool.fee.toString(),
amountIn,
amountOut,
}
}

for (let tradeType of [TradeType.EXACT_INPUT, TradeType.EXACT_OUTPUT]) {
describe('RouterTradeAdapter ' + tradeType, () => {
const getAmountToken = (
Expand All @@ -724,84 +802,6 @@ describe('Uniswap', () => {
: CurrencyAmount.fromRawAmount(tokenOut, amount)
}

const mockV2PoolInRoute = (
pair: Pair,
tokenIn: Token,
tokenOut: Token,
amountIn: string,
amountOut: string
): V2PoolInRoute => {
// get token0 and token1
const token0 = tokenIn.sortsBefore(tokenOut) ? tokenIn : tokenOut
const token1 = tokenIn.sortsBefore(tokenOut) ? tokenOut : tokenIn

return {
type: PoolType.V2Pool,
tokenIn: {
address: tokenIn.address,
chainId: 1,
symbol: tokenIn.symbol!,
decimals: String(tokenIn.decimals),
},
tokenOut: {
address: tokenOut.address,
chainId: 1,
symbol: tokenOut.symbol!,
decimals: String(tokenOut.decimals),
},
reserve0: {
token: {
address: token0.address,
chainId: 1,
symbol: token0.symbol!,
decimals: String(token0.decimals),
},
quotient: pair.reserve0.quotient.toString(),
},
reserve1: {
token: {
address: token1.address,
chainId: 1,
symbol: token1.symbol!,
decimals: String(token1.decimals),
},
quotient: pair.reserve1.quotient.toString(),
},
amountIn,
amountOut,
}
}

const mockV3PoolInRoute = (
pool: Pool,
tokenIn: Token,
tokenOut: Token,
amountIn: string,
amountOut: string
): V3PoolInRoute => {
return {
type: PoolType.V3Pool,
tokenIn: {
address: tokenIn.address,
chainId: 1,
symbol: tokenIn.symbol!,
decimals: String(tokenIn.decimals),
},
tokenOut: {
address: tokenOut.address,
chainId: 1,
symbol: tokenOut.symbol!,
decimals: String(tokenOut.decimals),
},
sqrtRatioX96: pool.sqrtRatioX96.toString(),
liquidity: pool.liquidity.toString(),
tickCurrent: pool.tickCurrent.toString(),
fee: pool.fee.toString(),
amountIn,
amountOut,
}
}

function compareUniswapTrades(left: UniswapTrade, right: UniswapTrade): void {
expect(SwapRouter.swapCallParameters(left).calldata).to.eq(SwapRouter.swapCallParameters(right).calldata)
expect(SwapRouter.swapCallParameters(left).value).to.eq(SwapRouter.swapCallParameters(right).value)
Expand Down Expand Up @@ -1186,4 +1186,97 @@ describe('Uniswap', () => {
})
})
}

describe('RouterTradeAdapter handles malformed classic quote', () => {
it('throws on missing route', async () => {
const classicQuote: any = {
tokenIn: WETH.address,
tokenOut: USDC.address,
tradeType: TradeType.EXACT_INPUT,
}
expect(() => RouterTradeAdapter.fromClassicQuote(classicQuote)).to.throw('Expected route to be present')
})
it('throws on no route', async () => {
const classicQuote: any = {
tokenIn: WETH.address,
tokenOut: USDC.address,
tradeType: TradeType.EXACT_INPUT,
route: [],
}
expect(() => RouterTradeAdapter.fromClassicQuote(classicQuote)).to.throw(
'Expected there to be at least one route'
)
})
it('throws on route with no pools', async () => {
const classicQuote: any = {
tokenIn: WETH.address,
tokenOut: USDC.address,
tradeType: TradeType.EXACT_INPUT,
route: [[]],
}
expect(() => RouterTradeAdapter.fromClassicQuote(classicQuote)).to.throw(
'Expected all routes to have at least one pool'
)
})
it('throws on quote missing tokenIn/Out', async () => {
const classicQuote: any = {
tokenIn: WETH.address,
tokenOut: USDC.address,
tradeType: TradeType.EXACT_INPUT,
route: [
[
{
...mockV2PoolInRoute(USDC_DAI_V2, DAI, USDC, '1000', '1000'),
tokenIn: undefined,
},
],
],
}
expect(() => RouterTradeAdapter.fromClassicQuote(classicQuote)).to.throw(
'Expected both tokenIn and tokenOut to be present'
)
})
it('throws on route with mismatched token chainIds', async () => {
const classicQuote: PartialClassicQuote = {
tokenIn: DAI.address,
tokenOut: USDC.address,
tradeType: TradeType.EXACT_INPUT,
route: [
[
{
...mockV2PoolInRoute(USDC_DAI_V2, DAI, USDC, '1000', '1000'),
tokenIn: {
address: DAI.address,
// Different chainId
chainId: 2,
symbol: DAI.symbol!,
decimals: String(DAI.decimals),
},
},
],
],
}
expect(() => RouterTradeAdapter.fromClassicQuote(classicQuote)).to.throw(
'Expected tokenIn and tokenOut to be have same chainId'
)
})
it('throws on route with missing amountIn/Out', async () => {
const classicQuote: any = {
tokenIn: WETH.address,
tokenOut: USDC.address,
tradeType: TradeType.EXACT_INPUT,
route: [
[
{
...mockV2PoolInRoute(USDC_DAI_V2, DAI, USDC, '1000', '1000'),
amountIn: undefined,
},
],
],
}
expect(() => RouterTradeAdapter.fromClassicQuote(classicQuote)).to.throw(
'Expected both raw amountIn and raw amountOut to be present'
)
})
})
})

0 comments on commit c65b595

Please sign in to comment.