Search Swap Path

In this example, we will do path searching with exact input amount (amount of payed token). The complete example codes can be found here.

The search result can be used in Exact Input mode of swap or quoter . Also we can use same search result in Exact Output mode of swap or quoter.

If you want to do path searching with exact output amount (amount of acquired token), You can refer the codes in the here.

The only difference of these two modes while doing path searching can be viewed in this subsection

1. Some imports

1import {BaseChain, ChainId, initialChainTable, TokenInfoFormatted} from '../../src/base/types'
2import Web3 from 'web3';
3import { BigNumber } from 'bignumber.js'
4import { getMulticallContracts } from '../../src/base';
5import { PoolPair, SearchPathQueryParams, SwapDirection } from '../../src/search/types';
6import { searchPathQuery } from '../../src/search/func'

Here searchPathQuery will help us do path searching.

2. Initialization

Here we take example of bsc test net. And we need address of Quoter, LiquidityManager and Multicall, Tokens

Multicall is contracts we deployed on each chain to speed up view or pure calling.

You can refer to our docs for addresses of those contracts on different chain.

In this example, payed token from user is BNB and acquired token is USDT And also we may need some middle token for searching, etc, iZi, USDC, iUSD

And in this example, we init max amount of BNB is 10.0

 1const rpc = 'https://data-seed-prebsc-1-s3.binance.org:8545/'
 2console.log('rpc: ', rpc)
 3const web3 = new Web3(new Web3.providers.HttpProvider(rpc))
 4
 5const quoterAddress = '0x4bCACcF9A0FC3246449AC8A42A8918F2349Ed543'
 6
 7const BNB = {
 8    chainId: ChainId.BSCTestnet,
 9    symbol: "BNB",
10    address: "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd",
11    decimal: 18,
12} as TokenInfoFormatted
13
14const USDT = {
15    chainId: ChainId.BSCTestnet,
16    symbol: "USDT",
17    address: "0x6AECfe44225A50895e9EC7ca46377B9397D1Bb5b",
18    decimal: 6
19} as TokenInfoFormatted
20
21const USDC = {
22    chainId: ChainId.BSCTestnet,
23    symbol: "USDC",
24    address: "0x876508837C162aCedcc5dd7721015E83cbb4e339",
25    decimal: 6
26}
27
28const iZi = {
29    chainId: ChainId.BSCTestnet,
30    symbol: "iZi",
31    address: "0x60D01EC2D5E98Ac51C8B4cF84DfCCE98D527c747",
32    decimal: 18
33}
34
35const iUSD = {
36    chainId: ChainId.BSCTestnet,
37    symbol: "iUSD",
38    address: "0x60FE1bE62fa2082b0897eA87DF8D2CfD45185D30",
39    decimal: 18,
40}
41
42const support001Pools = [
43    {
44        tokenA: iUSD,
45        tokenB: USDT,
46        feeContractNumber: 100
47    } as PoolPair,
48    {
49        tokenA: USDC,
50        tokenB: USDT,
51        feeContractNumber: 100
52    } as PoolPair,
53    {
54        tokenA: USDC,
55        tokenB: iUSD,
56        feeContractNumber: 100
57    } as PoolPair,
58]
59
60// example of exact input amount
61const amountInputBNB = new BigNumber(10).times(10 ** BNB.decimal).toFixed(0)
62
63const multicallAddress = '0x5712A9aeB4538104471dD85659Bd621Cdd7e07D8'
64const multicallContract = getMulticallContracts(multicallAddress, web3)
65const liquidityManagerAddress = '0xDE02C26c46AC441951951C97c8462cD85b3A124c'

3. construct params

 1// params
 2const searchParams = {
 3    chainId: Number(ChainId.BSCTestnet),
 4    web3: web3,
 5    multicall: multicallContract,
 6    tokenIn: BNB,
 7    tokenOut: USDT,
 8    liquidityManagerAddress,
 9    quoterAddress,
10    poolBlackList: [],
11    midTokenList: [BNB, USDT, USDC, iZi],
12    supportFeeContractNumbers: [2000, 400, 100],
13    support001Pools,
14    direction: SwapDirection.ExactIn,
15    amount: amountInputBNB
16} as SearchPathQueryParams

