@clonegod/ttd-base-common 1.0.26 → 1.1.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.
Files changed (144) hide show
  1. package/dist/appconfig/BaseQuoteAppConfig.d.ts +10 -0
  2. package/dist/appconfig/BaseQuoteAppConfig.js +36 -0
  3. package/dist/appconfig/BaseTradeAppConfig.d.ts +7 -0
  4. package/dist/appconfig/BaseTradeAppConfig.js +13 -0
  5. package/dist/appconfig/base_dex_env_args.d.ts +5 -0
  6. package/dist/appconfig/base_dex_env_args.js +68 -0
  7. package/dist/appconfig/base_env_args.d.ts +82 -0
  8. package/dist/appconfig/base_env_args.js +91 -0
  9. package/dist/appconfig/ensure_core_env.d.ts +1 -0
  10. package/dist/appconfig/ensure_core_env.js +18 -0
  11. package/dist/appconfig/index.d.ts +5 -0
  12. package/dist/appconfig/index.js +21 -0
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +2 -2
  15. package/dist/quote/depth/amm_depth_calculator.d.ts +19 -0
  16. package/dist/quote/depth/amm_depth_calculator.js +55 -0
  17. package/dist/quote/depth/clmm_depth_calculator.d.ts +28 -0
  18. package/dist/quote/depth/clmm_depth_calculator.js +176 -0
  19. package/dist/quote/depth/index.d.ts +51 -0
  20. package/dist/quote/depth/index.js +264 -0
  21. package/dist/quote/depth/tick_liquidity_snapshot.d.ts +58 -0
  22. package/dist/quote/depth/tick_liquidity_snapshot.js +143 -0
  23. package/dist/quote/event/index.d.ts +1 -0
  24. package/dist/quote/event/index.js +1 -0
  25. package/dist/quote/event/pool_event_listener.d.ts +5 -3
  26. package/dist/quote/event/pool_event_listener.js +128 -150
  27. package/dist/quote/event/swap_debouncer.d.ts +22 -0
  28. package/dist/quote/event/swap_debouncer.js +80 -0
  29. package/dist/quote/get_base_token_price.d.ts +6 -0
  30. package/dist/quote/get_base_token_price.js +90 -0
  31. package/dist/quote/index.d.ts +7 -0
  32. package/dist/quote/index.js +7 -0
  33. package/dist/quote/preload_token_prices.d.ts +2 -0
  34. package/dist/quote/preload_token_prices.js +37 -0
  35. package/dist/quote/price_feed_handler.d.ts +15 -0
  36. package/dist/quote/price_feed_handler.js +56 -0
  37. package/dist/quote/pricing/fee_helpers.d.ts +13 -0
  38. package/dist/quote/pricing/fee_helpers.js +68 -0
  39. package/dist/quote/pricing/index.d.ts +3 -1
  40. package/dist/quote/pricing/index.js +3 -1
  41. package/dist/quote/pricing/pool_state_initializer.d.ts +12 -0
  42. package/dist/quote/pricing/pool_state_initializer.js +191 -0
  43. package/dist/quote/pricing/sdk_token_factory.d.ts +2 -0
  44. package/dist/quote/pricing/sdk_token_factory.js +21 -0
  45. package/dist/quote/quote_amount.d.ts +4 -0
  46. package/dist/quote/quote_amount.js +24 -0
  47. package/dist/quote/tick/cached_tick_data_provider.d.ts +12 -0
  48. package/dist/quote/tick/cached_tick_data_provider.js +45 -0
  49. package/dist/quote/tick/clmm_tick_cache.d.ts +42 -0
  50. package/dist/quote/tick/clmm_tick_cache.js +236 -0
  51. package/dist/quote/tick/index.d.ts +4 -0
  52. package/dist/{ws → quote/tick}/index.js +4 -2
  53. package/dist/quote/tick/state_view_tick_loader.d.ts +17 -0
  54. package/dist/quote/tick/state_view_tick_loader.js +136 -0
  55. package/dist/quote/tick/tick_lens_loaders.d.ts +24 -0
  56. package/dist/quote/tick/tick_lens_loaders.js +158 -0
  57. package/dist/quote/verify/index.d.ts +2 -0
  58. package/dist/quote/verify/index.js +5 -0
  59. package/dist/quote/verify/quote_price_verify.d.ts +30 -0
  60. package/dist/quote/verify/quote_price_verify.js +240 -0
  61. package/dist/redis/redis_client.d.ts +3 -2
  62. package/dist/redis/redis_client.js +86 -116
  63. package/dist/send-tx/constants.d.ts +2 -0
  64. package/dist/send-tx/constants.js +6 -0
  65. package/dist/send-tx/index.d.ts +2 -0
  66. package/dist/{config → send-tx}/index.js +2 -1
  67. package/dist/send-tx/types.d.ts +4 -0
  68. package/dist/send-tx/types.js +2 -0
  69. package/dist/trade/abstract_dex_trade.d.ts +43 -21
  70. package/dist/trade/abstract_dex_trade.js +347 -133
  71. package/dist/trade/caller_manager.d.ts +31 -0
  72. package/dist/trade/caller_manager.js +202 -0
  73. package/dist/trade/check/abstract_tx_result_checker.d.ts +28 -0
  74. package/dist/trade/check/abstract_tx_result_checker.js +192 -0
  75. package/dist/trade/check/index.d.ts +1 -1
  76. package/dist/trade/check/index.js +1 -1
  77. package/dist/trade/index.d.ts +2 -2
  78. package/dist/trade/index.js +2 -2
  79. package/dist/trade/parse/base_parser.d.ts +1 -2
  80. package/dist/trade/parse/base_parser.js +36 -36
  81. package/dist/trade/trade_trace.d.ts +17 -0
  82. package/dist/trade/trade_trace.js +65 -0
  83. package/dist/types/config_types.d.ts +3 -3
  84. package/dist/types/event_types.d.ts +3 -3
  85. package/dist/types/pool_state.d.ts +140 -13
  86. package/dist/utils/ethers_compat.d.ts +13 -0
  87. package/dist/utils/ethers_compat.js +18 -0
  88. package/dist/utils/fast_signer.d.ts +1 -0
  89. package/dist/utils/fast_signer.js +87 -0
  90. package/dist/utils/gas_helper.d.ts +2 -2
  91. package/dist/utils/gas_helper.js +48 -60
  92. package/dist/utils/index.d.ts +5 -2
  93. package/dist/utils/index.js +6 -2
  94. package/dist/utils/pool_filter.d.ts +8 -0
  95. package/dist/utils/pool_filter.js +38 -0
  96. package/dist/utils/trade_direction.d.ts +14 -0
  97. package/dist/utils/trade_direction.js +23 -0
  98. package/package.json +3 -3
  99. package/dist/config/base_env_args.d.ts +0 -11
  100. package/dist/config/base_env_args.js +0 -19
  101. package/dist/config/index.d.ts +0 -1
  102. package/dist/quote/event/verify_clmm_swap_event.d.ts +0 -1
  103. package/dist/quote/event/verify_clmm_swap_event.js +0 -178
  104. package/dist/quote/pricing/token_price_cache.d.ts +0 -10
  105. package/dist/quote/pricing/token_price_cache.js +0 -40
  106. package/dist/trade/abstract_dex_trade_plus.d.ts +0 -43
  107. package/dist/trade/abstract_dex_trade_plus.js +0 -421
  108. package/dist/trade/check/tx_websocket_manager.d.ts +0 -23
  109. package/dist/trade/check/tx_websocket_manager.js +0 -119
  110. package/dist/trade/send/alchemy_base.d.ts +0 -5
  111. package/dist/trade/send/alchemy_base.js +0 -48
  112. package/dist/trade/send/ankr_base.d.ts +0 -5
  113. package/dist/trade/send/ankr_base.js +0 -48
  114. package/dist/trade/send/base_rpc.d.ts +0 -5
  115. package/dist/trade/send/base_rpc.js +0 -48
  116. package/dist/trade/send/blockpi_base.d.ts +0 -5
  117. package/dist/trade/send/blockpi_base.js +0 -48
  118. package/dist/trade/send/bloxroute_base.d.ts +0 -11
  119. package/dist/trade/send/bloxroute_base.js +0 -115
  120. package/dist/trade/send/chainstack_base.d.ts +0 -5
  121. package/dist/trade/send/chainstack_base.js +0 -48
  122. package/dist/trade/send/drpc_base.d.ts +0 -5
  123. package/dist/trade/send/drpc_base.js +0 -48
  124. package/dist/trade/send/getblock_base.d.ts +0 -5
  125. package/dist/trade/send/getblock_base.js +0 -48
  126. package/dist/trade/send/index.d.ts +0 -15
  127. package/dist/trade/send/index.js +0 -33
  128. package/dist/trade/send/infura_base.d.ts +0 -5
  129. package/dist/trade/send/infura_base.js +0 -48
  130. package/dist/trade/send/moralis_base.d.ts +0 -5
  131. package/dist/trade/send/moralis_base.js +0 -48
  132. package/dist/trade/send/onerpc_base.d.ts +0 -5
  133. package/dist/trade/send/onerpc_base.js +0 -48
  134. package/dist/trade/send/quicknode_base.d.ts +0 -5
  135. package/dist/trade/send/quicknode_base.js +0 -48
  136. package/dist/trade/send/send_tx.d.ts +0 -17
  137. package/dist/trade/send/send_tx.js +0 -163
  138. package/dist/ws/event_filter.d.ts +0 -8
  139. package/dist/ws/event_filter.js +0 -36
  140. package/dist/ws/index.d.ts +0 -2
  141. package/dist/ws/subscribe_v2_events.d.ts +0 -14
  142. package/dist/ws/subscribe_v2_events.js +0 -174
  143. package/dist/ws/subscribe_v3_events.d.ts +0 -14
  144. package/dist/ws/subscribe_v3_events.js +0 -174
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildTickLiquiditySnapshot = buildTickLiquiditySnapshot;
4
+ const clmm_depth_calculator_1 = require("./clmm_depth_calculator");
5
+ const Q96 = 1n << 96n;
6
+ const DEFAULT_RANGE_BPS = 200;
7
+ function buildTickLiquiditySnapshot(input) {
8
+ const { poolAddress, pair, protocolId, tickCache, currentTick, tickSpacing, token0, token1, baseIsToken0, token0PriceUsd, token1PriceUsd, } = input;
9
+ const currentSqrtPriceX96 = BigInt(input.sqrtPriceX96);
10
+ const currentLiquidity = BigInt(input.liquidity);
11
+ const rangeBps = input.rangeBps ?? DEFAULT_RANGE_BPS;
12
+ if (currentLiquidity === 0n)
13
+ return undefined;
14
+ const allTickIndices = tickCache.getCachedTickIndices(poolAddress);
15
+ if (allTickIndices.length === 0)
16
+ return undefined;
17
+ const upperSqrtPrice = (0, clmm_depth_calculator_1.computeTargetSqrtPrice)(currentSqrtPriceX96, rangeBps, false);
18
+ const lowerSqrtPrice = (0, clmm_depth_calculator_1.computeTargetSqrtPrice)(currentSqrtPriceX96, rangeBps, true);
19
+ const upperTick = sqrtPriceToTick(upperSqrtPrice);
20
+ const lowerTick = sqrtPriceToTick(lowerSqrtPrice);
21
+ const ticksInRange = allTickIndices.filter(t => t >= lowerTick && t <= upperTick);
22
+ if (ticksInRange.length === 0)
23
+ return undefined;
24
+ const liquidityMap = computeLiquidityAtTicks(ticksInRange, currentTick, currentLiquidity, tickCache, poolAddress);
25
+ const tickDataPoints = [];
26
+ let totalTvlUsd = 0;
27
+ for (let i = 0; i < ticksInRange.length - 1; i++) {
28
+ const tickLower = ticksInRange[i];
29
+ const tickUpper = ticksInRange[i + 1];
30
+ const liq = liquidityMap.get(tickLower) ?? 0n;
31
+ if (liq <= 0n) {
32
+ const price0 = tickToPrice(tickLower, token0.decimals, token1.decimals);
33
+ const price1 = price0 > 0 ? 1 / price0 : 0;
34
+ tickDataPoints.push({
35
+ tick: tickLower,
36
+ price0, price1,
37
+ liquidity: '0',
38
+ amount0: 0, amount1: 0, tvlUsd: 0,
39
+ buyable: 0, sellable: 0,
40
+ });
41
+ continue;
42
+ }
43
+ const sqrtPriceLower = (0, clmm_depth_calculator_1.getSqrtRatioAtTick)(tickLower);
44
+ const sqrtPriceUpper = (0, clmm_depth_calculator_1.getSqrtRatioAtTick)(tickUpper);
45
+ const { amount0, amount1 } = computeTokenAmounts(sqrtPriceLower, sqrtPriceUpper, currentSqrtPriceX96, liq, token0.decimals, token1.decimals);
46
+ const tvlUsd = amount0 * token0PriceUsd + amount1 * token1PriceUsd;
47
+ totalTvlUsd += tvlUsd;
48
+ const { buyable, sellable } = computeBuySell(sqrtPriceLower, sqrtPriceUpper, liq, token0.decimals, token1.decimals, baseIsToken0);
49
+ const price0 = tickToPrice(tickLower, token0.decimals, token1.decimals);
50
+ const price1 = price0 > 0 ? 1 / price0 : 0;
51
+ tickDataPoints.push({
52
+ tick: tickLower,
53
+ price0, price1,
54
+ liquidity: liq.toString(),
55
+ amount0, amount1, tvlUsd,
56
+ buyable, sellable,
57
+ });
58
+ }
59
+ const currentPriceFloat = Number(currentSqrtPriceX96) / Number(Q96);
60
+ const currentPrice = (currentPriceFloat * currentPriceFloat) *
61
+ Math.pow(10, token0.decimals - token1.decimals);
62
+ return {
63
+ poolAddress,
64
+ pair,
65
+ protocolId,
66
+ currentTick,
67
+ tickSpacing,
68
+ currentPrice,
69
+ token0,
70
+ token1,
71
+ ticks: tickDataPoints,
72
+ totalTvlUsd,
73
+ timestamp: Date.now(),
74
+ };
75
+ }
76
+ function computeLiquidityAtTicks(sortedTicks, currentTick, currentLiquidity, tickCache, poolAddress) {
77
+ const result = new Map();
78
+ let splitIdx = sortedTicks.findIndex(t => t > currentTick);
79
+ if (splitIdx === -1)
80
+ splitIdx = sortedTicks.length;
81
+ let liq = currentLiquidity;
82
+ for (let i = splitIdx; i < sortedTicks.length; i++) {
83
+ const tick = sortedTicks[i];
84
+ const netLiq = tickCache.getTickLiquidityNet(poolAddress, tick) ?? 0n;
85
+ liq = liq + netLiq;
86
+ result.set(tick, liq > 0n ? liq : 0n);
87
+ }
88
+ liq = currentLiquidity;
89
+ for (let i = splitIdx - 1; i >= 0; i--) {
90
+ const tick = sortedTicks[i];
91
+ result.set(tick, liq > 0n ? liq : 0n);
92
+ const netLiq = tickCache.getTickLiquidityNet(poolAddress, tick) ?? 0n;
93
+ liq = liq - netLiq;
94
+ }
95
+ return result;
96
+ }
97
+ function computeTokenAmounts(sqrtPriceLower, sqrtPriceUpper, currentSqrtPrice, liquidity, decimals0, decimals1) {
98
+ let amount0Wei = 0n;
99
+ let amount1Wei = 0n;
100
+ if (currentSqrtPrice <= sqrtPriceLower) {
101
+ amount0Wei = calcToken0Amount(sqrtPriceLower, sqrtPriceUpper, liquidity);
102
+ }
103
+ else if (currentSqrtPrice >= sqrtPriceUpper) {
104
+ amount1Wei = calcToken1Amount(sqrtPriceLower, sqrtPriceUpper, liquidity);
105
+ }
106
+ else {
107
+ amount0Wei = calcToken0Amount(currentSqrtPrice, sqrtPriceUpper, liquidity);
108
+ amount1Wei = calcToken1Amount(sqrtPriceLower, currentSqrtPrice, liquidity);
109
+ }
110
+ return {
111
+ amount0: Number(amount0Wei) / Math.pow(10, decimals0),
112
+ amount1: Number(amount1Wei) / Math.pow(10, decimals1),
113
+ };
114
+ }
115
+ function computeBuySell(sqrtPriceLower, sqrtPriceUpper, liquidity, decimals0, decimals1, baseIsToken0) {
116
+ const token0Out = Number(calcToken0Amount(sqrtPriceLower, sqrtPriceUpper, liquidity)) / Math.pow(10, decimals0);
117
+ const token1Out = Number(calcToken1Amount(sqrtPriceLower, sqrtPriceUpper, liquidity)) / Math.pow(10, decimals1);
118
+ if (baseIsToken0) {
119
+ return { buyable: token0Out, sellable: token1Out };
120
+ }
121
+ else {
122
+ return { buyable: token1Out, sellable: token0Out };
123
+ }
124
+ }
125
+ function calcToken0Amount(sqrtPriceLower, sqrtPriceUpper, liquidity) {
126
+ if (sqrtPriceLower >= sqrtPriceUpper || liquidity === 0n)
127
+ return 0n;
128
+ const diff = sqrtPriceUpper - sqrtPriceLower;
129
+ return liquidity * diff * Q96 / (sqrtPriceLower * sqrtPriceUpper);
130
+ }
131
+ function calcToken1Amount(sqrtPriceLower, sqrtPriceUpper, liquidity) {
132
+ if (sqrtPriceLower >= sqrtPriceUpper || liquidity === 0n)
133
+ return 0n;
134
+ const diff = sqrtPriceUpper - sqrtPriceLower;
135
+ return liquidity * diff / Q96;
136
+ }
137
+ function sqrtPriceToTick(sqrtPriceX96) {
138
+ const sqrtPriceFloat = Number(sqrtPriceX96) / Number(Q96);
139
+ return Math.floor(Math.log(sqrtPriceFloat * sqrtPriceFloat) / Math.log(1.0001));
140
+ }
141
+ function tickToPrice(tick, decimals0, decimals1) {
142
+ return Math.pow(1.0001, tick) * Math.pow(10, decimals0 - decimals1);
143
+ }
@@ -1 +1,2 @@
1
1
  export * from './pool_event_listener';
