@clonegod/ttd-core 3.1.23 → 3.1.25

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 (33) hide show
  1. package/dist/alert/codes.js +1 -0
  2. package/dist/alert/log_rules.js +1 -0
  3. package/dist/app_config/AppConfig.d.ts +1 -3
  4. package/dist/app_config/AppConfig.js +1 -14
  5. package/dist/app_config/EnvArgs.d.ts +2 -1
  6. package/dist/app_config/env_registry.js +2 -1
  7. package/dist/cache/arb_cache.d.ts +1 -2
  8. package/dist/cache/arb_cache.js +46 -37
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +0 -1
  11. package/dist/market_price/estimate_token_amount.js +3 -3
  12. package/dist/pool/cache_pool_config.js +45 -53
  13. package/dist/token/cache_token_config.js +25 -36
  14. package/dist/token/price/get_bsc_token_price.d.ts +4 -1
  15. package/dist/token/price/get_bsc_token_price.js +27 -12
  16. package/dist/token/price/get_eth_token_price.d.ts +4 -1
  17. package/dist/token/price/get_eth_token_price.js +27 -12
  18. package/dist/token/price/get_solana_token_price.d.ts +4 -1
  19. package/dist/token/price/get_solana_token_price.js +27 -12
  20. package/dist/token/price/get_sui_token_price.d.ts +4 -1
  21. package/dist/token/price/get_sui_token_price.js +27 -12
  22. package/dist/token/price/get_tron_token_price.d.ts +4 -1
  23. package/dist/token/price/get_tron_token_price.js +27 -12
  24. package/dist/token/price/get_xlayer_token_price.d.ts +4 -1
  25. package/dist/token/price/get_xlayer_token_price.js +27 -12
  26. package/dist/token/price/index.d.ts +2 -0
  27. package/dist/token/price/index.js +2 -0
  28. package/dist/token/price/price_cache.d.ts +6 -1
  29. package/dist/token/price/price_cache.js +67 -76
  30. package/dist/token/price/token_price_syncer.d.ts +25 -0
  31. package/dist/token/price/token_price_syncer.js +80 -0
  32. package/package.json +1 -1
  33. package/types/index.d.ts +2 -0
