@clonegod/ttd-core 3.1.24 → 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.
- package/dist/alert/codes.js +1 -0
- package/dist/alert/log_rules.js +1 -0
- package/dist/app_config/AppConfig.d.ts +1 -3
- package/dist/app_config/AppConfig.js +0 -11
- package/dist/app_config/EnvArgs.d.ts +2 -1
- package/dist/app_config/env_registry.js +2 -1
- package/dist/cache/arb_cache.d.ts +1 -2
- package/dist/cache/arb_cache.js +46 -37
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/market_price/estimate_token_amount.js +3 -3
- package/dist/token/price/get_bsc_token_price.d.ts +4 -1
- package/dist/token/price/get_bsc_token_price.js +27 -12
- package/dist/token/price/get_eth_token_price.d.ts +4 -1
- package/dist/token/price/get_eth_token_price.js +27 -12
- package/dist/token/price/get_solana_token_price.d.ts +4 -1
- package/dist/token/price/get_solana_token_price.js +27 -12
- package/dist/token/price/get_sui_token_price.d.ts +4 -1
- package/dist/token/price/get_sui_token_price.js +27 -12
- package/dist/token/price/get_tron_token_price.d.ts +4 -1
- package/dist/token/price/get_tron_token_price.js +27 -12
- package/dist/token/price/get_xlayer_token_price.d.ts +4 -1
- package/dist/token/price/get_xlayer_token_price.js +27 -12
- package/dist/token/price/index.d.ts +2 -0
- package/dist/token/price/index.js +2 -0
- package/dist/token/price/price_cache.d.ts +6 -1
- package/dist/token/price/price_cache.js +67 -76
- package/dist/token/price/token_price_syncer.d.ts +25 -0
- package/dist/token/price/token_price_syncer.js +80 -0
- package/package.json +1 -1
- package/types/index.d.ts +2 -0
package/dist/alert/codes.js
CHANGED
|
@@ -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: {
|
package/dist/alert/log_rules.js
CHANGED
|
@@ -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 {
|
|
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
|
}
|
|
@@ -47,16 +47,5 @@ class AppConfig extends events_1.default {
|
|
|
47
47
|
yield this.arb_event_subscriber.subscribe_config_change_event(refresh_trade_runtime);
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
cache_token_market_price(token_info_with_price_1) {
|
|
51
|
-
return __awaiter(this, arguments, void 0, function* (token_info_with_price, ttl = -1) {
|
|
52
|
-
token_info_with_price.update_time = (0, __1.getCurDateTime)();
|
|
53
|
-
yield this.arb_cache.cache_token_market_price(token_info_with_price, ttl);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
get_token_market_price(symbol) {
|
|
57
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
-
return yield this.arb_cache.get_token_market_price(symbol);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
50
|
}
|
|
62
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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<{
|
package/dist/cache/arb_cache.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
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
|
|
64
|
+
(0, index_1.log_debug)(`try-2 get market price from in-memory token_address_map`);
|
|
65
65
|
}
|
|
66
|
-
token_info =
|
|
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
|
|
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;
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import { FormattedTokenPrice } from "../types";
|
|
2
|
-
export
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
(
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
(
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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>>;
|
|
@@ -18,16 +18,18 @@ const index_1 = require("../../index");
|
|
|
18
18
|
const gecko_terminal_1 = require("./gecko_terminal");
|
|
19
19
|
const price_cache_1 = require("./price_cache");
|
|
20
20
|
const defi_llama_1 = require("./defi_llama");
|
|
21
|
-
function get_solana_token_price_info(addresses) {
|
|
21
|
+
function get_solana_token_price_info(addresses, opts) {
|
|
22
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
var _a;
|
|
24
|
+
const source = (_a = opts === null || opts === void 0 ? void 0 : opts.source) !== null && _a !== void 0 ? _a : 'cache_only';
|
|
23
25
|
const result = new Map();
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
const cachedChannel = {
|
|
27
|
+
name: 'CachedPrice',
|
|
28
|
+
fetchFn: price_cache_1.fetchPriceFromCache,
|
|
29
|
+
batchSize: 100,
|
|
30
|
+
batchDelay: 1000,
|
|
31
|
+
};
|
|
32
|
+
const externalChannels = [
|
|
31
33
|
{
|
|
32
34
|
name: 'Jupiter',
|
|
33
35
|
fetchFn: fetchPriceFromJupiter,
|
|
@@ -47,6 +49,10 @@ function get_solana_token_price_info(addresses) {
|
|
|
47
49
|
batchDelay: 2000,
|
|
48
50
|
},
|
|
49
51
|
];
|
|
52
|
+
const PRICE_CHANNELS = source === 'cache_only' ? [cachedChannel] :
|
|
53
|
+
source === 'cache_first' ? [cachedChannel, ...externalChannels] :
|
|
54
|
+
[...externalChannels];
|
|
55
|
+
const externalHits = [];
|
|
50
56
|
try {
|
|
51
57
|
for (const channel of PRICE_CHANNELS) {
|
|
52
58
|
if (addresses.length === 0)
|
|
@@ -66,7 +72,7 @@ function get_solana_token_price_info(addresses) {
|
|
|
66
72
|
result.set(address, priceInfo);
|
|
67
73
|
remainingAddresses = remainingAddresses.filter(addr => addr !== address);
|
|
68
74
|
if (channel.name !== 'CachedPrice') {
|
|
69
|
-
(
|
|
75
|
+
externalHits.push({ address, price: priceInfo.price, source: channel.name });
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
(0, index_1.log_debug)(`[get_token_price_info] ${channel.name} found prices for ${channelResult.size}/${batch.length} tokens in batch ${i + 1}`);
|
|
@@ -84,17 +90,26 @@ function get_solana_token_price_info(addresses) {
|
|
|
84
90
|
break;
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
|
-
if (
|
|
93
|
+
if (externalHits.length > 0) {
|
|
94
|
+
yield (0, price_cache_1.cache_new_market_price_batch)(externalHits);
|
|
95
|
+
}
|
|
96
|
+
if (source === 'cache_only' && addresses.length > 0) {
|
|
97
|
+
(0, price_cache_1.warnPriceCacheMiss)(addresses);
|
|
98
|
+
}
|
|
99
|
+
if (source === 'force_fetch' && result.size === 0) {
|
|
88
100
|
throw new Error(`Unable to get price information for any token: ${addresses.join(', ')}`);
|
|
89
101
|
}
|
|
90
|
-
if (addresses.length > 0) {
|
|
102
|
+
if (source !== 'cache_only' && addresses.length > 0) {
|
|
91
103
|
(0, index_1.log_warn)(`[get_token_price_info] Failed to get prices for ${addresses.length} tokens after trying all channels: ${addresses.join(', ')}`);
|
|
92
104
|
}
|
|
93
105
|
(0, index_1.log_debug)(`[get_token_price_info] Completed price fetching for ${result.size} tokens`);
|
|
94
106
|
return result;
|
|
95
107
|
}
|
|
96
108
|
catch (error) {
|
|
97
|
-
|
|
109
|
+
if (source === 'force_fetch') {
|
|
110
|
+
throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
98
113
|
}
|
|
99
114
|
});
|
|
100
115
|
}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import { FormattedTokenPrice } from "../types";
|
|
2
|
-
|
|
2
|
+
import type { PriceSource } from './get_bsc_token_price';
|
|
3
|
+
export declare function get_sui_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_sui_token_price_info(addresses) {
|
|
18
|
+
function get_sui_token_price_info(addresses, opts) {
|
|
19
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
var _a;
|
|
20
21
|
(0, index_1.log_info)(`get_sui_token_price_info`, addresses);
|
|
22
|
+
const source = (_a = opts === null || opts === void 0 ? void 0 : opts.source) !== null && _a !== void 0 ? _a : 'cache_only';
|
|
21
23
|
const result = new Map();
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.SUI, address_list),
|
|
@@ -39,6 +41,10 @@ function get_sui_token_price_info(addresses) {
|
|
|
39
41
|
batchDelay: 2000,
|
|
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_sui_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
|
-
(
|
|
67
|
+
externalHits.push({ address, price: priceInfo.price, source: channel.name });
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
(0, index_1.log_debug)(`[get_token_price_info] ${channel.name} found prices for ${channelResult.size}/${batch.length} tokens in batch ${i + 1}`);
|
|
@@ -76,17 +82,26 @@ function get_sui_token_price_info(addresses) {
|
|
|
76
82
|
break;
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
|
-
if (
|
|
85
|
+
if (externalHits.length > 0) {
|
|
86
|
+
yield (0, price_cache_1.cache_new_market_price_batch)(externalHits);
|
|
87
|
+
}
|
|
88
|
+
if (source === 'cache_only' && addresses.length > 0) {
|
|
89
|
+
(0, price_cache_1.warnPriceCacheMiss)(addresses);
|
|
90
|
+
}
|
|
91
|
+
if (source === 'force_fetch' && result.size === 0) {
|
|
80
92
|
throw new Error(`Unable to get price information for any token: ${addresses.join(', ')}`);
|
|
81
93
|
}
|
|
82
|
-
if (addresses.length > 0) {
|
|
94
|
+
if (source !== 'cache_only' && addresses.length > 0) {
|
|
83
95
|
(0, index_1.log_warn)(`[get_token_price_info] Failed to get prices for ${addresses.length} tokens after trying all channels: ${addresses.join(', ')}`);
|
|
84
96
|
}
|
|
85
97
|
(0, index_1.log_debug)(`[get_token_price_info] Completed price fetching for ${result.size} tokens`);
|
|
86
98
|
return result;
|
|
87
99
|
}
|
|
88
100
|
catch (error) {
|
|
89
|
-
|
|
101
|
+
if (source === 'force_fetch') {
|
|
102
|
+
throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
90
105
|
}
|
|
91
106
|
});
|
|
92
107
|
}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import { FormattedTokenPrice } from "../types";
|
|
2
|
-
|
|
2
|
+
import type { PriceSource } from './get_bsc_token_price';
|
|
3
|
+
export declare function get_tron_token_price_info(addresses: string[], opts?: {
|
|
4
|
+
source?: PriceSource;
|
|
5
|
+
}): Promise<Map<string, FormattedTokenPrice>>;
|
|
@@ -18,17 +18,19 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
18
18
|
const index_1 = require("../../index");
|
|
19
19
|
const gecko_terminal_1 = require("./gecko_terminal");
|
|
20
20
|
const price_cache_1 = require("./price_cache");
|
|
21
|
-
function get_tron_token_price_info(addresses) {
|
|
21
|
+
function get_tron_token_price_info(addresses, opts) {
|
|
22
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
var _a;
|
|
23
24
|
(0, index_1.log_info)(`get_tron_token_price_info`, addresses);
|
|
25
|
+
const source = (_a = opts === null || opts === void 0 ? void 0 : opts.source) !== null && _a !== void 0 ? _a : 'cache_only';
|
|
24
26
|
const result = new Map();
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const cachedChannel = {
|
|
28
|
+
name: 'CachedPrice',
|
|
29
|
+
fetchFn: price_cache_1.fetchPriceFromCache,
|
|
30
|
+
batchSize: 100,
|
|
31
|
+
batchDelay: 1000,
|
|
32
|
+
};
|
|
33
|
+
const externalChannels = [
|
|
32
34
|
{
|
|
33
35
|
name: 'TronscanALL',
|
|
34
36
|
fetchFn: fetchPriceFromTronscanALL,
|
|
@@ -48,6 +50,10 @@ function get_tron_token_price_info(addresses) {
|
|
|
48
50
|
batchDelay: 2000,
|
|
49
51
|
},
|
|
50
52
|
];
|
|
53
|
+
const PRICE_CHANNELS = source === 'cache_only' ? [cachedChannel] :
|
|
54
|
+
source === 'cache_first' ? [cachedChannel, ...externalChannels] :
|
|
55
|
+
[...externalChannels];
|
|
56
|
+
const externalHits = [];
|
|
51
57
|
try {
|
|
52
58
|
for (const channel of PRICE_CHANNELS) {
|
|
53
59
|
if (addresses.length === 0)
|
|
@@ -67,7 +73,7 @@ function get_tron_token_price_info(addresses) {
|
|
|
67
73
|
result.set(address, priceInfo);
|
|
68
74
|
remainingAddresses = remainingAddresses.filter(addr => addr !== address);
|
|
69
75
|
if (channel.name !== 'CachedPrice') {
|
|
70
|
-
(
|
|
76
|
+
externalHits.push({ address, price: priceInfo.price, source: channel.name });
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
(0, index_1.log_debug)(`[get_token_price_info] ${channel.name} found prices for ${channelResult.size}/${batch.length} tokens in batch ${i + 1}`);
|
|
@@ -85,17 +91,26 @@ function get_tron_token_price_info(addresses) {
|
|
|
85
91
|
break;
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
2
|
+
import type { PriceSource } from './get_bsc_token_price';
|
|
3
|
+
export declare function get_xlayer_token_price_info(addresses: string[], opts?: {
|
|
4
|
+
source?: PriceSource;
|
|
5
|
+
}): Promise<Map<string, FormattedTokenPrice>>;
|
|
@@ -15,18 +15,20 @@ 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_xlayer_token_price_info(addresses) {
|
|
18
|
+
function get_xlayer_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
|
(0, index_1.log_info)(`get_xlayer_token_price_info`, addresses);
|
|
22
24
|
const result = new Map();
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
const cachedChannel = {
|
|
26
|
+
name: 'CachedPrice',
|
|
27
|
+
fetchFn: price_cache_1.fetchPriceFromCache,
|
|
28
|
+
batchSize: 100,
|
|
29
|
+
batchDelay: 1000,
|
|
30
|
+
};
|
|
31
|
+
const externalChannels = [
|
|
30
32
|
{
|
|
31
33
|
name: 'DefiLlama',
|
|
32
34
|
fetchFn: (address_list) => (0, defi_llama_1.fetchPriceFromDefiLlama)(index_1.CHAIN_ID.XLAYER, address_list),
|
|
@@ -40,6 +42,10 @@ function get_xlayer_token_price_info(addresses) {
|
|
|
40
42
|
batchDelay: 2000,
|
|
41
43
|
},
|
|
42
44
|
];
|
|
45
|
+
const PRICE_CHANNELS = source === 'cache_only' ? [cachedChannel] :
|
|
46
|
+
source === 'cache_first' ? [cachedChannel, ...externalChannels] :
|
|
47
|
+
[...externalChannels];
|
|
48
|
+
const externalHits = [];
|
|
43
49
|
try {
|
|
44
50
|
for (const channel of PRICE_CHANNELS) {
|
|
45
51
|
if (addresses.length === 0)
|
|
@@ -59,7 +65,7 @@ function get_xlayer_token_price_info(addresses) {
|
|
|
59
65
|
result.set(address, priceInfo);
|
|
60
66
|
remainingAddresses = remainingAddresses.filter(addr => addr !== address);
|
|
61
67
|
if (channel.name !== 'CachedPrice') {
|
|
62
|
-
(
|
|
68
|
+
externalHits.push({ address, price: priceInfo.price, source: channel.name });
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
(0, index_1.log_debug)(`[get_token_price_info] ${channel.name} found prices for ${channelResult.size}/${batch.length} tokens in batch ${i + 1}`);
|
|
@@ -77,17 +83,26 @@ function get_xlayer_token_price_info(addresses) {
|
|
|
77
83
|
break;
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
|
-
if (
|
|
86
|
+
if (externalHits.length > 0) {
|
|
87
|
+
yield (0, price_cache_1.cache_new_market_price_batch)(externalHits);
|
|
88
|
+
}
|
|
89
|
+
if (source === 'cache_only' && addresses.length > 0) {
|
|
90
|
+
(0, price_cache_1.warnPriceCacheMiss)(addresses);
|
|
91
|
+
}
|
|
92
|
+
if (source === 'force_fetch' && result.size === 0) {
|
|
81
93
|
throw new Error(`Unable to get price information for any token: ${addresses.join(', ')}`);
|
|
82
94
|
}
|
|
83
|
-
if (addresses.length > 0) {
|
|
95
|
+
if (source !== 'cache_only' && addresses.length > 0) {
|
|
84
96
|
(0, index_1.log_warn)(`[get_token_price_info] Failed to get prices for ${addresses.length} tokens after trying all channels: ${addresses.join(', ')}`);
|
|
85
97
|
}
|
|
86
98
|
(0, index_1.log_debug)(`[get_token_price_info] Completed price fetching for ${result.size} tokens`);
|
|
87
99
|
return result;
|
|
88
100
|
}
|
|
89
101
|
catch (error) {
|
|
90
|
-
|
|
102
|
+
if (source === 'force_fetch') {
|
|
103
|
+
throw new Error(`Failed to get token price information: ${error instanceof Error ? error.message : String(error)}`);
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
91
106
|
}
|
|
92
107
|
});
|
|
93
108
|
}
|
|
@@ -20,3 +20,5 @@ __exportStar(require("./get_solana_token_price"), exports);
|
|
|
20
20
|
__exportStar(require("./get_tron_token_price"), exports);
|
|
21
21
|
__exportStar(require("./get_sui_token_price"), exports);
|
|
22
22
|
__exportStar(require("./get_xlayer_token_price"), exports);
|
|
23
|
+
__exportStar(require("./price_cache"), exports);
|
|
24
|
+
__exportStar(require("./token_price_syncer"), exports);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { FormattedTokenPrice } from "../..";
|
|
2
2
|
export declare const fetchPriceFromCache: (token_address_list: string[]) => Promise<Map<string, FormattedTokenPrice>>;
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function cache_new_market_price_batch(hits: Array<{
|
|
4
|
+
address: string;
|
|
5
|
+
price: string;
|
|
6
|
+
source: string;
|
|
7
|
+
}>): Promise<void>;
|
|
8
|
+
export declare function warnPriceCacheMiss(missed_addresses: string[]): void;
|
|
@@ -10,21 +10,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.fetchPriceFromCache = void 0;
|
|
13
|
-
exports.
|
|
13
|
+
exports.cache_new_market_price_batch = cache_new_market_price_batch;
|
|
14
|
+
exports.warnPriceCacheMiss = warnPriceCacheMiss;
|
|
14
15
|
const __1 = require("../..");
|
|
15
|
-
const check_token_price_timeout = (token_info, token_price_timeout_seconds) => {
|
|
16
|
-
if ((0, __1.isEmpty)(token_info.market_price) || (0, __1.isEmpty)(token_info.update_time)) {
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
let last_update_time = token_info.update_time;
|
|
20
|
-
let diff_seconds = Math.floor((Date.now() - (0, __1.parseDateTimeStrToMills)(last_update_time)) / 1000);
|
|
21
|
-
return diff_seconds > token_price_timeout_seconds;
|
|
22
|
-
};
|
|
23
|
-
const get_diff_seconds = (last_update_time, now) => {
|
|
24
|
-
return Math.floor((now - (0, __1.parseDateTimeStrToMills)(last_update_time)) / 1000);
|
|
25
|
-
};
|
|
26
16
|
const fetchPriceFromCache = (token_address_list) => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
-
|
|
17
|
+
const result = new Map();
|
|
28
18
|
let global_app_config;
|
|
29
19
|
try {
|
|
30
20
|
global_app_config = (0, __1.getGlobalAppConfig)();
|
|
@@ -33,85 +23,86 @@ const fetchPriceFromCache = (token_address_list) => __awaiter(void 0, void 0, vo
|
|
|
33
23
|
(0, __1.log_warn)('global_app_config is not set, skip get token price from cache');
|
|
34
24
|
return result;
|
|
35
25
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
result.set(token_info.address, {
|
|
48
|
-
address: token_info.address,
|
|
49
|
-
price: token_info.market_price,
|
|
50
|
-
update_time: token_info.update_time,
|
|
51
|
-
decimals: token_info.decimals,
|
|
52
|
-
});
|
|
53
|
-
(0, __1.log_debug)(`get token price from cache(token_info) success: ${token_info.symbol} ${token_info.address}, price=${token_info.market_price} (${diff_seconds}s ago)`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
(0, __1.log_error)(`get token price from cache failed`, err);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
for (let token_info of not_found_token_list) {
|
|
61
|
-
let price_info = yield global_app_config.get_token_market_price(token_info.symbol);
|
|
62
|
-
if (!price_info || check_token_price_timeout(price_info, token_price_timeout_seconds)) {
|
|
63
|
-
continue;
|
|
26
|
+
const map = global_app_config.arb_cache.token_address_map;
|
|
27
|
+
for (const address of token_address_list) {
|
|
28
|
+
const token = map.get(address.toLowerCase());
|
|
29
|
+
if (token === null || token === void 0 ? void 0 : token.market_price) {
|
|
30
|
+
result.set(address, {
|
|
31
|
+
address: token.address,
|
|
32
|
+
price: token.market_price,
|
|
33
|
+
update_time: token.update_time,
|
|
34
|
+
decimals: token.decimals,
|
|
35
|
+
});
|
|
36
|
+
(0, __1.log_debug)(`cache hit: ${token.symbol} ${address}, price=${token.market_price}, update_time=${token.update_time}`);
|
|
64
37
|
}
|
|
65
|
-
result.set(token_info.address, {
|
|
66
|
-
address: token_info.address,
|
|
67
|
-
price: price_info.market_price,
|
|
68
|
-
update_time: price_info.update_time,
|
|
69
|
-
decimals: price_info.decimals,
|
|
70
|
-
});
|
|
71
|
-
let diff_seconds = get_diff_seconds(price_info.update_time, Date.now());
|
|
72
|
-
(0, __1.log_debug)(`get token price from cache(market_price) success: ${token_info.symbol} ${token_info.address}, price=${price_info.market_price} (${diff_seconds}s ago)`);
|
|
73
38
|
}
|
|
74
39
|
return result;
|
|
75
40
|
});
|
|
76
41
|
exports.fetchPriceFromCache = fetchPriceFromCache;
|
|
77
|
-
function
|
|
42
|
+
function cache_new_market_price_batch(hits) {
|
|
78
43
|
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
if (hits.length === 0)
|
|
45
|
+
return;
|
|
79
46
|
let global_app_config;
|
|
80
47
|
try {
|
|
81
48
|
global_app_config = (0, __1.getGlobalAppConfig)();
|
|
82
49
|
}
|
|
83
50
|
catch (err) {
|
|
84
|
-
(0, __1.log_warn)('global_app_config is not set, skip
|
|
51
|
+
(0, __1.log_warn)('global_app_config is not set, skip cache_new_market_price_batch');
|
|
85
52
|
return;
|
|
86
53
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
54
|
+
const map = global_app_config.arb_cache.token_address_map;
|
|
55
|
+
const updated = [];
|
|
56
|
+
for (const { address, price, source } of hits) {
|
|
57
|
+
if (!price || Number(price) <= 0) {
|
|
58
|
+
(0, __1.log_warn)(`skip cache invalid market_price from ${source}`, { address, price });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const tokenInfo = map.get(address.toLowerCase());
|
|
62
|
+
if (!tokenInfo) {
|
|
63
|
+
(0, __1.log_warn)(`tokenInfo not found in arb_cache.token_address_map: address=${address}`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
updated.push(Object.assign(Object.assign({}, tokenInfo), { market_price: price, update_time: (0, __1.getCurDateTime)() }));
|
|
91
67
|
}
|
|
92
|
-
if (
|
|
93
|
-
(0, __1.log_warn)(`tokenInfo is not found in cache: token_address=${token_address}`);
|
|
68
|
+
if (updated.length === 0)
|
|
94
69
|
return;
|
|
70
|
+
try {
|
|
71
|
+
yield global_app_config.arb_cache.update_token_prices(updated);
|
|
72
|
+
(0, __1.log_info)(`cache_new_market_price_batch success: wrote ${updated.length} token prices`);
|
|
95
73
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
let clone_token_info = (0, __1.deep_clone)(tokenInfo);
|
|
99
|
-
clone_token_info.market_price = market_price;
|
|
100
|
-
global_app_config.cache_token_market_price(clone_token_info, ttl)
|
|
101
|
-
.then(() => {
|
|
102
|
-
(0, __1.log_info)(`cache new market price success: ${symbol} ${address}, price=${market_price} (${ttl}s), source=${market_source}`);
|
|
103
|
-
})
|
|
104
|
-
.catch(err => {
|
|
105
|
-
(0, __1.log_error)(`cache new market price failed: ${symbol} ${address}, price=${market_price} (${ttl}s)`, err);
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
(0, __1.log_warn)(`skip cache invalid market_price from ${market_source}`, {
|
|
110
|
-
symbol,
|
|
111
|
-
address,
|
|
112
|
-
market_price,
|
|
113
|
-
market_source,
|
|
114
|
-
});
|
|
74
|
+
catch (err) {
|
|
75
|
+
(0, __1.log_error)(`cache_new_market_price_batch failed`, err);
|
|
115
76
|
}
|
|
116
77
|
});
|
|
117
78
|
}
|
|
79
|
+
const _priceMissLastWarnAt = new Map();
|
|
80
|
+
const PRICE_MISS_THROTTLE_MS = 60000;
|
|
81
|
+
function warnPriceCacheMiss(missed_addresses) {
|
|
82
|
+
var _a;
|
|
83
|
+
if (missed_addresses.length === 0)
|
|
84
|
+
return;
|
|
85
|
+
let cfg;
|
|
86
|
+
try {
|
|
87
|
+
cfg = (0, __1.getGlobalAppConfig)();
|
|
88
|
+
}
|
|
89
|
+
catch (_b) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
const map = cfg.arb_cache.token_address_map;
|
|
94
|
+
const to_warn = [];
|
|
95
|
+
for (const addr of missed_addresses) {
|
|
96
|
+
const known = map.has(addr.toLowerCase());
|
|
97
|
+
if (!known)
|
|
98
|
+
continue;
|
|
99
|
+
const last = (_a = _priceMissLastWarnAt.get(addr)) !== null && _a !== void 0 ? _a : 0;
|
|
100
|
+
if (now - last < PRICE_MISS_THROTTLE_MS)
|
|
101
|
+
continue;
|
|
102
|
+
_priceMissLastWarnAt.set(addr, now);
|
|
103
|
+
to_warn.push(addr);
|
|
104
|
+
}
|
|
105
|
+
if (to_warn.length > 0) {
|
|
106
|
+
(0, __1.log_warn)(`price cache miss: ${to_warn.length} tokens known but no price in cache: ${to_warn.join(', ')}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ArbCache } from '../../cache/arb_cache';
|
|
2
|
+
import { CHAIN_ID } from '../..';
|
|
3
|
+
export interface FetchedTokenPrice {
|
|
4
|
+
price: string;
|
|
5
|
+
symbol?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
update_time?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TokenPriceSyncerOpts {
|
|
10
|
+
chain_id: CHAIN_ID;
|
|
11
|
+
arb_cache: ArbCache;
|
|
12
|
+
refresh_interval_seconds: number;
|
|
13
|
+
fetchPrices: (addresses: string[]) => Promise<Map<string, FetchedTokenPrice>>;
|
|
14
|
+
first_sync_delay_ms?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class TokenPriceSyncer {
|
|
17
|
+
private readonly opts;
|
|
18
|
+
private timer;
|
|
19
|
+
private trade_token_map;
|
|
20
|
+
constructor(opts: TokenPriceSyncerOpts);
|
|
21
|
+
start(): Promise<void>;
|
|
22
|
+
stop(): void;
|
|
23
|
+
runOnce(): Promise<void>;
|
|
24
|
+
private loadTradeTokensFromCache;
|
|
25
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TokenPriceSyncer = void 0;
|
|
13
|
+
const __1 = require("../..");
|
|
14
|
+
class TokenPriceSyncer {
|
|
15
|
+
constructor(opts) {
|
|
16
|
+
this.timer = null;
|
|
17
|
+
this.trade_token_map = new Map();
|
|
18
|
+
this.opts = opts;
|
|
19
|
+
}
|
|
20
|
+
start() {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
var _a;
|
|
23
|
+
const delay = (_a = this.opts.first_sync_delay_ms) !== null && _a !== void 0 ? _a : 10000;
|
|
24
|
+
yield (0, __1.sleep)(delay);
|
|
25
|
+
(0, __1.log_info)(`[TokenPriceSyncer] first sync start, chain=${this.opts.chain_id}`);
|
|
26
|
+
const t0 = Date.now();
|
|
27
|
+
yield this.runOnce().catch(err => (0, __1.log_error)(`[TokenPriceSyncer] first sync error`, err));
|
|
28
|
+
(0, __1.log_info)(`[TokenPriceSyncer] first sync done, took ${Date.now() - t0}ms`);
|
|
29
|
+
(0, __1.log_info)(`[TokenPriceSyncer] timer started, interval=${this.opts.refresh_interval_seconds}s`);
|
|
30
|
+
this.timer = setInterval(() => this.runOnce().catch(err => (0, __1.log_error)(`[TokenPriceSyncer] run error`, err)), this.opts.refresh_interval_seconds * 1000);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
stop() {
|
|
34
|
+
if (this.timer) {
|
|
35
|
+
clearInterval(this.timer);
|
|
36
|
+
this.timer = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
runOnce() {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
var _a;
|
|
42
|
+
yield this.loadTradeTokensFromCache();
|
|
43
|
+
const addresses = Array.from(this.trade_token_map.keys());
|
|
44
|
+
if (addresses.length === 0) {
|
|
45
|
+
(0, __1.log_info)(`[TokenPriceSyncer] no trade tokens to sync`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
(0, __1.log_info)(`[TokenPriceSyncer] syncing ${addresses.length} token prices`);
|
|
49
|
+
let priceMap;
|
|
50
|
+
try {
|
|
51
|
+
priceMap = yield this.opts.fetchPrices(addresses);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
(0, __1.log_error)(`[TokenPriceSyncer] fetchPrices error: ${err.message}`, err);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const failed = [];
|
|
58
|
+
for (const addr of addresses) {
|
|
59
|
+
if (!((_a = priceMap.get(addr)) === null || _a === void 0 ? void 0 : _a.price)) {
|
|
60
|
+
const token = this.trade_token_map.get(addr);
|
|
61
|
+
if (token)
|
|
62
|
+
failed.push(`${token.symbol}(${addr})`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
(0, __1.log_info)(`[TokenPriceSyncer] sync done, priced=${priceMap.size}, failed=${failed.length}`);
|
|
66
|
+
if (failed.length > 0) {
|
|
67
|
+
(0, __1.log_warn)(`[TokenPriceSyncer] ${failed.length} tokens without price: ${failed.join(', ')}`);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
loadTradeTokensFromCache() {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const tokens = yield this.opts.arb_cache.get_all_trade_tokens();
|
|
74
|
+
for (const token of tokens) {
|
|
75
|
+
this.trade_token_map.set(token.address, token);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.TokenPriceSyncer = TokenPriceSyncer;
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED