@clonegod/ttd-bsc-common 2.0.1 → 3.0.2
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/config/BscQuoteAppConfig.d.ts +1 -1
- package/dist/config/BscQuoteAppConfig.js +5 -5
- package/dist/config/bsc_env_args.d.ts +1 -1
- package/dist/config/bsc_env_args.js +2 -2
- package/dist/quote/event/pool_event_listener.d.ts +1 -1
- package/dist/quote/event/pool_event_listener.js +31 -31
- package/dist/quote/event/swap_debouncer.js +2 -2
- package/dist/quote/pricing/pool_state_initializer.js +7 -7
- package/dist/quote/pricing/sdk_token_factory.js +2 -2
- package/dist/quote/pricing/token_price_cache.js +4 -4
- package/dist/quote/tick/clmm_tick_cache.js +12 -12
- package/dist/quote/tick/tick_lens_loaders.js +5 -5
- package/dist/redis/redis_client.js +6 -6
- package/dist/trade/abstract_dex_trade.d.ts +1 -1
- package/dist/trade/abstract_dex_trade.js +28 -37
- package/dist/trade/caller_manager.js +13 -13
- package/dist/trade/check/tx_websocket_manager.js +11 -11
- package/dist/trade/parse/base_parser.js +2 -2
- package/dist/utils/gas_helper.js +9 -9
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/trade_direction.d.ts +14 -0
- package/dist/utils/trade_direction.js +23 -0
- package/dist/ws/event_filter.js +2 -2
- package/dist/yyws/yyws_client.js +2 -2
- package/package.json +2 -2
|
@@ -11,9 +11,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.BscQuoteAppConfig = void 0;
|
|
13
13
|
const bsc_env_args_1 = require("./bsc_env_args");
|
|
14
|
-
const
|
|
14
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
15
15
|
const events_1 = require("events");
|
|
16
|
-
class BscQuoteAppConfig extends
|
|
16
|
+
class BscQuoteAppConfig extends ttd_core_1.AppConfig {
|
|
17
17
|
constructor() {
|
|
18
18
|
super();
|
|
19
19
|
this.eventEmitter = new events_1.EventEmitter();
|
|
@@ -34,9 +34,9 @@ class BscQuoteAppConfig extends dist_1.AppConfig {
|
|
|
34
34
|
yield _super.init.call(this);
|
|
35
35
|
yield this.arb_cache.init();
|
|
36
36
|
if (!this.arb_event_subscriber) {
|
|
37
|
-
this.arb_event_subscriber = (0,
|
|
37
|
+
this.arb_event_subscriber = (0, ttd_core_1.getArbEventSubscriber)(this.arb_cache);
|
|
38
38
|
}
|
|
39
|
-
(0,
|
|
39
|
+
(0, ttd_core_1.log_info)('BscQuoteAppConfig initialized', {
|
|
40
40
|
chain_id: this.env_args.chain_id,
|
|
41
41
|
dex_id: this.env_args.dex_id,
|
|
42
42
|
quote_pair: this.env_args.quote_pair,
|
|
@@ -48,7 +48,7 @@ class BscQuoteAppConfig extends dist_1.AppConfig {
|
|
|
48
48
|
subscribe_config_change() {
|
|
49
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
50
50
|
this.on('config_change', (config) => __awaiter(this, void 0, void 0, function* () {
|
|
51
|
-
(0,
|
|
51
|
+
(0, ttd_core_1.log_info)('Config change received', config);
|
|
52
52
|
if (config.env_args) {
|
|
53
53
|
this.env_args = new bsc_env_args_1.BscEnvArgs();
|
|
54
54
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BscEnvArgs = void 0;
|
|
4
|
-
const
|
|
4
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
5
|
const decimal_js_1 = require("decimal.js");
|
|
6
|
-
class BscEnvArgs extends
|
|
6
|
+
class BscEnvArgs extends ttd_core_1.EnvArgs {
|
|
7
7
|
constructor() {
|
|
8
8
|
super();
|
|
9
9
|
this.large_trade_threshold_usd = parseInt(process.env.LARGE_TRADE_THRESHOLD_USD || '500');
|
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.PoolEventListener = void 0;
|
|
13
13
|
const ethers_1 = require("ethers");
|
|
14
|
-
const
|
|
14
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
15
15
|
const common_1 = require("../../common");
|
|
16
16
|
const CONFIG = {
|
|
17
17
|
MAX_RETRIES: 3,
|
|
@@ -48,37 +48,37 @@ class PoolEventListener {
|
|
|
48
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
49
49
|
this.poolList = poolList.filter(pool => ethers_1.ethers.utils.isAddress(pool.pool_address));
|
|
50
50
|
if (this.poolList.length !== poolList.length) {
|
|
51
|
-
(0,
|
|
51
|
+
(0, ttd_core_1.log_warn)(`Found ${poolList.length - this.poolList.length} invalid pool addresses, filtered out`, '');
|
|
52
52
|
}
|
|
53
53
|
yield this.connect();
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
start() {
|
|
57
57
|
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
-
(0,
|
|
58
|
+
(0, ttd_core_1.log_info)(`Starting block listener...`);
|
|
59
59
|
if (!this.isConnected || !this.wsProvider) {
|
|
60
|
-
(0,
|
|
60
|
+
(0, ttd_core_1.log_warn)('WebSocket not connected, cannot start listener', '');
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
yield this.startBlockListener();
|
|
64
64
|
this.isStarted = true;
|
|
65
|
-
(0,
|
|
65
|
+
(0, ttd_core_1.log_info)('Block listener started successfully');
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
stop() {
|
|
69
69
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
-
(0,
|
|
70
|
+
(0, ttd_core_1.log_info)(`Stopping block listener...`);
|
|
71
71
|
this.stopBlockListener();
|
|
72
72
|
this.cleanup();
|
|
73
73
|
this.isStarted = false;
|
|
74
|
-
(0,
|
|
74
|
+
(0, ttd_core_1.log_info)(`Block listener stopped`);
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
77
|
connect() {
|
|
78
78
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
79
|
for (let i = 0; i < CONFIG.MAX_RETRIES; i++) {
|
|
80
80
|
try {
|
|
81
|
-
(0,
|
|
81
|
+
(0, ttd_core_1.log_info)(`Connecting to WebSocket: ${this.ws_endpoint} (Attempt ${i + 1}/${CONFIG.MAX_RETRIES})`);
|
|
82
82
|
this.wsProvider = new ethers_1.ethers.providers.WebSocketProvider(this.ws_endpoint);
|
|
83
83
|
const wsPromise = this.wsProvider.ready;
|
|
84
84
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -87,7 +87,7 @@ class PoolEventListener {
|
|
|
87
87
|
yield Promise.race([wsPromise, timeoutPromise]);
|
|
88
88
|
this.isConnected = true;
|
|
89
89
|
this.reconnectAttempts = 0;
|
|
90
|
-
(0,
|
|
90
|
+
(0, ttd_core_1.log_info)(`WebSocket connected: ${this.ws_endpoint}`);
|
|
91
91
|
this.setupWebSocketListeners();
|
|
92
92
|
if (this.isStarted) {
|
|
93
93
|
yield this.startBlockListener();
|
|
@@ -95,12 +95,12 @@ class PoolEventListener {
|
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
catch (error) {
|
|
98
|
-
(0,
|
|
98
|
+
(0, ttd_core_1.log_error)(`WebSocket connection failed (Attempt ${i + 1}/${CONFIG.MAX_RETRIES}):`, error);
|
|
99
99
|
if (i === CONFIG.MAX_RETRIES - 1) {
|
|
100
100
|
throw error;
|
|
101
101
|
}
|
|
102
102
|
const delay = CONFIG.RETRY_DELAY_MULTIPLIER * (i + 1);
|
|
103
|
-
(0,
|
|
103
|
+
(0, ttd_core_1.log_info)(`Waiting ${delay / 1000} seconds before retry...`);
|
|
104
104
|
yield new Promise(resolve => setTimeout(resolve, delay));
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -110,15 +110,15 @@ class PoolEventListener {
|
|
|
110
110
|
if (!this.wsProvider)
|
|
111
111
|
return;
|
|
112
112
|
this.wsProvider.on('error', (err) => {
|
|
113
|
-
(0,
|
|
113
|
+
(0, ttd_core_1.log_error)(`WebSocket error:`, err, '');
|
|
114
114
|
this.handleConnectionIssue();
|
|
115
115
|
});
|
|
116
116
|
this.wsProvider._websocket.on('close', (code, reason) => {
|
|
117
|
-
(0,
|
|
117
|
+
(0, ttd_core_1.log_warn)(`WebSocket connection closed, code: ${code}, reason: ${reason || 'unknown'}`);
|
|
118
118
|
this.handleConnectionIssue();
|
|
119
119
|
});
|
|
120
120
|
this.wsProvider._websocket.on('open', () => {
|
|
121
|
-
(0,
|
|
121
|
+
(0, ttd_core_1.log_info)('WebSocket connection opened');
|
|
122
122
|
this.isConnected = true;
|
|
123
123
|
this.reconnectAttempts = 0;
|
|
124
124
|
});
|
|
@@ -138,12 +138,12 @@ class PoolEventListener {
|
|
|
138
138
|
this.lastHeartbeatResponse = Date.now();
|
|
139
139
|
const timeSinceLastHeartbeat = Date.now() - this.lastHeartbeatResponse;
|
|
140
140
|
if (timeSinceLastHeartbeat > CONFIG.HEARTBEAT_TIMEOUT) {
|
|
141
|
-
(0,
|
|
141
|
+
(0, ttd_core_1.log_warn)(`No heartbeat response for ${timeSinceLastHeartbeat / 1000} seconds`);
|
|
142
142
|
this.handleConnectionIssue();
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
catch (error) {
|
|
146
|
-
(0,
|
|
146
|
+
(0, ttd_core_1.log_error)('Heartbeat check failed:', error);
|
|
147
147
|
this.handleConnectionIssue();
|
|
148
148
|
}
|
|
149
149
|
}), CONFIG.HEARTBEAT_INTERVAL);
|
|
@@ -151,7 +151,7 @@ class PoolEventListener {
|
|
|
151
151
|
handleConnectionIssue() {
|
|
152
152
|
return __awaiter(this, void 0, void 0, function* () {
|
|
153
153
|
if (this.isReconnecting) {
|
|
154
|
-
(0,
|
|
154
|
+
(0, ttd_core_1.log_info)('Already attempting to reconnect, skipping...');
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
157
|
this.isReconnecting = true;
|
|
@@ -161,20 +161,20 @@ class PoolEventListener {
|
|
|
161
161
|
this.cleanup();
|
|
162
162
|
this.reconnectAttempts++;
|
|
163
163
|
const delay = Math.min(CONFIG.BASE_RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts), CONFIG.MAX_RECONNECT_DELAY);
|
|
164
|
-
(0,
|
|
164
|
+
(0, ttd_core_1.log_info)(`Attempting to reconnect ${this.reconnectAttempts}/${CONFIG.MAX_RECONNECT_ATTEMPTS}, delay ${delay}ms, node: ${this.ws_endpoint}`);
|
|
165
165
|
if (this.reconnectAttempts > CONFIG.MAX_RECONNECT_ATTEMPTS) {
|
|
166
|
-
(0,
|
|
166
|
+
(0, ttd_core_1.log_error)(`Reconnection failed, reached maximum attempts (${CONFIG.MAX_RECONNECT_ATTEMPTS}), exiting`, new Error('Max reconnect attempts failed'), '');
|
|
167
167
|
this.appConfig.emit(common_1.EVENT_NAMES.WS_CONNECTION_FAILED, {
|
|
168
168
|
endpoint: this.ws_endpoint,
|
|
169
169
|
attempts: CONFIG.MAX_RECONNECT_ATTEMPTS,
|
|
170
170
|
});
|
|
171
171
|
process.exit(1);
|
|
172
172
|
}
|
|
173
|
-
yield (0,
|
|
173
|
+
yield (0, ttd_core_1.sleep)(delay);
|
|
174
174
|
yield this.connect();
|
|
175
175
|
}
|
|
176
176
|
catch (error) {
|
|
177
|
-
(0,
|
|
177
|
+
(0, ttd_core_1.log_error)('Reconnection attempt failed:', error);
|
|
178
178
|
setTimeout(() => this.handleConnectionIssue(), CONFIG.BASE_RECONNECT_DELAY);
|
|
179
179
|
}
|
|
180
180
|
finally {
|
|
@@ -185,19 +185,19 @@ class PoolEventListener {
|
|
|
185
185
|
startBlockListener() {
|
|
186
186
|
return __awaiter(this, void 0, void 0, function* () {
|
|
187
187
|
if (!this.wsProvider || !this.isConnected) {
|
|
188
|
-
(0,
|
|
188
|
+
(0, ttd_core_1.log_warn)(`Cannot start block listener: WebSocket not connected`);
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
191
|
try {
|
|
192
192
|
const blockNumber = yield this.wsProvider.getBlockNumber();
|
|
193
193
|
this.lastProcessedBlockNumber = blockNumber;
|
|
194
|
-
(0,
|
|
194
|
+
(0, ttd_core_1.log_info)(`Initializing block listener, current block: ${blockNumber}`);
|
|
195
195
|
this.wsProvider.on('block', (blockNumber) => __awaiter(this, void 0, void 0, function* () {
|
|
196
196
|
try {
|
|
197
197
|
if (!this.isConnected || !this.isBlockListenerActive)
|
|
198
198
|
return;
|
|
199
199
|
this.lastMessageTime = Date.now();
|
|
200
|
-
(0,
|
|
200
|
+
(0, ttd_core_1.log_info)(`------- Block: ${blockNumber} -------`);
|
|
201
201
|
const previousBlockNumber = this.lastProcessedBlockNumber;
|
|
202
202
|
this.lastProcessedBlockNumber = blockNumber;
|
|
203
203
|
const blockUpdateEvent = {
|
|
@@ -208,16 +208,16 @@ class PoolEventListener {
|
|
|
208
208
|
this.appConfig.emit(common_1.EVENT_NAMES.BLOCK_UPDATE, blockUpdateEvent);
|
|
209
209
|
}
|
|
210
210
|
catch (error) {
|
|
211
|
-
(0,
|
|
211
|
+
(0, ttd_core_1.log_error)(`Error processing block event:`, error, '');
|
|
212
212
|
}
|
|
213
213
|
}));
|
|
214
214
|
this.startMessageMonitor();
|
|
215
215
|
this.startDiagnosticMonitor();
|
|
216
216
|
this.isBlockListenerActive = true;
|
|
217
|
-
(0,
|
|
217
|
+
(0, ttd_core_1.log_info)(`Block listener started using WebSocket event subscription`);
|
|
218
218
|
}
|
|
219
219
|
catch (error) {
|
|
220
|
-
(0,
|
|
220
|
+
(0, ttd_core_1.log_error)(`Failed to start block listener:`, error, '');
|
|
221
221
|
this.isBlockListenerActive = false;
|
|
222
222
|
}
|
|
223
223
|
});
|
|
@@ -229,7 +229,7 @@ class PoolEventListener {
|
|
|
229
229
|
this.messageMonitorInterval = setInterval(() => {
|
|
230
230
|
const timeSinceLastMessage = Date.now() - this.lastMessageTime;
|
|
231
231
|
if (timeSinceLastMessage > CONFIG.MESSAGE_TIMEOUT) {
|
|
232
|
-
(0,
|
|
232
|
+
(0, ttd_core_1.log_warn)(`No messages received for ${CONFIG.MESSAGE_TIMEOUT / 1000} seconds, preparing to resubscribe`);
|
|
233
233
|
this.handleConnectionIssue();
|
|
234
234
|
}
|
|
235
235
|
}, CONFIG.MESSAGE_CHECK_INTERVAL);
|
|
@@ -244,7 +244,7 @@ class PoolEventListener {
|
|
|
244
244
|
}, CONFIG.DIAGNOSTIC_INTERVAL);
|
|
245
245
|
}
|
|
246
246
|
logDiagnostics(diagnostics) {
|
|
247
|
-
(0,
|
|
247
|
+
(0, ttd_core_1.log_info)(`WebSocket Connection Diagnostics:
|
|
248
248
|
Connection Status: ${diagnostics.isConnected ? 'Connected' : 'Disconnected'}
|
|
249
249
|
Reconnection Status: ${diagnostics.isReconnecting ? 'Reconnecting' : 'Not Reconnecting'}
|
|
250
250
|
Listener Status: ${diagnostics.isStarted ? 'Started' : 'Not Started'}
|
|
@@ -279,7 +279,7 @@ class PoolEventListener {
|
|
|
279
279
|
clearInterval(this.diagnosticInterval);
|
|
280
280
|
this.diagnosticInterval = null;
|
|
281
281
|
}
|
|
282
|
-
(0,
|
|
282
|
+
(0, ttd_core_1.log_info)(`Block listener stopped`);
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
cleanup() {
|
|
@@ -291,7 +291,7 @@ class PoolEventListener {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
catch (error) {
|
|
294
|
-
(0,
|
|
294
|
+
(0, ttd_core_1.log_error)(`Failed to cleanup WebSocket resources:`, error, '');
|
|
295
295
|
}
|
|
296
296
|
this.wsProvider = null;
|
|
297
297
|
}
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.SwapDebouncer = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
class SwapDebouncer {
|
|
15
15
|
constructor(config) {
|
|
16
16
|
this.windows = new Map();
|
|
@@ -28,7 +28,7 @@ class SwapDebouncer {
|
|
|
28
28
|
if (blockNumber > 0) {
|
|
29
29
|
const lastBlock = this.latestBlock.get(poolAddress) || 0;
|
|
30
30
|
if (blockNumber < lastBlock) {
|
|
31
|
-
(0,
|
|
31
|
+
(0, ttd_core_1.log_warn)(`SwapDebouncer: stale block dropped, pool=${poolAddress}, event block=${blockNumber}, latest=${lastBlock}`);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
this.latestBlock.set(poolAddress, blockNumber);
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.PoolStateInitializer = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
const V2_PAIR_ABI = [
|
|
15
15
|
'function token0() view returns (address)',
|
|
16
16
|
'function token1() view returns (address)',
|
|
@@ -49,13 +49,13 @@ class PoolStateInitializer {
|
|
|
49
49
|
reserve0: reserves[0].toString(),
|
|
50
50
|
reserve1: reserves[1].toString(),
|
|
51
51
|
};
|
|
52
|
-
(0,
|
|
52
|
+
(0, ttd_core_1.log_info)(`[PoolStateInitializer] AMM pool initialized: ${poolAddress}`, {
|
|
53
53
|
token0: state.token0, token1: state.token1, fee: state.fee,
|
|
54
54
|
});
|
|
55
55
|
return state;
|
|
56
56
|
}
|
|
57
57
|
catch (error) {
|
|
58
|
-
(0,
|
|
58
|
+
(0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init AMM pool ${poolAddress}:`, error);
|
|
59
59
|
throw error;
|
|
60
60
|
}
|
|
61
61
|
});
|
|
@@ -84,14 +84,14 @@ class PoolStateInitializer {
|
|
|
84
84
|
sqrtPriceX96: sqrtPriceX96.toString(),
|
|
85
85
|
liquidity: liquidity.toString(),
|
|
86
86
|
};
|
|
87
|
-
(0,
|
|
87
|
+
(0, ttd_core_1.log_info)(`[PoolStateInitializer] CLMM pool initialized: ${poolAddress}`, {
|
|
88
88
|
token0: state.token0, token1: state.token1,
|
|
89
89
|
fee: state.fee, tickSpacing: state.tickSpacing, tick: state.tick,
|
|
90
90
|
});
|
|
91
91
|
return state;
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
94
|
-
(0,
|
|
94
|
+
(0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init CLMM pool ${poolAddress}:`, error);
|
|
95
95
|
throw error;
|
|
96
96
|
}
|
|
97
97
|
});
|
|
@@ -125,7 +125,7 @@ class PoolStateInitializer {
|
|
|
125
125
|
lpFee: Number(lpFee),
|
|
126
126
|
protocolFee: Number(protocolFee),
|
|
127
127
|
};
|
|
128
|
-
(0,
|
|
128
|
+
(0, ttd_core_1.log_info)(`[PoolStateInitializer] Infinity pool initialized: ${poolKey}`, {
|
|
129
129
|
currency0: state.currency0, currency1: state.currency1,
|
|
130
130
|
fee: state.fee, tickSpacing: state.tickSpacing, tick: state.tick,
|
|
131
131
|
lpFee: state.lpFee, protocolFee: state.protocolFee,
|
|
@@ -133,7 +133,7 @@ class PoolStateInitializer {
|
|
|
133
133
|
return state;
|
|
134
134
|
}
|
|
135
135
|
catch (error) {
|
|
136
|
-
(0,
|
|
136
|
+
(0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init Infinity pool ${poolKey}:`, error);
|
|
137
137
|
throw error;
|
|
138
138
|
}
|
|
139
139
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildSdkTokenMap = buildSdkTokenMap;
|
|
4
|
-
const
|
|
4
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
5
|
function buildSdkTokenMap(chainId, poolInfoMap, TokenClass) {
|
|
6
6
|
const sdkTokens = new Map();
|
|
7
7
|
for (const poolInfo of poolInfoMap.values()) {
|
|
@@ -14,7 +14,7 @@ function buildSdkTokenMap(chainId, poolInfoMap, TokenClass) {
|
|
|
14
14
|
let name = tokenInfo.name || `Token ${address.substring(0, 8)}`;
|
|
15
15
|
const token = new TokenClass(chainId, address, tokenInfo.decimals, symbol, name);
|
|
16
16
|
sdkTokens.set(address, token);
|
|
17
|
-
(0,
|
|
17
|
+
(0, ttd_core_1.log_debug)(`Created SDK Token: ${address}, symbol: ${symbol}, decimals: ${tokenInfo.decimals}`, '');
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
return sdkTokens;
|
|
@@ -10,12 +10,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.TokenPriceCache = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
class TokenPriceCache {
|
|
15
15
|
constructor() {
|
|
16
16
|
this.tokenPriceCache = new Map();
|
|
17
17
|
this.PRICE_CACHE_TIMEOUT_MILLS = parseInt(process.env.PRICE_CACHE_TIMEOUT_MILLS || Number(1000 * 60 * 60 * 1).toString());
|
|
18
|
-
(0,
|
|
18
|
+
(0, ttd_core_1.log_info)(`代币价格缓存超时时间: ${this.PRICE_CACHE_TIMEOUT_MILLS} ms`);
|
|
19
19
|
}
|
|
20
20
|
getTokenPrice(tokenAddress) {
|
|
21
21
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -23,10 +23,10 @@ class TokenPriceCache {
|
|
|
23
23
|
const now = Date.now();
|
|
24
24
|
const cachedData = this.tokenPriceCache.get(tokenAddress);
|
|
25
25
|
if (cachedData && (now - cachedData.timestamp) < this.PRICE_CACHE_TIMEOUT_MILLS) {
|
|
26
|
-
(0,
|
|
26
|
+
(0, ttd_core_1.log_debug)(`use cached token price: ${tokenAddress}, price: ${cachedData.price}`, '');
|
|
27
27
|
return cachedData;
|
|
28
28
|
}
|
|
29
|
-
const priceMap = yield (0,
|
|
29
|
+
const priceMap = yield (0, ttd_core_1.get_bsc_token_price_info)([tokenAddress]);
|
|
30
30
|
const tokenPrice = priceMap.get(tokenAddress);
|
|
31
31
|
if (!tokenPrice || !tokenPrice.price || Number(tokenPrice.price) <= 0) {
|
|
32
32
|
throw new Error(`无法获取代币 ${tokenAddress} 的有效USD价格`);
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.ClmmTickCache = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
class ClmmTickCache {
|
|
15
15
|
constructor(loader, config = {}) {
|
|
16
16
|
var _a, _b, _c;
|
|
@@ -27,7 +27,7 @@ class ClmmTickCache {
|
|
|
27
27
|
initPool(poolAddress, currentTick, tickSpacing) {
|
|
28
28
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29
29
|
const bitmapIndexes = this.calcBitmapIndexes(currentTick, tickSpacing);
|
|
30
|
-
(0,
|
|
30
|
+
(0, ttd_core_1.log_info)(`[TickCache] Init pool ${poolAddress}: tick=${currentTick}, tickSpacing=${tickSpacing}, loading ${bitmapIndexes.length} bitmap words`);
|
|
31
31
|
const ticks = yield this.loader.loadBitmapWords(poolAddress, bitmapIndexes);
|
|
32
32
|
this.pools.set(poolAddress, {
|
|
33
33
|
ticks,
|
|
@@ -39,7 +39,7 @@ class ClmmTickCache {
|
|
|
39
39
|
loading: false,
|
|
40
40
|
});
|
|
41
41
|
this.startPeriodicRefresh(poolAddress);
|
|
42
|
-
(0,
|
|
42
|
+
(0, ttd_core_1.log_info)(`[TickCache] Pool ${poolAddress} initialized: ${ticks.size} ticks loaded`);
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
onSwap(poolAddress, newTick) {
|
|
@@ -49,7 +49,7 @@ class ClmmTickCache {
|
|
|
49
49
|
state.currentTick = newTick;
|
|
50
50
|
const currentBitmapIndex = this.getBitmapIndex(newTick, state.tickSpacing);
|
|
51
51
|
if (!state.bitmapIndexes.includes(currentBitmapIndex)) {
|
|
52
|
-
(0,
|
|
52
|
+
(0, ttd_core_1.log_info)(`[TickCache] Pool ${poolAddress}: tick ${newTick} out of cached range, scheduling refresh`);
|
|
53
53
|
this.scheduleFullRefresh(poolAddress);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -65,7 +65,7 @@ class ClmmTickCache {
|
|
|
65
65
|
applyLiquidityDelta(poolAddress, tickLower, tickUpper, delta) {
|
|
66
66
|
const state = this.pools.get(poolAddress);
|
|
67
67
|
if (!state) {
|
|
68
|
-
(0,
|
|
68
|
+
(0, ttd_core_1.log_warn)(`[TickCache] Pool ${poolAddress} not initialized, ignoring liquidity event`);
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
const absDelta = delta < BigInt(0) ? -delta : delta;
|
|
@@ -75,7 +75,7 @@ class ClmmTickCache {
|
|
|
75
75
|
if (needAsyncRefresh) {
|
|
76
76
|
this.scheduleFullRefresh(poolAddress);
|
|
77
77
|
}
|
|
78
|
-
(0,
|
|
78
|
+
(0, ttd_core_1.log_debug)(`[TickCache] Pool ${poolAddress}: liquidity delta applied, tickLower=${tickLower}, tickUpper=${tickUpper}, delta=${delta}`);
|
|
79
79
|
}
|
|
80
80
|
updateTickLiquidity(state, tickIndex, netDelta, grossDelta, isLower) {
|
|
81
81
|
const existing = state.ticks.get(tickIndex);
|
|
@@ -84,7 +84,7 @@ class ClmmTickCache {
|
|
|
84
84
|
existing.liquidityGross += (netDelta > BigInt(0) ? grossDelta : -grossDelta);
|
|
85
85
|
if (existing.liquidityGross <= BigInt(0)) {
|
|
86
86
|
state.ticks.delete(tickIndex);
|
|
87
|
-
(0,
|
|
87
|
+
(0, ttd_core_1.log_debug)(`[TickCache] Tick ${tickIndex} de-initialized (liquidityGross <= 0)`);
|
|
88
88
|
}
|
|
89
89
|
return false;
|
|
90
90
|
}
|
|
@@ -94,11 +94,11 @@ class ClmmTickCache {
|
|
|
94
94
|
liquidityNet: netDelta,
|
|
95
95
|
liquidityGross: grossDelta,
|
|
96
96
|
});
|
|
97
|
-
(0,
|
|
97
|
+
(0, ttd_core_1.log_debug)(`[TickCache] New tick ${tickIndex} initialized in cached range`);
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
100
|
else {
|
|
101
|
-
(0,
|
|
101
|
+
(0, ttd_core_1.log_debug)(`[TickCache] New tick ${tickIndex} outside cached range, will refresh async`);
|
|
102
102
|
return true;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -140,7 +140,7 @@ class ClmmTickCache {
|
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
142
|
this.doFullRefresh(poolAddress).catch(err => {
|
|
143
|
-
(0,
|
|
143
|
+
(0, ttd_core_1.log_warn)(`[TickCache] Full refresh failed for ${poolAddress}: ${err.message}`);
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
146
|
startPeriodicRefresh(poolAddress) {
|
|
@@ -152,7 +152,7 @@ class ClmmTickCache {
|
|
|
152
152
|
yield this.doFullRefresh(poolAddress);
|
|
153
153
|
}
|
|
154
154
|
catch (err) {
|
|
155
|
-
(0,
|
|
155
|
+
(0, ttd_core_1.log_warn)(`[TickCache] Periodic refresh failed for ${poolAddress}: ${err.message}`);
|
|
156
156
|
}
|
|
157
157
|
}), this.config.refreshInterval);
|
|
158
158
|
this.refreshTimers.set(poolAddress, timer);
|
|
@@ -174,7 +174,7 @@ class ClmmTickCache {
|
|
|
174
174
|
state.bitmapIndexes = bitmapIndexes;
|
|
175
175
|
state.lastFullLoadTime = Date.now();
|
|
176
176
|
state.pendingForceRefresh = false;
|
|
177
|
-
(0,
|
|
177
|
+
(0, ttd_core_1.log_debug)(`[TickCache] Full refresh for ${poolAddress}: ${ticks.size} ticks, ${bitmapIndexes.length} words`);
|
|
178
178
|
}
|
|
179
179
|
finally {
|
|
180
180
|
state.loading = false;
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.MulticallTickLensLoader = exports.BSC_TICK_LENS_ADDRESSES = exports.MULTICALL3_ADDRESS = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
exports.MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
|
|
15
15
|
exports.BSC_TICK_LENS_ADDRESSES = {
|
|
16
16
|
PANCAKE_V3: '0x9a489505a00cE272eAa5e07Dba6491314CaE3796',
|
|
@@ -130,13 +130,13 @@ class MulticallTickLensLoader {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
catch (e) {
|
|
133
|
-
(0,
|
|
133
|
+
(0, ttd_core_1.log_warn)(`[TickLensLoader] Failed to decode bitmap index ${bitmapIndexes[i]}: ${e.message}`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
-
(0,
|
|
136
|
+
(0, ttd_core_1.log_debug)(`[TickLensLoader] Loaded ${result.size} ticks from ${bitmapIndexes.length} bitmap words (1 RPC call)`);
|
|
137
137
|
}
|
|
138
138
|
catch (error) {
|
|
139
|
-
(0,
|
|
139
|
+
(0, ttd_core_1.log_warn)(`[TickLensLoader] Multicall failed, falling back to individual queries: ${error.message}`);
|
|
140
140
|
yield this.loadBitmapWordsIndividually(poolAddress, bitmapIndexes, result);
|
|
141
141
|
}
|
|
142
142
|
return result;
|
|
@@ -160,7 +160,7 @@ class MulticallTickLensLoader {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
catch (e) {
|
|
163
|
-
(0,
|
|
163
|
+
(0, ttd_core_1.log_warn)(`[TickLensLoader] Bitmap index ${bitmapIndex} query failed for ${poolAddress}`);
|
|
164
164
|
}
|
|
165
165
|
}));
|
|
166
166
|
yield Promise.all(promises);
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.SimpleRedisClient = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
class SimpleRedisClient {
|
|
15
15
|
constructor(lock_prefix) {
|
|
16
16
|
this.lock_prefix = lock_prefix;
|
|
@@ -22,7 +22,7 @@ class SimpleRedisClient {
|
|
|
22
22
|
getRedisClient() {
|
|
23
23
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
24
|
if (!this.redisClient) {
|
|
25
|
-
this.redisClient = (yield (0,
|
|
25
|
+
this.redisClient = (yield (0, ttd_core_1.getRedisCache)());
|
|
26
26
|
}
|
|
27
27
|
return this.redisClient;
|
|
28
28
|
});
|
|
@@ -37,7 +37,7 @@ class SimpleRedisClient {
|
|
|
37
37
|
NX: true,
|
|
38
38
|
EX: expireSeconds
|
|
39
39
|
});
|
|
40
|
-
(0,
|
|
40
|
+
(0, ttd_core_1.log_info)(`try acquireLock: lock_key=${lock_key}, lock_value=${lock_value}, expireSeconds=${expireSeconds}, result=${result}`);
|
|
41
41
|
const success = result === 'OK';
|
|
42
42
|
return success;
|
|
43
43
|
});
|
|
@@ -85,13 +85,13 @@ class SimpleRedisClient {
|
|
|
85
85
|
if (acquired) {
|
|
86
86
|
const release_delay = parseInt(process.env.NONCE_LOCK_RELEASE_DELAY_MS || String(release_lock_delay_ms));
|
|
87
87
|
if (release_delay > 0) {
|
|
88
|
-
yield (0,
|
|
88
|
+
yield (0, ttd_core_1.sleep)(release_delay);
|
|
89
89
|
}
|
|
90
90
|
this.releaseLock(lock_key, lock_value);
|
|
91
|
-
(0,
|
|
91
|
+
(0, ttd_core_1.log_info)(`withLock success: lock_key=${lock_key}, lock_value=${lock_value}, retry times=${retries}, get lock take ${get_lock_time - first_try_time}ms, release_delay=${release_delay}ms, hold lock ${Date.now() - get_lock_time}ms`);
|
|
92
92
|
}
|
|
93
93
|
else {
|
|
94
|
-
(0,
|
|
94
|
+
(0, ttd_core_1.log_warn)(`withLock failed: lock_key=${lock_key}, lock_value=${lock_value}, retry times=${retries}, took ${Date.now() - first_try_time}ms`);
|
|
95
95
|
throw new Error(`get lock for ${lock_key} failed`);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbastrcatTrade, AppConfig, TradeContext } from
|
|
1
|
+
import { AbastrcatTrade, AppConfig, TradeContext } from '@clonegod/ttd-core';
|
|
2
2
|
import { ethers } from "ethers";
|
|
3
3
|
import { EvmChainConfig } from "../types";
|
|
4
4
|
import { CallerManager } from "./caller_manager";
|
|
@@ -14,11 +14,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.AbstractDexTrade = void 0;
|
|
16
16
|
exports.buildTradeConfig = buildTradeConfig;
|
|
17
|
-
const
|
|
17
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
18
18
|
const ethers_1 = require("ethers");
|
|
19
19
|
const caller_manager_1 = require("./caller_manager");
|
|
20
20
|
const ttd_bsc_send_tx_1 = require("@clonegod/ttd-bsc-send-tx");
|
|
21
21
|
const redis_1 = require("../redis");
|
|
22
|
+
const trade_direction_1 = require("../utils/trade_direction");
|
|
22
23
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
23
24
|
function buildTradeConfig() {
|
|
24
25
|
const vaultAddress = process.env.VAULT_ADDRESS;
|
|
@@ -38,7 +39,7 @@ function buildTradeConfig() {
|
|
|
38
39
|
const VAULT_ABI = [
|
|
39
40
|
'function delegatecallExecutorToTrade(bytes32 executorId, bytes calldata data, uint256 deadline) external payable returns (bytes memory result)'
|
|
40
41
|
];
|
|
41
|
-
class AbstractDexTrade extends
|
|
42
|
+
class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
42
43
|
constructor(appConfig) {
|
|
43
44
|
super();
|
|
44
45
|
this.appConfig = appConfig;
|
|
@@ -51,7 +52,8 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
51
52
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
53
|
this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.chainConfig.rpcEndpoint);
|
|
53
54
|
this.transactionSender = new ttd_bsc_send_tx_1.TransactionSender(this.appConfig);
|
|
54
|
-
const
|
|
55
|
+
const defaultCallerId = `${this.chainNameLower.toUpperCase()}-CALLER`;
|
|
56
|
+
const callerGroupIds = (process.env.CALLER_GROUP_IDS || defaultCallerId).trim().split(',').filter(Boolean);
|
|
55
57
|
this.callerManager = new caller_manager_1.CallerManager({
|
|
56
58
|
chainName: this.chainNameLower,
|
|
57
59
|
provider: this.provider,
|
|
@@ -60,7 +62,7 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
60
62
|
});
|
|
61
63
|
yield this.callerManager.init();
|
|
62
64
|
this.subscribeTradeMonitor();
|
|
63
|
-
(0,
|
|
65
|
+
(0, ttd_core_1.log_info)(`AbstractDexTrade initialized, vault=${this.tradeConfig.vaultAddress}, callers=${this.callerManager.getCallerCount()}`);
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
68
|
execute(context) {
|
|
@@ -69,7 +71,7 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
69
71
|
const { price_msg, order_msg, slippage_bps, order_trace_id, pool_info } = context;
|
|
70
72
|
const { pair } = price_msg;
|
|
71
73
|
const { aToB, amount, unique_order_msg_id } = order_msg;
|
|
72
|
-
(0,
|
|
74
|
+
(0, ttd_core_1.log_info)(`执行 Vault 交易, orderId=${order_trace_id}`, {
|
|
73
75
|
unique_order_msg_id,
|
|
74
76
|
pair,
|
|
75
77
|
aToB,
|
|
@@ -90,10 +92,10 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
90
92
|
if (nonce_from_error > 0) {
|
|
91
93
|
nonce = nonce_from_error;
|
|
92
94
|
nonce_from_error = 0;
|
|
93
|
-
(0,
|
|
95
|
+
(0, ttd_core_1.log_info)(`Attempt ${i}/${maxAttempts}, using nonce from error msg: ${nonce}`, {}, order_trace_id);
|
|
94
96
|
}
|
|
95
97
|
else {
|
|
96
|
-
(0,
|
|
98
|
+
(0, ttd_core_1.log_warn)(`Attempt ${i}/${maxAttempts}, no nonce in error msg, abort`, {}, order_trace_id);
|
|
97
99
|
break;
|
|
98
100
|
}
|
|
99
101
|
}
|
|
@@ -113,7 +115,7 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
113
115
|
};
|
|
114
116
|
const signedTx = yield caller.signTransaction(tx);
|
|
115
117
|
txid = ethers_1.ethers.utils.keccak256(signedTx);
|
|
116
|
-
(0,
|
|
118
|
+
(0, ttd_core_1.log_info)(`Vault 交易已签名`, { txid, nonce, gasPriceGwei: realGasPriceGwei }, order_trace_id);
|
|
117
119
|
const tipNonce = nonce + 1;
|
|
118
120
|
const eoa_tip_transaction = (eoa_address) => __awaiter(this, void 0, void 0, function* () {
|
|
119
121
|
const transfer_amount_gwei = this.getBuilderTipAmoutGwei(context);
|
|
@@ -122,7 +124,7 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
122
124
|
});
|
|
123
125
|
const only_bundle = order_msg.is_dex_maker;
|
|
124
126
|
yield this.transactionSender.sendTransaction(signedTx, eoa_tip_transaction, order_trace_id, pair, only_bundle);
|
|
125
|
-
(0,
|
|
127
|
+
(0, ttd_core_1.log_info)(`Vault 交易发送成功`, {
|
|
126
128
|
pair, unique_order_msg_id, txid,
|
|
127
129
|
attempt: i, caller: caller.address
|
|
128
130
|
}, order_trace_id);
|
|
@@ -131,19 +133,19 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
131
133
|
catch (error) {
|
|
132
134
|
const errorMessage = ((_a = error.message) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
|
|
133
135
|
if (errorMessage.includes('rate limit')) {
|
|
134
|
-
(0,
|
|
136
|
+
(0, ttd_core_1.log_warn)(`Rate limit error, no retry! i=${i}`, {}, order_trace_id);
|
|
135
137
|
return txid;
|
|
136
138
|
}
|
|
137
139
|
if (errorMessage.includes('replacement transaction underpriced')) {
|
|
138
|
-
(0,
|
|
140
|
+
(0, ttd_core_1.log_warn)(`Replacement tx underpriced, continue! i=${i}`, {}, order_trace_id);
|
|
139
141
|
continue;
|
|
140
142
|
}
|
|
141
143
|
const isNonceError = errorMessage.includes('nonce');
|
|
142
144
|
if (!isNonceError || i >= maxAttempts) {
|
|
143
|
-
(0,
|
|
145
|
+
(0, ttd_core_1.log_warn)(`Non-nonce error or max retries, i=${i}, error: ${errorMessage}`, {}, order_trace_id);
|
|
144
146
|
return txid;
|
|
145
147
|
}
|
|
146
|
-
(0,
|
|
148
|
+
(0, ttd_core_1.log_info)(`Nonce error detected, will retry! i=${i}`, {}, order_trace_id);
|
|
147
149
|
if (errorMessage.includes('nonce too')) {
|
|
148
150
|
const correctNonce = this.extractNonceFromErrorMsg(errorMessage);
|
|
149
151
|
if (correctNonce !== null && correctNonce > 0) {
|
|
@@ -165,22 +167,22 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
165
167
|
subscribeTradeMonitor() {
|
|
166
168
|
const wsUrl = process.env.TRADE_MONITOR_WS_URL;
|
|
167
169
|
if (!wsUrl) {
|
|
168
|
-
(0,
|
|
170
|
+
(0, ttd_core_1.log_warn)('TRADE_MONITOR_WS_URL not configured, trade result nonce correction disabled');
|
|
169
171
|
return;
|
|
170
172
|
}
|
|
171
|
-
const wsClient = new
|
|
173
|
+
const wsClient = new ttd_core_1.WebSocketClient(wsUrl);
|
|
172
174
|
wsClient.onOpen(() => {
|
|
173
175
|
wsClient.send(JSON.stringify({
|
|
174
|
-
|
|
176
|
+
address: this.tradeConfig.vaultAddress,
|
|
175
177
|
}));
|
|
176
|
-
(0,
|
|
178
|
+
(0, ttd_core_1.log_info)(`Subscribed to trade-monitor for ${this.tradeConfig.vaultAddress}`);
|
|
177
179
|
});
|
|
178
180
|
wsClient.onMessage((event) => {
|
|
179
181
|
if (event.type === 'TradeResult' && event.data) {
|
|
180
182
|
const { caller, callerNonce } = event.data;
|
|
181
183
|
if (caller) {
|
|
182
184
|
const nextNonce = callerNonce + 2;
|
|
183
|
-
this.callerManager.confirmNonce(caller, nextNonce).catch(err => (0,
|
|
185
|
+
this.callerManager.confirmNonce(caller, nextNonce).catch(err => (0, ttd_core_1.log_warn)(`Failed to confirm nonce from trade-monitor`, err));
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
});
|
|
@@ -214,7 +216,7 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
214
216
|
chainId: this.chainConfig.chainId
|
|
215
217
|
};
|
|
216
218
|
const signedTx = yield wallet.signTransaction(tx_data);
|
|
217
|
-
(0,
|
|
219
|
+
(0, ttd_core_1.log_info)(`构建 tip 转账交易`, {
|
|
218
220
|
to, nonce,
|
|
219
221
|
tipGwei: real_transfer_amount_gwei,
|
|
220
222
|
txhash: ethers_1.ethers.utils.keccak256(signedTx)
|
|
@@ -223,34 +225,23 @@ class AbstractDexTrade extends dist_1.AbastrcatTrade {
|
|
|
223
225
|
});
|
|
224
226
|
}
|
|
225
227
|
determineInputOutputTokens(order_msg, pool_info) {
|
|
226
|
-
const {
|
|
227
|
-
const { tokenA, tokenB, quote_token } = pool_info;
|
|
228
|
-
const quoteToken = [tokenA, tokenB].find(token => token.symbol === quote_token);
|
|
229
|
-
const nonQuoteToken = [tokenA, tokenB].find(token => token.symbol !== quote_token);
|
|
230
|
-
const inputToken = aToB ? nonQuoteToken : quoteToken;
|
|
231
|
-
const outputToken = aToB ? quoteToken : nonQuoteToken;
|
|
232
|
-
if (!inputToken || !outputToken) {
|
|
233
|
-
throw new Error(`无法确定输入/输出代币: ${JSON.stringify({
|
|
234
|
-
tokenA: tokenA.symbol, tokenB: tokenB.symbol,
|
|
235
|
-
quote: quote_token, aToB
|
|
236
|
-
})}`);
|
|
237
|
-
}
|
|
228
|
+
const { inputToken, outputToken } = (0, trade_direction_1.resolveTradeDirection)(pool_info, order_msg.isBuy);
|
|
238
229
|
return { inputToken, outputToken };
|
|
239
230
|
}
|
|
240
231
|
calculateAmountOutMin(context, inputToken, outputToken) {
|
|
241
232
|
const { price_msg, slippage_bps, order_msg } = context;
|
|
242
|
-
const {
|
|
233
|
+
const { isBuy } = order_msg;
|
|
243
234
|
const slippage = slippage_bps / 10000;
|
|
244
235
|
const inputAmount = new decimal_js_1.default(order_msg.amount.toString());
|
|
245
236
|
let expectedOut;
|
|
246
|
-
if (
|
|
247
|
-
const price = new decimal_js_1.default(price_msg.bid.price);
|
|
248
|
-
expectedOut = inputAmount.mul(price);
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
237
|
+
if (isBuy) {
|
|
251
238
|
const price = new decimal_js_1.default(price_msg.ask.price);
|
|
252
239
|
expectedOut = inputAmount.div(price);
|
|
253
240
|
}
|
|
241
|
+
else {
|
|
242
|
+
const price = new decimal_js_1.default(price_msg.bid.price);
|
|
243
|
+
expectedOut = inputAmount.mul(price);
|
|
244
|
+
}
|
|
254
245
|
const minOutput = expectedOut.mul(new decimal_js_1.default(1).sub(new decimal_js_1.default(slippage)));
|
|
255
246
|
return ethers_1.ethers.utils.parseUnits(minOutput.toFixed(outputToken.decimals), outputToken.decimals);
|
|
256
247
|
}
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.CallerManager = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
const ethers_1 = require("ethers");
|
|
15
15
|
const redis_1 = require("../redis");
|
|
16
16
|
const CALLER_NONCE_KEY = 'caller:nonce';
|
|
@@ -24,9 +24,9 @@ class CallerManager {
|
|
|
24
24
|
}
|
|
25
25
|
init() {
|
|
26
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
-
const walletInfos = (0,
|
|
27
|
+
const walletInfos = (0, ttd_core_1.load_wallet_multi)(this.config.callerGroupIds, false);
|
|
28
28
|
const allWallets = walletInfos.map(info => new ethers_1.ethers.Wallet(info.private_key, this.config.provider));
|
|
29
|
-
(0,
|
|
29
|
+
(0, ttd_core_1.log_info)(`CallerManager: loaded ${allWallets.length} wallets from CALLER_GROUP_IDS`, allWallets.map(w => w.address));
|
|
30
30
|
const vaultCallersKey = `${this.config.chainName}:${VAULT_CALLERS_KEY}`;
|
|
31
31
|
const allowedAddresses = yield this.redis.lrange(vaultCallersKey);
|
|
32
32
|
if (allowedAddresses && allowedAddresses.length > 0) {
|
|
@@ -34,14 +34,14 @@ class CallerManager {
|
|
|
34
34
|
this.callers = allWallets.filter(w => allowedSet.has(w.address.toLowerCase()));
|
|
35
35
|
const matched = this.callers.map(w => w.address);
|
|
36
36
|
const skipped = allWallets.filter(w => !allowedSet.has(w.address.toLowerCase())).map(w => w.address);
|
|
37
|
-
(0,
|
|
37
|
+
(0, ttd_core_1.log_info)(`CallerManager: matched ${this.callers.length} callers with Vault whitelist`, matched);
|
|
38
38
|
if (skipped.length > 0) {
|
|
39
|
-
(0,
|
|
39
|
+
(0, ttd_core_1.log_warn)(`CallerManager: skipped ${skipped.length} wallets not in Vault whitelist`, skipped);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
43
|
this.callers = allWallets;
|
|
44
|
-
(0,
|
|
44
|
+
(0, ttd_core_1.log_warn)(`CallerManager: Vault whitelist not found in Redis (${vaultCallersKey}), using all ${this.callers.length} loaded wallets`);
|
|
45
45
|
}
|
|
46
46
|
if (this.callers.length === 0) {
|
|
47
47
|
throw new Error('CallerManager: no valid callers after whitelist matching');
|
|
@@ -51,14 +51,14 @@ class CallerManager {
|
|
|
51
51
|
const nonceKey = this.getNonceRedisKey();
|
|
52
52
|
const existing = yield this.redis.hgetvalue(nonceKey, address);
|
|
53
53
|
if (existing !== null && existing !== undefined) {
|
|
54
|
-
(0,
|
|
54
|
+
(0, ttd_core_1.log_info)(`Caller ${caller.address} nonce from Redis: ${existing}`);
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
const nonce = yield this.config.provider.getTransactionCount(caller.address, 'pending');
|
|
58
58
|
yield this.redis.hsetValue(nonceKey, address, String(nonce), 24 * 60 * 60);
|
|
59
|
-
(0,
|
|
59
|
+
(0, ttd_core_1.log_info)(`Caller ${caller.address} nonce from chain: ${nonce}`);
|
|
60
60
|
})));
|
|
61
|
-
(0,
|
|
61
|
+
(0, ttd_core_1.log_info)(`CallerManager initialized, ${this.callers.length} active callers`, this.callers.map(w => w.address));
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
acquireCaller() {
|
|
@@ -76,7 +76,7 @@ class CallerManager {
|
|
|
76
76
|
continue;
|
|
77
77
|
const nonce = yield this.getNonce(caller.address);
|
|
78
78
|
yield this.updateLastUsed(caller.address);
|
|
79
|
-
(0,
|
|
79
|
+
(0, ttd_core_1.log_info)(`acquireCaller: ${caller.address}, nonce=${nonce}, took ${Date.now() - startTime}ms`);
|
|
80
80
|
const address = caller.address;
|
|
81
81
|
return {
|
|
82
82
|
wallet: caller,
|
|
@@ -87,7 +87,7 @@ class CallerManager {
|
|
|
87
87
|
})
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
-
yield (0,
|
|
90
|
+
yield (0, ttd_core_1.sleep)(retryInterval);
|
|
91
91
|
}
|
|
92
92
|
throw new Error(`acquireCaller failed: all ${this.callers.length} callers are busy, waited ${Date.now() - startTime}ms`);
|
|
93
93
|
});
|
|
@@ -97,7 +97,7 @@ class CallerManager {
|
|
|
97
97
|
const current = yield this.getNonce(address);
|
|
98
98
|
if (confirmedNonce > current) {
|
|
99
99
|
yield this.setNonce(address, confirmedNonce);
|
|
100
|
-
(0,
|
|
100
|
+
(0, ttd_core_1.log_info)(`confirmNonce: ${address}, ${current} → ${confirmedNonce}`);
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
103
|
}
|
|
@@ -106,7 +106,7 @@ class CallerManager {
|
|
|
106
106
|
const current = yield this.getNonce(address);
|
|
107
107
|
if (nonce !== current) {
|
|
108
108
|
yield this.setNonce(address, nonce);
|
|
109
|
-
(0,
|
|
109
|
+
(0, ttd_core_1.log_info)(`forceSetNonce: ${address}, ${current} → ${nonce}`);
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
112
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WebSocketManager = void 0;
|
|
4
4
|
const ethers_1 = require("ethers");
|
|
5
|
-
const
|
|
5
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
6
6
|
class WebSocketManager {
|
|
7
7
|
constructor() {
|
|
8
8
|
this.wsProvider = null;
|
|
@@ -50,32 +50,32 @@ class WebSocketManager {
|
|
|
50
50
|
getWsProvider(wsEndpoint, network) {
|
|
51
51
|
var _a;
|
|
52
52
|
if (!wsEndpoint) {
|
|
53
|
-
(0,
|
|
53
|
+
(0, ttd_core_1.log_warn)('WebSocket endpoint not configured');
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
56
|
if (!this.wsProvider) {
|
|
57
|
-
(0,
|
|
57
|
+
(0, ttd_core_1.log_info)('Creating new WebSocket provider');
|
|
58
58
|
try {
|
|
59
59
|
this.wsProvider = new ethers_1.ethers.providers.WebSocketProvider(wsEndpoint, network);
|
|
60
60
|
this.wsProvider.on('error', (error) => {
|
|
61
|
-
(0,
|
|
61
|
+
(0, ttd_core_1.log_error)('[WS] provider error', Object.assign(Object.assign({}, this.formatConn(wsEndpoint, network)), this.formatErr(error)));
|
|
62
62
|
this.handleWsDisconnection(wsEndpoint, network);
|
|
63
63
|
});
|
|
64
64
|
const rawWs = (_a = this.wsProvider) === null || _a === void 0 ? void 0 : _a._websocket;
|
|
65
65
|
if (rawWs === null || rawWs === void 0 ? void 0 : rawWs.on) {
|
|
66
66
|
rawWs.on('close', (code, reason) => {
|
|
67
67
|
const reasonText = this.normalizeReason(reason);
|
|
68
|
-
(0,
|
|
68
|
+
(0, ttd_core_1.log_warn)('[WS] socket close', Object.assign(Object.assign({}, this.formatConn(wsEndpoint, network)), { code, reason: reasonText }));
|
|
69
69
|
this.handleWsDisconnection(wsEndpoint, network);
|
|
70
70
|
});
|
|
71
71
|
rawWs.on('error', (err) => {
|
|
72
|
-
(0,
|
|
72
|
+
(0, ttd_core_1.log_error)('[WS] socket error', Object.assign(Object.assign({}, this.formatConn(wsEndpoint, network)), this.formatErr(err)));
|
|
73
73
|
this.handleWsDisconnection(wsEndpoint, network);
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
catch (error) {
|
|
78
|
-
(0,
|
|
78
|
+
(0, ttd_core_1.log_error)('[WS] create provider failed', Object.assign(Object.assign({}, this.formatConn(wsEndpoint, network)), this.formatErr(error)));
|
|
79
79
|
this.wsProvider = null;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -97,16 +97,16 @@ class WebSocketManager {
|
|
|
97
97
|
}
|
|
98
98
|
incrementConnections() {
|
|
99
99
|
this.wsConnections++;
|
|
100
|
-
(0,
|
|
100
|
+
(0, ttd_core_1.log_info)(`WebSocket active connections: ${this.wsConnections}`);
|
|
101
101
|
}
|
|
102
102
|
decrementConnections() {
|
|
103
103
|
var _a, _b, _c, _d;
|
|
104
104
|
if (this.wsConnections > 0) {
|
|
105
105
|
this.wsConnections--;
|
|
106
106
|
}
|
|
107
|
-
(0,
|
|
107
|
+
(0, ttd_core_1.log_info)(`WebSocket active connections: ${this.wsConnections}`);
|
|
108
108
|
if (this.wsConnections === 0 && this.wsProvider) {
|
|
109
|
-
(0,
|
|
109
|
+
(0, ttd_core_1.log_info)('No active connections, closing WebSocket');
|
|
110
110
|
try {
|
|
111
111
|
this.wsProvider.removeAllListeners();
|
|
112
112
|
const rawWs = (_a = this.wsProvider) === null || _a === void 0 ? void 0 : _a._websocket;
|
|
@@ -119,7 +119,7 @@ class WebSocketManager {
|
|
|
119
119
|
}
|
|
120
120
|
listenToEvent(eventName, handler) {
|
|
121
121
|
if (!this.wsProvider) {
|
|
122
|
-
(0,
|
|
122
|
+
(0, ttd_core_1.log_warn)('WebSocket provider not available for event listener');
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
if (!this.eventListeners.has(eventName)) {
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.BaseTxParser = void 0;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
const ethers_1 = require("ethers");
|
|
15
15
|
class BaseTxParser {
|
|
16
16
|
constructor(config) {
|
|
@@ -66,7 +66,7 @@ class BaseTxParser {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
catch (error) {
|
|
69
|
-
(0,
|
|
69
|
+
(0, ttd_core_1.log_error)('calculateGasFee error:', error);
|
|
70
70
|
return {
|
|
71
71
|
base_fee: 0,
|
|
72
72
|
priority_fee: 0,
|
package/dist/utils/gas_helper.js
CHANGED
|
@@ -11,18 +11,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.EVMGasHelper = void 0;
|
|
13
13
|
const ethers_1 = require("ethers");
|
|
14
|
-
const
|
|
14
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
15
15
|
class EVMGasHelper {
|
|
16
16
|
static wrapNativeToken(provider_1, wallet_1, wrappedTokenAddress_1, amount_1) {
|
|
17
17
|
return __awaiter(this, arguments, void 0, function* (provider, wallet, wrappedTokenAddress, amount, minReserve = "0.1", tokenSymbol = "ETH") {
|
|
18
18
|
const nativeBalance = yield provider.getBalance(wallet.address);
|
|
19
19
|
const nativeBalanceEth = ethers_1.ethers.utils.formatEther(nativeBalance);
|
|
20
|
-
(0,
|
|
20
|
+
(0, ttd_core_1.log_info)(`当前${tokenSymbol}余额: ${nativeBalanceEth} ${tokenSymbol}`);
|
|
21
21
|
let wrapAmount;
|
|
22
22
|
if (amount === "max") {
|
|
23
23
|
const minReserveBN = ethers_1.ethers.utils.parseEther(minReserve);
|
|
24
24
|
if (nativeBalance.lte(minReserveBN)) {
|
|
25
|
-
(0,
|
|
25
|
+
(0, ttd_core_1.log_info)(`${tokenSymbol}余额不足最小保留额 ${minReserve} ${tokenSymbol},不执行转换`);
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
wrapAmount = nativeBalance.sub(minReserveBN);
|
|
@@ -31,19 +31,19 @@ class EVMGasHelper {
|
|
|
31
31
|
wrapAmount = ethers_1.ethers.utils.parseEther(amount);
|
|
32
32
|
const minReserveBN = ethers_1.ethers.utils.parseEther(minReserve);
|
|
33
33
|
if (nativeBalance.sub(wrapAmount).lt(minReserveBN)) {
|
|
34
|
-
(0,
|
|
34
|
+
(0, ttd_core_1.log_info)(`转换后${tokenSymbol}余额将低于最小保留额 ${minReserve} ${tokenSymbol},调整转换金额`);
|
|
35
35
|
wrapAmount = nativeBalance.sub(minReserveBN);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
if (wrapAmount.lte(ethers_1.ethers.constants.Zero)) {
|
|
39
|
-
(0,
|
|
39
|
+
(0, ttd_core_1.log_info)(`计算的转换金额为0或负数,不执行转换`);
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
const wrappedTokenContract = new ethers_1.ethers.Contract(wrappedTokenAddress, [
|
|
43
43
|
"function deposit() external payable",
|
|
44
44
|
"function withdraw(uint wad) external"
|
|
45
45
|
], wallet);
|
|
46
|
-
(0,
|
|
46
|
+
(0, ttd_core_1.log_info)(`准备将 ${ethers_1.ethers.utils.formatEther(wrapAmount)} ${tokenSymbol} 转换为 W${tokenSymbol}`);
|
|
47
47
|
const gasPrice = yield provider.getGasPrice();
|
|
48
48
|
const txOptions = {
|
|
49
49
|
value: wrapAmount,
|
|
@@ -52,13 +52,13 @@ class EVMGasHelper {
|
|
|
52
52
|
};
|
|
53
53
|
try {
|
|
54
54
|
const tx = yield wrappedTokenContract.deposit(txOptions);
|
|
55
|
-
(0,
|
|
55
|
+
(0, ttd_core_1.log_info)(`${tokenSymbol}转换为W${tokenSymbol}交易已发送,txHash: ${tx.hash}`);
|
|
56
56
|
yield tx.wait();
|
|
57
|
-
(0,
|
|
57
|
+
(0, ttd_core_1.log_info)(`${tokenSymbol}转换为W${tokenSymbol}交易已确认,txHash: ${tx.hash}`);
|
|
58
58
|
return tx.hash;
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
|
-
(0,
|
|
61
|
+
(0, ttd_core_1.log_error)(`${tokenSymbol}转换为W${tokenSymbol}失败: ${error.message}`, error);
|
|
62
62
|
throw error;
|
|
63
63
|
}
|
|
64
64
|
});
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ethers } from "ethers";
|
|
2
2
|
import Decimal from "decimal.js";
|
|
3
3
|
export * from './gas_helper';
|
|
4
|
+
export * from './trade_direction';
|
|
4
5
|
export declare const sleep: (ms: number) => Promise<void>;
|
|
5
6
|
export declare const formatPrice: (price: number, precision?: number) => string;
|
|
6
7
|
export declare const formatUnits: (value: ethers.BigNumber, decimals: number) => Decimal;
|
package/dist/utils/index.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.formatNumberHighPrecision = exports.formatUnits = exports.formatPrice =
|
|
|
21
21
|
const ethers_1 = require("ethers");
|
|
22
22
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
23
23
|
__exportStar(require("./gas_helper"), exports);
|
|
24
|
+
__exportStar(require("./trade_direction"), exports);
|
|
24
25
|
const sleep = (ms) => {
|
|
25
26
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
26
27
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface TradeDirectionResult<T = any> {
|
|
2
|
+
isBuy: boolean;
|
|
3
|
+
inputToken: T;
|
|
4
|
+
outputToken: T;
|
|
5
|
+
baseToken: T;
|
|
6
|
+
quoteToken: T;
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveTradeDirection<T = any>(poolInfo: {
|
|
9
|
+
tokenA: T;
|
|
10
|
+
tokenB: T;
|
|
11
|
+
quote_token: string;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}, isBuy: boolean): TradeDirectionResult<T>;
|
|
14
|
+
export declare function calculateStandardPrice(inputAmount: number, outputAmount: number, isBuy: boolean): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTradeDirection = resolveTradeDirection;
|
|
4
|
+
exports.calculateStandardPrice = calculateStandardPrice;
|
|
5
|
+
function resolveTradeDirection(poolInfo, isBuy) {
|
|
6
|
+
const { tokenA, tokenB, quote_token } = poolInfo;
|
|
7
|
+
const quoteToken = [tokenA, tokenB].find(t => t.symbol === quote_token);
|
|
8
|
+
const baseToken = [tokenA, tokenB].find(t => t.symbol !== quote_token);
|
|
9
|
+
if (!quoteToken || !baseToken) {
|
|
10
|
+
throw new Error(`Cannot resolve tokens: tokenA=${tokenA === null || tokenA === void 0 ? void 0 : tokenA.symbol}, tokenB=${tokenB === null || tokenB === void 0 ? void 0 : tokenB.symbol}, quote_token=${quote_token}`);
|
|
11
|
+
}
|
|
12
|
+
const inputToken = isBuy ? quoteToken : baseToken;
|
|
13
|
+
const outputToken = isBuy ? baseToken : quoteToken;
|
|
14
|
+
return { isBuy, inputToken, outputToken, baseToken, quoteToken };
|
|
15
|
+
}
|
|
16
|
+
function calculateStandardPrice(inputAmount, outputAmount, isBuy) {
|
|
17
|
+
if (isBuy) {
|
|
18
|
+
return (inputAmount / outputAmount).toFixed(18);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return (outputAmount / inputAmount).toFixed(18);
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/ws/event_filter.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EventFilter = void 0;
|
|
4
|
-
const
|
|
4
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
5
|
class EventFilter {
|
|
6
6
|
static initializeCleanup() {
|
|
7
7
|
if (this.cleanupInitialized) {
|
|
@@ -21,7 +21,7 @@ class EventFilter {
|
|
|
21
21
|
this.initializeCleanup();
|
|
22
22
|
const key = `${poolAddress}-${blockNumber}-${eventType.toUpperCase()}`;
|
|
23
23
|
if (this.poolEventMap.has(key)) {
|
|
24
|
-
(0,
|
|
24
|
+
(0, ttd_core_1.log_warn)(`Event ${eventType} already occurred on pool: ${poolAddress}, block: ${blockNumber}`);
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
this.poolEventMap.set(key, Date.now());
|
package/dist/yyws/yyws_client.js
CHANGED
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.subscribe_pool_price_from_yyws = subscribe_pool_price_from_yyws;
|
|
13
|
-
const
|
|
13
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
14
14
|
const ws_client_1 = require("@clonegod/ttd-core/dist/ws/ws_client");
|
|
15
15
|
function subscribe_pool_price_from_yyws(poolInfoList, callback) {
|
|
16
16
|
const ws_url = process.env.SUBSCIBE_YY_WS_URL || 'ws://43.165.135.114:10100/ws';
|
|
@@ -70,7 +70,7 @@ function log_yyws_price_msg(poolInfo, priceData) {
|
|
|
70
70
|
const { poolAddress, token0, token1, askToken0InToken1, bidToken0InToken1, askToken1InToken0, bidToken1InToken0 } = poolPrice;
|
|
71
71
|
line += `${token0}/${token1} - ${poolAddress} - ${askToken0InToken1} - ${bidToken0InToken1} - ${askToken1InToken0} - ${bidToken1InToken0}\n`;
|
|
72
72
|
}
|
|
73
|
-
(0,
|
|
73
|
+
(0, ttd_core_1.appendLogWithPeriodicReset)("logs", `yyws-price-msg-${poolInfo.pool_address}.txt`, line, 10)
|
|
74
74
|
.catch(err => console.error('log_yyws_price_msg error', err));
|
|
75
75
|
});
|
|
76
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clonegod/ttd-bsc-common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "BSC common library",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"push": "npm run build && npm publish"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@clonegod/ttd-core": "
|
|
17
|
+
"@clonegod/ttd-core": "3.0.3",
|
|
18
18
|
"@clonegod/ttd-bsc-send-tx": "1.0.0",
|
|
19
19
|
"axios": "^1.12.0",
|
|
20
20
|
"dotenv": "^16.4.7",
|