@@ -16,6 +16,7 @@ exports.ALERT_CODES = {
16
16
  QUOTE_TICK_STALE: { code: 'QUOTE_TICK_STALE', severity: S.ERROR, category: 'quote', desc: 'tick 数据过期但仍在报价', suggested_action: '风险提示' },
17
17
  QUOTE_PRICE_ANOMALY: { code: 'QUOTE_PRICE_ANOMALY', severity: S.ERROR, category: 'quote', desc: '报价偏离参考价 > 阈值', suggested_action: '检查数据源' },
18
18
  QUOTE_INTERVAL_BREACH: { code: 'QUOTE_INTERVAL_BREACH', severity: S.WARN, category: 'quote', desc: '询价间隔超出预期' },
19
+ QUOTE_PRICE_CACHE_MISS: { code: 'QUOTE_PRICE_CACHE_MISS', severity: S.WARN, category: 'quote', desc: 'token 在系统里但 cache 无价(scheduler 未刷到)', suggested_action: '观察 market-data TokenPriceSyncer 是否正常' },
19
20
  TRADE_ENCODE_FAIL: { code: 'TRADE_ENCODE_FAIL', severity: S.ERROR, category: 'trade', desc: 'calldata 编码失败', suggested_action: '检查订单参数' },
20
21
  TRADE_SIGN_FAIL: { code: 'TRADE_SIGN_FAIL', severity: S.ERROR, category: 'trade', desc: '交易签名失败', suggested_action: '检查 caller 钱包' },
21
22
  TRADE_NONCE_CONFLICT: {
@@ -45,6 +45,7 @@ registerLogRules([
45
45
  level: 'error', code: 'THIRDPARTY_AUTH_FAIL' },
46
46
  { pattern: /pricefeed.*(parse failed|not an array|empty)|empty response at page/i,
47
47
  level: 'warn', code: 'THIRDPARTY_DATA_INVALID' },
48
+ { pattern: /price cache miss/i, level: 'warn', code: 'QUOTE_PRICE_CACHE_MISS' },
48
49
  { pattern: /sqlite.*(disk|full|i\/o error|cleanup error|write.*fail)/i,
49
50
  level: 'error', code: 'ANALYZE_SQLITE_WRITE_FAIL' },
50
51
  { pattern: /db size.*\d+mb.*limit/i, level: 'warn', code: 'ANALYZE_SQLITE_FULL' },
@@ -1,5 +1,5 @@
1
1
  import EventEmitter from "events";
2
- import { StandardTokenInfoType, TradeRuntimeType } from "../../types";
2
+ import { TradeRuntimeType } from "../../types";
3
3
  import { ArbCache, ArbEventSubscriber } from "../cache";
4
4
  import { EnvArgs } from "./EnvArgs";
5
5
  export declare class AppConfig extends EventEmitter {
@@ -11,6 +11,4 @@ export declare class AppConfig extends EventEmitter {
11
11
  constructor();
12
12
  init(): Promise<void>;
13
13
  subscribe_config_change(): Promise<void>;
14
- cache_token_market_price(token_info_with_price: StandardTokenInfoType, ttl?: number): Promise<void>;
15
- get_token_market_price(symbol: string): Promise<StandardTokenInfoType>;
16
14
  }
@@ -15,13 +15,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.AppConfig = void 0;
16
16
  const events_1 = __importDefault(require("events"));
17
17
  const __1 = require("..");
18
- const EnvArgs_1 = require("./EnvArgs");
19
18
  const core_env_1 = require("./core_env");
20
19
  class AppConfig extends events_1.default {
21
20
  constructor() {
22
21
  super();
23
- this.env_args = new EnvArgs_1.EnvArgs();
24
- (0, core_env_1.setCoreEnv)(this.env_args);
22
+ this.env_args = (0, core_env_1.getCoreEnv)();
25
23
  let { chain_id, app_name, dex_id } = this.env_args;
26
24
  this.app_full_name = `${chain_id}_${dex_id}_${app_name}`;
27
25
  global.app_config = this;
@@ -49,16 +47,5 @@ class AppConfig extends events_1.default {
49
47
  yield this.arb_event_subscriber.subscribe_config_change_event(refresh_trade_runtime);
50
48
  });
51
49
  }
52
- cache_token_market_price(token_info_with_price_1) {
53
- return __awaiter(this, arguments, void 0, function* (token_info_with_price, ttl = -1) {
54
- token_info_with_price.update_time = (0, __1.getCurDateTime)();
55
- yield this.arb_cache.cache_token_market_price(token_info_with_price, ttl);
56
- });
57
- }
58
- get_token_market_price(symbol) {
59
- return __awaiter(this, void 0, void 0, function* () {
60
- return yield this.arb_cache.get_token_market_price(symbol);
61
- });
62
- }
63
50
  }
64
51
  exports.AppConfig = AppConfig;
@@ -10,6 +10,7 @@ export declare class EnvArgs {
10
10
  redis_host: string;
11
11
  redis_port: string;
12
12
  config_center_host: string;
13
+ market_data_host: string;
13
14
  enable_init_cache: string;
14
15
  trade_analyze_url: string;
15
16
  trade_analyze_host: string;
@@ -31,7 +32,7 @@ export declare class EnvArgs {
31
32
  trade_pair: string;
32
33
  trade_log_report_cex_url: string;
33
34
  trade_log_report_cex_data_type: string;
34
- token_price_cache_seconds: number;
35
+ token_price_refresh_interval_seconds: number;
35
36
  token_market_price_url: string;
36
37
  wallet_dir: string;
37
38
  encryption_key: string;
@@ -24,6 +24,7 @@ registerEnvVars({
24
24
  grpc_endpoint: { env: 'GRPC_ENDPOINT', type: 'string', default: '', desc: 'gRPC 端点' },
25
25
  grpc_token: { env: 'GRPC_TOKEN', type: 'string', default: '', sensitive: true, desc: 'gRPC 认证 token' },
26
26
  config_center_host: { env: 'CONFIG_CENTER_HOST', type: 'string', default: '127.0.0.1', desc: '配置中心' },
27
+ market_data_host: { env: 'MARKET_DATA_HOST', type: 'string', default: '127.0.0.1', desc: 'market-data 服务(L3 现查 / fetch-pool 调用)' },
27
28
  enable_init_cache: { env: 'ENABLE_INIT_CACHE', type: 'string', default: '', desc: '仅 config-center:是否初始化缓存(空或 true 视为启用;false 则跳过)' },
28
29
  trade_analyze_host: { env: 'TRADE_ANALYZE_HOST', type: 'string', default: '', desc: 'analyze 上报地址(IP);为空则不上报' },
29
30
  auto_quote_enable: { env: 'AUTO_QUOTE_ENABLE', type: 'boolean', default: false, desc: '自动报价开关' },
@@ -44,7 +45,7 @@ registerEnvVars({
44
45
  encryption_key: { env: 'ENCRYPTION_KEY', type: 'string', default: '', sensitive: true, desc: '对称加密密钥(用于钱包文件等)' },
45
46
  trade_analyze_url: { env: 'TRADE_ANALYZE_URL', type: 'string', default: '', desc: 'analyze HTTP 基址(旧 EnvArgs;未配则 analyze 上报走默认拼接)' },
46
47
  providers_file_path: { env: 'PROVIDERS_FILE_PATH', type: 'string', default: '', desc: 'stream-quote providers.json 绝对路径;留空则仅写 Redis' },
47
- token_price_cache_seconds: { env: 'TOKEN_PRICE_CACHE_SECONDS', type: 'number', default: 1200, desc: 'token 价格缓存时间(s)' },
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
49
  token_market_price_url: { env: 'TOKEN_MARKET_PRICE_URL', type: 'string', default: '', desc: '【使用方: ttd-core estimate_token_amount】config-center 市价估算 HTTP 基址;为空则跳过远程回退' },
49
50
  fixed_symbol_address_file: { env: 'FIXED_SYMBOL_ADDRESS_FILE', type: 'string', default: '', desc: '固定 symbol/address 映射文件路径(覆盖默认按 CHAIN_ID 推断的路径)' },
50
51
  zh_pinyin_map_file: { env: 'ZH_PINYIN_MAP_FILE', type: 'string', default: '', desc: '中文拼音映射文件路径(覆盖默认路径)' },
@@ -51,8 +51,7 @@ export declare class ArbCache {
51
51
  create_trade_runtime(chain_id: string, group_id: string, dex_id: string, pair_name: string): Promise<TradeRuntimeType>;
52
52
  find_pair_in_trade_config(pair_name: string, trade_config: TradeServiceConfigType): TradePairType;
53
53
  find_pair_dex_in_trade_config(dex_id: string, pair_config: TradePairType): TradePairDexPoolsType;
54
- cache_token_market_price(token_with_price: StandardTokenInfoType, ttl?: number): Promise<void>;
55
- get_token_market_price(symbol: string): Promise<StandardTokenInfoType>;
54
+ update_token_prices(input_tokens: StandardTokenInfoType[]): Promise<number>;
56
55
  cache_price_message(price_msg: PriceMessageType, ttl?: number): Promise<void>;
57
56
  get_price_message(price_id: string): Promise<PriceMessageType>;
58
57
  cache_order_message(order_msg: OrderMessageType, ttl?: number): Promise<{
@@ -81,18 +81,40 @@ class ArbCache {
81
81
  return path_1.default.join('config', chain_id, file_name);
82
82
  }
83
83
  refresh_local_cache_token_list(token_list) {
84
- this.token_list = token_list;
84
+ const new_by_addr = new Map();
85
+ const merged = [];
86
+ for (const fresh of token_list) {
87
+ new_by_addr.set(fresh.address, fresh);
88
+ }
89
+ const to_delete_addr = [];
90
+ for (const [addr, existing] of this.token_address_map) {
91
+ const fresh = new_by_addr.get(addr);
92
+ if (fresh) {
93
+ Object.assign(existing, fresh);
94
+ merged.push(existing);
95
+ new_by_addr.delete(addr);
96
+ }
97
+ else {
98
+ to_delete_addr.push(addr);
99
+ }
100
+ }
101
+ for (const addr of to_delete_addr) {
102
+ this.token_address_map.delete(addr);
103
+ }
104
+ for (const fresh of new_by_addr.values()) {
105
+ this.token_address_map.set(fresh.address, fresh);
106
+ merged.push(fresh);
107
+ }
85
108
  this.token_symbol_map.clear();
86
- this.token_address_map.clear();
87
- for (let token of token_list) {
109
+ for (const token of merged) {
88
110
  this.token_symbol_map.set(token.symbol, token);
89
- this.token_address_map.set(token.address, token);
90
111
  }
112
+ this.token_list = merged;
91
113
  this.token_list_cache_last_update_time = (0, index_1.getCurDateTime)();
92
114
  (0, index_1.log_trace)('refresh_local_cache_token_list, success', {
93
115
  token_list_len: this.token_list.length,
94
116
  token_symbol_map_size: this.token_symbol_map.size,
95
- token_address_map_szie: this.token_address_map.size
117
+ token_address_map_szie: this.token_address_map.size,
96
118
  });
97
119
  }
98
120
  refresh_local_cache_pool_list(pool_list) {
@@ -141,9 +163,6 @@ class ArbCache {
141
163
  if (index_1.LOG.debug) {
142
164
  (0, index_1.log_trace)(`cache_token_list, symbol=${field}, result: ` + JSON.stringify(result));
143
165
  }
144
- if (token.market_price) {
145
- this.cache_token_market_price(token, -1);
146
- }
147
166
  }
148
167
  this.refresh_local_cache_token_list(token_list);
149
168
  let result = yield this.loading_cache.hlen(this.chain_id, index_1.CACHE_KEY_TYPE.CONFIG_TOKEN_LIST);
@@ -644,36 +663,26 @@ class ArbCache {
644
663
  let pair_dex_config = pair_config.dex_list.find(e => e.dex_id === dex_id);
645
664
  return pair_dex_config;
646
665
  }
647
- cache_token_market_price(token_with_price_1) {
648
- return __awaiter(this, arguments, void 0, function* (token_with_price, ttl = -1) {
649
- (0, index_1.log_trace)(`cache_market_price, start`);
650
- if ((0, index_1.isEmpty)(token_with_price.market_price)) {
651
- (0, index_1.log_warn)(`cache_token_market_price skip: empty market_price, symbol=${token_with_price.symbol}, address=${token_with_price.address}`);
652
- return;
653
- }
654
- let { symbol } = token_with_price;
655
- let field = (0, index_1.format_symbol_name)(symbol);
656
- let value = JSON.stringify(token_with_price);
657
- let result = yield this.loading_cache.hset_ex(this.chain_id, index_1.CACHE_KEY_TYPE.MARKET_TOKEN_PRICE, field, value, ttl);
658
- (0, index_1.log_trace)('cache_market_price, end', result);
659
- });
660
- }
661
- get_token_market_price(symbol) {
666
+ update_token_prices(input_tokens) {
662
667
  return __awaiter(this, void 0, void 0, function* () {
663
- try {
664
- let id = (0, index_1.format_symbol_name)(symbol);
665
- let res = yield this.loading_cache.hget(this.chain_id, index_1.CACHE_KEY_TYPE.MARKET_TOKEN_PRICE, id);
666
- if (index_1.LOG.debug) {
667
- (0, index_1.log_trace)(`get_token_market_price, symbol=${symbol}, res=`, res);
668
- }
669
- let { key, field, value } = res;
670
- let token_with_price = JSON.parse(value);
671
- return token_with_price;
672
- }
673
- catch (err) {
674
- (0, index_1.log_error)('get_token_market_price error!!! key expired ???', err);
675
- throw new Error(`get_token_market_price error! err=${err.message}`);
676
- }
668
+ if ((0, index_1.isEmpty)(input_tokens))
669
+ return 0;
670
+ let count = 0;
671
+ for (const token of input_tokens) {
672
+ if (!(token === null || token === void 0 ? void 0 : token.symbol) || (0, index_1.isEmpty)(token.market_price))
673
+ continue;
674
+ const field = (0, index_1.format_symbol_name)(token.symbol);
675
+ yield this.loading_cache.hset(this.chain_id, index_1.CACHE_KEY_TYPE.CONFIG_TOKEN_LIST, field, JSON.stringify(token));
676
+ count++;
677
+ }
678
+ if (count > 0) {
679
+ yield this.redis_event_publisher.publish_config_change_event({
680
+ event_type: index_1.REDIS_EVENT_TYPE_CONFIG_CHANGE.TOKEN_CONFIG_CHANGE,
681
+ event_time: Date.now(),
682
+ });
683
+ }
684
+ (0, index_1.log_trace)(`update_token_prices, end. count=${count}`);
685
+ return count;
677
686
  });
678
687
  }
679
688
  cache_price_message(price_msg_1) {
package/dist/index.d.ts CHANGED
@@ -115,7 +115,6 @@ export declare enum OnChainDataSubscribeType {
115
115
  GRPC = "grpc"
116
116
  }
117
117
  export declare enum CACHE_KEY_TYPE {
118
- MARKET_TOKEN_PRICE = "m:price",
119
118
  CONFIG_TOKEN_LIST = "c:token",
120
119
  CONFIG_POOL_LIST = "c:pool",
121
120
  CONFIG_PAIR_LIST = "c:pair",
package/dist/index.js CHANGED
@@ -207,7 +207,6 @@ var OnChainDataSubscribeType;
207
207
  })(OnChainDataSubscribeType || (exports.OnChainDataSubscribeType = OnChainDataSubscribeType = {}));
208
208
  var CACHE_KEY_TYPE;
209
209
  (function (CACHE_KEY_TYPE) {
210
- CACHE_KEY_TYPE["MARKET_TOKEN_PRICE"] = "m:price";
211
210
  CACHE_KEY_TYPE["CONFIG_TOKEN_LIST"] = "c:token";
212
211
  CACHE_KEY_TYPE["CONFIG_POOL_LIST"] = "c:pool";
213
212
  CACHE_KEY_TYPE["CONFIG_PAIR_LIST"] = "c:pair";
@@ -61,12 +61,12 @@ class EstimateAmountInToken {
61
61
  }
62
62
  else {
63
63
  if (index_1.LOG.debug) {
64
- (0, index_1.log_debug)(`try-2 get market price from remote cache`);
64
+ (0, index_1.log_debug)(`try-2 get market price from in-memory token_address_map`);
65
65
  }
66
- token_info = yield arb_cache.get_token_market_price(symbol);
66
+ token_info = arb_cache.token_symbol_map.get(symbol);
67
67
  set_tmp_market_price(token_info);
68
68
  if (index_1.LOG.debug) {
69
- (0, index_1.log_debug)(`try-2 get market price from remote cache, res=`, token_info);
69
+ (0, index_1.log_debug)(`try-2 get market price from in-memory map, res=`, token_info);
70
70
  }
71
71
  }
72
72
  let market_price = token_info === null || token_info === void 0 ? void 0 : token_info.market_price;
@@ -24,63 +24,55 @@ const cache_pool_config = (chain_id, valid_tokens_map, sol_pools_cache) => __awa
24
24
  let skip_pool_list = [];
25
25
  const genv = (0, core_env_1.getCoreEnv)();
26
26
  const ccBase = `http://${(genv.config_center_host || '127.0.0.1').trim()}:${service_ports_1.SERVICE_PORT.CONFIG_CENTER_HTTP}/${chain_id}/config`;
27
- let config_pool_url = `${ccBase}/pools`;
28
- for (let [dex_id, pool_list] of sol_pools_cache) {
29
- let batch_pools = (0, index_1.chunkArray)(pool_list, 10);
30
- for (let one_pool_list of batch_pools) {
31
- let valid_pool_list = [];
32
- for (let pool of one_pool_list) {
33
- let is_contains_unkown_symbol = !valid_tokens_map.has(pool.tokenA.symbol) || !valid_tokens_map.has(pool.tokenB.symbol);
34
- if (is_contains_unkown_symbol || (0, is_not_arb_token_1.is_not_arb_token)(pool.pool_name)) {
35
- skip_pool_list.push(pool);
36
- continue;
37
- }
38
- let std_pool = {
39
- program_id: pool.program_id,
40
- authority: pool.authority,
41
- subscribe_type: 'grpc',
42
- pair: pool.pair,
43
- dex_id: dex_id,
44
- pool_name: pool.pool_name,
45
- pool_address: pool.pool_address,
46
- pool_address_hex: '',
47
- tokenA: null,
48
- tokenB: null,
49
- vaultA: pool.vaultA,
50
- vaultB: pool.vaultB,
51
- subscribe_vault_change: pool.subscribe_vault_change,
52
- router_id: null,
53
- fee_rate: pool.fee_rate_bps,
54
- quote_token: pool.quote_token,
55
- quote_amount_usd: genv.quote_amount_usd,
56
- quote_price_decimals: 18,
57
- is_reverse_token: null,
58
- cu_limit: get_cu_limit(dex_id),
59
- tvl: pool.tvl,
60
- vol_24h: pool.vol_24h,
61
- update_time: (0, index_1.getCurDateTime)(),
62
- is_trade_native_token: pool.is_trade_native_token,
63
- enable: true,
64
- };
65
- valid_pool_list.push(std_pool);
66
- count = count + 1;
67
- }
68
- if (valid_pool_list.length === 0) {
27
+ const config_pool_url = `${ccBase}/pools`;
28
+ const body = [];
29
+ for (const [dex_id, pool_list] of sol_pools_cache) {
30
+ const valid_pool_list = [];
31
+ for (const pool of pool_list) {
32
+ const is_contains_unkown_symbol = !valid_tokens_map.has(pool.tokenA.symbol) || !valid_tokens_map.has(pool.tokenB.symbol);
33
+ if (is_contains_unkown_symbol || (0, is_not_arb_token_1.is_not_arb_token)(pool.pool_name)) {
34
+ skip_pool_list.push(pool);
69
35
  continue;
70
36
  }
71
- let body = [
72
- {
73
- dex_id: dex_id,
74
- pool_list: valid_pool_list
75
- }
76
- ];
77
- const res = (yield axios_1.default.post(config_pool_url, body, {
78
- headers: { 'Content-Type': 'application/json' }
79
- })).data;
80
- (0, index_1.log_trace)('cache_pool_config, res', res);
81
- yield (0, index_1.sleep)(200);
37
+ valid_pool_list.push({
38
+ program_id: pool.program_id,
39
+ authority: pool.authority,
40
+ subscribe_type: 'grpc',
41
+ pair: pool.pair,
42
+ dex_id,
43
+ pool_name: pool.pool_name,
44
+ pool_address: pool.pool_address,
45
+ pool_address_hex: '',
46
+ tokenA: null,
47
+ tokenB: null,
48
+ vaultA: pool.vaultA,
49
+ vaultB: pool.vaultB,
50
+ subscribe_vault_change: pool.subscribe_vault_change,
51
+ router_id: null,
52
+ fee_rate: pool.fee_rate_bps,
53
+ quote_token: pool.quote_token,
54
+ quote_amount_usd: genv.quote_amount_usd,
55
+ quote_price_decimals: 18,
56
+ is_reverse_token: null,
57
+ cu_limit: get_cu_limit(dex_id),
58
+ tvl: pool.tvl,
59
+ vol_24h: pool.vol_24h,
60
+ update_time: (0, index_1.getCurDateTime)(),
61
+ is_trade_native_token: pool.is_trade_native_token,
62
+ enable: true,
63
+ });
64
+ count = count + 1;
65
+ }
66
+ if (valid_pool_list.length > 0) {
67
+ body.push({ dex_id, pool_list: valid_pool_list });
82
68
  }
83
69
  }
70
+ if (body.length > 0) {
71
+ const res = (yield axios_1.default.post(config_pool_url, body, {
72
+ headers: { 'Content-Type': 'application/json' },
73
+ })).data;
74
+ (0, index_1.log_trace)('cache_pool_config, res', res);
75
+ }
84
76
  (0, index_1.log_info)(`cache_pool_config finished! sync pool count:${count}, skip: ${skip_pool_list.length}`);
85
77
  skip_pool_list.sort((a, b) => a.pair.localeCompare(b.pair));
86
78
  (0, index_1.writeFile)(`dist/${chain_id.toLowerCase()}_skip_pool_list.json`, JSON.stringify({
@@ -22,46 +22,35 @@ const cache_token_config = (chain_id, valid_tokens_map) => __awaiter(void 0, voi
22
22
  (0, index_1.log_info)(`cache_token_config start`);
23
23
  const genv = (0, core_env_1.getCoreEnv)();
24
24
  const ccBase = `http://${(genv.config_center_host || '127.0.0.1').trim()}:${service_ports_1.SERVICE_PORT.CONFIG_CENTER_HTTP}/${chain_id}/config`;
25
- let config_token_url = `${ccBase}/tokens`;
26
- let count = 0;
27
- let batch_symbols = (0, index_1.chunkArray)(Array.from(valid_tokens_map.keys()), 20);
28
- for (let one_batch of batch_symbols) {
29
- let valid_tokens = [];
30
- for (let key of one_batch) {
31
- let value = valid_tokens_map.get(key);
32
- if (!value || value.length !== 1) {
33
- continue;
34
- }
35
- let token = value[0];
36
- let std_token = {
37
- symbol: token.symbol,
38
- address: token.address,
39
- address_hex: '',
40
- decimals: token.decimals,
41
- name: token.name,
42
- is_token2022: token.is_token_2022,
43
- market_price: token.price,
44
- update_time: (0, index_1.getCurDateTime)(),
45
- alias: (0, index_1.get_token_symbol_alias)(token.address),
46
- enable: true
47
- };
48
- if ((0, is_not_arb_token_1.is_not_arb_token)(std_token.symbol)) {
49
- continue;
50
- }
51
- valid_tokens.push(std_token);
52
- count += 1;
53
- }
54
- if (valid_tokens.length === 0) {
25
+ const config_token_url = `${ccBase}/tokens`;
26
+ const valid_tokens = [];
27
+ for (const [, value] of valid_tokens_map) {
28
+ if (!value || value.length !== 1)
55
29
  continue;
56
- }
57
- const body = valid_tokens;
58
- const res = (yield axios_1.default.post(config_token_url, body, {
59
- headers: { 'Content-Type': 'application/json' }
30
+ const token = value[0];
31
+ const std_token = {
32
+ symbol: token.symbol,
33
+ address: token.address,
34
+ address_hex: '',
35
+ decimals: token.decimals,
36
+ name: token.name,
37
+ is_token2022: token.is_token_2022,
38
+ market_price: token.price,
39
+ update_time: (0, index_1.getCurDateTime)(),
40
+ alias: (0, index_1.get_token_symbol_alias)(token.address),
41
+ enable: true,
42
+ };
43
+ if ((0, is_not_arb_token_1.is_not_arb_token)(std_token.symbol))
44
+ continue;
45
+ valid_tokens.push(std_token);
46
+ }
47
+ if (valid_tokens.length > 0) {
48
+ const res = (yield axios_1.default.post(config_token_url, valid_tokens, {
49
+ headers: { 'Content-Type': 'application/json' },
60
50
  })).data;
61
51
  (0, index_1.log_trace)('cache_token_config, res', res);
62
- yield (0, index_1.sleep)(200);
63
52
  }
64
- (0, index_1.log_info)(`cache_token_config finish! sync token count: ${count}`);
53
+ (0, index_1.log_info)(`cache_token_config finish! sync token count: ${valid_tokens.length}`);
65
54
  let notify_config_change_url = `${ccBase}/change?type=token`;
66
55
  let res = (yield axios_1.default.get(notify_config_change_url)).data;
67
56
  (0, index_1.log_info)('fetch tokens end, notify config change', {
@@ -1,2 +1,5 @@
1
1
  import { FormattedTokenPrice } from "../types";
2
- export declare function get_bsc_token_price_info(addresses: string[]): Promise<Map<string, FormattedTokenPrice>>;
2
+ export type PriceSource = 'cache_only' | 'cache_first' | 'force_fetch';
3
+ export declare function get_bsc_token_price_info(addresses: string[], opts?: {
4
+ source?: PriceSource;
5
+ }): Promise<Map<string, FormattedTokenPrice>>;
@@ -15,17 +15,19 @@ const index_1 = require("../../index");
15
15
  const defi_llama_1 = require("./defi_llama");
16
16
  const gecko_terminal_1 = require("./gecko_terminal");
17
17
  const price_cache_1 = require("./price_cache");
18
- function get_bsc_token_price_info(addresses) {
18
+ function get_bsc_token_price_info(addresses, opts) {
19
19
  return __awaiter(this, void 0, void 0, function* () {
20
+ var _a;
21
+ const source = (_a = opts === null || opts === void 0 ? void 0 : opts.source) !== null && _a !== void 0 ? _a : 'cache_only';
20
22
  addresses = addresses.map(addr => addr.toLowerCase());
21
23
  const result = new Map();
22
- const PRICE_CHANNELS = [
23
- {
24
- name: 'CachedPrice',
25
- fetchFn: price_cache_1.fetchPriceFromCache,
26
- batchSize: 100,
27
- batchDelay: 1000,
28
- },
24
+ const cachedChannel = {
25
+ name: 'CachedPrice',
26
+ fetchFn: price_cache_1.fetchPriceFromCache,
27
+ batchSize: 100,
28
+ batchDelay: 1000,
29
+ };
30
+ const externalChannels = [
29
31
  {
30
32
  name: 'DefiLlama',
31
33
  fetchFn: (address_list) => (0, defi_llama_1.fetchPriceFromDefiLlama)(index_1.CHAIN_ID.BSC, address_list),
@@ -39,6 +41,10 @@ function get_bsc_token_price_info(addresses) {
39
41
  batchDelay: 1000,
40
42
  },
41
43
  ];
44
+ const PRICE_CHANNELS = source === 'cache_only' ? [cachedChannel] :
45
+ source === 'cache_first' ? [cachedChannel, ...externalChannels] :
46
+ [...externalChannels];
47
+ const externalHits = [];
42
48
  try {
43
49
  for (const channel of PRICE_CHANNELS) {
44
50
  if (addresses.length === 0)
@@ -58,7 +64,7 @@ function get_bsc_token_price_info(addresses) {
58
64
  result.set(address, priceInfo);
59
65
  remainingAddresses = remainingAddresses.filter(addr => addr !== address);
60
66
  if (channel.name !== 'CachedPrice') {
61
- (0, price_cache_1.cache_new_market_price)(address, priceInfo.price, channel.name);
67
+ externalHits.push({ address, price: priceInfo.price, source: channel.name });
62
68
  }
63
69
  }
64
70
  }
@@ -75,17 +81,26 @@ function get_bsc_token_price_info(addresses) {
75
81
  break;
76
82
  }
77
83
  }
78
- if (result.size === 0) {
84
+ if (externalHits.length > 0) {
85
+ yield (0, price_cache_1.cache_new_market_price_batch)(externalHits);
86
+ }
87
+ if (source === 'cache_only' && addresses.length > 0) {
88
+ (0, price_cache_1.warnPriceCacheMiss)(addresses);
89
+ }
90
+ if (source === 'force_fetch' && result.size === 0) {
79
91
  throw new Error(`Unable to get price information for any token: ${addresses.join(', ')}`);
80
92
  }
81
- if (addresses.length > 0) {
93
+ if (source !== 'cache_only' && addresses.length > 0) {
82
94
  (0, index_1.log_warn)(`[get_token_price_info] Failed to get prices for ${addresses.length} tokens after trying all channels: ${addresses.join(', ')}`);
83
95
  }
84
96
  (0, index_1.log_debug)(`[get_token_price_info] Completed price fetching for ${result.size} tokens`);
85
97
  return result;
86
98
  }
87
99
  catch (error) {
88
- throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
100
+ if (source === 'force_fetch') {
101
+ throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
102
+ }
103
+ return result;
89
104
  }
90
105
  });
91
106
  }
@@ -1,3 +1,6 @@
1
1
  import { EvmChainInfoType } from '../../chains';
2
2
  import { FormattedTokenPrice } from "../types";
3
- export declare function get_eth_token_price_info(evm_chain_info: EvmChainInfoType, addresses: string[]): Promise<Map<string, FormattedTokenPrice>>;
3
+ import type { PriceSource } from './get_bsc_token_price';
4
+ export declare function get_eth_token_price_info(evm_chain_info: EvmChainInfoType, addresses: string[], opts?: {
5
+ source?: PriceSource;
6
+ }): Promise<Map<string, FormattedTokenPrice>>;
@@ -19,17 +19,19 @@ const index_1 = require("../../index");
19
19
  const defi_llama_1 = require("./defi_llama");
20
20
  const gecko_terminal_1 = require("./gecko_terminal");
21
21
  const price_cache_1 = require("./price_cache");
22
- function get_eth_token_price_info(evm_chain_info, addresses) {
22
+ function get_eth_token_price_info(evm_chain_info, addresses, opts) {
23
23
  return __awaiter(this, void 0, void 0, function* () {
24
+ var _a;
25
+ const source = (_a = opts === null || opts === void 0 ? void 0 : opts.source) !== null && _a !== void 0 ? _a : 'cache_only';
24
26
  addresses = addresses.map(addr => addr.toLowerCase());
25
27
  const result = new Map();
26
- const PRICE_CHANNELS = [
27
- {
28
- name: 'CachedPrice',
29
- fetchFn: price_cache_1.fetchPriceFromCache,
30
- batchSize: 100,
31
- batchDelay: 1000,
32
- },
28
+ const cachedChannel = {
29
+ name: 'CachedPrice',
30
+ fetchFn: price_cache_1.fetchPriceFromCache,
31
+ batchSize: 100,
32
+ batchDelay: 1000,
33
+ };
34
+ const externalChannels = [
33
35
  {
34
36
  name: 'DefiLlama',
35
37
  fetchFn: (address_list) => (0, defi_llama_1.fetchPriceFromDefiLlama)(evm_chain_info.chain_short_name, address_list),
@@ -49,6 +51,10 @@ function get_eth_token_price_info(evm_chain_info, addresses) {
49
51
  batchDelay: 1000,
50
52
  },
51
53
  ];
54
+ const PRICE_CHANNELS = source === 'cache_only' ? [cachedChannel] :
55
+ source === 'cache_first' ? [cachedChannel, ...externalChannels] :
56
+ [...externalChannels];
57
+ const externalHits = [];
52
58
  try {
53
59
  for (const channel of PRICE_CHANNELS) {
54
60
  if (addresses.length === 0)
@@ -68,7 +74,7 @@ function get_eth_token_price_info(evm_chain_info, addresses) {
68
74
  result.set(address, priceInfo);
69
75
  remainingAddresses = remainingAddresses.filter(addr => addr !== address);
70
76
  if (channel.name !== 'CachedPrice') {
71
- (0, price_cache_1.cache_new_market_price)(address, priceInfo.price, channel.name);
77
+ externalHits.push({ address, price: priceInfo.price, source: channel.name });
72
78
  }
73
79
  }
74
80
  }
@@ -85,17 +91,26 @@ function get_eth_token_price_info(evm_chain_info, addresses) {
85
91
  break;
86
92
  }
87
93
  }
88
- if (result.size === 0) {
94
+ if (externalHits.length > 0) {
95
+ yield (0, price_cache_1.cache_new_market_price_batch)(externalHits);
96
+ }
97
+ if (source === 'cache_only' && addresses.length > 0) {
98
+ (0, price_cache_1.warnPriceCacheMiss)(addresses);
99
+ }
100
+ if (source === 'force_fetch' && result.size === 0) {
89
101
  throw new Error(`Unable to get price information for any token: ${addresses.join(', ')}`);
90
102
  }
91
- if (addresses.length > 0) {
103
+ if (source !== 'cache_only' && addresses.length > 0) {
92
104
  (0, index_1.log_warn)(`[get_token_price_info] Failed to get prices for ${addresses.length} tokens after trying all channels: ${addresses.join(', ')}`);
93
105
  }
94
106
  (0, index_1.log_debug)(`[get_token_price_info] Completed price fetching for ${result.size} tokens`);
95
107
  return result;
96
108
  }
97
109
  catch (error) {
98
- throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
110
+ if (source === 'force_fetch') {
111
+ throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
112
+ }
113
+ return result;
99
114
  }
100
115
  });
101
116
  }
@@ -1,2 +1,5 @@
1
1
  import { FormattedTokenPrice } from "../types";
2
- export declare function get_solana_token_price_info(addresses: string[]): Promise<Map<string, FormattedTokenPrice>>;
2
+ import type { PriceSource } from './get_bsc_token_price';
3
+ export declare function get_solana_token_price_info(addresses: string[], opts?: {
4
+ source?: PriceSource;
5
+ }): Promise<Map<string, FormattedTokenPrice>>;