-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfindOptimalPath.ts
155 lines (131 loc) · 5.39 KB
/
findOptimalPath.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import BigNumber from 'bignumber.js';
import { estimateOut, findBestPool, findPoolsByOneTokenAndWhiteBlacklist } from "./utils";
import { CENTRAL_TOKENS, BNB_ATTACHED_TOKENS, BUSD_ATTACHED_TOKENS, WHOLE_TOKENS, BNB, BUSD, CENTRAL_TOKENS_GRAPH, TOP_30_POOLS, viewerContract, PANCAKE_SWAP_ADDRESS } from "./statics";
import { OptimalPath, pathWithInfo, PoolDto } from "dtos";
import { fetchNowPools } from "./fetchPoolInfos";
export const findOptimalPath = async (from: string, to: string, amountInRaw: number, pools: PoolDto[]): Promise<any> => {
from = from.toLowerCase();
to = to.toLowerCase();
if (!WHOLE_TOKENS.includes(from) || !WHOLE_TOKENS.includes(to)) {
console.log("This token can not be swapped in this service.");
return{
path: [],
amountOut: 0,
protocols: [],
pools: []
}
}
let amountIn = BigNumber(amountInRaw);
let path = [from];
let protocols = [];
let pathPools = [];
// const real_to = to;
// ------------------------------------------------------------------
// 1) if from is BNB_ATTACHED_TOKENS or BUSD_ATTACHED_TOKENS, just swap to BNB or BUSD.
// ------------------------------------------------------------------
if (BNB_ATTACHED_TOKENS.includes(from)) {
const bnbPool = findBestPool(pools, from, BNB);
amountIn = estimateOut(bnbPool, from, amountIn);
path.push(BNB);
protocols.push(bnbPool['protocol']);
pathPools.push(bnbPool);
from = BNB;
} else if (BUSD_ATTACHED_TOKENS.includes(from)) {
const busdPool = findBestPool(pools, from, BUSD);
amountIn = estimateOut(busdPool, from, amountIn);
path.push(BUSD);
protocols.push(busdPool['protocol']);
pathPools.push(busdPool);
from = BUSD;
}
// ------------------------------------------------------------------
// 2) if from and to is both BNB_ATTACHED_TOKENS, just swap and all done. Same with BUSD_ATTACHED_TOKENS
// ------------------------------------------------------------------
if (
(from === BNB && BNB_ATTACHED_TOKENS.includes(to)) ||
(from === BUSD && BUSD_ATTACHED_TOKENS.includes(to))
) {
path.push(to);
return {
path,
amountOut: amountIn.toNumber(),
protocols,
pools: pathPools
};
}
// ------------------------------------------------------------------
// 3) find optimal path in 7 central tokens
// ------------------------------------------------------------------
const real_to = to;
if (BNB_ATTACHED_TOKENS.includes(real_to)) {
to = BNB;
} else if (BUSD_ATTACHED_TOKENS.includes(real_to)) {
to = BUSD;
}
// recursive function:
// (1) find and exchange LP's that were not in the previous path and store output.
// (2) if we reach to 'to token;, return and exit function.
// (3) choose the maximum output path
let pathsWithAmountOut: {path: string[], protocols: string[], pools: PoolDto[], amountOut: BigNumber}[] = [];
const addPath = (nowPath: string[], nowPathProtocols: string[], nowPools: PoolDto[], amountIn: BigNumber) => {
const nowFrom = nowPath[nowPath.length - 1];
if (nowFrom === to) {
pathsWithAmountOut.push({ path: nowPath, protocols: nowPathProtocols, pools: nowPools, amountOut: amountIn });
return;
}
const candidatePools = findPoolsByOneTokenAndWhiteBlacklist(pools, nowFrom, CENTRAL_TOKENS, nowPath);
candidatePools.forEach((pool) => {
// use slice function just for copy
const nowPathForExtend = nowPath.slice();
const nowPathProtocolsExtend = nowPathProtocols.slice()
const nowPoolsExtend = nowPools.slice()
const swappedToken = pool["token0"] === nowFrom ? pool["token1"] : pool["token0"];
const amountOut = estimateOut(pool, nowFrom, amountIn);
nowPathForExtend.push(swappedToken);
nowPathProtocolsExtend.push(pool["protocol"]);
const nowPool = {...pool};
nowPool['exchangeRate'] = amountOut.div(amountIn).toNumber();
nowPoolsExtend.push(nowPool);
addPath(nowPathForExtend, nowPathProtocolsExtend, nowPoolsExtend, amountOut);
})
};
// in this section, just for central tokens.
addPath(path, protocols, pathPools, amountIn);
let maxPath: string[] = [];
let maxAmountOut = BigNumber(0);
let maxProtocols: string[] = [];
let maxPools: PoolDto[] = [];
pathsWithAmountOut.forEach((pathWithAmountOut) => {
if (pathWithAmountOut.amountOut > maxAmountOut) {
maxPath = pathWithAmountOut.path;
maxAmountOut = pathWithAmountOut.amountOut;
maxProtocols = pathWithAmountOut.protocols;
maxPools = pathWithAmountOut.pools;
}
});
path.pop();
path = path.concat(maxPath);
// ------------------------------------------------------------------
// 4) return final path
// ------------------------------------------------------------------
if (!CENTRAL_TOKENS.includes(real_to)) {
console.log('to: ', to);
console.log('real_to: ', real_to);
const pool = findBestPool(pools, to, real_to);
maxAmountOut = estimateOut(pool, to, maxAmountOut);
maxPath.push(real_to);
maxProtocols.push(pool['protocol']);
maxPools.push(pool);
}
return {
path: maxPath,
amountOut: maxAmountOut.toNumber(),
protocols: maxProtocols,
pools: maxPools.map((pool) => {
const poolForResponse : any = {...pool};
poolForResponse['token0Reserve'] = pool['token0Reserve'].toNumber();
poolForResponse['token1Reserve'] = pool['token1Reserve'].toNumber();
return poolForResponse;
}),
};
};