SearchPathQueryParams defined fields of params need by our path-searching function.

In this example, the swap mode is swap with exact input, and we need to fill SearchPathQueryParams.direction with SwapDirection.ExactIn and to fill SearchPathQueryParams.amount with undecimal amount of input token (BNB)

supportFeeContractNumbers is a list containing supported fees of swap-pool. And we only consider pools with fee within this list in our path searching. And number 2000 means fee tier of 0.2%

Due to the fact that pools with fee tier of 0.01% may consume much more gas than others. We need to limit the usage of such pools. And field support001Pools is a list containing supported pool with fee tier of 0.01%. When we are doing path searching, pools with 0.01% fee tier and outside that list will not be considered. Each element in support001Pools is a struct of PoolPair.

If we want to ignore some pool, we can fill the field poolBlackList. Element in poolBlackList is also struct of PoolPair.

4. difference for mode of exact output

If we want to do path searching in the mode of swap with exact output, we need to fill SearchPathQueryParams.direction with SwapDirection.ExactOut and to fill SearchPathQueryParams.amount with undecimal amount of output token (USDT) in the above code.

You can refer the codes here for more details.

5. Searching!

 1// pathQueryResult stores optimized swap-path
 2//     and estimated swap-amount (output amount for exactIn, and input amount for exactOut)
 3// preQueryResult caches data of pools and their state (current point)
 4//     which will be used during path-searching
 5//     preQueryResult can be used for speed-up for next search
 6//     etc, if you want to speed up a little next search,
 7//     just use following code:
 8//     await searchPathQuery(searchParams, preQueryResult)
 9//     cached data in preQueryResult can be used for different
10//     pair of <inputToken, outputToken> or different direction
11//     but notice that, cached data in preQueryResult can not be
12//     used in different chain
13const {pathQueryResult, preQueryResult} = await searchPathQuery(
14    searchParams
15)

5. take result

After calling searchPathQuery, we can print the optimized path.

1// print output amount
2console.log('output amount: ', pathQueryResult.amount)
3// print path info
4// which can be filled to swap params
5// see example of "example/quoterAndSwap/"
6console.log('fee chain: ', pathQueryResult.path.feeContractNumber)
7console.log('token chain: ', pathQueryResult.path.tokenChain)

In the above code, pathQueryResult.path.feeContractNumber is type of number[] and pathQueryResult.path.tokenChain is type of TokenInfoFormatted[] data of these 2 arrays can be used to fill the fields named feeChain and tokenChain in 2 structs named SwapChainWithExactInputParams and SwapChainWithExactOutputParams which is used as input parameter for interfaces of swap.

The fields of SwapChainWithExactInputParams and SwapChainWithExactOutputParams can be viewed in following code.

 1export interface SwapChainWithExactInputParams {
 2    // input: tokenChain[0]
 3    // output: tokenChain[1]
 4    tokenChain: TokenInfoFormatted[];
 5    // fee / 1e6 is feeTier
 6    // 3000 means 0.3%
 7    feeChain: number[];
 8    // 10-decimal format integer number, like 100, 150000, ...
 9    // or hex format number start with '0x'
10    // decimal amount = inputAmount / (10 ** inputToken.decimal)
11    inputAmount: string;
12    minOutputAmount: string;
13    recipient?: string;
14    deadline?: string;
15    // true if treat wrapped coin(wbnb or weth ...) as erc20 token
16    strictERC20Token?: boolean;
17}
18
19export interface SwapChainWithExactOutputParams {
20    // input: tokenChain[0]
21    // output: tokenChain[1]
22    tokenChain: TokenInfoFormatted[];
23    // fee / 1e6 is feeTier
24    // 3000 means 0.3%
25    feeChain: number[];
26    // 10-decimal format number, like 100, 150000, ...
27    // or hex format number start with '0x'
28    // amount = outputAmount / (10 ** outputToken.decimal)
29    outputAmount: string;
30    maxInputAmount: string;
31    recipient?: string;
32    deadline?: string;
33    // true if treat wrapped coin(wbnb or weth ...) as erc20 token
34    strictERC20Token?: boolean;
35}

And the example codes of calling interfaces of swap can be viewed in Exact input mode or Exact output mode