@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
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findRouteWithIterativeSplitting = findRouteWithIterativeSplitting;
4
+ const my_local_storage_1 = require("../../swap/my_local_storage");
5
+ const multiHopSwap_1 = require("./multiHopSwap");
6
+ // Simple route key - just concatenate pool IDs (much faster than object-hash)
7
+ function getRouteKey(swaps) {
8
+ return swaps.map(s => s.poolId).join('→');
9
+ }
10
+ let graphCache = null;
11
+ const GRAPH_CACHE_TTL_MS = 2000; // 2 second cache
12
+ function getPoolsHash(pools) {
13
+ return pools.map(p => p.poolId).sort().join(',');
14
+ }
15
+ function getCachedGraph(pools) {
16
+ const poolsHash = getPoolsHash(pools);
17
+ const now = Date.now();
18
+ // Return cached graph if valid
19
+ if (graphCache &&
20
+ graphCache.poolIds === poolsHash &&
21
+ now - graphCache.timestamp < GRAPH_CACHE_TTL_MS) {
22
+ return graphCache.graph;
23
+ }
24
+ // Create new graph and cache it
25
+ const graph = (0, multiHopSwap_1.createGraph)(pools);
26
+ graphCache = {
27
+ graph,
28
+ poolIds: poolsHash,
29
+ timestamp: now
30
+ };
31
+ return graph;
32
+ }
33
+ /* Simple algorithm that splits the input amount into (100/step) parts of step% each and finds the best route for each split.
34
+ The algorithm to find the best route for each iteration finds the route with the highest output amount.
35
+ (code is seen in ./multiHopSwap.ts)
36
+ After each iteration, the pools are updated with the amounts that passed through them.
37
+ */
38
+ async function findRouteWithIterativeSplitting(tokenA, tokenB, amountIn, pools, chainId) {
39
+ const graph = getCachedGraph(pools);
40
+ // percentage of the amountIn that we split into (5% = 20 iterations, faster than 2% = 50 iterations)
41
+ const step = 5;
42
+ let amountOut = BigInt(0);
43
+ const poolMap = new Map(pools.map((pool) => [pool.poolId, pool]));
44
+ const routes = new Map();
45
+ const splitAmountIn = (amountIn * BigInt(step)) / BigInt(100);
46
+ for (let i = 0; i < 100; i += step) {
47
+ const route = (0, multiHopSwap_1.multiHopSwap)(splitAmountIn, tokenA, tokenB, graph);
48
+ const routeHash = getRouteKey(route.swaps);
49
+ let existingRoute = routes.get(routeHash);
50
+ if (!existingRoute) {
51
+ route.percentage = step;
52
+ routes.set(routeHash, route);
53
+ }
54
+ else {
55
+ existingRoute.percentage += step;
56
+ }
57
+ amountOut += route.quote;
58
+ updatePoolsInRoute(poolMap, route, splitAmountIn);
59
+ }
60
+ const foundRoutes = [];
61
+ for (let route of routes.values()) {
62
+ route.amountIn = (BigInt(route.percentage) * amountIn) / BigInt(100);
63
+ foundRoutes.push(route);
64
+ }
65
+ const missingAmount = amountIn - foundRoutes.reduce((acc, route) => acc + route.amountIn, BigInt(0));
66
+ foundRoutes[0].amountIn += missingAmount;
67
+ const quote = { routes: foundRoutes, quote: amountOut };
68
+ let total = BigInt(0);
69
+ const resetPools = new Set();
70
+ for (const route of quote.routes) {
71
+ let progress = route.amountIn;
72
+ for (const swap of route.swaps) {
73
+ const pool = my_local_storage_1.myLocalStorage.getItem(swap.poolId.toLowerCase());
74
+ if (!pool)
75
+ throw Error("Error caching pools");
76
+ if (!resetPools.has(swap.poolId.toLowerCase())) {
77
+ pool.reset();
78
+ resetPools.add(swap.poolId.toLowerCase());
79
+ }
80
+ const amount = pool.calculateExpectedOutputAmount(swap.tokenIn, swap.tokenOut, progress);
81
+ pool.update(swap.tokenIn, swap.tokenOut, progress, amount);
82
+ progress = amount;
83
+ }
84
+ route.quote = progress;
85
+ total += progress;
86
+ }
87
+ quote.quote = total;
88
+ if (quote.routes[0].swaps.length == 0)
89
+ quote.quote = BigInt(0);
90
+ return quote;
91
+ }
92
+ // Function to update all the pools in a route with the amounts that passed through them
93
+ function updatePoolsInRoute(poolMap, route, amountIn) {
94
+ for (let swap of route.swaps) {
95
+ const pool = poolMap.get(swap.poolId);
96
+ if (!pool) {
97
+ console.log('Pool ', swap.poolId, " doesn't exist!");
98
+ continue;
99
+ }
100
+ const amountOut = pool.calculateExpectedOutputAmount(swap.tokenIn, swap.tokenOut, amountIn);
101
+ pool.update(swap.tokenIn, swap.tokenOut, amountIn, amountOut);
102
+ amountIn = amountOut;
103
+ }
104
+ }
@@ -0,0 +1,4 @@
1
+ import { Route, Pool } from '../../utils/types/types';
2
+ declare function multiHopSwap(amountIn: bigint, tokenIn: string, tokenOut: string, graph: Map<string, Pool[]>): Route;
3
+ declare function createGraph(pools: Pool[]): Map<string, Pool[]>;
4
+ export { multiHopSwap, createGraph };
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.multiHopSwap = multiHopSwap;
4
+ exports.createGraph = createGraph;
5
+ const max_hops = 4;
6
+ /* The algorithm to find the best route for each iteration (highest output amount) is seen below.
7
+ * It is based on dynamic programming.
8
+ * @param amountIn: The amount of tokenIn that we want to swap (in wei)
9
+ * @param tokenIn: The address of the token we want to swap (address on Arbitrum)
10
+ * @param tokenOut: The address of the token we want to receive (address on Arbitrum)
11
+ * @param graph: The graph of all the fetched pools
12
+ */
13
+ function multiHopSwap(amountIn, tokenIn, tokenOut, graph) {
14
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
15
+ tokenIn = tokenIn.toLowerCase();
16
+ tokenOut = tokenOut.toLowerCase();
17
+ // dp[hop][token]
18
+ const dp = new Map();
19
+ dp.set(0, new Map());
20
+ (_a = dp.get(0)) === null || _a === void 0 ? void 0 : _a.set(tokenIn, { amountOut: amountIn, path: [tokenIn], swaps: [] });
21
+ const res = { amountOut: BigInt(-1), path: [], swaps: [] };
22
+ for (let hop = 0; hop < max_hops - 1; hop++) {
23
+ (_b = dp.get(hop)) === null || _b === void 0 ? void 0 : _b.forEach((dpInfo, tokenA) => {
24
+ var _a;
25
+ (_a = graph.get(tokenA)) === null || _a === void 0 ? void 0 : _a.forEach((pool) => {
26
+ pool.tokens.forEach((tokenB) => {
27
+ var _a, _b, _c;
28
+ if (dpInfo.path.includes(tokenB._address)) {
29
+ return;
30
+ }
31
+ // console.log(hop, pool.poolId, tokenA, tokenB._address, dpInfo.amountOut)
32
+ const amountOut = pool.calculateExpectedOutputAmount(tokenA, tokenB._address, dpInfo.amountOut);
33
+ if (amountOut <= 0) {
34
+ return;
35
+ }
36
+ const newPath = [...dpInfo.path, tokenB._address];
37
+ const currSwap = { poolId: pool.poolId, dexId: pool.dexId, tokenIn: tokenA, tokenOut: tokenB._address };
38
+ const newSwaps = [...dpInfo.swaps, currSwap];
39
+ if (!dp.has(hop + 1)) {
40
+ dp.set(hop + 1, new Map());
41
+ }
42
+ const dpEntry = dp.get(hop + 1);
43
+ if (!(dpEntry === null || dpEntry === void 0 ? void 0 : dpEntry.has(tokenB._address))) {
44
+ (_a = dp.get(hop + 1)) === null || _a === void 0 ? void 0 : _a.set(tokenB._address, { amountOut: amountOut, path: newPath, swaps: newSwaps });
45
+ }
46
+ else if (amountOut > (((_b = dpEntry === null || dpEntry === void 0 ? void 0 : dpEntry.get(tokenB._address)) === null || _b === void 0 ? void 0 : _b.amountOut) || 0)) {
47
+ (_c = dp.get(hop + 1)) === null || _c === void 0 ? void 0 : _c.set(tokenB._address, { amountOut: amountOut, path: newPath, swaps: newSwaps });
48
+ }
49
+ });
50
+ });
51
+ });
52
+ if (((_c = dp.get(hop + 1)) === null || _c === void 0 ? void 0 : _c.has(tokenOut)) && (((_e = (_d = dp.get(hop + 1)) === null || _d === void 0 ? void 0 : _d.get(tokenOut)) === null || _e === void 0 ? void 0 : _e.amountOut) || -1) > res.amountOut) {
53
+ res.amountOut = ((_g = (_f = dp.get(hop + 1)) === null || _f === void 0 ? void 0 : _f.get(tokenOut)) === null || _g === void 0 ? void 0 : _g.amountOut) || BigInt(0);
54
+ res.path = ((_j = (_h = dp.get(hop + 1)) === null || _h === void 0 ? void 0 : _h.get(tokenOut)) === null || _j === void 0 ? void 0 : _j.path) || [];
55
+ res.swaps = ((_l = (_k = dp.get(hop + 1)) === null || _k === void 0 ? void 0 : _k.get(tokenOut)) === null || _l === void 0 ? void 0 : _l.swaps) || [];
56
+ }
57
+ }
58
+ return {
59
+ swaps: res.swaps,
60
+ quote: res.amountOut,
61
+ percentage: 0,
62
+ amountIn: BigInt(0) // will be set in iterative splitting when we know percentage
63
+ };
64
+ }
65
+ /* Function to create a graph from all the fetched pools
66
+ * Graph maps every token to a list of pools that token is in
67
+ * @param pools: The fetched pools
68
+ * @returns The graph
69
+ */
70
+ function createGraph(pools) {
71
+ var _a;
72
+ const graph = new Map();
73
+ for (let pool of pools) {
74
+ const poolId = pool.poolId;
75
+ for (let token of pool.tokens) {
76
+ if (!graph.has(token._address)) {
77
+ graph.set(token._address, []);
78
+ }
79
+ (_a = graph.get(token._address)) === null || _a === void 0 ? void 0 : _a.push(pool);
80
+ }
81
+ }
82
+ return graph;
83
+ }
@@ -0,0 +1,2 @@
1
+ import { Quote, Pool } from "../utils/types/types";
2
+ export declare function findRoute(tokenIn: string, tokenOut: string, amountIn: bigint, pools: Pool[], chainId: number): Promise<Quote>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findRoute = findRoute;
4
+ const main_1 = require("./uni_like_algo/main");
5
+ const main_2 = require("./iterative_spliting/main");
6
+ /**
7
+ * This algo works good if we are using pools with two tokens,
8
+ * but it doesn't scale well for pools with more than two tokens because
9
+ * amount of routes rapidly increases with number of tokens in pool as there are more ways to go
10
+ * from token A to token B. This makes algo really slow, especially slow if we include curve dex.
11
+ * That being said, it will stay here as a reference for future algo ideas and improvements but right
12
+ * now iterative splitting is way to go.
13
+ * */
14
+ const UNI_LIKE_ALGO_ACTIVE = false;
15
+ async function findRoute(tokenIn, tokenOut, amountIn, pools, chainId) {
16
+ if (UNI_LIKE_ALGO_ACTIVE) {
17
+ return (0, main_1.findRouteUniLikeAlgo)(tokenIn, tokenOut, amountIn, pools);
18
+ }
19
+ else {
20
+ return (await (0, main_2.findRouteWithIterativeSplitting)(tokenIn, tokenOut, amountIn, pools, chainId));
21
+ }
22
+ }
@@ -0,0 +1,2 @@
1
+ import { AlgoParams } from "./types";
2
+ export declare const algoParams: AlgoParams;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.algoParams = void 0;
4
+ exports.algoParams = {
5
+ maxHops: 4,
6
+ distributionPercentage: 2,
7
+ maxSplit: 8
8
+ };
@@ -0,0 +1,2 @@
1
+ import { AmountPercentage } from "./types";
2
+ export default function calculateAmountDistribution(amountIn: bigint, distributionPercentage: number): AmountPercentage[];
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = calculateAmountDistribution;
4
+ function calculateAmountDistribution(amountIn, distributionPercentage) {
5
+ const percentages = [];
6
+ const amounts = [];
7
+ for (let i = 1; i <= 100 / distributionPercentage; ++i) {
8
+ percentages.push(distributionPercentage * i);
9
+ amounts.push(amountIn * BigInt(distributionPercentage * i) / BigInt(100));
10
+ }
11
+ return amounts.map((amount, index) => {
12
+ return {
13
+ amountIn: amount,
14
+ percentage: percentages[index]
15
+ };
16
+ });
17
+ }
@@ -0,0 +1,3 @@
1
+ import { Pool } from "../../utils/types/types";
2
+ import { TRoute } from "./types";
3
+ export default function computeRoutes(tokenIn: string, tokenOut: string, pools: Pool[], maxHops: number): TRoute[];
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = computeRoutes;
4
+ const types_1 = require("./types");
5
+ function computeRoutes(tokenIn, tokenOut, pools, maxHops) {
6
+ const usedPools = Array(pools.length).fill(false);
7
+ const routes = [];
8
+ const params = new types_1.ComputeRoutesParams(tokenIn, tokenOut, pools, maxHops);
9
+ _computeRoutes(params, [], usedPools, routes, tokenIn);
10
+ return routes;
11
+ }
12
+ function _computeRoutes(params, currentRoute, usedPools, foundRoutes, previousTokenOut) {
13
+ if (currentRoute.length > params.maxHops) {
14
+ return;
15
+ }
16
+ if (routeFound(currentRoute, params)) {
17
+ foundRoutes.push({
18
+ steps: [...currentRoute],
19
+ tokenIn: params.tokenIn,
20
+ tokenOut: params.tokenOut
21
+ });
22
+ return;
23
+ }
24
+ for (let i = 0; i < params.pools.length; i++) {
25
+ if (usedPools[i]) {
26
+ continue;
27
+ }
28
+ const curPool = params.pools[i];
29
+ if (!curPool.containsToken(previousTokenOut)) {
30
+ continue;
31
+ }
32
+ const tokensToExplore = curPool.tokens.filter((token) => token._address.toLowerCase() !== previousTokenOut.toLowerCase());
33
+ for (let token of tokensToExplore) {
34
+ currentRoute.push({ pool: curPool, tokenOut: token._address });
35
+ usedPools[i] = true;
36
+ _computeRoutes(params, currentRoute, usedPools, foundRoutes, token._address);
37
+ usedPools[i] = false;
38
+ currentRoute.pop();
39
+ }
40
+ }
41
+ }
42
+ function routeFound(route, params) {
43
+ return route.length > 0 && route[route.length - 1].tokenOut.toLowerCase() === params.tokenOut.toLowerCase();
44
+ }
@@ -0,0 +1,2 @@
1
+ import { Pool, Quote } from "../../utils/types/types";
2
+ export declare function findRouteUniLikeAlgo(tokenIn: string, tokenOut: string, amountIn: bigint, pools: Pool[]): Quote;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.findRouteUniLikeAlgo = findRouteUniLikeAlgo;
7
+ const compute_routes_backtrack_1 = __importDefault(require("./compute_routes_backtrack"));
8
+ const amount_distribution_1 = __importDefault(require("./amount_distribution"));
9
+ const routes_quoter_1 = require("./routes_quoter");
10
+ const swap_finder_1 = require("./swap_finder");
11
+ const algo_config_1 = require("./algo_config");
12
+ function findRouteUniLikeAlgo(tokenIn, tokenOut, amountIn, pools) {
13
+ const routes = (0, compute_routes_backtrack_1.default)(tokenIn, tokenOut, pools, algo_config_1.algoParams.maxHops);
14
+ const amounts = (0, amount_distribution_1.default)(amountIn, algo_config_1.algoParams.distributionPercentage);
15
+ console.log("Amounts:", amounts);
16
+ console.log("Amounts size:", amounts.length);
17
+ console.log("Routes size:", routes.length);
18
+ const routesWithQuotes = (0, routes_quoter_1.getRoutesWithQuotes)(routes, amounts);
19
+ const swapFinder = new swap_finder_1.SwapFinder(algo_config_1.algoParams, routesWithQuotes, amounts.map(amount => amount.percentage), amountIn);
20
+ const quote = swapFinder.findBestRoute();
21
+ console.log("UniLikeQuote:", quote);
22
+ return convertResponseToFoundQuoteType(quote);
23
+ }
24
+ function convertResponseToFoundQuoteType(q) {
25
+ const routes = q.routes.map(r => {
26
+ const route = r.route;
27
+ let tokenIn = route.tokenIn;
28
+ let swaps = [];
29
+ route.steps.forEach(step => {
30
+ swaps.push({
31
+ poolId: step.pool.poolId,
32
+ dexId: step.pool.dexId,
33
+ tokenIn: tokenIn,
34
+ tokenOut: step.tokenOut
35
+ });
36
+ tokenIn = step.tokenOut;
37
+ });
38
+ return {
39
+ swaps: swaps,
40
+ amountIn: r.amount.amountIn,
41
+ percentage: r.amount.percentage,
42
+ quote: r.quote
43
+ };
44
+ });
45
+ return {
46
+ routes: routes,
47
+ quote: q.quote
48
+ };
49
+ }
@@ -0,0 +1,21 @@
1
+ import { AmountPercentage, TRoute, TRouteWithQuote } from "./types";
2
+ /**
3
+ * We will calcualte everything offchain. Why?
4
+ * Because if we want to call for quotes on uni, we will need separate call for each pool.
5
+ * We can have route that theoretically looks like this:
6
+ * sushiPool->uniPool->curvePool->uniPool->uniPool->balancerPool->uniPool
7
+ * Here, we can't batch any calls, because we need amount out from previous pool. There is no other way than
8
+ * to call for quote on each uni pool separately.
9
+ * If we have 40 routes, 10 splitted amounts, and 2 uni pool on average per route, we will need 800 rpc calls.
10
+ *
11
+ * We can batch it, but that would require to calculate everything on chain. Which is also doable to try if we have time.
12
+ *
13
+ * Start with calculating everything offchain, and check final result.
14
+ * The problem we can have is that for large trades we fetch only +- 15 ticks. Maybe that would be solved if
15
+ * we split amount on lets say 5% percentage or even 2%. That would be fast because we will have offchain calculation. The only
16
+ * concert is that we can end up with to many splits for large trade.
17
+ *
18
+ * */
19
+ export declare function getRoutesWithQuotes(routes: TRoute[], amounts: AmountPercentage[]): TRouteWithQuote[];
20
+ export declare function getSingleRouteWithAllQuotes(route: TRoute, amounts: AmountPercentage[]): TRouteWithQuote[];
21
+ export declare function getSingleRouteWithSingleQuote(route: TRoute, amount: AmountPercentage): TRouteWithQuote;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRoutesWithQuotes = getRoutesWithQuotes;
4
+ exports.getSingleRouteWithAllQuotes = getSingleRouteWithAllQuotes;
5
+ exports.getSingleRouteWithSingleQuote = getSingleRouteWithSingleQuote;
6
+ /**
7
+ * We will calcualte everything offchain. Why?
8
+ * Because if we want to call for quotes on uni, we will need separate call for each pool.
9
+ * We can have route that theoretically looks like this:
10
+ * sushiPool->uniPool->curvePool->uniPool->uniPool->balancerPool->uniPool
11
+ * Here, we can't batch any calls, because we need amount out from previous pool. There is no other way than
12
+ * to call for quote on each uni pool separately.
13
+ * If we have 40 routes, 10 splitted amounts, and 2 uni pool on average per route, we will need 800 rpc calls.
14
+ *
15
+ * We can batch it, but that would require to calculate everything on chain. Which is also doable to try if we have time.
16
+ *
17
+ * Start with calculating everything offchain, and check final result.
18
+ * The problem we can have is that for large trades we fetch only +- 15 ticks. Maybe that would be solved if
19
+ * we split amount on lets say 5% percentage or even 2%. That would be fast because we will have offchain calculation. The only
20
+ * concert is that we can end up with to many splits for large trade.
21
+ *
22
+ * */
23
+ function getRoutesWithQuotes(routes, amounts) {
24
+ const routesWithQuotes = [];
25
+ routes.forEach(route => {
26
+ routesWithQuotes.push(getSingleRouteWithAllQuotes(route, amounts));
27
+ });
28
+ return routesWithQuotes.flat();
29
+ }
30
+ function getSingleRouteWithAllQuotes(route, amounts) {
31
+ const routes = [];
32
+ amounts.forEach(amount => {
33
+ routes.push(getSingleRouteWithSingleQuote(route, amount));
34
+ });
35
+ return routes;
36
+ }
37
+ function getSingleRouteWithSingleQuote(route, amount) {
38
+ let tokenIn = route.tokenIn;
39
+ let amountOut = amount.amountIn;
40
+ for (let step of route.steps) {
41
+ const tokenOut = step.tokenOut;
42
+ amountOut = step.pool.calculateExpectedOutputAmount(tokenIn, tokenOut, amountOut);
43
+ if (amountOut <= BigInt(0)) {
44
+ break;
45
+ }
46
+ tokenIn = tokenOut;
47
+ }
48
+ return {
49
+ route: route,
50
+ quote: amountOut,
51
+ amount: amount
52
+ };
53
+ }
@@ -0,0 +1,25 @@
1
+ import { AlgoParams, TQuoteUniLike, TRouteWithQuote } from "./types";
2
+ export declare class SwapFinder {
3
+ private algoParams;
4
+ private percentagesToSortedQuotes;
5
+ private percentages;
6
+ private amountIn;
7
+ private bestQuote;
8
+ private bestSwap;
9
+ private queue;
10
+ private numOfSplits;
11
+ constructor(algoParams: AlgoParams, routesWithQuotes: TRouteWithQuote[], percentages: number[], amountIn: bigint);
12
+ private readyToFinishSplitting;
13
+ findBestRoute(): TQuoteUniLike;
14
+ private addMissingAmountIn;
15
+ private processLayer;
16
+ private processPairedItemsInLayer;
17
+ private processPairedItem;
18
+ private updateBestQuoteAndSwapIfBetter;
19
+ private initBestQuoteAndSwapForFullAmount;
20
+ private initQueueWithHighestQuotes;
21
+ private insertHigestQuoteForPercentageIfExist;
22
+ private insertSecondHigestQuoteForPercentageIfExist;
23
+ private getSortedQuotes;
24
+ private findFirstRouteNotUsingUsedPools;
25
+ }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SwapFinder = void 0;
7
+ const queue_1 = __importDefault(require("mnemonist/queue"));
8
+ class SwapFinder {
9
+ constructor(algoParams, routesWithQuotes, percentages, amountIn) {
10
+ this.algoParams = algoParams;
11
+ this.percentages = percentages;
12
+ this.amountIn = amountIn;
13
+ this.percentagesToSortedQuotes = this.getSortedQuotes(routesWithQuotes);
14
+ this.bestQuote = BigInt(0);
15
+ this.bestSwap = [];
16
+ this.queue = new queue_1.default();
17
+ this.numOfSplits = 1;
18
+ }
19
+ readyToFinishSplitting() {
20
+ const maxSplitsReached = this.numOfSplits > this.algoParams.maxSplit;
21
+ const bestSwapNotImprovedWithNewSplit = this.numOfSplits >= 3 && (this.bestSwap.length < this.numOfSplits - 1);
22
+ return maxSplitsReached || bestSwapNotImprovedWithNewSplit;
23
+ }
24
+ findBestRoute() {
25
+ this.initBestQuoteAndSwapForFullAmount();
26
+ this.initQueueWithHighestQuotes();
27
+ while (this.queue.size > 0) {
28
+ let layer = this.queue.size;
29
+ this.numOfSplits++;
30
+ if (this.readyToFinishSplitting()) {
31
+ break;
32
+ }
33
+ this.processLayer(layer);
34
+ }
35
+ this.addMissingAmountIn();
36
+ return {
37
+ quote: this.bestQuote,
38
+ routes: this.bestSwap
39
+ };
40
+ }
41
+ addMissingAmountIn() {
42
+ const totalAmountIn = this.bestSwap.reduce((acc, route) => acc + route.amount.amountIn, BigInt(0));
43
+ const diff = this.amountIn - totalAmountIn;
44
+ if (diff > BigInt(0)) {
45
+ this.bestSwap[this.bestSwap.length - 1].amount.amountIn += diff;
46
+ }
47
+ }
48
+ processLayer(layer) {
49
+ while (layer > 0) {
50
+ layer--;
51
+ const q = this.queue.dequeue();
52
+ this.processPairedItemsInLayer(q);
53
+ }
54
+ }
55
+ processPairedItemsInLayer(q) {
56
+ for (let i = q.percentageIndex; i >= 0; --i) {
57
+ const percentage = this.percentages[i];
58
+ if (percentage > q.ramainingPercentage || !this.percentagesToSortedQuotes.has(percentage)) {
59
+ continue;
60
+ }
61
+ this.processPairedItem(q, percentage, i);
62
+ }
63
+ }
64
+ processPairedItem(q, percentage, index) {
65
+ const candidateRoutes = this.percentagesToSortedQuotes.get(percentage);
66
+ const routeWithQuote = this.findFirstRouteNotUsingUsedPools(q.currentRoutes, candidateRoutes);
67
+ if (!routeWithQuote) {
68
+ return;
69
+ }
70
+ const newRemainingPercentage = q.ramainingPercentage - percentage;
71
+ const newCurrentRoutes = [...q.currentRoutes, routeWithQuote];
72
+ if (newRemainingPercentage === 0) {
73
+ this.updateBestQuoteAndSwapIfBetter(newCurrentRoutes);
74
+ }
75
+ else {
76
+ this.queue.enqueue({
77
+ percentageIndex: index,
78
+ currentRoutes: newCurrentRoutes,
79
+ ramainingPercentage: newRemainingPercentage
80
+ });
81
+ }
82
+ }
83
+ updateBestQuoteAndSwapIfBetter(currentRoutes) {
84
+ const quote = currentRoutes.reduce((acc, route) => acc + route.quote, BigInt(0));
85
+ if (quote > this.bestQuote) {
86
+ this.bestQuote = quote;
87
+ this.bestSwap = currentRoutes;
88
+ }
89
+ }
90
+ initBestQuoteAndSwapForFullAmount() {
91
+ if (this.percentagesToSortedQuotes.has(100)) {
92
+ this.bestQuote = this.percentagesToSortedQuotes.get(100)[0].quote;
93
+ this.bestSwap = [this.percentagesToSortedQuotes.get(100)[0]];
94
+ }
95
+ }
96
+ initQueueWithHighestQuotes() {
97
+ for (let i = this.percentages.length - 1; i >= 0; --i) {
98
+ this.insertHigestQuoteForPercentageIfExist(i);
99
+ this.insertSecondHigestQuoteForPercentageIfExist(i);
100
+ }
101
+ }
102
+ insertHigestQuoteForPercentageIfExist(percentageIndex) {
103
+ const percentage = this.percentages[percentageIndex];
104
+ if (this.percentagesToSortedQuotes.has(percentage)) {
105
+ this.queue.enqueue({
106
+ percentageIndex: percentageIndex,
107
+ currentRoutes: [this.percentagesToSortedQuotes.get(percentage)[0]],
108
+ ramainingPercentage: 100 - percentage
109
+ });
110
+ }
111
+ }
112
+ insertSecondHigestQuoteForPercentageIfExist(percentageIndex) {
113
+ const percentage = this.percentages[percentageIndex];
114
+ if (this.percentagesToSortedQuotes.get(percentage)[1]) {
115
+ this.queue.enqueue({
116
+ percentageIndex: percentageIndex,
117
+ currentRoutes: [this.percentagesToSortedQuotes.get(percentage)[1]],
118
+ ramainingPercentage: 100 - percentage
119
+ });
120
+ }
121
+ }
122
+ getSortedQuotes(routeWithQuotes) {
123
+ const map = new Map();
124
+ routeWithQuotes.forEach(routeWithQuote => {
125
+ var _a;
126
+ const percentage = routeWithQuote.amount.percentage;
127
+ if (!map.has(percentage)) {
128
+ map.set(percentage, []);
129
+ }
130
+ (_a = map.get(percentage)) === null || _a === void 0 ? void 0 : _a.push(routeWithQuote);
131
+ });
132
+ map.forEach((value, key) => {
133
+ value.sort((a, b) => {
134
+ return Number(b.quote - a.quote);
135
+ });
136
+ });
137
+ return map;
138
+ }
139
+ findFirstRouteNotUsingUsedPools(usedRoutes, candidateRoutes) {
140
+ const usedPoolsSet = new Set();
141
+ usedRoutes.forEach(route => {
142
+ route.route.steps.forEach(step => usedPoolsSet.add(step.pool.poolId));
143
+ });
144
+ for (const candidateRoute of candidateRoutes) {
145
+ const candidatePools = candidateRoute.route.steps.map(step => step.pool.poolId);
146
+ if (candidatePools.some(pool => usedPoolsSet.has(pool))) {
147
+ continue;
148
+ }
149
+ return candidateRoute;
150
+ }
151
+ return null;
152
+ }
153
+ }
154
+ exports.SwapFinder = SwapFinder;
@@ -0,0 +1,40 @@
1
+ import { Pool } from "../../utils/types/types";
2
+ export interface TRouteStep {
3
+ pool: Pool;
4
+ tokenOut: string;
5
+ }
6
+ export interface TRoute {
7
+ steps: TRouteStep[];
8
+ tokenIn: string;
9
+ tokenOut: string;
10
+ }
11
+ export interface TRouteWithQuote {
12
+ route: TRoute;
13
+ quote: bigint;
14
+ amount: AmountPercentage;
15
+ }
16
+ export interface AmountPercentage {
17
+ amountIn: bigint;
18
+ percentage: number;
19
+ }
20
+ export declare class ComputeRoutesParams {
21
+ tokenIn: string;
22
+ tokenOut: string;
23
+ pools: Pool[];
24
+ maxHops: number;
25
+ constructor(tokenIn: string, tokenOut: string, pools: Pool[], maxHops: number);
26
+ }
27
+ export interface QueueItem {
28
+ percentageIndex: number;
29
+ currentRoutes: TRouteWithQuote[];
30
+ ramainingPercentage: number;
31
+ }
32
+ export interface TQuoteUniLike {
33
+ routes: TRouteWithQuote[];
34
+ quote: bigint;
35
+ }
36
+ export type AlgoParams = {
37
+ maxHops: number;
38
+ distributionPercentage: number;
39
+ maxSplit: number;
40
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ComputeRoutesParams = void 0;
4
+ class ComputeRoutesParams {
5
+ constructor(tokenIn, tokenOut, pools, maxHops) {
6
+ this.tokenIn = tokenIn;
7
+ this.tokenOut = tokenOut;
8
+ this.pools = pools;
9
+ this.maxHops = maxHops;
10
+ }
11
+ }
12
+ exports.ComputeRoutesParams = ComputeRoutesParams;