@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.
- 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 +1 -14
- 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/pool/cache_pool_config.js +45 -53
- package/dist/token/cache_token_config.js +25 -36
- 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
|
@@ -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;
|