2
+ export * from './swap_debouncer';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./pool_event_listener"), exports);
18
+ __exportStar(require("./swap_debouncer"), exports);
@@ -1,9 +1,11 @@
1
1
  import { StandardPoolInfoType } from '@clonegod/ttd-core';
2
- import { AppConfig } from '@clonegod/ttd-core/dist';
2
+ import { AppConfig } from '@clonegod/ttd-core';
3
3
  export interface BlockUpdateEvent {
4
4
  blockNumber: number;
5
- previousBlockNumber: number;
6
- timestamp: number;
5
+ previousBlockNumber?: number;
6
+ blockTimestamp?: number;
7
+ blockTimestampMs?: number;
8
+ recvBlockTime: number;
7
9
  }
8
10
  export interface ConnectionDiagnostics {
9
11
  isConnected: boolean;
@@ -1,17 +1,8 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.PoolEventListener = void 0;
13
- const ethers_1 = require("ethers");
14
- const dist_1 = require("@clonegod/ttd-core/dist");
4
+ const ethers_compat_1 = require("../../utils/ethers_compat");
5
+ const ttd_core_1 = require("@clonegod/ttd-core");
15
6
  const common_1 = require("../../common");
16
7
  const CONFIG = {
17
8
  MAX_RETRIES: 3,
@@ -44,81 +35,73 @@ class PoolEventListener {
44
35
  this.appConfig = appConfig;
45
36
  this.ws_endpoint = ws_endpoint;
46
37
  }
47
- init(poolList) {
48
- return __awaiter(this, void 0, void 0, function* () {
49
- this.poolList = poolList.filter(pool => ethers_1.ethers.utils.isAddress(pool.pool_address));
50
- if (this.poolList.length !== poolList.length) {
51
- (0, dist_1.log_warn)(`Found ${poolList.length - this.poolList.length} invalid pool addresses, filtered out`, '');
52
- }
53
- yield this.connect();
54
- });
38
+ async init(poolList) {
39
+ this.poolList = poolList.filter(pool => ethers_compat_1.ethersCompat.isAddress(pool.pool_address));
40
+ if (this.poolList.length !== poolList.length) {
41
+ (0, ttd_core_1.log_warn)(`Found ${poolList.length - this.poolList.length} invalid pool addresses, filtered out`, '');
42
+ }
43
+ await this.connect();
55
44
  }
56
- start() {
57
- return __awaiter(this, void 0, void 0, function* () {
58
- (0, dist_1.log_info)(`Starting block listener...`);
59
- if (!this.isConnected || !this.wsProvider) {
60
- (0, dist_1.log_warn)('WebSocket not connected, cannot start listener', '');
61
- return;
62
- }
63
- yield this.startBlockListener();
64
- this.isStarted = true;
65
- (0, dist_1.log_info)('Block listener started successfully');
66
- });
45
+ async start() {
46
+ (0, ttd_core_1.log_info)(`Starting block listener...`);
47
+ if (!this.isConnected || !this.wsProvider) {
48
+ (0, ttd_core_1.log_warn)('WebSocket not connected, cannot start listener', '');
49
+ return;
50
+ }
51
+ await this.startBlockListener();
52
+ this.isStarted = true;
53
+ (0, ttd_core_1.log_info)('Block listener started successfully');
67
54
  }
68
- stop() {
69
- return __awaiter(this, void 0, void 0, function* () {
70
- (0, dist_1.log_info)(`Stopping block listener...`);
71
- this.stopBlockListener();
72
- this.cleanup();
73
- this.isStarted = false;
74
- (0, dist_1.log_info)(`Block listener stopped`);
75
- });
55
+ async stop() {
56
+ (0, ttd_core_1.log_info)(`Stopping block listener...`);
57
+ this.stopBlockListener();
58
+ this.cleanup();
59
+ this.isStarted = false;
60
+ (0, ttd_core_1.log_info)(`Block listener stopped`);
76
61
  }
77
- connect() {
78
- return __awaiter(this, void 0, void 0, function* () {
79
- for (let i = 0; i < CONFIG.MAX_RETRIES; i++) {
80
- try {
81
- (0, dist_1.log_info)(`Connecting to WebSocket: ${this.ws_endpoint} (Attempt ${i + 1}/${CONFIG.MAX_RETRIES})`);
82
- this.wsProvider = new ethers_1.ethers.providers.WebSocketProvider(this.ws_endpoint);
83
- const wsPromise = this.wsProvider.ready;
84
- const timeoutPromise = new Promise((_, reject) => {
85
- setTimeout(() => reject(new Error('WebSocket connection timeout')), CONFIG.CONNECTION_TIMEOUT);
86
- });
87
- yield Promise.race([wsPromise, timeoutPromise]);
88
- this.isConnected = true;
89
- this.reconnectAttempts = 0;
90
- (0, dist_1.log_info)(`WebSocket connected: ${this.ws_endpoint}`);
91
- this.setupWebSocketListeners();
92
- if (this.isStarted) {
93
- yield this.startBlockListener();
94
- }
95
- return;
62
+ async connect() {
63
+ for (let i = 0; i < CONFIG.MAX_RETRIES; i++) {
64
+ try {
65
+ (0, ttd_core_1.log_info)(`Connecting to WebSocket: ${this.ws_endpoint} (Attempt ${i + 1}/${CONFIG.MAX_RETRIES})`);
66
+ this.wsProvider = new ethers_compat_1.ethersCompat.WebSocketProvider(this.ws_endpoint);
67
+ const wsPromise = this.wsProvider.ready;
68
+ const timeoutPromise = new Promise((_, reject) => {
69
+ setTimeout(() => reject(new Error('WebSocket connection timeout')), CONFIG.CONNECTION_TIMEOUT);
70
+ });
71
+ await Promise.race([wsPromise, timeoutPromise]);
72
+ this.isConnected = true;
73
+ this.reconnectAttempts = 0;
74
+ (0, ttd_core_1.log_info)(`WebSocket connected: ${this.ws_endpoint}`);
75
+ this.setupWebSocketListeners();
76
+ if (this.isStarted) {
77
+ await this.startBlockListener();
96
78
  }
97
- catch (error) {
98
- (0, dist_1.log_error)(`WebSocket connection failed (Attempt ${i + 1}/${CONFIG.MAX_RETRIES}):`, error);
99
- if (i === CONFIG.MAX_RETRIES - 1) {
100
- throw error;
101
- }
102
- const delay = CONFIG.RETRY_DELAY_MULTIPLIER * (i + 1);
103
- (0, dist_1.log_info)(`Waiting ${delay / 1000} seconds before retry...`);
104
- yield new Promise(resolve => setTimeout(resolve, delay));
79
+ return;
80
+ }
81
+ catch (error) {
82
+ (0, ttd_core_1.log_error)(`WebSocket connection failed (Attempt ${i + 1}/${CONFIG.MAX_RETRIES}):`, error);
83
+ if (i === CONFIG.MAX_RETRIES - 1) {
84
+ throw error;
105
85
  }
86
+ const delay = CONFIG.RETRY_DELAY_MULTIPLIER * (i + 1);
87
+ (0, ttd_core_1.log_info)(`Waiting ${delay / 1000} seconds before retry...`);
88
+ await new Promise(resolve => setTimeout(resolve, delay));
106
89
  }
107
- });
90
+ }
108
91
  }
109
92
  setupWebSocketListeners() {
110
93
  if (!this.wsProvider)
111
94
  return;
112
95
  this.wsProvider.on('error', (err) => {
113
- (0, dist_1.log_error)(`WebSocket error:`, err, '');
96
+ (0, ttd_core_1.log_error)(`WebSocket error:`, err, '');
114
97
  this.handleConnectionIssue();
115
98
  });
116
99
  this.wsProvider._websocket.on('close', (code, reason) => {
117
- (0, dist_1.log_warn)(`WebSocket connection closed, code: ${code}, reason: ${reason || 'unknown'}`);
100
+ (0, ttd_core_1.log_warn)(`WebSocket connection closed, code: ${code}, reason: ${reason || 'unknown'}`);
118
101
  this.handleConnectionIssue();
119
102
  });
120
103
  this.wsProvider._websocket.on('open', () => {
121
- (0, dist_1.log_info)('WebSocket connection opened');
104
+ (0, ttd_core_1.log_info)('WebSocket connection opened');
122
105
  this.isConnected = true;
123
106
  this.reconnectAttempts = 0;
124
107
  });
@@ -128,7 +111,7 @@ class PoolEventListener {
128
111
  if (this.heartbeatInterval) {
129
112
  clearInterval(this.heartbeatInterval);
130
113
  }
131
- this.heartbeatInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
114
+ this.heartbeatInterval = setInterval(async () => {
132
115
  try {
133
116
  if (!this.wsProvider || !this.isConnected) {
134
117
  this.handleConnectionIssue();
@@ -138,89 +121,85 @@ class PoolEventListener {
138
121
  this.lastHeartbeatResponse = Date.now();
139
122
  const timeSinceLastHeartbeat = Date.now() - this.lastHeartbeatResponse;
140
123
  if (timeSinceLastHeartbeat > CONFIG.HEARTBEAT_TIMEOUT) {
141
- (0, dist_1.log_warn)(`No heartbeat response for ${timeSinceLastHeartbeat / 1000} seconds`);
124
+ (0, ttd_core_1.log_warn)(`No heartbeat response for ${timeSinceLastHeartbeat / 1000} seconds`);
142
125
  this.handleConnectionIssue();
143
126
  }
144
127
  }
145
128
  catch (error) {
146
- (0, dist_1.log_error)('Heartbeat check failed:', error);
129
+ (0, ttd_core_1.log_error)('Heartbeat check failed:', error);
147
130
  this.handleConnectionIssue();
148
131
  }
149
- }), CONFIG.HEARTBEAT_INTERVAL);
132
+ }, CONFIG.HEARTBEAT_INTERVAL);
150
133
  }
151
- handleConnectionIssue() {
152
- return __awaiter(this, void 0, void 0, function* () {
153
- if (this.isReconnecting) {
154
- (0, dist_1.log_info)('Already attempting to reconnect, skipping...');
155
- return;
156
- }
157
- this.isReconnecting = true;
158
- this.isConnected = false;
159
- try {
160
- this.stopBlockListener();
161
- this.cleanup();
162
- this.reconnectAttempts++;
163
- const delay = Math.min(CONFIG.BASE_RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts), CONFIG.MAX_RECONNECT_DELAY);
164
- (0, dist_1.log_info)(`Attempting to reconnect ${this.reconnectAttempts}/${CONFIG.MAX_RECONNECT_ATTEMPTS}, delay ${delay}ms, node: ${this.ws_endpoint}`);
165
- if (this.reconnectAttempts > CONFIG.MAX_RECONNECT_ATTEMPTS) {
166
- (0, dist_1.log_error)(`Reconnection failed, reached maximum attempts (${CONFIG.MAX_RECONNECT_ATTEMPTS}), exiting`, new Error('Max reconnect attempts failed'), '');
167
- this.appConfig.emit(common_1.EVENT_NAMES.WS_CONNECTION_FAILED, {
168
- endpoint: this.ws_endpoint,
169
- attempts: CONFIG.MAX_RECONNECT_ATTEMPTS,
170
- });
171
- process.exit(1);
172
- }
173
- yield (0, dist_1.sleep)(delay);
174
- yield this.connect();
175
- }
176
- catch (error) {
177
- (0, dist_1.log_error)('Reconnection attempt failed:', error);
178
- setTimeout(() => this.handleConnectionIssue(), CONFIG.BASE_RECONNECT_DELAY);
179
- }
180
- finally {
181
- this.isReconnecting = false;
134
+ async handleConnectionIssue() {
135
+ if (this.isReconnecting) {
136
+ (0, ttd_core_1.log_info)('Already attempting to reconnect, skipping...');
137
+ return;
138
+ }
139
+ this.isReconnecting = true;
140
+ this.isConnected = false;
141
+ try {
142
+ this.stopBlockListener();
143
+ this.cleanup();
144
+ this.reconnectAttempts++;
145
+ const delay = Math.min(CONFIG.BASE_RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts), CONFIG.MAX_RECONNECT_DELAY);
146
+ (0, ttd_core_1.log_info)(`Attempting to reconnect ${this.reconnectAttempts}/${CONFIG.MAX_RECONNECT_ATTEMPTS}, delay ${delay}ms, node: ${this.ws_endpoint}`);
147
+ if (this.reconnectAttempts > CONFIG.MAX_RECONNECT_ATTEMPTS) {
148
+ (0, ttd_core_1.log_error)(`Reconnection failed, reached maximum attempts (${CONFIG.MAX_RECONNECT_ATTEMPTS}), exiting`, new Error('Max reconnect attempts failed'), '');
149
+ this.appConfig.emit(common_1.EVENT_NAMES.WS_CONNECTION_FAILED, {
150
+ endpoint: this.ws_endpoint,
151
+ attempts: CONFIG.MAX_RECONNECT_ATTEMPTS,
152
+ });
153
+ process.exit(1);
182
154
  }
183
- });
155
+ await (0, ttd_core_1.sleep)(delay);
156
+ await this.connect();
157
+ }
158
+ catch (error) {
159
+ (0, ttd_core_1.log_error)('Reconnection attempt failed:', error);
160
+ setTimeout(() => this.handleConnectionIssue(), CONFIG.BASE_RECONNECT_DELAY);
161
+ }
162
+ finally {
163
+ this.isReconnecting = false;
164
+ }
184
165
  }
185
- startBlockListener() {
186
- return __awaiter(this, void 0, void 0, function* () {
187
- if (!this.wsProvider || !this.isConnected) {
188
- (0, dist_1.log_warn)(`Cannot start block listener: WebSocket not connected`);
189
- return;
190
- }
191
- try {
192
- const blockNumber = yield this.wsProvider.getBlockNumber();
193
- this.lastProcessedBlockNumber = blockNumber;
194
- (0, dist_1.log_info)(`Initializing block listener, current block: ${blockNumber}`);
195
- this.wsProvider.on('block', (blockNumber) => __awaiter(this, void 0, void 0, function* () {
196
- try {
197
- if (!this.isConnected || !this.isBlockListenerActive)
198
- return;
199
- this.lastMessageTime = Date.now();
200
- (0, dist_1.log_info)(`------- Block: ${blockNumber} -------`);
201
- const previousBlockNumber = this.lastProcessedBlockNumber;
202
- this.lastProcessedBlockNumber = blockNumber;
203
- const blockUpdateEvent = {
204
- blockNumber,
205
- previousBlockNumber,
206
- timestamp: Date.now(),
207
- };
208
- this.appConfig.emit(common_1.EVENT_NAMES.BLOCK_UPDATE, blockUpdateEvent);
209
- }
210
- catch (error) {
211
- (0, dist_1.log_error)(`Error processing block event:`, error, '');
212
- }
213
- }));
214
- this.startMessageMonitor();
215
- this.startDiagnosticMonitor();
216
- this.isBlockListenerActive = true;
217
- (0, dist_1.log_info)(`Block listener started using WebSocket event subscription`);
218
- }
219
- catch (error) {
220
- (0, dist_1.log_error)(`Failed to start block listener:`, error, '');
221
- this.isBlockListenerActive = false;
222
- }
223
- });
166
+ async startBlockListener() {
167
+ if (!this.wsProvider || !this.isConnected) {
168
+ (0, ttd_core_1.log_warn)(`Cannot start block listener: WebSocket not connected`);
169
+ return;
170
+ }
171
+ try {
172
+ const blockNumber = await this.wsProvider.getBlockNumber();
173
+ this.lastProcessedBlockNumber = blockNumber;
174
+ (0, ttd_core_1.log_info)(`Initializing block listener, current block: ${blockNumber}`);
175
+ this.wsProvider.on('block', async (blockNumber) => {
176
+ try {
177
+ if (!this.isConnected || !this.isBlockListenerActive)
178
+ return;
179
+ this.lastMessageTime = Date.now();
180
+ (0, ttd_core_1.log_info)(`------- Block: ${blockNumber} -------`);
181
+ const previousBlockNumber = this.lastProcessedBlockNumber;
182
+ this.lastProcessedBlockNumber = blockNumber;
183
+ const blockUpdateEvent = {
184
+ blockNumber,
185
+ previousBlockNumber,
186
+ recvBlockTime: Date.now(),
187
+ };
188
+ this.appConfig.emit(common_1.EVENT_NAMES.BLOCK_UPDATE, blockUpdateEvent);
189
+ }
190
+ catch (error) {
191
+ (0, ttd_core_1.log_error)(`Error processing block event:`, error, '');
192
+ }
193
+ });
194
+ this.startMessageMonitor();
195
+ this.startDiagnosticMonitor();
196
+ this.isBlockListenerActive = true;
197
+ (0, ttd_core_1.log_info)(`Block listener started using WebSocket event subscription`);
198
+ }
199
+ catch (error) {
200
+ (0, ttd_core_1.log_error)(`Failed to start block listener:`, error, '');
201
+ this.isBlockListenerActive = false;
202
+ }
224
203
  }
225
204
  startMessageMonitor() {
226
205
  if (this.messageMonitorInterval) {
@@ -229,7 +208,7 @@ class PoolEventListener {
229
208
  this.messageMonitorInterval = setInterval(() => {
230
209
  const timeSinceLastMessage = Date.now() - this.lastMessageTime;
231
210
  if (timeSinceLastMessage > CONFIG.MESSAGE_TIMEOUT) {
232
- (0, dist_1.log_warn)(`No messages received for ${CONFIG.MESSAGE_TIMEOUT / 1000} seconds, preparing to resubscribe`);
211
+ (0, ttd_core_1.log_warn)(`No messages received for ${CONFIG.MESSAGE_TIMEOUT / 1000} seconds, preparing to resubscribe`);
233
212
  this.handleConnectionIssue();
234
213
  }
235
214
  }, CONFIG.MESSAGE_CHECK_INTERVAL);
@@ -244,7 +223,7 @@ class PoolEventListener {
244
223
  }, CONFIG.DIAGNOSTIC_INTERVAL);
245
224
  }
246
225
  logDiagnostics(diagnostics) {
247
- (0, dist_1.log_info)(`WebSocket Connection Diagnostics:
226
+ (0, ttd_core_1.log_info)(`WebSocket Connection Diagnostics:
248
227
  Connection Status: ${diagnostics.isConnected ? 'Connected' : 'Disconnected'}
249
228
  Reconnection Status: ${diagnostics.isReconnecting ? 'Reconnecting' : 'Not Reconnecting'}
250
229
  Listener Status: ${diagnostics.isStarted ? 'Started' : 'Not Started'}
@@ -279,7 +258,7 @@ class PoolEventListener {
279
258
  clearInterval(this.diagnosticInterval);
280
259
  this.diagnosticInterval = null;
281
260
  }
282
- (0, dist_1.log_info)(`Block listener stopped`);
261
+ (0, ttd_core_1.log_info)(`Block listener stopped`);
283
262
  }
284
263
  }
285
264
  cleanup() {
@@ -291,7 +270,7 @@ class PoolEventListener {
291
270
  }
292
271
  }
293
272
  catch (error) {
294
- (0, dist_1.log_error)(`Failed to cleanup WebSocket resources:`, error, '');
273
+ (0, ttd_core_1.log_error)(`Failed to cleanup WebSocket resources:`, error, '');
295
274
  }
296
275
  this.wsProvider = null;
297
276
  }
@@ -311,8 +290,7 @@ class PoolEventListener {
311
290
  this.isReconnecting = false;
312
291
  }
313
292
  isWSConnected() {
314
- var _a, _b;
315
- return this.isConnected && ((_b = (_a = this.wsProvider) === null || _a === void 0 ? void 0 : _a._websocket) === null || _b === void 0 ? void 0 : _b.readyState) === 1;
293
+ return this.isConnected && this.wsProvider?._websocket?.readyState === 1;
316
294
  }
317
295
  getConnectionDiagnostics() {
318
296
  return {
@@ -0,0 +1,22 @@
1
+ export declare class SwapDebouncer<TEvent extends {
2
+ pool_address: string;
3
+ type: string;
4
+ data: {
5
+ blockNumber: number;
6
+ };
7
+ }, TResult = void> {
8
+ private debounceMs;
9
+ private onCalculate;
10
+ private onPublish;
11
+ private debouncedTypes;
12
+ private windows;
13
+ private latestBlock;
14
+ constructor(config: {
15
+ onCalculate: (event: TEvent) => TResult | Promise<TResult>;
16
+ onPublish: (event: TEvent, result: TResult) => void;
17
+ debouncedEventTypes?: string[];
18
+ });
19
+ onEvent(event: TEvent): Promise<void>;
20
+ flush(): void;
21
+ private startWindow;
22
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SwapDebouncer = void 0;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ class SwapDebouncer {
6
+ constructor(config) {
7
+ this.windows = new Map();
8
+ this.latestBlock = new Map();
9
+ this.debounceMs = parseInt(process.env.SWAP_DEBOUNCE_MS || '3', 10);
10
+ this.onCalculate = config.onCalculate;
11
+ this.onPublish = config.onPublish;
12
+ this.debouncedTypes = new Set((config.debouncedEventTypes || ['swap']).map(t => t.toLowerCase()));
13
+ }
14
+ async onEvent(event) {
15
+ const poolAddress = event.pool_address;
16
+ const blockNumber = event.data?.blockNumber || 0;
17
+ if (blockNumber > 0) {
18
+ const lastBlock = this.latestBlock.get(poolAddress) || 0;
19
+ if (blockNumber < lastBlock) {
20
+ (0, ttd_core_1.log_warn)(`SwapDebouncer: stale block dropped, pool=${poolAddress}, event block=${blockNumber}, latest=${lastBlock}`);
21
+ return;
22
+ }
23
+ this.latestBlock.set(poolAddress, blockNumber);
24
+ }
25
+ const eventType = event.type?.toLowerCase();
26
+ if (!this.debouncedTypes.has(eventType)) {
27
+ const result = await this.onCalculate(event);
28
+ this.onPublish(event, result);
29
+ return;
30
+ }
31
+ const window = this.windows.get(poolAddress);
32
+ const result = await this.onCalculate(event);
33
+ if (window) {
34
+ if (blockNumber > window.blockNumber) {
35
+ if (!window.expired) {
36
+ clearTimeout(window.timer);
37
+ this.onPublish(window.latestEvent, window.latestResult);
38
+ }
39
+ this.windows.delete(poolAddress);
40
+ this.startWindow(poolAddress, event, result, blockNumber);
41
+ }
42
+ else if (window.expired) {
43
+ this.onPublish(event, result);
44
+ }
45
+ else {
46
+ window.latestEvent = event;
47
+ window.latestResult = result;
48
+ }
49
+ }
50
+ else {
51
+ this.startWindow(poolAddress, event, result, blockNumber);
52
+ }
53
+ }
54
+ flush() {
55
+ for (const [_, window] of this.windows) {
56
+ if (!window.expired) {
57
+ clearTimeout(window.timer);
58
+ this.onPublish(window.latestEvent, window.latestResult);
59
+ }
60
+ }
61
+ this.windows.clear();
62
+ }
63
+ startWindow(poolAddress, event, result, blockNumber) {
64
+ const timer = setTimeout(() => {
65
+ const w = this.windows.get(poolAddress);
66
+ if (w) {
67
+ w.expired = true;
68
+ this.onPublish(w.latestEvent, w.latestResult);
69
+ }
70
+ }, this.debounceMs);
71
+ this.windows.set(poolAddress, {
72
+ blockNumber,
73
+ latestEvent: event,
74
+ latestResult: result,
75
+ timer,
76
+ expired: false,
77
+ });
78
+ }
79
+ }
80
+ exports.SwapDebouncer = SwapDebouncer;
@@ -0,0 +1,6 @@
1
+ import { FormattedTokenPrice } from '@clonegod/ttd-core';
2
+ type PriceSource = 'force_fetch' | 'cache_only' | 'cache_first';
3
+ export declare function get_base_token_price_info(addresses: string[], opts?: {
4
+ source?: PriceSource;
5
+ }): Promise<Map<string, FormattedTokenPrice>>;
6
+ export {};