@marko00/routing-finder-mare 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/.idea/copilot.data.migration.agent.xml +6 -0
  2. package/.idea/copilot.data.migration.ask.xml +6 -0
  3. package/.idea/copilot.data.migration.ask2agent.xml +6 -0
  4. package/.idea/copilot.data.migration.edit.xml +6 -0
  5. package/.idea/modules.xml +8 -0
  6. package/.idea/php.xml +19 -0
  7. package/.idea/ratex-sdk.iml +8 -0
  8. package/.idea/vcs.xml +6 -0
  9. package/LICENSE-MIT +21 -0
  10. package/README.md +209 -0
  11. package/contracts/abi/BalancerHelperAbi.ts +1 -0
  12. package/contracts/abi/CamelotHelperAbi.ts +1 -0
  13. package/contracts/abi/RateXAbi.ts +482 -0
  14. package/contracts/abi/SushiSwapHelperAbi.ts +1 -0
  15. package/contracts/abi/UniswapHelperAbi.ts +1 -0
  16. package/contracts/abi/UniswapV2HelperAbi.ts +1 -0
  17. package/contracts/addresses-arbitrum.ts +8 -0
  18. package/contracts/addresses-mainnet.ts +7 -0
  19. package/contracts/addresses-polkadot.ts +9 -0
  20. package/contracts/addresses-sei.ts +9 -0
  21. package/contracts/rateX/BalancerHelper.ts +13 -0
  22. package/contracts/rateX/CamelotHelper.ts +13 -0
  23. package/contracts/rateX/SushiSwapHelper.ts +13 -0
  24. package/contracts/rateX/UniswapHelper.ts +19 -0
  25. package/contracts/rateX/UniswapV2Helper.ts +19 -0
  26. package/dexes/dexIdsList.ts +9 -0
  27. package/dexes/graph_queries/BalancerV2.ts +150 -0
  28. package/dexes/graph_queries/CamelotV2.ts +202 -0
  29. package/dexes/graph_queries/SushiSwapV2.ts +283 -0
  30. package/dexes/graph_queries/UniswapV2.ts +252 -0
  31. package/dexes/graph_queries/UniswapV3.ts +229 -0
  32. package/dexes/graph_queries/graphQueryFilters.ts +41 -0
  33. package/dexes/graph_queries/x_template.ts +67 -0
  34. package/dexes/pools/Balancer/BalancerState.ts +34 -0
  35. package/dexes/pools/Balancer/BalancerWeightedPool.ts +96 -0
  36. package/dexes/pools/Camelot.ts +164 -0
  37. package/dexes/pools/SushiSwapV2.ts +35 -0
  38. package/dexes/pools/UniswapV2.ts +36 -0
  39. package/dexes/pools/uniswap/UniswapV3.ts +40 -0
  40. package/dexes/pools/uniswap/testUniswapOffchainQuoter.ts +169 -0
  41. package/dexes/pools/uniswap/types.ts +174 -0
  42. package/dexes/pools/uniswap/uniswapOffchainQuoter.ts +173 -0
  43. package/dexes/pools/uniswap/uniswapState.ts +56 -0
  44. package/dexes/pools/uniswap/utils.ts +71 -0
  45. package/dist/contracts/abi/BalancerHelperAbi.d.ts +25 -0
  46. package/dist/contracts/abi/BalancerHelperAbi.js +4 -0
  47. package/dist/contracts/abi/CamelotHelperAbi.d.ts +45 -0
  48. package/dist/contracts/abi/CamelotHelperAbi.js +4 -0
  49. package/dist/contracts/abi/RateXAbi.d.ts +71 -0
  50. package/dist/contracts/abi/RateXAbi.js +485 -0
  51. package/dist/contracts/abi/SushiSwapHelperAbi.d.ts +45 -0
  52. package/dist/contracts/abi/SushiSwapHelperAbi.js +4 -0
  53. package/dist/contracts/abi/UniswapHelperAbi.d.ts +39 -0
  54. package/dist/contracts/abi/UniswapHelperAbi.js +4 -0
  55. package/dist/contracts/abi/UniswapV2HelperAbi.d.ts +45 -0
  56. package/dist/contracts/abi/UniswapV2HelperAbi.js +4 -0
  57. package/dist/contracts/addresses-arbitrum.d.ts +6 -0
  58. package/dist/contracts/addresses-arbitrum.js +10 -0
  59. package/dist/contracts/addresses-mainnet.d.ts +6 -0
  60. package/dist/contracts/addresses-mainnet.js +10 -0
  61. package/dist/contracts/addresses-polkadot.d.ts +6 -0
  62. package/dist/contracts/addresses-polkadot.js +10 -0
  63. package/dist/contracts/addresses-sei.d.ts +6 -0
  64. package/dist/contracts/addresses-sei.js +10 -0
  65. package/dist/contracts/rateX/BalancerHelper.d.ts +26 -0
  66. package/dist/contracts/rateX/BalancerHelper.js +14 -0
  67. package/dist/contracts/rateX/CamelotHelper.d.ts +46 -0
  68. package/dist/contracts/rateX/CamelotHelper.js +14 -0
  69. package/dist/contracts/rateX/SushiSwapHelper.d.ts +46 -0
  70. package/dist/contracts/rateX/SushiSwapHelper.js +14 -0
  71. package/dist/contracts/rateX/UniswapHelper.d.ts +40 -0
  72. package/dist/contracts/rateX/UniswapHelper.js +22 -0
  73. package/dist/contracts/rateX/UniswapV2Helper.d.ts +46 -0
  74. package/dist/contracts/rateX/UniswapV2Helper.js +22 -0
  75. package/dist/dexes/dexIdsList.d.ts +9 -0
  76. package/dist/dexes/dexIdsList.js +12 -0
  77. package/dist/dexes/graph_queries/BalancerV2.d.ts +14 -0
  78. package/dist/dexes/graph_queries/BalancerV2.js +141 -0
  79. package/dist/dexes/graph_queries/CamelotV2.d.ts +14 -0
  80. package/dist/dexes/graph_queries/CamelotV2.js +183 -0
  81. package/dist/dexes/graph_queries/SushiSwapV2.d.ts +14 -0
  82. package/dist/dexes/graph_queries/SushiSwapV2.js +263 -0
  83. package/dist/dexes/graph_queries/UniswapV2.d.ts +14 -0
  84. package/dist/dexes/graph_queries/UniswapV2.js +217 -0
  85. package/dist/dexes/graph_queries/UniswapV3.d.ts +14 -0
  86. package/dist/dexes/graph_queries/UniswapV3.js +198 -0
  87. package/dist/dexes/graph_queries/graphQueryFilters.d.ts +19 -0
  88. package/dist/dexes/graph_queries/graphQueryFilters.js +40 -0
  89. package/dist/dexes/graph_queries/x_template.d.ts +12 -0
  90. package/dist/dexes/graph_queries/x_template.js +57 -0
  91. package/dist/dexes/pools/Balancer/BalancerState.d.ts +6 -0
  92. package/dist/dexes/pools/Balancer/BalancerState.js +32 -0
  93. package/dist/dexes/pools/Balancer/BalancerWeightedPool.d.ts +12 -0
  94. package/dist/dexes/pools/Balancer/BalancerWeightedPool.js +109 -0
  95. package/dist/dexes/pools/Camelot.d.ts +12 -0
  96. package/dist/dexes/pools/Camelot.js +135 -0
  97. package/dist/dexes/pools/SushiSwapV2.d.ts +9 -0
  98. package/dist/dexes/pools/SushiSwapV2.js +34 -0
  99. package/dist/dexes/pools/UniswapV2.d.ts +9 -0
  100. package/dist/dexes/pools/UniswapV2.js +34 -0
  101. package/dist/dexes/pools/uniswap/UniswapV3.d.ts +7 -0
  102. package/dist/dexes/pools/uniswap/UniswapV3.js +36 -0
  103. package/dist/dexes/pools/uniswap/types.d.ts +76 -0
  104. package/dist/dexes/pools/uniswap/types.js +111 -0
  105. package/dist/dexes/pools/uniswap/uniswapOffchainQuoter.d.ts +13 -0
  106. package/dist/dexes/pools/uniswap/uniswapOffchainQuoter.js +121 -0
  107. package/dist/dexes/pools/uniswap/uniswapState.d.ts +14 -0
  108. package/dist/dexes/pools/uniswap/uniswapState.js +51 -0
  109. package/dist/dexes/pools/uniswap/utils.d.ts +3 -0
  110. package/dist/dexes/pools/uniswap/utils.js +41 -0
  111. package/dist/index.d.ts +26 -0
  112. package/dist/index.js +41 -0
  113. package/dist/routes.d.ts +1 -0
  114. package/dist/routes.js +20 -0
  115. package/dist/routing/iterative_spliting/main.d.ts +3 -0
  116. package/dist/routing/iterative_spliting/main.js +104 -0
  117. package/dist/routing/iterative_spliting/multiHopSwap.d.ts +4 -0
  118. package/dist/routing/iterative_spliting/multiHopSwap.js +83 -0
  119. package/dist/routing/main.d.ts +2 -0
  120. package/dist/routing/main.js +22 -0
  121. package/dist/routing/uni_like_algo/algo_config.d.ts +2 -0
  122. package/dist/routing/uni_like_algo/algo_config.js +8 -0
  123. package/dist/routing/uni_like_algo/amount_distribution.d.ts +2 -0
  124. package/dist/routing/uni_like_algo/amount_distribution.js +17 -0
  125. package/dist/routing/uni_like_algo/compute_routes_backtrack.d.ts +3 -0
  126. package/dist/routing/uni_like_algo/compute_routes_backtrack.js +44 -0
  127. package/dist/routing/uni_like_algo/main.d.ts +2 -0
  128. package/dist/routing/uni_like_algo/main.js +49 -0
  129. package/dist/routing/uni_like_algo/routes_quoter.d.ts +21 -0
  130. package/dist/routing/uni_like_algo/routes_quoter.js +53 -0
  131. package/dist/routing/uni_like_algo/swap_finder.d.ts +25 -0
  132. package/dist/routing/uni_like_algo/swap_finder.js +154 -0
  133. package/dist/routing/uni_like_algo/types.d.ts +40 -0
  134. package/dist/routing/uni_like_algo/types.js +12 -0
  135. package/dist/swap/graph_communication.d.ts +5 -0
  136. package/dist/swap/graph_communication.js +187 -0
  137. package/dist/swap/my_local_storage.d.ts +8 -0
  138. package/dist/swap/my_local_storage.js +16 -0
  139. package/dist/utils/addresses.d.ts +24 -0
  140. package/dist/utils/addresses.js +60 -0
  141. package/dist/utils/math/fixed-points.d.ts +14 -0
  142. package/dist/utils/math/fixed-points.js +123 -0
  143. package/dist/utils/math/log-exp.d.ts +5 -0
  144. package/dist/utils/math/log-exp.js +385 -0
  145. package/dist/utils/math/math.d.ts +12 -0
  146. package/dist/utils/math/math.js +50 -0
  147. package/dist/utils/types/types.d.ts +51 -0
  148. package/dist/utils/types/types.js +25 -0
  149. package/dist/utils/utils.d.ts +20 -0
  150. package/dist/utils/utils.js +72 -0
  151. package/images/decenter_logo.png +0 -0
  152. package/index.ts +50 -0
  153. package/package.json +39 -0
  154. package/routes.ts +27 -0
  155. package/routing/iterative_spliting/main.ts +131 -0
  156. package/routing/iterative_spliting/multiHopSwap.ts +98 -0
  157. package/routing/main.ts +22 -0
  158. package/routing/uni_like_algo/algo_config.ts +7 -0
  159. package/routing/uni_like_algo/amount_distribution.ts +16 -0
  160. package/routing/uni_like_algo/compute_routes_backtrack.ts +81 -0
  161. package/routing/uni_like_algo/main.ts +65 -0
  162. package/routing/uni_like_algo/routes_quoter.ts +63 -0
  163. package/routing/uni_like_algo/swap_finder.ts +185 -0
  164. package/routing/uni_like_algo/types.ts +54 -0
  165. package/swap/graph_communication.ts +212 -0
  166. package/swap/my_local_storage.ts +27 -0
  167. package/tsconfig.json +26 -0
  168. package/utils/addresses.ts +64 -0
  169. package/utils/math/fixed-points.ts +88 -0
  170. package/utils/math/log-exp.ts +469 -0
  171. package/utils/math/math.ts +46 -0
  172. package/utils/types/types.ts +100 -0
  173. package/utils/utils.ts +125 -0
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@marko00/routing-finder-mare",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "type": "commonjs",
6
+ "dependencies": {
7
+ "@graphql-typed-document-node/core": "^3.2.0",
8
+ "@uniswap/sdk-core": "^4.0.6",
9
+ "@uniswap/v3-sdk": "^3.10.0",
10
+ "bignumber.js": "^9.1.2",
11
+ "crypto": "^1.0.1",
12
+ "ethers": "^6.7.0",
13
+ "graphql": "^16.7.1",
14
+ "graphql-request": "^6.1.0",
15
+ "js-sha256": "^0.10.1",
16
+ "mnemonist": "^0.39.5",
17
+ "object-hash": "^3.0.0",
18
+ "path": "^0.12.7",
19
+ "ratex-sdk": "^2.0.1",
20
+ "sha.js": "^2.4.11",
21
+ "sha256": "^0.2.0",
22
+ "ts-node": "^10.9.1",
23
+ "tstl": "^2.5.13",
24
+ "web3": "^4.11.1"
25
+ },
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
28
+ "scripts": {
29
+ "test": "echo \"Error: no test specified\" && exit 1",
30
+ "build": "tsc",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "devDependencies": {
34
+ "@types/object-hash": "^3.0.6",
35
+ "@types/web3": "^1.2.2",
36
+ "typescript": "^5.5.4"
37
+ },
38
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
39
+ }
package/routes.ts ADDED
@@ -0,0 +1,27 @@
1
+ import {RateX, Dexes} from "./index";
2
+
3
+ async function main() {
4
+ const rateX = new RateX({
5
+ rpcUrl: "https://tiniest-cold-general.sei-pacific.quiknode.pro/dfa7ceb1fbe6d4fdbce164d5e52201488999dd15",
6
+ chainId: 1329,
7
+ dexes: [Dexes.UNISWAP_V2, Dexes.UNISWAP_V3],
8
+ graphApiKey: "",
9
+ });
10
+
11
+ const tokenIn = "0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7"; // real Sei token
12
+ const tokenOut = "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392"; // real Sei token
13
+ const amountIn = BigInt("1000000000000000000");
14
+ const quote = await rateX.getQuote(tokenIn, tokenOut, amountIn);
15
+
16
+ const swapParams = await rateX.getSwapParameters(
17
+ tokenIn,
18
+ tokenOut,
19
+ amountIn,
20
+ 1, // 1% slippage
21
+ "0xEC744EA7c6792c2D78abdd3E1B1254fdCd32E6fe",
22
+ 30 // 30 minutes deadline
23
+ );
24
+ console.log("Swap parameters:", swapParams);
25
+ }
26
+
27
+ main().catch(console.error);
@@ -0,0 +1,131 @@
1
+ import { myLocalStorage } from "../../swap/my_local_storage";
2
+ import { Quote, Route, Pool, PoolInfo } from "../../utils/types/types";
3
+ import { createGraph, multiHopSwap } from "./multiHopSwap";
4
+
5
+ // Simple route key - just concatenate pool IDs (much faster than object-hash)
6
+ function getRouteKey(swaps: { poolId: string }[]): string {
7
+ return swaps.map(s => s.poolId).join('→');
8
+ }
9
+
10
+ // Graph cache for performance optimization
11
+ interface CachedGraph {
12
+ graph: Map<string, Pool[]>;
13
+ poolIds: string; // Hash of pool IDs to detect changes
14
+ timestamp: number;
15
+ }
16
+
17
+ let graphCache: CachedGraph | null = null;
18
+ const GRAPH_CACHE_TTL_MS = 2000; // 2 second cache
19
+
20
+ function getPoolsHash(pools: Pool[]): string {
21
+ return pools.map(p => p.poolId).sort().join(',');
22
+ }
23
+
24
+ function getCachedGraph(pools: Pool[]): Map<string, Pool[]> {
25
+ const poolsHash = getPoolsHash(pools);
26
+ const now = Date.now();
27
+
28
+ // Return cached graph if valid
29
+ if (graphCache &&
30
+ graphCache.poolIds === poolsHash &&
31
+ now - graphCache.timestamp < GRAPH_CACHE_TTL_MS) {
32
+ return graphCache.graph;
33
+ }
34
+
35
+ // Create new graph and cache it
36
+ const graph = createGraph(pools);
37
+ graphCache = {
38
+ graph,
39
+ poolIds: poolsHash,
40
+ timestamp: now
41
+ };
42
+
43
+ return graph;
44
+ }
45
+
46
+ /* Simple algorithm that splits the input amount into (100/step) parts of step% each and finds the best route for each split.
47
+ The algorithm to find the best route for each iteration finds the route with the highest output amount.
48
+ (code is seen in ./multiHopSwap.ts)
49
+ After each iteration, the pools are updated with the amounts that passed through them.
50
+ */
51
+ async function findRouteWithIterativeSplitting(tokenA: string, tokenB: string, amountIn: bigint, pools: Pool[], chainId: number): Promise<Quote> {
52
+ const graph = getCachedGraph(pools)
53
+
54
+ // percentage of the amountIn that we split into (5% = 20 iterations, faster than 2% = 50 iterations)
55
+ const step: number = 5
56
+
57
+ let amountOut: bigint = BigInt(0)
58
+ const poolMap: Map<string, Pool> = new Map<string, Pool>(pools.map((pool: Pool) => [pool.poolId, pool]))
59
+ const routes: Map<string, Route> = new Map<string, Route>()
60
+ const splitAmountIn: bigint = (amountIn * BigInt(step)) / BigInt(100)
61
+
62
+ for (let i = 0; i < 100; i += step) {
63
+ const route: Route = multiHopSwap(splitAmountIn, tokenA, tokenB, graph)
64
+ const routeHash = getRouteKey(route.swaps)
65
+
66
+ let existingRoute: Route | undefined = routes.get(routeHash)
67
+ if (!existingRoute) {
68
+ route.percentage = step
69
+ routes.set(routeHash, route)
70
+ } else {
71
+ existingRoute.percentage += step
72
+ }
73
+
74
+ amountOut += route.quote
75
+ updatePoolsInRoute(poolMap, route, splitAmountIn)
76
+ }
77
+
78
+ const foundRoutes: Route[] = [];
79
+
80
+ for (let route of routes.values()) {
81
+ route.amountIn = (BigInt(route.percentage) * amountIn) / BigInt(100);
82
+ foundRoutes.push(route);
83
+ }
84
+ const missingAmount = amountIn - foundRoutes.reduce((acc, route) => acc + route.amountIn, BigInt(0));
85
+ foundRoutes[0].amountIn += missingAmount;
86
+
87
+ const quote: Quote = { routes: foundRoutes, quote: amountOut };
88
+
89
+
90
+
91
+ let total = BigInt(0);
92
+ const resetPools = new Set<string>()
93
+ for (const route of quote.routes) {
94
+ let progress = route.amountIn;
95
+ for (const swap of route.swaps) {
96
+ const pool = myLocalStorage.getItem(swap.poolId.toLowerCase());
97
+ if (!pool)
98
+ throw Error("Error caching pools");
99
+ if (!resetPools.has(swap.poolId.toLowerCase())) {
100
+ pool.reset();
101
+ resetPools.add(swap.poolId.toLowerCase());
102
+ }
103
+ const amount = pool.calculateExpectedOutputAmount(swap.tokenIn, swap.tokenOut, progress)
104
+ pool.update(swap.tokenIn, swap.tokenOut, progress, amount);
105
+ progress = amount;
106
+ }
107
+ route.quote = progress;
108
+ total += progress;
109
+ }
110
+ quote.quote = total;
111
+ if (quote.routes[0].swaps.length == 0)
112
+ quote.quote = BigInt(0)
113
+ return quote;
114
+ }
115
+
116
+ // Function to update all the pools in a route with the amounts that passed through them
117
+ function updatePoolsInRoute(poolMap: Map<string, Pool>, route: Route, amountIn: bigint): void {
118
+ for (let swap of route.swaps) {
119
+ const pool: Pool | undefined = poolMap.get(swap.poolId)
120
+ if (!pool) {
121
+ console.log('Pool ', swap.poolId, " doesn't exist!")
122
+ continue
123
+ }
124
+
125
+ const amountOut: bigint = pool.calculateExpectedOutputAmount(swap.tokenIn, swap.tokenOut, amountIn)
126
+ pool.update(swap.tokenIn, swap.tokenOut, amountIn, amountOut)
127
+ amountIn = amountOut
128
+ }
129
+ }
130
+
131
+ export { findRouteWithIterativeSplitting }
@@ -0,0 +1,98 @@
1
+ import {Route, SwapStep, Pool, Token} from '../../utils/types/types'
2
+
3
+ type DpInfo = {
4
+ amountOut: bigint
5
+ path: string[]
6
+ swaps: SwapStep[]
7
+ }
8
+
9
+ const max_hops = 4
10
+
11
+ /* The algorithm to find the best route for each iteration (highest output amount) is seen below.
12
+ * It is based on dynamic programming.
13
+ * @param amountIn: The amount of tokenIn that we want to swap (in wei)
14
+ * @param tokenIn: The address of the token we want to swap (address on Arbitrum)
15
+ * @param tokenOut: The address of the token we want to receive (address on Arbitrum)
16
+ * @param graph: The graph of all the fetched pools
17
+ */
18
+ function multiHopSwap(amountIn: bigint, tokenIn: string, tokenOut: string, graph: Map<string, Pool[]>): Route {
19
+ tokenIn = tokenIn.toLowerCase()
20
+ tokenOut = tokenOut.toLowerCase()
21
+
22
+ // dp[hop][token]
23
+ const dp: Map<number, Map<string, DpInfo>> = new Map<number, Map<string, DpInfo>>()
24
+ dp.set(0, new Map<string, DpInfo>())
25
+ dp.get(0)?.set(tokenIn, { amountOut: amountIn, path: [tokenIn], swaps: [] })
26
+
27
+ const res: DpInfo = { amountOut: BigInt(-1), path: [], swaps: [] }
28
+
29
+ for (let hop = 0; hop < max_hops - 1; hop++) {
30
+ dp.get(hop)?.forEach((dpInfo: DpInfo, tokenA: string) => {
31
+ graph.get(tokenA)?.forEach((pool: Pool) => {
32
+ pool.tokens.forEach((tokenB: Token) => {
33
+ if (dpInfo.path.includes(tokenB._address)) {
34
+ return
35
+ }
36
+
37
+ // console.log(hop, pool.poolId, tokenA, tokenB._address, dpInfo.amountOut)
38
+ const amountOut: bigint = pool.calculateExpectedOutputAmount(tokenA, tokenB._address, dpInfo.amountOut)
39
+ if (amountOut <= 0) {
40
+ return
41
+ }
42
+
43
+ const newPath: string[] = [...dpInfo.path, tokenB._address]
44
+ const currSwap: SwapStep = { poolId: pool.poolId, dexId: pool.dexId, tokenIn: tokenA, tokenOut: tokenB._address }
45
+ const newSwaps: SwapStep[] = [...dpInfo.swaps, currSwap]
46
+
47
+ if (!dp.has(hop + 1)) {
48
+ dp.set(hop + 1, new Map<string, DpInfo>())
49
+ }
50
+
51
+ const dpEntry = dp.get(hop + 1)
52
+
53
+ if (!dpEntry?.has(tokenB._address)) {
54
+ dp.get(hop + 1)?.set(tokenB._address, { amountOut: amountOut, path: newPath, swaps: newSwaps })
55
+ } else if (amountOut > (dpEntry?.get(tokenB._address)?.amountOut || 0)) {
56
+ dp.get(hop + 1)?.set(tokenB._address, { amountOut: amountOut, path: newPath, swaps: newSwaps })
57
+ }
58
+ })
59
+ })
60
+ })
61
+
62
+ if (dp.get(hop + 1)?.has(tokenOut) && (dp.get(hop + 1)?.get(tokenOut)?.amountOut || -1) > res.amountOut) {
63
+ res.amountOut = dp.get(hop + 1)?.get(tokenOut)?.amountOut || BigInt(0)
64
+ res.path = dp.get(hop + 1)?.get(tokenOut)?.path || []
65
+ res.swaps = dp.get(hop + 1)?.get(tokenOut)?.swaps || []
66
+ }
67
+ }
68
+
69
+ return {
70
+ swaps: res.swaps,
71
+ quote: res.amountOut,
72
+ percentage: 0,
73
+ amountIn: BigInt(0) // will be set in iterative splitting when we know percentage
74
+ }
75
+ }
76
+
77
+ /* Function to create a graph from all the fetched pools
78
+ * Graph maps every token to a list of pools that token is in
79
+ * @param pools: The fetched pools
80
+ * @returns The graph
81
+ */
82
+ function createGraph(pools: Pool[]): Map<string, Pool[]> {
83
+ const graph: Map<string, Pool[]> = new Map<string, Pool[]>()
84
+ for (let pool of pools) {
85
+ const poolId = pool.poolId
86
+
87
+ for (let token of pool.tokens) {
88
+ if (!graph.has(token._address)) {
89
+ graph.set(token._address, [])
90
+ }
91
+ graph.get(token._address)?.push(pool)
92
+ }
93
+ }
94
+
95
+ return graph
96
+ }
97
+
98
+ export { multiHopSwap, createGraph }
@@ -0,0 +1,22 @@
1
+ import {Quote, Pool} from "../utils/types/types";
2
+ import {findRouteUniLikeAlgo} from "./uni_like_algo/main";
3
+ import {findRouteWithIterativeSplitting} from "./iterative_spliting/main";
4
+
5
+ /**
6
+ * This algo works good if we are using pools with two tokens,
7
+ * but it doesn't scale well for pools with more than two tokens because
8
+ * amount of routes rapidly increases with number of tokens in pool as there are more ways to go
9
+ * from token A to token B. This makes algo really slow, especially slow if we include curve dex.
10
+ * That being said, it will stay here as a reference for future algo ideas and improvements but right
11
+ * now iterative splitting is way to go.
12
+ * */
13
+ const UNI_LIKE_ALGO_ACTIVE = false;
14
+
15
+ export async function findRoute(tokenIn: string, tokenOut: string, amountIn: bigint, pools: Pool[], chainId: number): Promise<Quote> {
16
+
17
+ if (UNI_LIKE_ALGO_ACTIVE) {
18
+ return findRouteUniLikeAlgo(tokenIn, tokenOut, amountIn, pools);
19
+ } else {
20
+ return (await findRouteWithIterativeSplitting(tokenIn, tokenOut, amountIn, pools, chainId));
21
+ }
22
+ }
@@ -0,0 +1,7 @@
1
+ import {AlgoParams} from "./types";
2
+
3
+ export const algoParams: AlgoParams = {
4
+ maxHops: 4,
5
+ distributionPercentage: 2,
6
+ maxSplit: 8
7
+ };
@@ -0,0 +1,16 @@
1
+ import {AmountPercentage} from "./types";
2
+
3
+ export default function calculateAmountDistribution(amountIn: bigint, distributionPercentage: number): AmountPercentage[] {
4
+ const percentages: number[] = [];
5
+ const amounts: bigint[] = [];
6
+ for (let i = 1; i <= 100 / distributionPercentage; ++i) {
7
+ percentages.push(distributionPercentage * i);
8
+ amounts.push(amountIn * BigInt(distributionPercentage * i) / BigInt(100));
9
+ }
10
+ return amounts.map((amount, index) => {
11
+ return {
12
+ amountIn: amount,
13
+ percentage: percentages[index]
14
+ }
15
+ });
16
+ }
@@ -0,0 +1,81 @@
1
+ import {Pool} from "../../utils/types/types";
2
+ import {ComputeRoutesParams, TRoute, TRouteStep} from "./types";
3
+
4
+ export default function computeRoutes(
5
+ tokenIn: string,
6
+ tokenOut: string,
7
+ pools: Pool[],
8
+ maxHops: number
9
+ ) {
10
+ const usedPools = Array<boolean>(pools.length).fill(false);
11
+ const routes: TRoute[] = [];
12
+
13
+ const params = new ComputeRoutesParams(tokenIn, tokenOut, pools, maxHops);
14
+
15
+ _computeRoutes(
16
+ params,
17
+ [],
18
+ usedPools,
19
+ routes,
20
+ tokenIn
21
+ );
22
+
23
+ return routes;
24
+ }
25
+
26
+ function _computeRoutes(
27
+ params: ComputeRoutesParams,
28
+ currentRoute: TRouteStep[],
29
+ usedPools: boolean[],
30
+ foundRoutes: TRoute[],
31
+ previousTokenOut: string
32
+ ) {
33
+ if (currentRoute.length > params.maxHops) {
34
+ return;
35
+ }
36
+
37
+ if (routeFound(currentRoute, params)) {
38
+ foundRoutes.push({
39
+ steps: [...currentRoute],
40
+ tokenIn: params.tokenIn,
41
+ tokenOut: params.tokenOut
42
+ });
43
+ return;
44
+ }
45
+
46
+ for (let i = 0; i < params.pools.length; i++) {
47
+
48
+ if (usedPools[i]) {
49
+ continue;
50
+ }
51
+
52
+ const curPool = params.pools[i];
53
+
54
+ if (!curPool.containsToken(previousTokenOut)) {
55
+ continue;
56
+ }
57
+
58
+ const tokensToExplore = curPool.tokens.filter((token) => token._address.toLowerCase() !== previousTokenOut.toLowerCase());
59
+
60
+ for (let token of tokensToExplore) {
61
+ currentRoute.push({pool: curPool, tokenOut: token._address});
62
+ usedPools[i] = true;
63
+
64
+ _computeRoutes(
65
+ params,
66
+ currentRoute,
67
+ usedPools,
68
+ foundRoutes,
69
+ token._address
70
+ );
71
+
72
+ usedPools[i] = false;
73
+ currentRoute.pop();
74
+ }
75
+ }
76
+ }
77
+
78
+ function routeFound(route: TRouteStep[], params: ComputeRoutesParams): boolean {
79
+ return route.length > 0 && route[route.length - 1].tokenOut.toLowerCase() === params.tokenOut.toLowerCase();
80
+ }
81
+
@@ -0,0 +1,65 @@
1
+ import {Pool, Quote, Route, SwapStep} from "../../utils/types/types";
2
+ import {TQuoteUniLike, TRoute,} from "./types";
3
+ import computeRoutes from "./compute_routes_backtrack";
4
+ import calculateAmountDistribution from "./amount_distribution";
5
+ import {getRoutesWithQuotes} from "./routes_quoter";
6
+ import {SwapFinder} from "./swap_finder";
7
+ import {algoParams} from "./algo_config";
8
+
9
+ export function findRouteUniLikeAlgo(
10
+ tokenIn: string,
11
+ tokenOut: string,
12
+ amountIn: bigint,
13
+ pools: Pool[]
14
+ ): Quote {
15
+
16
+ const routes: TRoute[] = computeRoutes(tokenIn, tokenOut, pools, algoParams.maxHops);
17
+ const amounts = calculateAmountDistribution(amountIn, algoParams.distributionPercentage);
18
+ console.log("Amounts:", amounts);
19
+ console.log("Amounts size:", amounts.length);
20
+ console.log("Routes size:", routes.length);
21
+
22
+ const routesWithQuotes = getRoutesWithQuotes(routes, amounts);
23
+
24
+ const swapFinder = new SwapFinder(
25
+ algoParams,
26
+ routesWithQuotes,
27
+ amounts.map(amount => amount.percentage),
28
+ amountIn
29
+ );
30
+ const quote = swapFinder.findBestRoute();
31
+ console.log("UniLikeQuote:", quote);
32
+
33
+ return convertResponseToFoundQuoteType(quote);
34
+ }
35
+
36
+ function convertResponseToFoundQuoteType(q: TQuoteUniLike): Quote {
37
+ const routes = q.routes.map(r => {
38
+ const route = r.route;
39
+ let tokenIn = route.tokenIn;
40
+ let swaps: SwapStep[] = [];
41
+
42
+ route.steps.forEach(step => {
43
+ swaps.push({
44
+ poolId: step.pool.poolId,
45
+ dexId: step.pool.dexId,
46
+ tokenIn: tokenIn,
47
+ tokenOut: step.tokenOut
48
+ });
49
+
50
+ tokenIn = step.tokenOut;
51
+ });
52
+
53
+ return {
54
+ swaps: swaps,
55
+ amountIn: r.amount.amountIn,
56
+ percentage: r.amount.percentage,
57
+ quote: r.quote
58
+ } as Route;
59
+ });
60
+
61
+ return {
62
+ routes: routes,
63
+ quote: q.quote
64
+ };
65
+ }
@@ -0,0 +1,63 @@
1
+ import {AmountPercentage, TRoute, TRouteWithQuote} from "./types";
2
+
3
+ /**
4
+ * We will calcualte everything offchain. Why?
5
+ * Because if we want to call for quotes on uni, we will need separate call for each pool.
6
+ * We can have route that theoretically looks like this:
7
+ * sushiPool->uniPool->curvePool->uniPool->uniPool->balancerPool->uniPool
8
+ * Here, we can't batch any calls, because we need amount out from previous pool. There is no other way than
9
+ * to call for quote on each uni pool separately.
10
+ * If we have 40 routes, 10 splitted amounts, and 2 uni pool on average per route, we will need 800 rpc calls.
11
+ *
12
+ * We can batch it, but that would require to calculate everything on chain. Which is also doable to try if we have time.
13
+ *
14
+ * Start with calculating everything offchain, and check final result.
15
+ * The problem we can have is that for large trades we fetch only +- 15 ticks. Maybe that would be solved if
16
+ * we split amount on lets say 5% percentage or even 2%. That would be fast because we will have offchain calculation. The only
17
+ * concert is that we can end up with to many splits for large trade.
18
+ *
19
+ * */
20
+
21
+ export function getRoutesWithQuotes(
22
+ routes: TRoute[],
23
+ amounts: AmountPercentage[]
24
+ ): TRouteWithQuote[] {
25
+
26
+ const routesWithQuotes: TRouteWithQuote[][] = [];
27
+ routes.forEach(route => {
28
+ routesWithQuotes.push(getSingleRouteWithAllQuotes(route, amounts));
29
+ });
30
+ return routesWithQuotes.flat();
31
+ }
32
+
33
+ export function getSingleRouteWithAllQuotes(route: TRoute, amounts: AmountPercentage[]): TRouteWithQuote[] {
34
+ const routes: TRouteWithQuote[] = [];
35
+
36
+ amounts.forEach(amount => {
37
+ routes.push(getSingleRouteWithSingleQuote(route, amount));
38
+ });
39
+
40
+ return routes;
41
+ }
42
+
43
+ export function getSingleRouteWithSingleQuote(route: TRoute, amount: AmountPercentage): TRouteWithQuote {
44
+ let tokenIn = route.tokenIn;
45
+ let amountOut = amount.amountIn;
46
+
47
+ for (let step of route.steps) {
48
+ const tokenOut = step.tokenOut;
49
+
50
+ amountOut = step.pool.calculateExpectedOutputAmount(tokenIn, tokenOut, amountOut);
51
+
52
+ if (amountOut <= BigInt(0)) {
53
+ break;
54
+ }
55
+
56
+ tokenIn = tokenOut;
57
+ }
58
+ return {
59
+ route: route,
60
+ quote: amountOut,
61
+ amount: amount
62
+ }
63
+ }