imm-cli 0.1.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.
- package/README.md +315 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +251 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/immbook.d.ts +16 -0
- package/dist/commands/immbook.d.ts.map +1 -0
- package/dist/commands/immbook.js +795 -0
- package/dist/commands/immbook.js.map +1 -0
- package/dist/commands/jaine.d.ts +3 -0
- package/dist/commands/jaine.d.ts.map +1 -0
- package/dist/commands/jaine.js +1397 -0
- package/dist/commands/jaine.js.map +1 -0
- package/dist/commands/send.d.ts +3 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +229 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +83 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/slop-app.d.ts +9 -0
- package/dist/commands/slop-app.d.ts.map +1 -0
- package/dist/commands/slop-app.js +793 -0
- package/dist/commands/slop-app.js.map +1 -0
- package/dist/commands/slop.d.ts +3 -0
- package/dist/commands/slop.d.ts.map +1 -0
- package/dist/commands/slop.js +1053 -0
- package/dist/commands/slop.js.map +1 -0
- package/dist/commands/wallet.d.ts +3 -0
- package/dist/commands/wallet.d.ts.map +1 -0
- package/dist/commands/wallet.js +298 -0
- package/dist/commands/wallet.js.map +1 -0
- package/dist/config/paths.d.ts +6 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +24 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/store.d.ts +44 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +109 -0
- package/dist/config/store.js.map +1 -0
- package/dist/constants/chain.d.ts +56 -0
- package/dist/constants/chain.d.ts.map +1 -0
- package/dist/constants/chain.js +50 -0
- package/dist/constants/chain.js.map +1 -0
- package/dist/errors.d.ts +86 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +100 -0
- package/dist/errors.js.map +1 -0
- package/dist/immbook/api.d.ts +38 -0
- package/dist/immbook/api.d.ts.map +1 -0
- package/dist/immbook/api.js +86 -0
- package/dist/immbook/api.js.map +1 -0
- package/dist/immbook/auth.d.ts +31 -0
- package/dist/immbook/auth.d.ts.map +1 -0
- package/dist/immbook/auth.js +93 -0
- package/dist/immbook/auth.js.map +1 -0
- package/dist/immbook/comments.d.ts +26 -0
- package/dist/immbook/comments.d.ts.map +1 -0
- package/dist/immbook/comments.js +20 -0
- package/dist/immbook/comments.js.map +1 -0
- package/dist/immbook/follows.d.ts +19 -0
- package/dist/immbook/follows.d.ts.map +1 -0
- package/dist/immbook/follows.js +21 -0
- package/dist/immbook/follows.js.map +1 -0
- package/dist/immbook/jwtCache.d.ts +15 -0
- package/dist/immbook/jwtCache.d.ts.map +1 -0
- package/dist/immbook/jwtCache.js +63 -0
- package/dist/immbook/jwtCache.js.map +1 -0
- package/dist/immbook/points.d.ts +35 -0
- package/dist/immbook/points.d.ts.map +1 -0
- package/dist/immbook/points.js +20 -0
- package/dist/immbook/points.js.map +1 -0
- package/dist/immbook/posts.d.ts +46 -0
- package/dist/immbook/posts.d.ts.map +1 -0
- package/dist/immbook/posts.js +43 -0
- package/dist/immbook/posts.js.map +1 -0
- package/dist/immbook/profile.d.ts +29 -0
- package/dist/immbook/profile.d.ts.map +1 -0
- package/dist/immbook/profile.js +14 -0
- package/dist/immbook/profile.js.map +1 -0
- package/dist/immbook/submolts.d.ts +22 -0
- package/dist/immbook/submolts.d.ts.map +1 -0
- package/dist/immbook/submolts.js +24 -0
- package/dist/immbook/submolts.js.map +1 -0
- package/dist/immbook/tradeProof.d.ts +21 -0
- package/dist/immbook/tradeProof.d.ts.map +1 -0
- package/dist/immbook/tradeProof.js +14 -0
- package/dist/immbook/tradeProof.js.map +1 -0
- package/dist/immbook/votes.d.ts +17 -0
- package/dist/immbook/votes.d.ts.map +1 -0
- package/dist/immbook/votes.js +20 -0
- package/dist/immbook/votes.js.map +1 -0
- package/dist/intents/store.d.ts +22 -0
- package/dist/intents/store.d.ts.map +1 -0
- package/dist/intents/store.js +76 -0
- package/dist/intents/store.js.map +1 -0
- package/dist/intents/types.d.ts +21 -0
- package/dist/intents/types.d.ts.map +1 -0
- package/dist/intents/types.js +2 -0
- package/dist/intents/types.js.map +1 -0
- package/dist/jaine/abi/erc20.d.ts +90 -0
- package/dist/jaine/abi/erc20.d.ts.map +1 -0
- package/dist/jaine/abi/erc20.js +65 -0
- package/dist/jaine/abi/erc20.js.map +1 -0
- package/dist/jaine/abi/factory.d.ts +38 -0
- package/dist/jaine/abi/factory.d.ts.map +1 -0
- package/dist/jaine/abi/factory.js +26 -0
- package/dist/jaine/abi/factory.js.map +1 -0
- package/dist/jaine/abi/index.d.ts +11 -0
- package/dist/jaine/abi/index.d.ts.map +1 -0
- package/dist/jaine/abi/index.js +11 -0
- package/dist/jaine/abi/index.js.map +1 -0
- package/dist/jaine/abi/nftManager.d.ts +282 -0
- package/dist/jaine/abi/nftManager.d.ts.map +1 -0
- package/dist/jaine/abi/nftManager.js +182 -0
- package/dist/jaine/abi/nftManager.js.map +1 -0
- package/dist/jaine/abi/pool.d.ts +77 -0
- package/dist/jaine/abi/pool.d.ts.map +1 -0
- package/dist/jaine/abi/pool.js +56 -0
- package/dist/jaine/abi/pool.js.map +1 -0
- package/dist/jaine/abi/quoter.d.ts +84 -0
- package/dist/jaine/abi/quoter.d.ts.map +1 -0
- package/dist/jaine/abi/quoter.js +53 -0
- package/dist/jaine/abi/quoter.js.map +1 -0
- package/dist/jaine/abi/router.d.ts +135 -0
- package/dist/jaine/abi/router.d.ts.map +1 -0
- package/dist/jaine/abi/router.js +88 -0
- package/dist/jaine/abi/router.js.map +1 -0
- package/dist/jaine/abi/w0g.d.ts +41 -0
- package/dist/jaine/abi/w0g.d.ts.map +1 -0
- package/dist/jaine/abi/w0g.js +34 -0
- package/dist/jaine/abi/w0g.js.map +1 -0
- package/dist/jaine/allowance.d.ts +48 -0
- package/dist/jaine/allowance.d.ts.map +1 -0
- package/dist/jaine/allowance.js +192 -0
- package/dist/jaine/allowance.js.map +1 -0
- package/dist/jaine/coreTokens.d.ts +32 -0
- package/dist/jaine/coreTokens.d.ts.map +1 -0
- package/dist/jaine/coreTokens.js +91 -0
- package/dist/jaine/coreTokens.js.map +1 -0
- package/dist/jaine/pathEncoding.d.ts +39 -0
- package/dist/jaine/pathEncoding.d.ts.map +1 -0
- package/dist/jaine/pathEncoding.js +98 -0
- package/dist/jaine/pathEncoding.js.map +1 -0
- package/dist/jaine/paths.d.ts +11 -0
- package/dist/jaine/paths.d.ts.map +1 -0
- package/dist/jaine/paths.js +20 -0
- package/dist/jaine/paths.js.map +1 -0
- package/dist/jaine/poolCache.d.ts +42 -0
- package/dist/jaine/poolCache.d.ts.map +1 -0
- package/dist/jaine/poolCache.js +164 -0
- package/dist/jaine/poolCache.js.map +1 -0
- package/dist/jaine/routing.d.ts +41 -0
- package/dist/jaine/routing.d.ts.map +1 -0
- package/dist/jaine/routing.js +247 -0
- package/dist/jaine/routing.js.map +1 -0
- package/dist/jaine/userTokens.d.ts +27 -0
- package/dist/jaine/userTokens.d.ts.map +1 -0
- package/dist/jaine/userTokens.js +89 -0
- package/dist/jaine/userTokens.js.map +1 -0
- package/dist/slop/abi/factory.d.ts +128 -0
- package/dist/slop/abi/factory.d.ts.map +1 -0
- package/dist/slop/abi/factory.js +70 -0
- package/dist/slop/abi/factory.js.map +1 -0
- package/dist/slop/abi/feeCollector.d.ts +95 -0
- package/dist/slop/abi/feeCollector.d.ts.map +1 -0
- package/dist/slop/abi/feeCollector.js +71 -0
- package/dist/slop/abi/feeCollector.js.map +1 -0
- package/dist/slop/abi/index.d.ts +5 -0
- package/dist/slop/abi/index.d.ts.map +1 -0
- package/dist/slop/abi/index.js +5 -0
- package/dist/slop/abi/index.js.map +1 -0
- package/dist/slop/abi/registry.d.ts +135 -0
- package/dist/slop/abi/registry.d.ts.map +1 -0
- package/dist/slop/abi/registry.js +90 -0
- package/dist/slop/abi/registry.js.map +1 -0
- package/dist/slop/abi/token.d.ts +320 -0
- package/dist/slop/abi/token.d.ts.map +1 -0
- package/dist/slop/abi/token.js +251 -0
- package/dist/slop/abi/token.js.map +1 -0
- package/dist/slop/quote.d.ts +80 -0
- package/dist/slop/quote.d.ts.map +1 -0
- package/dist/slop/quote.js +174 -0
- package/dist/slop/quote.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +8 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -0
- package/dist/utils/canonicalJson.js +20 -0
- package/dist/utils/canonicalJson.js.map +1 -0
- package/dist/utils/env.d.ts +11 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +20 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/http.d.ts +19 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +61 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/output.d.ts +19 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +37 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/respond.d.ts +19 -0
- package/dist/utils/respond.d.ts.map +1 -0
- package/dist/utils/respond.js +25 -0
- package/dist/utils/respond.js.map +1 -0
- package/dist/utils/ui.d.ts +38 -0
- package/dist/utils/ui.d.ts.map +1 -0
- package/dist/utils/ui.js +126 -0
- package/dist/utils/ui.js.map +1 -0
- package/dist/wallet/client.d.ts +4 -0
- package/dist/wallet/client.d.ts.map +1 -0
- package/dist/wallet/client.js +53 -0
- package/dist/wallet/client.js.map +1 -0
- package/dist/wallet/keystore.d.ts +21 -0
- package/dist/wallet/keystore.d.ts.map +1 -0
- package/dist/wallet/keystore.js +111 -0
- package/dist/wallet/keystore.js.map +1 -0
- package/package.json +56 -0
- package/skills/imm/SKILL.md +617 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { concat, pad, toHex } from "viem";
|
|
2
|
+
/**
|
|
3
|
+
* Encode path for Uniswap V3 exactInput
|
|
4
|
+
* Format: token0 + fee0 + token1 + fee1 + token2 + ...
|
|
5
|
+
*
|
|
6
|
+
* @param tokens - Array of token addresses in swap order
|
|
7
|
+
* @param fees - Array of fee tiers (length = tokens.length - 1)
|
|
8
|
+
* @returns Encoded path bytes
|
|
9
|
+
*/
|
|
10
|
+
export function encodePath(tokens, fees) {
|
|
11
|
+
if (tokens.length < 2) {
|
|
12
|
+
throw new Error("Path must have at least 2 tokens");
|
|
13
|
+
}
|
|
14
|
+
if (fees.length !== tokens.length - 1) {
|
|
15
|
+
throw new Error("Fees length must be tokens.length - 1");
|
|
16
|
+
}
|
|
17
|
+
// Each address is 20 bytes, each fee is 3 bytes
|
|
18
|
+
const parts = [];
|
|
19
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
20
|
+
// Add token address (20 bytes)
|
|
21
|
+
parts.push(tokens[i].toLowerCase());
|
|
22
|
+
// Add fee if not last token (3 bytes)
|
|
23
|
+
if (i < fees.length) {
|
|
24
|
+
// Fee is uint24, encode as 3 bytes
|
|
25
|
+
const feeHex = pad(toHex(fees[i]), { size: 3 });
|
|
26
|
+
parts.push(feeHex);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return concat(parts);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Encode path for Uniswap V3 exactOutput
|
|
33
|
+
* Note: exactOutput requires REVERSED path (tokenOut first)
|
|
34
|
+
*
|
|
35
|
+
* @param tokens - Array of token addresses in swap order (tokenIn → tokenOut)
|
|
36
|
+
* @param fees - Array of fee tiers
|
|
37
|
+
* @returns Encoded path bytes (reversed for exactOutput)
|
|
38
|
+
*/
|
|
39
|
+
export function encodePathForExactOutput(tokens, fees) {
|
|
40
|
+
// Reverse both arrays for exactOutput
|
|
41
|
+
const reversedTokens = [...tokens].reverse();
|
|
42
|
+
const reversedFees = [...fees].reverse();
|
|
43
|
+
return encodePath(reversedTokens, reversedFees);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Decode path bytes into tokens and fees
|
|
47
|
+
*
|
|
48
|
+
* @param path - Encoded path bytes
|
|
49
|
+
* @returns Object with tokens array and fees array
|
|
50
|
+
*/
|
|
51
|
+
export function decodePath(path) {
|
|
52
|
+
// Remove 0x prefix
|
|
53
|
+
const data = path.slice(2);
|
|
54
|
+
// Each token is 20 bytes (40 hex chars), each fee is 3 bytes (6 hex chars)
|
|
55
|
+
const tokens = [];
|
|
56
|
+
const fees = [];
|
|
57
|
+
let offset = 0;
|
|
58
|
+
let isToken = true;
|
|
59
|
+
while (offset < data.length) {
|
|
60
|
+
if (isToken) {
|
|
61
|
+
// Read 20 bytes for token
|
|
62
|
+
const tokenHex = data.slice(offset, offset + 40);
|
|
63
|
+
tokens.push(`0x${tokenHex}`);
|
|
64
|
+
offset += 40;
|
|
65
|
+
isToken = false;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Read 3 bytes for fee
|
|
69
|
+
const feeHex = data.slice(offset, offset + 6);
|
|
70
|
+
fees.push(parseInt(feeHex, 16));
|
|
71
|
+
offset += 6;
|
|
72
|
+
isToken = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { tokens, fees };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Calculate the number of hops in a path
|
|
79
|
+
*/
|
|
80
|
+
export function getPathHops(tokens) {
|
|
81
|
+
return tokens.length - 1;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Format path as human-readable string
|
|
85
|
+
* e.g., "USDC → [0.3%] → w0G → [0.05%] → WETH"
|
|
86
|
+
*/
|
|
87
|
+
export function formatPath(tokens, fees, getSymbol) {
|
|
88
|
+
const parts = [];
|
|
89
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
90
|
+
parts.push(getSymbol(tokens[i]));
|
|
91
|
+
if (i < fees.length) {
|
|
92
|
+
const feePercent = (fees[i] / 10000).toFixed(2);
|
|
93
|
+
parts.push(`→ [${feePercent}%] →`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return parts.join(" ");
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=pathEncoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathEncoding.js","sourceRoot":"","sources":["../../src/jaine/pathEncoding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Jaine-specific data directory */
|
|
2
|
+
export declare const JAINE_DIR: string;
|
|
3
|
+
/** Pool cache file */
|
|
4
|
+
export declare const POOLS_CACHE_FILE: string;
|
|
5
|
+
/** User token aliases file */
|
|
6
|
+
export declare const TOKENS_FILE: string;
|
|
7
|
+
/**
|
|
8
|
+
* Ensure Jaine data directory exists
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensureJaineDir(): void;
|
|
11
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/jaine/paths.ts"],"names":[],"mappings":"AAKA,oCAAoC;AACpC,eAAO,MAAM,SAAS,QAA4B,CAAC;AAEnD,sBAAsB;AACtB,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAEvE,8BAA8B;AAC9B,eAAO,MAAM,WAAW,QAAiC,CAAC;AAE1D;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAKrC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CONFIG_DIR } from "../config/paths.js";
|
|
4
|
+
import logger from "../utils/logger.js";
|
|
5
|
+
/** Jaine-specific data directory */
|
|
6
|
+
export const JAINE_DIR = join(CONFIG_DIR, "jaine");
|
|
7
|
+
/** Pool cache file */
|
|
8
|
+
export const POOLS_CACHE_FILE = join(JAINE_DIR, "pools-cache.v1.json");
|
|
9
|
+
/** User token aliases file */
|
|
10
|
+
export const TOKENS_FILE = join(JAINE_DIR, "tokens.json");
|
|
11
|
+
/**
|
|
12
|
+
* Ensure Jaine data directory exists
|
|
13
|
+
*/
|
|
14
|
+
export function ensureJaineDir() {
|
|
15
|
+
if (!existsSync(JAINE_DIR)) {
|
|
16
|
+
mkdirSync(JAINE_DIR, { recursive: true });
|
|
17
|
+
logger.debug(`Created Jaine directory: ${JAINE_DIR}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/jaine/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,oCAAoC;AACpC,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAEnD,sBAAsB;AACtB,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC;AAEvE,8BAA8B;AAC9B,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,4BAA4B,SAAS,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
import { type FeeTier } from "./abi/factory.js";
|
|
3
|
+
export interface PoolInfo {
|
|
4
|
+
address: Address;
|
|
5
|
+
token0: Address;
|
|
6
|
+
token1: Address;
|
|
7
|
+
fee: FeeTier;
|
|
8
|
+
}
|
|
9
|
+
export interface PoolsCache {
|
|
10
|
+
version: number;
|
|
11
|
+
chainId: number;
|
|
12
|
+
generatedAt: string;
|
|
13
|
+
pools: PoolInfo[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load pools cache from disk
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadPoolsCache(): PoolsCache | null;
|
|
19
|
+
/**
|
|
20
|
+
* Save pools cache to disk (atomic write)
|
|
21
|
+
*/
|
|
22
|
+
export declare function savePoolsCache(cache: PoolsCache): void;
|
|
23
|
+
/**
|
|
24
|
+
* Scan factory for pools between core tokens
|
|
25
|
+
* @param feeTiers - Fee tiers to scan (defaults to all standard tiers)
|
|
26
|
+
* @param onProgress - Optional progress callback (poolsFound, pairsScanned)
|
|
27
|
+
*/
|
|
28
|
+
export declare function scanCorePools(feeTiers?: readonly FeeTier[], onProgress?: (poolsFound: number, pairsScanned: number) => void): Promise<PoolInfo[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Get pool for specific token pair and fee
|
|
31
|
+
*/
|
|
32
|
+
export declare function getPool(tokenA: Address, tokenB: Address, fee: FeeTier): Promise<Address | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Find pools containing a specific token from cache
|
|
35
|
+
*/
|
|
36
|
+
export declare function findPoolsForToken(token: Address, cache?: PoolsCache | null): PoolInfo[];
|
|
37
|
+
/**
|
|
38
|
+
* Find pool between two tokens from cache
|
|
39
|
+
* Returns all matching pools (different fee tiers)
|
|
40
|
+
*/
|
|
41
|
+
export declare function findPoolsBetweenTokens(tokenA: Address, tokenB: Address, cache?: PoolsCache | null): PoolInfo[];
|
|
42
|
+
//# sourceMappingURL=poolCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poolCache.d.ts","sourceRoot":"","sources":["../../src/jaine/poolCache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAQxE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,UAAU,GAAG,IAAI,CA0BlD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAoBtD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,QAAQ,GAAE,SAAS,OAAO,EAAc,EACxC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,GAC9D,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsDrB;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAmBzB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,QAAQ,EAAE,CAQvF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,OAAO,EACf,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GACxB,QAAQ,EAAE,CAYZ"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { getAddress, zeroAddress } from "viem";
|
|
4
|
+
import { POOLS_CACHE_FILE, ensureJaineDir } from "./paths.js";
|
|
5
|
+
import { FACTORY_ABI, FEE_TIERS } from "./abi/factory.js";
|
|
6
|
+
import { getCoreTokenAddresses } from "./coreTokens.js";
|
|
7
|
+
import { getPublicClient } from "../wallet/client.js";
|
|
8
|
+
import { loadConfig } from "../config/store.js";
|
|
9
|
+
import logger from "../utils/logger.js";
|
|
10
|
+
const CACHE_VERSION = 1;
|
|
11
|
+
/**
|
|
12
|
+
* Load pools cache from disk
|
|
13
|
+
*/
|
|
14
|
+
export function loadPoolsCache() {
|
|
15
|
+
if (!existsSync(POOLS_CACHE_FILE)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const raw = readFileSync(POOLS_CACHE_FILE, "utf-8");
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
// Validate version and chain
|
|
22
|
+
if (parsed.version !== CACHE_VERSION) {
|
|
23
|
+
logger.debug(`Pool cache version mismatch: ${parsed.version} != ${CACHE_VERSION}`);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const cfg = loadConfig();
|
|
27
|
+
if (parsed.chainId !== cfg.chain.chainId) {
|
|
28
|
+
logger.debug(`Pool cache chain mismatch: ${parsed.chainId} != ${cfg.chain.chainId}`);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
logger.error(`Failed to parse pool cache: ${err}`);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Save pools cache to disk (atomic write)
|
|
40
|
+
*/
|
|
41
|
+
export function savePoolsCache(cache) {
|
|
42
|
+
ensureJaineDir();
|
|
43
|
+
const dir = dirname(POOLS_CACHE_FILE);
|
|
44
|
+
const tmpFile = join(dir, `.pools-cache.tmp.${Date.now()}.json`);
|
|
45
|
+
try {
|
|
46
|
+
writeFileSync(tmpFile, JSON.stringify(cache, null, 2), "utf-8");
|
|
47
|
+
renameSync(tmpFile, POOLS_CACHE_FILE);
|
|
48
|
+
logger.debug(`Pool cache saved: ${cache.pools.length} pools`);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
try {
|
|
52
|
+
if (existsSync(tmpFile)) {
|
|
53
|
+
unlinkSync(tmpFile);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Ignore cleanup errors
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Scan factory for pools between core tokens
|
|
64
|
+
* @param feeTiers - Fee tiers to scan (defaults to all standard tiers)
|
|
65
|
+
* @param onProgress - Optional progress callback (poolsFound, pairsScanned)
|
|
66
|
+
*/
|
|
67
|
+
export async function scanCorePools(feeTiers = FEE_TIERS, onProgress) {
|
|
68
|
+
const cfg = loadConfig();
|
|
69
|
+
const client = getPublicClient();
|
|
70
|
+
const factoryAddress = cfg.protocol.jaineFactory;
|
|
71
|
+
const coreTokens = getCoreTokenAddresses();
|
|
72
|
+
const pools = [];
|
|
73
|
+
let pairsScanned = 0;
|
|
74
|
+
// Generate all unique token pairs
|
|
75
|
+
const pairs = [];
|
|
76
|
+
for (let i = 0; i < coreTokens.length; i++) {
|
|
77
|
+
for (let j = i + 1; j < coreTokens.length; j++) {
|
|
78
|
+
pairs.push([coreTokens[i], coreTokens[j]]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Scan each pair × fee tier sequentially (avoid rate limiting)
|
|
82
|
+
for (const [tokenA, tokenB] of pairs) {
|
|
83
|
+
for (const fee of feeTiers) {
|
|
84
|
+
try {
|
|
85
|
+
const poolAddress = await client.readContract({
|
|
86
|
+
address: factoryAddress,
|
|
87
|
+
abi: FACTORY_ABI,
|
|
88
|
+
functionName: "getPool",
|
|
89
|
+
args: [tokenA, tokenB, fee],
|
|
90
|
+
});
|
|
91
|
+
if (poolAddress && poolAddress !== zeroAddress) {
|
|
92
|
+
// Sort tokens to ensure consistent ordering (token0 < token1)
|
|
93
|
+
const [token0, token1] = tokenA.toLowerCase() < tokenB.toLowerCase()
|
|
94
|
+
? [tokenA, tokenB]
|
|
95
|
+
: [tokenB, tokenA];
|
|
96
|
+
pools.push({
|
|
97
|
+
address: getAddress(poolAddress),
|
|
98
|
+
token0: getAddress(token0),
|
|
99
|
+
token1: getAddress(token1),
|
|
100
|
+
fee,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
logger.debug(`Failed to query pool ${tokenA}/${tokenB}/${fee}: ${err}`);
|
|
106
|
+
// Continue scanning other pools
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
pairsScanned++;
|
|
110
|
+
if (onProgress) {
|
|
111
|
+
onProgress(pools.length, pairsScanned);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return pools;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get pool for specific token pair and fee
|
|
118
|
+
*/
|
|
119
|
+
export async function getPool(tokenA, tokenB, fee) {
|
|
120
|
+
const cfg = loadConfig();
|
|
121
|
+
const client = getPublicClient();
|
|
122
|
+
try {
|
|
123
|
+
const poolAddress = await client.readContract({
|
|
124
|
+
address: cfg.protocol.jaineFactory,
|
|
125
|
+
abi: FACTORY_ABI,
|
|
126
|
+
functionName: "getPool",
|
|
127
|
+
args: [tokenA, tokenB, fee],
|
|
128
|
+
});
|
|
129
|
+
if (poolAddress && poolAddress !== zeroAddress) {
|
|
130
|
+
return getAddress(poolAddress);
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Find pools containing a specific token from cache
|
|
140
|
+
*/
|
|
141
|
+
export function findPoolsForToken(token, cache) {
|
|
142
|
+
const c = cache ?? loadPoolsCache();
|
|
143
|
+
if (!c)
|
|
144
|
+
return [];
|
|
145
|
+
const tokenLower = token.toLowerCase();
|
|
146
|
+
return c.pools.filter((p) => p.token0.toLowerCase() === tokenLower || p.token1.toLowerCase() === tokenLower);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Find pool between two tokens from cache
|
|
150
|
+
* Returns all matching pools (different fee tiers)
|
|
151
|
+
*/
|
|
152
|
+
export function findPoolsBetweenTokens(tokenA, tokenB, cache) {
|
|
153
|
+
const c = cache ?? loadPoolsCache();
|
|
154
|
+
if (!c)
|
|
155
|
+
return [];
|
|
156
|
+
const aLower = tokenA.toLowerCase();
|
|
157
|
+
const bLower = tokenB.toLowerCase();
|
|
158
|
+
return c.pools.filter((p) => {
|
|
159
|
+
const t0 = p.token0.toLowerCase();
|
|
160
|
+
const t1 = p.token1.toLowerCase();
|
|
161
|
+
return (t0 === aLower && t1 === bLower) || (t0 === bLower && t1 === aLower);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=poolCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poolCache.js","sourceRoot":"","sources":["../../src/jaine/poolCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Address, Hex } from "viem";
|
|
2
|
+
import { type PoolsCache } from "./poolCache.js";
|
|
3
|
+
export interface Route {
|
|
4
|
+
tokens: Address[];
|
|
5
|
+
fees: number[];
|
|
6
|
+
pools: Address[];
|
|
7
|
+
}
|
|
8
|
+
export interface QuotedRoute extends Route {
|
|
9
|
+
amountIn: bigint;
|
|
10
|
+
amountOut: bigint;
|
|
11
|
+
encodedPath: Hex;
|
|
12
|
+
gasEstimate: bigint;
|
|
13
|
+
}
|
|
14
|
+
export interface FindRouteOptions {
|
|
15
|
+
maxHops?: number;
|
|
16
|
+
maxCandidates?: number;
|
|
17
|
+
cache?: PoolsCache | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Find the best route for a swap (exactInput)
|
|
21
|
+
* @param tokenIn - Input token address
|
|
22
|
+
* @param tokenOut - Output token address
|
|
23
|
+
* @param amountIn - Input amount
|
|
24
|
+
* @param options - Routing options
|
|
25
|
+
* @returns Best route or null if no route found
|
|
26
|
+
*/
|
|
27
|
+
export declare function findBestRouteExactInput(tokenIn: Address, tokenOut: Address, amountIn: bigint, options?: FindRouteOptions): Promise<QuotedRoute | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Find the best route for a swap (exactOutput)
|
|
30
|
+
* @param tokenIn - Input token address
|
|
31
|
+
* @param tokenOut - Output token address
|
|
32
|
+
* @param amountOut - Desired output amount
|
|
33
|
+
* @param options - Routing options
|
|
34
|
+
* @returns Best route or null if no route found
|
|
35
|
+
*/
|
|
36
|
+
export declare function findBestRouteExactOutput(tokenIn: Address, tokenOut: Address, amountOut: bigint, options?: FindRouteOptions): Promise<QuotedRoute | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Format route for display
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatRoute(route: Route, userAliases?: Record<string, Address>): string;
|
|
41
|
+
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/jaine/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAYhF,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,KAAK;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AA8KD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAyD7B;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAwD7B;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,KAAK,EACZ,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,MAAM,CAIR"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { getAddress } from "viem";
|
|
2
|
+
import { loadPoolsCache } from "./poolCache.js";
|
|
3
|
+
import { encodePath, encodePathForExactOutput, formatPath } from "./pathEncoding.js";
|
|
4
|
+
import { getTokenSymbol } from "./coreTokens.js";
|
|
5
|
+
import { QUOTER_ABI } from "./abi/quoter.js";
|
|
6
|
+
import { getPublicClient } from "../wallet/client.js";
|
|
7
|
+
import { loadConfig } from "../config/store.js";
|
|
8
|
+
import { ImmError, ErrorCodes } from "../errors.js";
|
|
9
|
+
import logger from "../utils/logger.js";
|
|
10
|
+
const DEFAULT_MAX_HOPS = 3;
|
|
11
|
+
const DEFAULT_MAX_CANDIDATES = 20;
|
|
12
|
+
/**
|
|
13
|
+
* Build a graph of pools for routing
|
|
14
|
+
*/
|
|
15
|
+
function buildPoolGraph(pools) {
|
|
16
|
+
// Graph: token -> (neighbor token -> pools)
|
|
17
|
+
const graph = new Map();
|
|
18
|
+
const ensureNode = (token) => {
|
|
19
|
+
if (!graph.has(token)) {
|
|
20
|
+
graph.set(token, new Map());
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
for (const pool of pools) {
|
|
24
|
+
const t0 = pool.token0.toLowerCase();
|
|
25
|
+
const t1 = pool.token1.toLowerCase();
|
|
26
|
+
ensureNode(t0);
|
|
27
|
+
ensureNode(t1);
|
|
28
|
+
// Add bidirectional edges
|
|
29
|
+
const t0Neighbors = graph.get(t0);
|
|
30
|
+
const t1Neighbors = graph.get(t1);
|
|
31
|
+
if (!t0Neighbors.has(t1)) {
|
|
32
|
+
t0Neighbors.set(t1, []);
|
|
33
|
+
}
|
|
34
|
+
t0Neighbors.get(t1).push(pool);
|
|
35
|
+
if (!t1Neighbors.has(t0)) {
|
|
36
|
+
t1Neighbors.set(t0, []);
|
|
37
|
+
}
|
|
38
|
+
t1Neighbors.get(t0).push(pool);
|
|
39
|
+
}
|
|
40
|
+
return graph;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* BFS to find all routes between tokenIn and tokenOut
|
|
44
|
+
*/
|
|
45
|
+
function findAllRoutes(tokenIn, tokenOut, pools, maxHops) {
|
|
46
|
+
const graph = buildPoolGraph(pools);
|
|
47
|
+
const routes = [];
|
|
48
|
+
const inLower = tokenIn.toLowerCase();
|
|
49
|
+
const outLower = tokenOut.toLowerCase();
|
|
50
|
+
const queue = [
|
|
51
|
+
{
|
|
52
|
+
current: inLower,
|
|
53
|
+
tokens: [getAddress(tokenIn)],
|
|
54
|
+
fees: [],
|
|
55
|
+
pools: [],
|
|
56
|
+
visited: new Set([inLower]),
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
while (queue.length > 0) {
|
|
60
|
+
const state = queue.shift();
|
|
61
|
+
const { current, tokens, fees, pools: routePools, visited } = state;
|
|
62
|
+
// Check if we reached destination
|
|
63
|
+
if (current === outLower) {
|
|
64
|
+
routes.push({
|
|
65
|
+
tokens: [...tokens],
|
|
66
|
+
fees: [...fees],
|
|
67
|
+
pools: [...routePools],
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// Don't explore further if max hops reached
|
|
72
|
+
if (tokens.length > maxHops) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Get neighbors
|
|
76
|
+
const neighbors = graph.get(current);
|
|
77
|
+
if (!neighbors)
|
|
78
|
+
continue;
|
|
79
|
+
for (const [neighborToken, poolsToNeighbor] of neighbors) {
|
|
80
|
+
// Skip if already visited (no cycles)
|
|
81
|
+
if (visited.has(neighborToken))
|
|
82
|
+
continue;
|
|
83
|
+
// For each pool option to this neighbor
|
|
84
|
+
for (const pool of poolsToNeighbor) {
|
|
85
|
+
queue.push({
|
|
86
|
+
current: neighborToken,
|
|
87
|
+
tokens: [...tokens, getAddress(neighborToken)],
|
|
88
|
+
fees: [...fees, pool.fee],
|
|
89
|
+
pools: [...routePools, pool.address],
|
|
90
|
+
visited: new Set([...visited, neighborToken]),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return routes;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Quote a route using the quoter contract
|
|
99
|
+
*/
|
|
100
|
+
async function quoteRoute(route, amountIn, quoterAddress, direction) {
|
|
101
|
+
const client = getPublicClient();
|
|
102
|
+
try {
|
|
103
|
+
if (direction === "exactInput") {
|
|
104
|
+
const path = encodePath(route.tokens, route.fees);
|
|
105
|
+
// QuoterV1 returns single uint256, not tuple
|
|
106
|
+
const amountOut = await client.readContract({
|
|
107
|
+
address: quoterAddress,
|
|
108
|
+
abi: QUOTER_ABI,
|
|
109
|
+
functionName: "quoteExactInput",
|
|
110
|
+
args: [path, amountIn],
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
...route,
|
|
114
|
+
amountIn,
|
|
115
|
+
amountOut,
|
|
116
|
+
encodedPath: path,
|
|
117
|
+
gasEstimate: 0n,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// exactOutput - path is reversed
|
|
122
|
+
const path = encodePathForExactOutput(route.tokens, route.fees);
|
|
123
|
+
// QuoterV1 returns single uint256, not tuple
|
|
124
|
+
const amountInRequired = await client.readContract({
|
|
125
|
+
address: quoterAddress,
|
|
126
|
+
abi: QUOTER_ABI,
|
|
127
|
+
functionName: "quoteExactOutput",
|
|
128
|
+
args: [path, amountIn], // amountIn is actually amountOut for exactOutput
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
...route,
|
|
132
|
+
amountIn: amountInRequired,
|
|
133
|
+
amountOut: amountIn, // The desired output
|
|
134
|
+
encodedPath: path,
|
|
135
|
+
gasEstimate: 0n,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
logger.debug(`Quote failed for route: ${err}`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Find the best route for a swap (exactInput)
|
|
146
|
+
* @param tokenIn - Input token address
|
|
147
|
+
* @param tokenOut - Output token address
|
|
148
|
+
* @param amountIn - Input amount
|
|
149
|
+
* @param options - Routing options
|
|
150
|
+
* @returns Best route or null if no route found
|
|
151
|
+
*/
|
|
152
|
+
export async function findBestRouteExactInput(tokenIn, tokenOut, amountIn, options = {}) {
|
|
153
|
+
const { maxHops = DEFAULT_MAX_HOPS, maxCandidates = DEFAULT_MAX_CANDIDATES } = options;
|
|
154
|
+
const cache = options.cache !== undefined ? options.cache : loadPoolsCache();
|
|
155
|
+
if (!cache || cache.pools.length === 0) {
|
|
156
|
+
throw new ImmError(ErrorCodes.NO_ROUTE_FOUND, "Pool cache is empty", "Run: imm jaine pools scan-core");
|
|
157
|
+
}
|
|
158
|
+
const cfg = loadConfig();
|
|
159
|
+
const quoterAddress = cfg.protocol.quoter;
|
|
160
|
+
// Find all possible routes
|
|
161
|
+
const routes = findAllRoutes(tokenIn, tokenOut, cache.pools, maxHops);
|
|
162
|
+
if (routes.length === 0) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
// Limit candidates
|
|
166
|
+
const candidates = routes.slice(0, maxCandidates);
|
|
167
|
+
// Quote all routes in parallel (with limit)
|
|
168
|
+
const quotedRoutes = [];
|
|
169
|
+
// Process in batches of 5 to avoid rate limiting
|
|
170
|
+
const batchSize = 5;
|
|
171
|
+
for (let i = 0; i < candidates.length; i += batchSize) {
|
|
172
|
+
const batch = candidates.slice(i, i + batchSize);
|
|
173
|
+
const results = await Promise.all(batch.map((route) => quoteRoute(route, amountIn, quoterAddress, "exactInput")));
|
|
174
|
+
for (const result of results) {
|
|
175
|
+
if (result) {
|
|
176
|
+
quotedRoutes.push(result);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (quotedRoutes.length === 0) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
// Sort by amountOut descending, then by fewer hops
|
|
184
|
+
quotedRoutes.sort((a, b) => {
|
|
185
|
+
const amountDiff = b.amountOut - a.amountOut;
|
|
186
|
+
if (amountDiff !== 0n) {
|
|
187
|
+
return amountDiff > 0n ? 1 : -1;
|
|
188
|
+
}
|
|
189
|
+
return a.tokens.length - b.tokens.length;
|
|
190
|
+
});
|
|
191
|
+
return quotedRoutes[0];
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Find the best route for a swap (exactOutput)
|
|
195
|
+
* @param tokenIn - Input token address
|
|
196
|
+
* @param tokenOut - Output token address
|
|
197
|
+
* @param amountOut - Desired output amount
|
|
198
|
+
* @param options - Routing options
|
|
199
|
+
* @returns Best route or null if no route found
|
|
200
|
+
*/
|
|
201
|
+
export async function findBestRouteExactOutput(tokenIn, tokenOut, amountOut, options = {}) {
|
|
202
|
+
const { maxHops = DEFAULT_MAX_HOPS, maxCandidates = DEFAULT_MAX_CANDIDATES } = options;
|
|
203
|
+
const cache = options.cache !== undefined ? options.cache : loadPoolsCache();
|
|
204
|
+
if (!cache || cache.pools.length === 0) {
|
|
205
|
+
throw new ImmError(ErrorCodes.NO_ROUTE_FOUND, "Pool cache is empty", "Run: imm jaine pools scan-core");
|
|
206
|
+
}
|
|
207
|
+
const cfg = loadConfig();
|
|
208
|
+
const quoterAddress = cfg.protocol.quoter;
|
|
209
|
+
// Find all possible routes
|
|
210
|
+
const routes = findAllRoutes(tokenIn, tokenOut, cache.pools, maxHops);
|
|
211
|
+
if (routes.length === 0) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
// Limit candidates
|
|
215
|
+
const candidates = routes.slice(0, maxCandidates);
|
|
216
|
+
// Quote all routes
|
|
217
|
+
const quotedRoutes = [];
|
|
218
|
+
const batchSize = 5;
|
|
219
|
+
for (let i = 0; i < candidates.length; i += batchSize) {
|
|
220
|
+
const batch = candidates.slice(i, i + batchSize);
|
|
221
|
+
const results = await Promise.all(batch.map((route) => quoteRoute(route, amountOut, quoterAddress, "exactOutput")));
|
|
222
|
+
for (const result of results) {
|
|
223
|
+
if (result) {
|
|
224
|
+
quotedRoutes.push(result);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (quotedRoutes.length === 0) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
// Sort by amountIn ascending (minimize input), then by fewer hops
|
|
232
|
+
quotedRoutes.sort((a, b) => {
|
|
233
|
+
const amountDiff = a.amountIn - b.amountIn;
|
|
234
|
+
if (amountDiff !== 0n) {
|
|
235
|
+
return amountDiff > 0n ? 1 : -1;
|
|
236
|
+
}
|
|
237
|
+
return a.tokens.length - b.tokens.length;
|
|
238
|
+
});
|
|
239
|
+
return quotedRoutes[0];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Format route for display
|
|
243
|
+
*/
|
|
244
|
+
export function formatRoute(route, userAliases) {
|
|
245
|
+
return formatPath(route.tokens, route.fees, (addr) => getTokenSymbol(addr, userAliases));
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=routing.js.map
|