@clonegod/ttd-core 3.1.26 → 3.1.28

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.
@@ -45,7 +45,7 @@ registerEnvVars({
45
45
  encryption_key: { env: 'ENCRYPTION_KEY', type: 'string', default: '', sensitive: true, desc: '对称加密密钥(用于钱包文件等)' },
46
46
  trade_analyze_url: { env: 'TRADE_ANALYZE_URL', type: 'string', default: '', desc: 'analyze HTTP 基址(旧 EnvArgs;未配则 analyze 上报走默认拼接)' },
47
47
  providers_file_path: { env: 'PROVIDERS_FILE_PATH', type: 'string', default: '', desc: 'stream-quote providers.json 绝对路径;留空则仅写 Redis' },
48
- token_price_refresh_interval_seconds: { env: 'TOKEN_PRICE_REFRESH_INTERVAL_SECONDS', type: 'number', default: 600, desc: 'TokenPriceSyncer 刷价周期(s):market-data 拉价并推送给 config-center token_list 的频率' },
48
+ token_price_refresh_interval_seconds: { env: 'TOKEN_PRICE_REFRESH_INTERVAL_SECONDS', type: 'number', default: 600, desc: 'TokenPriceSyncer 刷价周期(s):market-data 通过 force_fetch 拉外部价(DefiLlama→Gecko→...)并直接批量写入 Redis + 发 TOKEN_CONFIG_CHANGE 事件的频率' },
49
49
  token_market_price_url: { env: 'TOKEN_MARKET_PRICE_URL', type: 'string', default: '', desc: '【使用方: ttd-core estimate_token_amount】config-center 市价估算 HTTP 基址;为空则跳过远程回退' },
50
50
  fixed_symbol_address_file: { env: 'FIXED_SYMBOL_ADDRESS_FILE', type: 'string', default: '', desc: '固定 symbol/address 映射文件路径(覆盖默认按 CHAIN_ID 推断的路径)' },
51
51
  zh_pinyin_map_file: { env: 'ZH_PINYIN_MAP_FILE', type: 'string', default: '', desc: '中文拼音映射文件路径(覆盖默认路径)' },
@@ -24,6 +24,7 @@ export declare class ArbCache {
24
24
  init_configs(): Promise<void>;
25
25
  get_config_filepath(suffix: string): string;
26
26
  refresh_local_cache_token_list(token_list: StandardTokenInfoType[]): void;
27
+ getTokenByAddress(address: string): StandardTokenInfoType | undefined;
27
28
  refresh_local_cache_pool_list(pool_list: StandardPoolInfoType[]): void;
28
29
  refresh_local_cache_group_map(trade_config: TradeServiceConfigType): void;
29
30
  cache_token_list(): Promise<void>;
@@ -34,6 +35,32 @@ export declare class ArbCache {
34
35
  publish_token_change_event(): Promise<void>;
35
36
  cache_pool_list(): Promise<void>;
36
37
  private scan_duplicate_address;
38
+ resolve_duplicate_address(authoritative_mappings: Array<{
39
+ address: string;
40
+ symbol: string;
41
+ }>): Promise<{
42
+ cleaned_tokens: Array<{
43
+ address: string;
44
+ dropped_symbols: string[];
45
+ kept_symbol: string;
46
+ }>;
47
+ cleaned_pools: Array<{
48
+ pool_address: string;
49
+ pool_name: string;
50
+ dex_id: string;
51
+ }>;
52
+ skipped_trading_pools: Array<{
53
+ pool_address: string;
54
+ pool_name: string;
55
+ dex_id: string;
56
+ reason: string;
57
+ }>;
58
+ not_found: Array<{
59
+ address: string;
60
+ symbol: string;
61
+ reason: string;
62
+ }>;
63
+ }>;
37
64
  set_pool_token_info(pool: StandardPoolInfoType, token_info_list: StandardTokenInfoType[]): boolean;
38
65
  set_pool_extra(pool: StandardPoolInfoType): void;
39
66
  update_pool_list(input_dex_pool_list: StandardDexPoolConfigType[]): Promise<void>;
@@ -72,6 +72,15 @@ class ArbCache {
72
72
  (0, index_1.log_trace)(`init cache start`);
73
73
  yield this.cache_token_list();
74
74
  yield this.cache_pool_list();
75
+ const fixed_list = (0, fixed_symbol_address_1.get_fixed_symbol_address)();
76
+ if (fixed_list.length > 0) {
77
+ try {
78
+ yield this.resolve_duplicate_address(fixed_list.map(f => ({ address: f.address, symbol: f.symbol })));
79
+ }
80
+ catch (err) {
81
+ (0, index_1.log_warn)(`[init] auto resolve_duplicate_address failed (non-fatal)`, err);
82
+ }
83
+ }
75
84
  yield this.cache_trade_group();
76
85
  (0, index_1.log_trace)(`init cache finish`);
77
86
  });
@@ -108,6 +117,20 @@ class ArbCache {
108
117
  token_address_map_szie: this.token_address_map.size,
109
118
  });
110
119
  }
120
+ getTokenByAddress(address) {
121
+ var _a;
122
+ if (!address)
123
+ return undefined;
124
+ const exact = this.token_address_map.get(address);
125
+ if (exact)
126
+ return exact;
127
+ const lc = address.toLowerCase();
128
+ for (const m of this.token_address_map.values()) {
129
+ if (((_a = m.address) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === lc)
130
+ return m;
131
+ }
132
+ return undefined;
133
+ }
111
134
  refresh_local_cache_pool_list(pool_list) {
112
135
  this.pool_list = pool_list;
113
136
  this.pool_address_map.clear();
@@ -315,6 +338,85 @@ class ArbCache {
315
338
  });
316
339
  (0, index_1.log_warn)(`[token-duplicate] 发现 ${conflicts.length} 个 address 被多 symbol 引用(垃圾数据,需人工清理 token-list.json + pool-list.json + Redis):\n${lines.join('\n')}`);
317
340
  }
341
+ resolve_duplicate_address(authoritative_mappings) {
342
+ return __awaiter(this, void 0, void 0, function* () {
343
+ const token_list = yield this.get_token_list();
344
+ const by_addr = new Map();
345
+ for (const t of token_list) {
346
+ const addr = (t.address || '').toLowerCase();
347
+ if (!addr)
348
+ continue;
349
+ if (!by_addr.has(addr))
350
+ by_addr.set(addr, []);
351
+ by_addr.get(addr).push(t);
352
+ }
353
+ const tokens_to_disable = [];
354
+ const cleaned_tokens = [];
355
+ const not_found = [];
356
+ for (const { address, symbol } of authoritative_mappings) {
357
+ const addr = (address || '').toLowerCase();
358
+ const tokens = by_addr.get(addr) || [];
359
+ if (tokens.length === 0) {
360
+ not_found.push({ address: addr, symbol, reason: `address 在 token-list 中未找到` });
361
+ continue;
362
+ }
363
+ const canonical = tokens.find(t => t.symbol === symbol);
364
+ if (!canonical) {
365
+ not_found.push({ address: addr, symbol, reason: `symbol=${symbol} 在 address ${addr} 的条目中未找到(token-list 里 symbols=[${tokens.map(t => t.symbol).join(',')}])` });
366
+ continue;
367
+ }
368
+ const dropped = tokens.filter(t => t.symbol !== symbol);
369
+ if (dropped.length === 0)
370
+ continue;
371
+ for (const t of dropped) {
372
+ tokens_to_disable.push(Object.assign(Object.assign({}, t), { enable: false }));
373
+ }
374
+ cleaned_tokens.push({
375
+ address: addr,
376
+ dropped_symbols: dropped.map(t => t.symbol),
377
+ kept_symbol: symbol,
378
+ });
379
+ }
380
+ const dropped_symbols = new Set(tokens_to_disable.map(t => t.symbol));
381
+ const cleaned_pools = [];
382
+ const skipped_trading_pools = [];
383
+ if (dropped_symbols.size > 0) {
384
+ const pool_list = yield this.get_pool_list();
385
+ const trade_pool_set = new Set((yield this.get_all_trade_pools()).map(p => p.pool_address));
386
+ const pools_to_disable_by_dex = new Map();
387
+ for (const p of pool_list) {
388
+ const [t0, t1] = (p.pool_name || '').split('/');
389
+ if (!dropped_symbols.has(t0) && !dropped_symbols.has(t1))
390
+ continue;
391
+ if (trade_pool_set.has(p.pool_address)) {
392
+ skipped_trading_pools.push({
393
+ pool_address: p.pool_address,
394
+ pool_name: p.pool_name,
395
+ dex_id: p.dex_id,
396
+ reason: 'pool in trading config (protected)',
397
+ });
398
+ continue;
399
+ }
400
+ if (!pools_to_disable_by_dex.has(p.dex_id))
401
+ pools_to_disable_by_dex.set(p.dex_id, []);
402
+ pools_to_disable_by_dex.get(p.dex_id).push(Object.assign(Object.assign({}, p), { enable: false }));
403
+ cleaned_pools.push({ pool_address: p.pool_address, pool_name: p.pool_name, dex_id: p.dex_id });
404
+ }
405
+ if (pools_to_disable_by_dex.size > 0) {
406
+ const dex_pool_list = [];
407
+ for (const [dex_id, pools] of pools_to_disable_by_dex) {
408
+ dex_pool_list.push({ dex_id: dex_id, pool_list: pools });
409
+ }
410
+ yield this.update_pool_list(dex_pool_list);
411
+ }
412
+ }
413
+ if (tokens_to_disable.length > 0) {
414
+ yield this.update_token_list(tokens_to_disable);
415
+ }
416
+ (0, index_1.log_warn)(`[resolve-duplicate] 完成: disabled ${tokens_to_disable.length} 个 symbol, 删 ${cleaned_pools.length} 个 pool, 跳过交易中 pool ${skipped_trading_pools.length} 个, 映射 not_found ${not_found.length} 个`);
417
+ return { cleaned_tokens, cleaned_pools, skipped_trading_pools, not_found };
418
+ });
419
+ }
318
420
  set_pool_token_info(pool, token_info_list) {
319
421
  if (index_1.LOG.debug) {
320
422
  (0, index_1.log_trace)(`set_pool_token_info`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-core",
3
- "version": "3.1.26",
3
+ "version": "3.1.28",
4
4
  "description": "Common types and utilities for trading systems - use `npm run push` to publish",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/types/index.d.ts CHANGED
@@ -600,6 +600,12 @@ export declare class ArbCache {
600
600
  publish_token_change_event(): Promise<void>;
601
601
  cache_pool_list(): Promise<void>;
602
602
  set_pool_token_info(pool: StandardPoolInfoType, token_info_list: StandardTokenInfoType[]): boolean;
603
+ resolve_duplicate_address(authoritative_mappings: Array<{ address: string; symbol: string }>): Promise<{
604
+ cleaned_tokens: Array<{ address: string; dropped_symbols: string[]; kept_symbol: string }>;
605
+ cleaned_pools: Array<{ pool_address: string; pool_name: string; dex_id: string }>;
606
+ skipped_trading_pools: Array<{ pool_address: string; pool_name: string; dex_id: string; reason: string }>;
607
+ not_found: Array<{ address: string; symbol: string; reason: string }>;
608
+ }>;
603
609
  set_pool_extra(pool: StandardPoolInfoType): void;
604
610
  update_pool_list(input_dex_pool_list: StandardDexPoolConfigType[]): Promise<void>;
605
611
  get_pool_list_by_pair(pair: string): Promise<StandardPoolInfoType[]>;