@clonegod/ttd-sol-common 2.0.52 → 2.0.55

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.
@@ -39,8 +39,7 @@ function get_wallet_token_account(wallet_pubkey, pool_list) {
39
39
  }
40
40
  const create_token_account_if_not_exist = async (connection, owner, token_list) => {
41
41
  token_list.forEach(async (token) => {
42
- var _a;
43
- if (['SOL', 'WSOL', 'USDC', 'USDT'].includes((_a = token.symbol) === null || _a === void 0 ? void 0 : _a.toUpperCase())) {
42
+ if (['SOL', 'WSOL', 'USDC', 'USDT'].includes(token.symbol?.toUpperCase())) {
44
43
  return;
45
44
  }
46
45
  try {
@@ -1,6 +1,7 @@
1
1
  export declare const JITO_TIP_ACCOUNTS: string[];
2
2
  export declare class JitoUtils {
3
3
  static jitoTipAccounts: string[];
4
+ static jitoTipAccountUrl: string;
4
5
  static init(): Promise<void>;
5
6
  static fetchJitoTipAccounts(): Promise<string[]>;
6
7
  }
@@ -52,11 +52,15 @@ class JitoUtils {
52
52
  return JitoUtils.fetchJitoTipAccounts().then((accounts) => {
53
53
  JitoUtils.jitoTipAccounts = accounts;
54
54
  console.log(`fetchJitoTipAccounts success: ${accounts.length} accounts`, accounts);
55
+ }).catch((error) => {
56
+ (0, dist_1.log_warn)(`fetchJitoTipAccounts failed!`, {
57
+ url: JitoUtils.jitoTipAccountUrl,
58
+ error: error.message
59
+ });
55
60
  });
56
61
  }
57
62
  static async fetchJitoTipAccounts() {
58
- const url = 'https://mainnet.block-engine.jito.wtf/api/v1/getTipAccounts';
59
- const response = await axios_1.default.post(url, {
63
+ const response = await axios_1.default.post(JitoUtils.jitoTipAccountUrl, {
60
64
  jsonrpc: '2.0',
61
65
  id: 1,
62
66
  method: 'getTipAccounts',
@@ -67,6 +71,7 @@ class JitoUtils {
67
71
  }
68
72
  exports.JitoUtils = JitoUtils;
69
73
  JitoUtils.jitoTipAccounts = [];
74
+ JitoUtils.jitoTipAccountUrl = 'https://mainnet.block-engine.jito.wtf/api/v1/getTipAccounts';
70
75
  const getJitoTipAccount = () => {
71
76
  let jito_tip_accounts = JitoUtils.jitoTipAccounts;
72
77
  if (jito_tip_accounts.length === 0) {
@@ -77,7 +82,6 @@ const getJitoTipAccount = () => {
77
82
  exports.getJitoTipAccount = getJitoTipAccount;
78
83
  JitoUtils.init();
79
84
  const sendBundleWithJito = async (mainTxBase64, tipTxBase64) => {
80
- var _a, _b;
81
85
  let url = process.env.JITO_SEND_BUNDLE_URL || 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles';
82
86
  const client = (0, http_client_1.getHttpClient)(url);
83
87
  const requestData = {
@@ -99,8 +103,8 @@ const sendBundleWithJito = async (mainTxBase64, tipTxBase64) => {
99
103
  }
100
104
  catch (error) {
101
105
  if (error instanceof axios_1.AxiosError) {
102
- const status = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
103
- const statusText = (_b = error.response) === null || _b === void 0 ? void 0 : _b.statusText;
106
+ const status = error.response?.status;
107
+ const statusText = error.response?.statusText;
104
108
  (0, dist_1.log_warn)(`[sendBundleWithJito] Request failed: ${status} - ${statusText}`);
105
109
  }
106
110
  else {
@@ -36,7 +36,6 @@ class TransactionResultChecker extends dist_1.AbstractTransactionResultCheck {
36
36
  }
37
37
  on_subscibe_transaction() {
38
38
  this.event_emitter.once(dist_1.LOCAL_EVENT_NAME.EVENT_WALLET_TRANSACTION + '#' + this.txid, (message) => {
39
- var _a, _b, _c;
40
39
  try {
41
40
  this.trade_result_already_processed = true;
42
41
  let messageObj = message;
@@ -60,9 +59,9 @@ class TransactionResultChecker extends dist_1.AbstractTransactionResultCheck {
60
59
  result = messageObj.params.result;
61
60
  _txid = result.signature;
62
61
  slot = result.slot;
63
- transactionData = (_a = result.transaction) === null || _a === void 0 ? void 0 : _a.transaction;
64
- meta = (_b = result.transaction) === null || _b === void 0 ? void 0 : _b.meta;
65
- version = ((_c = result.transaction) === null || _c === void 0 ? void 0 : _c.version) !== undefined ? result.transaction.version : 0;
62
+ transactionData = result.transaction?.transaction;
63
+ meta = result.transaction?.meta;
64
+ version = result.transaction?.version !== undefined ? result.transaction.version : 0;
66
65
  }
67
66
  else {
68
67
  (0, dist_1.log_error)('Invalid message format: unrecognized structure', new Error(JSON.stringify(messageObj)));
@@ -66,16 +66,25 @@ class TransactionResultParser {
66
66
  block_number: slot,
67
67
  txid,
68
68
  pool_address,
69
- tokenA: Object.assign(Object.assign({}, tokenA), { pre_bal: tokenA_PreBalance, post_bal: tokenA_PostBalance, change: tokenAChange }),
70
- tokenB: Object.assign(Object.assign({}, tokenB), { pre_bal: tokenB_PreBalance, post_bal: tokenB_PostBalance, change: tokenBChange }),
69
+ tokenA: {
70
+ ...tokenA,
71
+ pre_bal: tokenA_PreBalance,
72
+ post_bal: tokenA_PostBalance,
73
+ change: tokenAChange
74
+ },
75
+ tokenB: {
76
+ ...tokenB,
77
+ pre_bal: tokenB_PreBalance,
78
+ post_bal: tokenB_PostBalance,
79
+ change: tokenBChange
80
+ },
71
81
  tx_price: price,
72
82
  gas_fee
73
83
  };
74
84
  return tradeResult;
75
85
  }
76
86
  getTokenBalance(owner, mint, tokenBalances) {
77
- var _a, _b;
78
- const balance = (_b = (_a = tokenBalances === null || tokenBalances === void 0 ? void 0 : tokenBalances.find((e) => e.owner === owner && e.mint == mint)) === null || _a === void 0 ? void 0 : _a.uiTokenAmount) === null || _b === void 0 ? void 0 : _b.uiAmount;
87
+ const balance = tokenBalances?.find((e) => e.owner === owner && e.mint == mint)?.uiTokenAmount?.uiAmount;
79
88
  if (typeof balance !== 'undefined' && balance !== null) {
80
89
  return balance;
81
90
  }
package/docs/clmm.md ADDED
@@ -0,0 +1,118 @@
1
+ 我想让你教会我完整分析CLMM池子的技术:
2
+ 1、有个叫bitmap的数据,是关于池子已经初始化的tick信息?
3
+ 2、bitmap -> tickArray -> currentTickIndex -> currentTickArray -> neiberhood TickArray
4
+ 3、比如我通过监听链上池子account change,可以知道当前池子的current tickIndex, liqudity, sqrtprice。目前我只会根据3个数据来处略的做询价(比如100U的单子的ask,bid价格,为后面做交易参考)。跟你聊了之后,我想做到更好。
5
+ 4、另外,池子的一些基本信息,我可以在启动时初始化。池子的费率,池子的tickspace。
6
+ 5、我想能更加详细的在本地使用公式来计算出池子的报价:当前静态价格、按某个size询价得到的ask,bid价格(比如我固定按USDT计价。 SOL/USDT 的池子,ask : SOL->USDC, bid: USDC->SOL)
7
+ 6、需要有两种方式:简化计算,精确计算(这个依赖于本地能获取到更多的tick信息,我希望能看到每一个计算步骤,丛当前tick->tick移动->跨tickArray的流程日志)。
8
+ 7、我还希望能知道池子深度,也就是当前tick附近可交易买卖的USDC,SOL的数量。这个表达可能有问题,应该是当前tick状态下,比如在千分之15价格范围可买卖的USDC和SOL的个数。类似CEX ORDER BOOK上的数据那样。有价格,有深度。
9
+ 你能教会上上面的那些技术吗?
10
+
11
+ -----------------------------
12
+
13
+
14
+ ## sqrtPrice
15
+
16
+ 同一个 tick 内,sqrtPrice 可以连续变化(每笔小单都产生新 sqrtPrice),所以单纯通过 tick 获取 sqrtPrice 确实有问题 ——它只能给你边界值,不是当前精确值。这就是为什么池子状态总是存 sqrtPriceX64 而非只存 tick。
17
+
18
+ tick 的作用:tick 是“价格区间的标签”(discrete anchors)。
19
+ 每个 tick 对应一个精确的边界点,Uniswap V3 / Raydium / Orca 约定俗成:tickToSqrtPriceX64(i) 返回的是 tick i 的左边界(下边界),即价格区间的最小 sqrtPrice。
20
+ 公式(Orca/Uniswap V3):P(i) = 1.0001^{i}
21
+
22
+ sqrtPriceX64 = √P(i) × 2^{64}(下边界)。上边界 = √P(i + 1) × 2^{64}。
23
+ sqrtPriceX64 的作用:这是当前精确价格(continuous),每笔交易后更新(连续偏移)。它总是在当前 tick 的 [下边界, 上边界) 内。 小单:sqrtPrice 在区间内小移,tick 不变。
24
+ 累积小单:sqrtPrice 逐步推进,最终跨边界 → tick 更新。
25
+
26
+
27
+ tickToSqrtPriceX64(tick) 只返回 边界(e.g., 下边界),不是当前真实 sqrtPrice。
28
+ 如果用这个计算价格,会忽略区间内滑点(e.g., 同一个 tick,价格从边界起点滑到终点,变化 ~0.01% × spacing)。
29
+
30
+ 总是优先用池子状态的 sqrtPriceX64 计算当前价格(实时精确)。
31
+ tick 只用于:边界检查、跨 tick 更新 L、初始化 tickArray。
32
+ 转换工具:tick → sqrtPrice(边界),sqrtPrice → tick(floor 到当前区间)。
33
+
34
+ 当前真实 sqrtPriceX64 总是满足:
35
+ tickToSqrtPriceX64(currentTick) ≤ sqrtPriceX64 < tickToSqrtPriceX64(currentTick + tickSpacing)
36
+
37
+ -----------------------------
38
+ pool config: tickspace, feerate, bitmap
39
+ bitmap: 标记的是哪些 TickArray 已初始化(有至少一个 tick 被使用),而不是标记单个 tick。
40
+ TickArray: 一组离散的Tick,不一定全初始化。核心参数:tick spacing(相邻tick之间的间距。 相邻tick之间的价差,指的是相邻tick的下边界sqrtprice的变化比率)
41
+ TickIndex: 当前tick的索引,通过它可定位处于tick属于哪个TickArray。由于tick是边界,因此通过tickToSqrtPrice实际上计算出的是 [sqrtPrice_下边界,sqrtPrice_上边界]的价格区间。
42
+
43
+ PoolState:
44
+ currentTickIndex
45
+ liqudity
46
+ sqrtPriceX64
47
+
48
+ -----------------------------
49
+ 在SOL/USDC这个池子进行“询价”:
50
+ -- 询价,实际上就是模拟做一笔交易,预测'未来'进行一笔差不多大小的交易的可成交价。
51
+
52
+ 1、初始化
53
+ - 初始化池子的配置信息:
54
+ tokenA, tokenB, feeRate
55
+ bitmap (一般在poolData中,不用单独订阅)
56
+ - 初始化池子的状态信息(随时会变):
57
+ tickArray
58
+ * 订阅当前tick前后相邻3个Tick Array的账户数据(根据currentTick动态调整被监控的TickArray账户列表)
59
+
60
+ 2、动态更新
61
+ - 监听池子的状态变换,可获得池子的最新状态
62
+ current_tick_index
63
+ liqudity
64
+ sqrtPrice
65
+
66
+ - 监听池子的事件
67
+ * solana 似乎没有基于事件监听的机制?比如监听池子的swap,burn,mint事件
68
+ 监听池子的交易,丛交易中解析是否存在burn,mint,如果有,则需要本地刷新bitmap,tickArray
69
+
70
+ 3、模拟交易量
71
+ - 输入100USDC
72
+ - 池子费率0.05%
73
+ - 扣除交易费用 100 * 0.05%,剩下的作为真正的inputAmount
74
+ - currentTick => currentTickOfTickArray
75
+ - neiberhoodTickArray => 相邻tickArray通过订阅
76
+ - 逐tick吃流动性,跨tick吃,跨tickArray吃
77
+ - inputAmount/outputAmount => usdc per SOL (用USDC计价)
78
+
79
+
80
+ -------------------
81
+ # CLMM 核心事实(2025版)
82
+
83
+ 1. sqrtPriceX64 是当前**真实价格**(连续),每笔交易都变
84
+ - 公式:price = (sqrtPriceX64 / 2^64)² → token1 ÷ token0
85
+ - 必须用这个算当前价格,不能用 tickToSqrtPriceX64
86
+
87
+ 2. tickIndex 是**当前价格所在的价格区间标签**(离散)
88
+ - tickToSqrtPriceX64(i) 返回的是 tick i 的**左边界**
89
+ - 真实关系:tickToSqrtPriceX64(currentTick) ≤ sqrtPriceX64 < tickToSqrtPriceX64(currentTick + spacing)
90
+
91
+ 3. 小单不会跨 tick,但 sqrtPrice 会连续下滑/上涨 → 价格实时滑点
92
+ 累积小单最终会跨 tick → 更新 liquidity
93
+
94
+ 4. TickArray 是存储结构:
95
+ - Raydium / Orca:每个 account 存 88 个 tick slot
96
+ - bitmap(Raydium)标记哪些 TickArray 已初始化(不是单个 tick)
97
+
98
+ 5. 生产级实时监控方案:
99
+ - 订阅 whirlpool account → 实时 currentTick/liquidity/sqrtPrice
100
+ - 维护当前 ±5 个 TickArray(通过 bitmap 或 PDA 计算)
101
+ - 每笔询价都用最新 sqrtPrice + 当前 liquidity + 预加载的 tick 数据模拟
102
+
103
+ 6. 询价 = 模拟 swap:
104
+ 输入 amountIn → 扣费 → 逐 tick 吃(跨 tick 更新 L,跨 array 加载下一个)→ 输出 amountOut + 最终价格
105
+
106
+
107
+ -------------------
108
+ CLMM 里 price 的定义:
109
+ - 核心状态是 `sqrtPriceX64`(Q64.64 定点)。
110
+ - 价格默认方向是 **tokenB per tokenA**,即「1 个 tokenA 要多少 tokenB」(quote/base)。
111
+ 计算公式:`price = (sqrtPriceX64^2) / 2^128`。
112
+ - 对应 tick:`sqrtPriceX64 = 1.0001^(tick/2) * 2^64`,因此
113
+ `tick = floor(log(price) / log(1.0001))`(按 tokenB/tokenA 方向)。
114
+ - 如果需要 **tokenA per tokenB**(倒数价格),取 `1 / price`。
115
+ 简化理解:CLMM 把价格存成 `sqrtPriceX64`,平方后除以 `2^128` 得到 tokenB/tokenA 的现价;tick 是以 1.0001 为底的价格刻度。
116
+
117
+
118
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-sol-common",
3
- "version": "2.0.52",
3
+ "version": "2.0.55",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "types/index.d.ts",
@@ -13,7 +13,7 @@
13
13
  "push": "npm run build && npm publish"
14
14
  },
15
15
  "dependencies": {
16
- "@clonegod/ttd-core": "2.1.7",
16
+ "@clonegod/ttd-core": "2.1.15",
17
17
  "@solana/web3.js": "1.91.6",
18
18
  "rpc-websockets": "7.10.0",
19
19
  "axios": "^1.2.3",
@@ -1,4 +1,4 @@
1
1
  export * from './constants'
2
2
  export * from './get_wallet_token_account'
3
3
  export * from './subscribe_account_update'
4
- export * from './get_signature'
4
+ export * from './get_signature'
@@ -15,17 +15,22 @@ export const JITO_TIP_ACCOUNTS = [
15
15
 
16
16
  export class JitoUtils {
17
17
  static jitoTipAccounts: string[] = []
18
+ static jitoTipAccountUrl = 'https://mainnet.block-engine.jito.wtf/api/v1/getTipAccounts'
18
19
 
19
20
  static async init(): Promise<void> {
20
21
  return JitoUtils.fetchJitoTipAccounts().then((accounts) => {
21
22
  JitoUtils.jitoTipAccounts = accounts;
22
23
  console.log(`fetchJitoTipAccounts success: ${accounts.length} accounts`, accounts)
24
+ }).catch((error) => {
25
+ log_warn(`fetchJitoTipAccounts failed!`, {
26
+ url: JitoUtils.jitoTipAccountUrl,
27
+ error: error.message
28
+ });
23
29
  });
24
30
  }
25
31
 
26
32
  static async fetchJitoTipAccounts(): Promise<string[]> {
27
- const url = 'https://mainnet.block-engine.jito.wtf/api/v1/getTipAccounts'
28
- const response = await axios.post<{ result: string[] }>(url, {
33
+ const response = await axios.post<{ result: string[] }>(JitoUtils.jitoTipAccountUrl, {
29
34
  jsonrpc: '2.0',
30
35
  id: 1,
31
36
  method: 'getTipAccounts',
package/tsconfig.json CHANGED
@@ -5,11 +5,10 @@
5
5
  "./types",
6
6
  ],
7
7
  "lib": [
8
- "es2017",
9
- "dom"
8
+ "es2020"
10
9
  ],
11
10
  "module": "CommonJS",
12
- "target": "es2017",
11
+ "target": "es2020",
13
12
  "rootDirs": [
14
13
  "src"
15
14
  ],
@@ -20,6 +19,6 @@
20
19
  "resolveJsonModule": true,
21
20
  "removeComments": true
22
21
  },
23
- "include": ["src/**/*"],
22
+ "include": ["src/**/*", "docs/clmm_local_calc.ts", "../ttd-sol-orca-clmm/src/quote/clmm_engine.ts"],
24
23
  "exclude": ["dist"]
25
24
  }