@clonegod/ttd-sol-common 2.0.83 → 2.0.85

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.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QuotePriceVerify = void 0;
4
4
  const trade_direction_1 = require("../../utils/trade_direction");
5
5
  const ttd_core_1 = require("@clonegod/ttd-core");
6
+ const USD_STABLECOINS = new Set(['USDT', 'USDC', 'USD1']);
6
7
  function pickMatchingTier(tiers, actualAmountIn) {
7
8
  if (!tiers || tiers.length === 0)
8
9
  return null;
@@ -183,25 +184,24 @@ class QuotePriceVerify {
183
184
  compareAndLog(poolAddress, poolName, poolInfo, cached, inputTokenAddress, outputTokenAddress, inputAmountUi, outputAmountUi, refPrice, execPriceNum, token0PriceUsd, token1PriceUsd, token0Address, swapBlockNumber, txHash, sources, primaryTierInfo) {
184
185
  if (inputAmountUi <= 0 || outputAmountUi <= 0)
185
186
  return;
186
- let swapUsd = 0;
187
- if (token0PriceUsd && token1PriceUsd && token0Address) {
188
- const inputIsToken0 = inputTokenAddress.toLowerCase() === token0Address.toLowerCase();
189
- swapUsd = inputIsToken0
190
- ? inputAmountUi * token0PriceUsd
191
- : inputAmountUi * token1PriceUsd;
192
- }
193
187
  const direction = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
194
188
  const baseToken = direction.baseToken;
195
189
  const quoteToken = direction.quoteToken;
196
190
  const inputIsQuoteToken = inputTokenAddress.toLowerCase() === quoteToken.address.toLowerCase();
197
191
  const isBuy = inputIsQuoteToken;
198
192
  const side = isBuy ? 'BUY' : 'SELL';
193
+ const quoteIsToken0 = quoteToken.address === token0Address;
194
+ const quotePriceUsd = USD_STABLECOINS.has((quoteToken.symbol || '').toUpperCase())
195
+ ? 1
196
+ : (quoteIsToken0 ? (token0PriceUsd || 0) : (token1PriceUsd || 0));
197
+ const quoteLegAmount = isBuy ? inputAmountUi : outputAmountUi;
198
+ const swapUsd = quotePriceUsd > 0 ? quoteLegAmount * quotePriceUsd : 0;
199
199
  if (refPrice <= 0 || execPriceNum <= 0)
200
200
  return;
201
201
  const diffBps = (refPrice - execPriceNum) / refPrice * 10000;
202
202
  const absDiffBps = Math.abs(diffBps);
203
203
  const status = absDiffBps < 5 ? '✅' : absDiffBps < 10 ? '⚠️' : '❌';
204
- const usdStr = swapUsd > 0 ? `$${swapUsd.toFixed(0)}` : '$?';
204
+ const usdStr = swapUsd > 0 ? `$${swapUsd.toFixed(2)}` : '$?';
205
205
  const maxUsd = cached.quoteAmountUsd * 2;
206
206
  const inRange = swapUsd <= 0 || swapUsd <= maxUsd;
207
207
  const rangeTag = inRange ? '' : ' [out]';
@@ -229,7 +229,7 @@ class QuotePriceVerify {
229
229
  ref_price: refPrice,
230
230
  exec_price: execPriceNum,
231
231
  diff_bps: parseFloat(diffBps.toFixed(1)),
232
- swap_usd: parseFloat(swapUsd.toFixed(0)),
232
+ swap_usd: parseFloat(swapUsd.toFixed(2)),
233
233
  in_range: inRange,
234
234
  input_amount: inputAmountUi,
235
235
  output_amount: outputAmountUi,
@@ -9,6 +9,7 @@ export declare class SolTransactionBuilder {
9
9
  recentBlockheight: number;
10
10
  constructor(appConfig: SolanaTradeAppConfig);
11
11
  init(): Promise<void>;
12
+ private warmupSigner;
12
13
  private handleBlockUpdateEvent;
13
14
  private getPreInstructions;
14
15
  private createComputeUnitLimitWithJitoDontFront;
@@ -41,9 +41,11 @@ const jitodontfrontAccounts = [
41
41
  'jitodontfront1111111111111111111111111111wP',
42
42
  'jitodontfront111111111111234565432123456RX5',
43
43
  ];
44
+ const jitodontfrontPubkeys = jitodontfrontAccounts.map(s => new web3_js_1.PublicKey(s));
45
+ const heliusTipPubkeys = helius_1.HELIUS_TIP_ACCOUNTS.map(s => new web3_js_1.PublicKey(s));
44
46
  function getJitoDontFrontAccount() {
45
- const randomIndex = Math.floor(Math.random() * jitodontfrontAccounts.length);
46
- return new web3_js_1.PublicKey(jitodontfrontAccounts[randomIndex]);
47
+ const randomIndex = Math.floor(Math.random() * jitodontfrontPubkeys.length);
48
+ return jitodontfrontPubkeys[randomIndex];
47
49
  }
48
50
  class SolTransactionBuilder {
49
51
  constructor(appConfig) {
@@ -54,6 +56,24 @@ class SolTransactionBuilder {
54
56
  }
55
57
  async init() {
56
58
  this.appConfig.arb_event_subscriber.subscribe_new_block(dist_1.CHAIN_ID.SOLANA, this.handleBlockUpdateEvent.bind(this));
59
+ this.warmupSigner();
60
+ }
61
+ warmupSigner() {
62
+ try {
63
+ for (let i = 0; i < 2; i++) {
64
+ const tx = new web3_js_1.Transaction();
65
+ tx.add(web3_js_1.SystemProgram.transfer({
66
+ fromPubkey: this.keypair.publicKey,
67
+ toPubkey: this.keypair.publicKey,
68
+ lamports: 0,
69
+ }));
70
+ tx.recentBlockhash = '11111111111111111111111111111111';
71
+ tx.feePayer = this.keypair.publicKey;
72
+ tx.sign(this.keypair);
73
+ }
74
+ }
75
+ catch {
76
+ }
57
77
  }
58
78
  async handleBlockUpdateEvent(eventData) {
59
79
  let blockUpdateEvent = JSON.parse(eventData);
@@ -88,6 +108,7 @@ class SolTransactionBuilder {
88
108
  return [];
89
109
  }
90
110
  buildTransactionForSwap(context, swapInstructions) {
111
+ const _t0 = Date.now();
91
112
  const max_block_offset = context.trade_runtime.settings.strategy.max_block_offset;
92
113
  const swapTx = new web3_js_1.Transaction();
93
114
  const preInstructions = this.getPreInstructions(context);
@@ -106,11 +127,14 @@ class SolTransactionBuilder {
106
127
  }
107
128
  let tip_instruction = web3_js_1.SystemProgram.transfer({
108
129
  fromPubkey: this.keypair.publicKey,
109
- toPubkey: new web3_js_1.PublicKey(helius_1.HELIUS_TIP_ACCOUNTS[Math.floor(Math.random() * helius_1.HELIUS_TIP_ACCOUNTS.length)]),
130
+ toPubkey: heliusTipPubkeys[Math.floor(Math.random() * heliusTipPubkeys.length)],
110
131
  lamports: tip_lamports,
111
132
  });
112
133
  swapTx.instructions.push(tip_instruction);
134
+ const _t1 = Date.now();
113
135
  swapTx.sign(this.keypair);
136
+ const _t2 = Date.now();
137
+ (0, dist_1.log_debug)(`[txBuilder] build breakdown: assemble=${_t1 - _t0}ms sign=${_t2 - _t1}ms total=${_t2 - _t0}ms`);
114
138
  return swapTx;
115
139
  }
116
140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-sol-common",
3
- "version": "2.0.83",
3
+ "version": "2.0.85",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,6 +16,9 @@
16
16
  import { resolveTradeDirection, calculateStandardPrice } from '../../utils/trade_direction';
17
17
  import { report_data_to_analyze, QuoteTier } from '@clonegod/ttd-core';
18
18
 
19
+ /** USD 稳定币:作 quote 时直接按 $1,无需查价。扩展时在此加。 */
20
+ const USD_STABLECOINS = new Set(['USDT', 'USDC', 'USD1']);
21
+
19
22
  // ========== 类型定义 ==========
20
23
 
21
24
  interface CachedQuote {
@@ -441,25 +444,24 @@ export class QuotePriceVerify {
441
444
  ): string | void {
442
445
  if (inputAmountUi <= 0 || outputAmountUi <= 0) return;
443
446
 
444
- // 估算 USD 金额
445
- let swapUsd = 0;
446
- if (token0PriceUsd && token1PriceUsd && token0Address) {
447
- const inputIsToken0 = inputTokenAddress.toLowerCase() === token0Address.toLowerCase();
448
- swapUsd = inputIsToken0
449
- ? inputAmountUi * token0PriceUsd
450
- : inputAmountUi * token1PriceUsd;
451
- }
452
-
453
- // 判断是 买入 base 还是 卖出 base
447
+ // 判断 买入/卖出 base(USD 估值也要用 quote 侧,提前算)
454
448
  const direction = resolveTradeDirection(poolInfo, true);
455
449
  const baseToken = direction.baseToken;
456
450
  const quoteToken = direction.quoteToken;
457
451
 
458
452
  const inputIsQuoteToken = inputTokenAddress.toLowerCase() === quoteToken.address.toLowerCase();
459
453
  const isBuy = inputIsQuoteToken;
460
-
461
454
  const side = isBuy ? 'BUY' : 'SELL';
462
455
 
456
+ // 估算 USD:只看 **quote 侧那条腿**(swap 必有一条 quote 腿,quote 是稳定/可靠定价侧)。
457
+ // quote 是 USD 稳定币 → 直接按 $1;否则(SOL 等)→ 用 quote 市价。base(pump 币)有没有价都不影响。
458
+ const quoteIsToken0 = quoteToken.address === token0Address; // base58 原值比较(不 lowercase)
459
+ const quotePriceUsd = USD_STABLECOINS.has((quoteToken.symbol || '').toUpperCase())
460
+ ? 1
461
+ : (quoteIsToken0 ? (token0PriceUsd || 0) : (token1PriceUsd || 0));
462
+ const quoteLegAmount = isBuy ? inputAmountUi : outputAmountUi; // 买:input=quote;卖:output=quote
463
+ const swapUsd = quotePriceUsd > 0 ? quoteLegAmount * quotePriceUsd : 0;
464
+
463
465
  if (refPrice <= 0 || execPriceNum <= 0) return;
464
466
 
465
467
  // sign 约定:(ref - exec) / ref —— "我们报价 vs 实际成交"
@@ -470,7 +472,7 @@ export class QuotePriceVerify {
470
472
  const absDiffBps = Math.abs(diffBps);
471
473
  // 阈值统一:<5 ✅ / 5-10 ⚠️ / ≥10 ❌(前端颜色阈值同步)
472
474
  const status = absDiffBps < 5 ? '✅' : absDiffBps < 10 ? '⚠️' : '❌';
473
- const usdStr = swapUsd > 0 ? `$${swapUsd.toFixed(0)}` : '$?';
475
+ const usdStr = swapUsd > 0 ? `$${swapUsd.toFixed(2)}` : '$?';
474
476
 
475
477
  // USD 范围标注(不过滤,全部打印)
476
478
  // 规则:仅排除"过大的 swap"(超过 2× 询价金额),偏差大可能是滑点而非报价不准;
@@ -509,7 +511,7 @@ export class QuotePriceVerify {
509
511
  ref_price: refPrice,
510
512
  exec_price: execPriceNum,
511
513
  diff_bps: parseFloat(diffBps.toFixed(1)),
512
- swap_usd: parseFloat(swapUsd.toFixed(0)),
514
+ swap_usd: parseFloat(swapUsd.toFixed(2)), // 显示到分:pump 币多是 <$1 的小单,取整会全成 $0
513
515
  in_range: inRange,
514
516
  input_amount: inputAmountUi,
515
517
  output_amount: outputAmountUi,
@@ -1,4 +1,4 @@
1
- import { CHAIN_ID, TradeContext } from "@clonegod/ttd-core/dist";
1
+ import { CHAIN_ID, log_debug, TradeContext } from "@clonegod/ttd-core/dist";
2
2
  import { ComputeBudgetProgram, Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction } from "@solana/web3.js";
3
3
  import { LAMPORTS_PER_MICRO_LAMPORTS } from "../common";
4
4
  import { SolanaBlockMetaUpdateEvent } from "../types";
@@ -55,9 +55,13 @@ const jitodontfrontAccounts = [
55
55
  'jitodontfront111111111111234565432123456RX5',
56
56
  ]
57
57
 
58
+ // 固定地址预构 PublicKey(避免每笔交易 base58 解码;模块加载时一次性)
59
+ const jitodontfrontPubkeys = jitodontfrontAccounts.map(s => new PublicKey(s));
60
+ const heliusTipPubkeys = HELIUS_TIP_ACCOUNTS.map(s => new PublicKey(s));
61
+
58
62
  function getJitoDontFrontAccount(): PublicKey {
59
- const randomIndex = Math.floor(Math.random() * jitodontfrontAccounts.length);
60
- return new PublicKey(jitodontfrontAccounts[randomIndex]);
63
+ const randomIndex = Math.floor(Math.random() * jitodontfrontPubkeys.length);
64
+ return jitodontfrontPubkeys[randomIndex];
61
65
  }
62
66
 
63
67
 
@@ -79,6 +83,29 @@ export class SolTransactionBuilder {
79
83
  public async init(): Promise<void> {
80
84
  // 订阅区块更新事件
81
85
  this.appConfig.arb_event_subscriber.subscribe_new_block(CHAIN_ID.SOLANA, this.handleBlockUpdateEvent.bind(this));
86
+ // 预热签名路径:web3.js 1.98 用 @noble/curves/ed25519,首次 sign 要懒构建 base-point wNAF 表
87
+ // + V8 JIT 整条 compileMessage/serialize 路径,冷启动 ~34ms。在 init 跑几笔 dummy sign
88
+ // 把这 ~30ms 从「第一笔真实交易的关键路径」挪到进程启动期(实测首笔 34ms→~5ms)。
89
+ this.warmupSigner()
90
+ }
91
+
92
+ /** 进程启动期预热 ed25519 签名 + 序列化热路径,消除第一笔交易的冷启动尖刺 */
93
+ private warmupSigner(): void {
94
+ try {
95
+ for (let i = 0; i < 2; i++) {
96
+ const tx = new Transaction()
97
+ tx.add(SystemProgram.transfer({
98
+ fromPubkey: this.keypair.publicKey,
99
+ toPubkey: this.keypair.publicKey,
100
+ lamports: 0,
101
+ }))
102
+ tx.recentBlockhash = '11111111111111111111111111111111' // 仅预热用,不广播
103
+ tx.feePayer = this.keypair.publicKey
104
+ tx.sign(this.keypair)
105
+ }
106
+ } catch {
107
+ // 预热失败不影响主流程(首笔退化为冷签名)
108
+ }
82
109
  }
83
110
 
84
111
  private async handleBlockUpdateEvent(eventData: string): Promise<void> {
@@ -157,6 +184,7 @@ export class SolTransactionBuilder {
157
184
  * @returns
158
185
  */
159
186
  buildTransactionForSwap(context: TradeContext, swapInstructions: TransactionInstruction[]): Transaction {
187
+ const _t0 = Date.now()
160
188
  const max_block_offset = context.trade_runtime.settings.strategy.max_block_offset
161
189
  // console.log(`buildTransactionForSwap, max_block_offset: ${max_block_offset}`)
162
190
 
@@ -185,12 +213,16 @@ export class SolTransactionBuilder {
185
213
  }
186
214
  let tip_instruction = SystemProgram.transfer({
187
215
  fromPubkey: this.keypair.publicKey,
188
- toPubkey: new PublicKey(HELIUS_TIP_ACCOUNTS[Math.floor(Math.random() * HELIUS_TIP_ACCOUNTS.length)]),
216
+ toPubkey: heliusTipPubkeys[Math.floor(Math.random() * heliusTipPubkeys.length)],
189
217
  lamports: tip_lamports,
190
218
  })
191
219
  swapTx.instructions.push(tip_instruction)
192
220
 
193
- swapTx.sign(this.keypair)
221
+ const _t1 = Date.now()
222
+ swapTx.sign(this.keypair) // = compileMessage(serialize) + @noble/curves ed25519 签名(已在 init 预热)
223
+ const _t2 = Date.now()
224
+ // 耗时拆分(debug 级,不刷屏):assemble=组装指令+设字段;sign=编译消息+ed25519 签名
225
+ log_debug(`[txBuilder] build breakdown: assemble=${_t1 - _t0}ms sign=${_t2 - _t1}ms total=${_t2 - _t0}ms`)
194
226
 
195
227
  return swapTx
196
228
  }