@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,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_base_token_price_info = get_base_token_price_info;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ const DEFI_LLAMA_BASE_CHAIN = 'base';
6
+ const GECKO_BASE_NETWORK = 'base';
7
+ const BATCH_SIZE = 10;
8
+ const BATCH_DELAY_MS = 1000;
9
+ async function fetchFromDefiLlama(addresses) {
10
+ const result = new Map();
11
+ if (addresses.length === 0)
12
+ return result;
13
+ const keys = addresses.map(a => `${DEFI_LLAMA_BASE_CHAIN}:${a}`).join(',');
14
+ const url = `https://coins.llama.fi/prices/current/${keys}`;
15
+ const res = await ttd_core_1.HttpUtil.get(url);
16
+ if (!res?.coins)
17
+ return result;
18
+ for (const addr of addresses) {
19
+ const key = `${DEFI_LLAMA_BASE_CHAIN}:${addr}`;
20
+ const c = res.coins[key];
21
+ if (c?.price != null) {
22
+ result.set(addr, {
23
+ _id: addr.toLowerCase(),
24
+ address: addr,
25
+ symbol: c.symbol,
26
+ decimals: c.decimals,
27
+ price: String(c.price),
28
+ update_time: c.timestamp ? new Date(c.timestamp * 1000).toISOString() : new Date().toISOString(),
29
+ });
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+ async function fetchFromGeckoTerminal(addresses) {
35
+ const result = new Map();
36
+ if (addresses.length === 0)
37
+ return result;
38
+ const url = `https://api.geckoterminal.com/api/v2/networks/${GECKO_BASE_NETWORK}/tokens/multi/${addresses.join(',')}`;
39
+ const res = await ttd_core_1.HttpUtil.get(url);
40
+ if (!res?.data)
41
+ return result;
42
+ for (const item of res.data) {
43
+ const attr = item?.attributes;
44
+ if (!attr?.address || attr.price_usd == null)
45
+ continue;
46
+ result.set(attr.address, {
47
+ _id: attr.address.toLowerCase(),
48
+ address: attr.address,
49
+ symbol: attr.symbol,
50
+ decimals: attr.decimals,
51
+ price: String(attr.price_usd),
52
+ update_time: new Date().toISOString(),
53
+ });
54
+ }
55
+ return result;
56
+ }
57
+ async function get_base_token_price_info(addresses, opts) {
58
+ addresses = Array.from(new Set(addresses.map(a => a.toLowerCase())));
59
+ const result = new Map();
60
+ if (addresses.length === 0)
61
+ return result;
62
+ const channels = [
63
+ { name: 'DefiLlama', fn: fetchFromDefiLlama },
64
+ { name: 'GeckoTerminal', fn: fetchFromGeckoTerminal },
65
+ ];
66
+ let remaining = [...addresses];
67
+ for (const ch of channels) {
68
+ if (remaining.length === 0)
69
+ break;
70
+ const batches = (0, ttd_core_1.chunkArray)(remaining, BATCH_SIZE);
71
+ (0, ttd_core_1.log_debug)(`[get_base_token_price] ${ch.name}: ${remaining.length} addrs in ${batches.length} batches`);
72
+ for (let i = 0; i < batches.length; i++) {
73
+ try {
74
+ const hit = await ch.fn(batches[i]);
75
+ for (const [k, v] of hit)
76
+ result.set(k, v);
77
+ }
78
+ catch (e) {
79
+ (0, ttd_core_1.log_warn)(`[get_base_token_price] ${ch.name} batch ${i + 1} error: ${e?.message ?? e}`);
80
+ }
81
+ if (i < batches.length - 1)
82
+ await (0, ttd_core_1.sleep)(BATCH_DELAY_MS);
83
+ }
84
+ remaining = remaining.filter(a => !result.has(a));
85
+ }
86
+ if (opts?.source === 'force_fetch' && result.size === 0) {
87
+ throw new Error(`Unable to fetch price for any token: ${addresses.join(', ')}`);
88
+ }
89
+ return result;
90
+ }
@@ -1,2 +1,9 @@
1
1
  export * from './event';
2
2
  export * from './pricing';
3
+ export * from './tick';
4
+ export * from './depth';
5
+ export * from './verify';
6
+ export * from './price_feed_handler';
7
+ export * from './quote_amount';
8
+ export * from './preload_token_prices';
9
+ export * from './get_base_token_price';
@@ -16,3 +16,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./event"), exports);
18
18
  __exportStar(require("./pricing"), exports);
19
+ __exportStar(require("./tick"), exports);
20
+ __exportStar(require("./depth"), exports);
21
+ __exportStar(require("./verify"), exports);
22
+ __exportStar(require("./price_feed_handler"), exports);
23
+ __exportStar(require("./quote_amount"), exports);
24
+ __exportStar(require("./preload_token_prices"), exports);
25
+ __exportStar(require("./get_base_token_price"), exports);
@@ -0,0 +1,2 @@
1
+ import { StandardPoolInfoType } from '@clonegod/ttd-core';
2
+ export declare function preloadTokenPrices(pool_list: StandardPoolInfoType[], label?: string): Promise<void>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preloadTokenPrices = preloadTokenPrices;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ const get_base_token_price_1 = require("./get_base_token_price");
6
+ async function preloadTokenPrices(pool_list, label = 'Quote') {
7
+ if (!pool_list || pool_list.length === 0)
8
+ return;
9
+ const addrSet = new Set();
10
+ for (const pool of pool_list) {
11
+ if (pool.tokenA?.address)
12
+ addrSet.add(pool.tokenA.address.toLowerCase());
13
+ if (pool.tokenB?.address)
14
+ addrSet.add(pool.tokenB.address.toLowerCase());
15
+ }
16
+ const addresses = Array.from(addrSet);
17
+ if (addresses.length === 0)
18
+ return;
19
+ try {
20
+ const priceMap = await (0, get_base_token_price_1.get_base_token_price_info)(addresses, { source: 'force_fetch' });
21
+ const got = [];
22
+ const miss = [];
23
+ for (const a of addresses) {
24
+ if (priceMap.get(a)?.price)
25
+ got.push(a);
26
+ else
27
+ miss.push(a);
28
+ }
29
+ (0, ttd_core_1.log_info)(`[${label}] preloadTokenPrices done, total=${addresses.length}, got=${got.length}, miss=${miss.length}`);
30
+ if (miss.length > 0) {
31
+ (0, ttd_core_1.log_warn)(`[${label}] preloadTokenPrices missing: ${miss.join(', ')}`);
32
+ }
33
+ }
34
+ catch (err) {
35
+ (0, ttd_core_1.log_warn)(`[${label}] preloadTokenPrices error (non-fatal): ${err.message}`);
36
+ }
37
+ }
@@ -0,0 +1,15 @@
1
+ import type { QuoteResultType, StandardPoolInfoType } from '@clonegod/ttd-core';
2
+ interface PriceFeedData {
3
+ blockNumber: number;
4
+ token0Symbol?: string;
5
+ token1Symbol?: string;
6
+ askToken0InToken1?: string;
7
+ bidToken0InToken1?: string;
8
+ askToken1InToken0?: string;
9
+ bidToken1InToken0?: string;
10
+ }
11
+ export declare function buildQuoteFromPriceFeed(poolInfo: StandardPoolInfoType, data: PriceFeedData): {
12
+ askQuote: QuoteResultType;
13
+ bidQuote: QuoteResultType;
14
+ } | null;
15
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildQuoteFromPriceFeed = buildQuoteFromPriceFeed;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ function buildQuoteFromPriceFeed(poolInfo, data) {
6
+ const quoteTokenSymbol = (poolInfo.quote_token || '').toUpperCase();
7
+ const tokenA = poolInfo.tokenA;
8
+ const tokenB = poolInfo.tokenB;
9
+ if (!tokenA || !tokenB) {
10
+ (0, ttd_core_1.log_warn)(`[PriceFeed] poolInfo missing tokenA/tokenB: ${poolInfo.pool_address}`);
11
+ return null;
12
+ }
13
+ const normSymbol = (s) => {
14
+ const u = (s || '').toUpperCase();
15
+ return u === 'WETH' ? 'ETH' : u;
16
+ };
17
+ const tokenAIsQuote = normSymbol(tokenA.symbol) === normSymbol(quoteTokenSymbol);
18
+ const tokenBIsQuote = normSymbol(tokenB.symbol) === normSymbol(quoteTokenSymbol);
19
+ if (!tokenAIsQuote && !tokenBIsQuote) {
20
+ (0, ttd_core_1.log_warn)(`[PriceFeed] quote_token "${quoteTokenSymbol}" 在池子配置里找不到 (tokenA=${tokenA.symbol}, tokenB=${tokenB.symbol}, pool=${poolInfo.pool_name})`);
21
+ return null;
22
+ }
23
+ let askPrice;
24
+ let bidPrice;
25
+ let baseToken;
26
+ let quoteToken;
27
+ if (tokenAIsQuote) {
28
+ quoteToken = tokenA;
29
+ baseToken = tokenB;
30
+ askPrice = data.askToken1InToken0;
31
+ bidPrice = data.bidToken1InToken0;
32
+ }
33
+ else {
34
+ quoteToken = tokenB;
35
+ baseToken = tokenA;
36
+ askPrice = data.askToken0InToken1;
37
+ bidPrice = data.bidToken0InToken1;
38
+ }
39
+ if (!askPrice || !bidPrice) {
40
+ (0, ttd_core_1.log_warn)(`[PriceFeed] Missing price for pool=${poolInfo.pool_name}, direction=${tokenAIsQuote ? 'token1InToken0' : 'token0InToken1'}, feedSymbols=[${data.token0Symbol}/${data.token1Symbol}]`);
41
+ return null;
42
+ }
43
+ const makeQuoteResult = (price, input, output) => ({
44
+ inputToken: input,
45
+ outputToken: output,
46
+ slippageBps: 0,
47
+ amountIn: 0,
48
+ amountOut: 0,
49
+ amountOutMin: 0,
50
+ priceImpact: 0,
51
+ price,
52
+ });
53
+ const askQuote = makeQuoteResult(askPrice, quoteToken, baseToken);
54
+ const bidQuote = makeQuoteResult(bidPrice, baseToken, quoteToken);
55
+ return { askQuote, bidQuote };
56
+ }
@@ -0,0 +1,13 @@
1
+ import { AmmPoolState, ClmmPoolState, InfinityPoolState } from '../../types/pool_state';
2
+ export declare const V4_DYNAMIC_FEE_FLAG = 8388608;
3
+ export declare function calculateSwapFeePpm(protocolFeePpm: number, lpFeePpm: number): number;
4
+ export declare function decodeV4ProtocolFee(encodedProtocolFee: number, zeroForOne: boolean): number;
5
+ export declare function v2EffectiveFeeBps(state: AmmPoolState): number;
6
+ export declare function v3EffectiveFeeBps(state: ClmmPoolState): number;
7
+ export declare function v4EffectiveFeeBps(state: InfinityPoolState): number;
8
+ export declare function v4EffectiveFeePpm(state: InfinityPoolState): number;
9
+ export declare function v4DirectionalFeePpm(state: InfinityPoolState, zeroForOne: boolean): number;
10
+ export declare function buildInfinityPoolSdkFee(state: InfinityPoolState): {
11
+ fee: number;
12
+ protocolFee: number;
13
+ };
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.V4_DYNAMIC_FEE_FLAG = void 0;
4
+ exports.calculateSwapFeePpm = calculateSwapFeePpm;
5
+ exports.decodeV4ProtocolFee = decodeV4ProtocolFee;
6
+ exports.v2EffectiveFeeBps = v2EffectiveFeeBps;
7
+ exports.v3EffectiveFeeBps = v3EffectiveFeeBps;
8
+ exports.v4EffectiveFeeBps = v4EffectiveFeeBps;
9
+ exports.v4EffectiveFeePpm = v4EffectiveFeePpm;
10
+ exports.v4DirectionalFeePpm = v4DirectionalFeePpm;
11
+ exports.buildInfinityPoolSdkFee = buildInfinityPoolSdkFee;
12
+ exports.V4_DYNAMIC_FEE_FLAG = 0x800000;
13
+ function calculateSwapFeePpm(protocolFeePpm, lpFeePpm) {
14
+ return protocolFeePpm + lpFeePpm - Math.floor(protocolFeePpm * lpFeePpm / 1000000);
15
+ }
16
+ function decodeV4ProtocolFee(encodedProtocolFee, zeroForOne) {
17
+ if (zeroForOne)
18
+ return encodedProtocolFee & 0xFFF;
19
+ return (encodedProtocolFee >> 12) & 0xFFF;
20
+ }
21
+ function v2EffectiveFeeBps(state) {
22
+ return state.protocolFeeBps;
23
+ }
24
+ function v3EffectiveFeeBps(state) {
25
+ return state.poolFee / 100;
26
+ }
27
+ function v4EffectiveFeeBps(state) {
28
+ return v4EffectiveFeePpm(state) / 100;
29
+ }
30
+ function v4EffectiveFeePpm(state) {
31
+ if (state.lastSwapFee != null) {
32
+ return state.lastSwapFee;
33
+ }
34
+ if (state.poolKeyFee !== exports.V4_DYNAMIC_FEE_FLAG) {
35
+ return state.poolKeyFee;
36
+ }
37
+ const lp = state.slot0LpFee ?? 0;
38
+ const proto = state.slot0ProtocolFee ?? 0;
39
+ const proto0to1 = proto & 0xFFF;
40
+ const proto1to0 = (proto >> 12) & 0xFFF;
41
+ const fee0to1 = calculateSwapFeePpm(proto0to1, lp);
42
+ const fee1to0 = calculateSwapFeePpm(proto1to0, lp);
43
+ return Math.max(fee0to1, fee1to0);
44
+ }
45
+ function v4DirectionalFeePpm(state, zeroForOne) {
46
+ if (state.lastSwapFee != null) {
47
+ return state.lastSwapFee;
48
+ }
49
+ if (state.poolKeyFee !== exports.V4_DYNAMIC_FEE_FLAG) {
50
+ return state.poolKeyFee;
51
+ }
52
+ const lp = state.slot0LpFee ?? 0;
53
+ const proto = state.slot0ProtocolFee ?? 0;
54
+ const directionalProto = decodeV4ProtocolFee(proto, zeroForOne);
55
+ return calculateSwapFeePpm(directionalProto, lp);
56
+ }
57
+ function buildInfinityPoolSdkFee(state) {
58
+ if (state.lastSwapFee != null) {
59
+ return { fee: state.lastSwapFee, protocolFee: 0 };
60
+ }
61
+ if (state.poolKeyFee !== exports.V4_DYNAMIC_FEE_FLAG) {
62
+ return { fee: state.poolKeyFee, protocolFee: 0 };
63
+ }
64
+ return {
65
+ fee: state.slot0LpFee ?? 0,
66
+ protocolFee: state.slot0ProtocolFee ?? 0,
67
+ };
68
+ }
@@ -1,2 +1,4 @@
1
- export * from './token_price_cache';
2
1
  export * from './pool';
2
+ export * from './sdk_token_factory';
3
+ export * from './pool_state_initializer';
4
+ export * from './fee_helpers';
@@ -14,5 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./token_price_cache"), exports);
18
17
  __exportStar(require("./pool"), exports);
18
+ __exportStar(require("./sdk_token_factory"), exports);
19
+ __exportStar(require("./pool_state_initializer"), exports);
20
+ __exportStar(require("./fee_helpers"), exports);
@@ -0,0 +1,12 @@
1
+ import { AmmPoolState, ClmmPoolState, InfinityPoolState } from "../../types/pool_state";
2
+ declare function withInitRetry<T>(fn: () => Promise<T>, label: string): Promise<T>;
3
+ export { withInitRetry };
4
+ export declare class PoolStateInitializer {
5
+ static initAmmPool(provider: any, poolAddress: string, dexId: string, ethersLib: any, opts?: {
6
+ aerodromePoolFactoryAddress?: string;
7
+ }): Promise<AmmPoolState>;
8
+ static initClmmPool(provider: any, poolAddress: string, ethersLib: any, poolAbi?: any[]): Promise<ClmmPoolState>;
9
+ static initInfinityPool(provider: any, poolKey: string, poolManagerAddress: string, poolManagerAbi: any, ethersLib: any, decodeParameters: (params: string) => {
10
+ tickSpacing: number;
11
+ }): Promise<InfinityPoolState>;
12
+ }
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PoolStateInitializer = void 0;
4
+ exports.withInitRetry = withInitRetry;
5
+ const ttd_core_1 = require("@clonegod/ttd-core");
6
+ async function withInitRetry(fn, label) {
7
+ try {
8
+ return await fn();
9
+ }
10
+ catch (err) {
11
+ (0, ttd_core_1.log_warn)(`[PoolStateInitializer] ${label} attempt 1 failed: ${err?.message || err}, retrying once...`);
12
+ try {
13
+ return await fn();
14
+ }
15
+ catch (err2) {
16
+ (0, ttd_core_1.log_error)(`[PoolStateInitializer] ${label} retry also failed`, err2 instanceof Error ? err2 : new Error(String(err2)));
17
+ const addrMatch = label.match(/0x[0-9a-fA-F]+/);
18
+ const identity = (addrMatch?.[0] || label).toLowerCase();
19
+ ttd_core_1.ALERT_TYPES.QUOTE_POOL_INIT_FAILED.report({
20
+ severity: 'critical',
21
+ identity,
22
+ scope: { pool_address: addrMatch ? identity : undefined },
23
+ title: `Pool init failed: ${label} — ${err2?.message || err2}`,
24
+ detail: { label, error: String(err2?.message || err2), stack: err2?.stack },
25
+ });
26
+ throw err2;
27
+ }
28
+ }
29
+ }
30
+ const V2_PAIR_ABI = [
31
+ 'function token0() view returns (address)',
32
+ 'function token1() view returns (address)',
33
+ 'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)',
34
+ ];
35
+ const AERODROME_AMM_EXTRA_ABI = [
36
+ 'function stable() view returns (bool)',
37
+ 'function decimals0() view returns (uint256)',
38
+ 'function decimals1() view returns (uint256)',
39
+ ];
40
+ const V3_POOL_ABI = [
41
+ 'function token0() view returns (address)',
42
+ 'function token1() view returns (address)',
43
+ 'function fee() view returns (uint24)',
44
+ 'function tickSpacing() view returns (int24)',
45
+ 'function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)',
46
+ 'function liquidity() view returns (uint128)',
47
+ ];
48
+ const AMM_FEE_MAP = {
49
+ 'PANCAKE-AMM': 25,
50
+ 'PANCAKE_AMM': 25,
51
+ 'UNISWAP-AMM': 30,
52
+ 'UNISWAP_AMM': 30,
53
+ 'AERODROME-AMM': 30,
54
+ 'AERODROME_AMM': 30,
55
+ };
56
+ const AERODROME_POOL_FACTORY_ABI = [
57
+ 'function getFee(address pool, bool stable) view returns (uint256)',
58
+ ];
59
+ class PoolStateInitializer {
60
+ static async initAmmPool(provider, poolAddress, dexId, ethersLib, opts) {
61
+ try {
62
+ const isAerodrome = dexId.toUpperCase().startsWith('AERODROME');
63
+ const abi = isAerodrome ? [...V2_PAIR_ABI, ...AERODROME_AMM_EXTRA_ABI] : V2_PAIR_ABI;
64
+ const poolContract = new ethersLib.Contract(poolAddress, abi, provider);
65
+ const basePromises = [
66
+ poolContract.getReserves(),
67
+ poolContract.token0(),
68
+ poolContract.token1(),
69
+ ];
70
+ if (isAerodrome) {
71
+ basePromises.push(poolContract.stable(), poolContract.decimals0(), poolContract.decimals1());
72
+ }
73
+ const results = await Promise.all(basePromises);
74
+ const [reserves, token0, token1] = results;
75
+ let protocolFeeBps = AMM_FEE_MAP[dexId] || AMM_FEE_MAP[dexId.replace('-', '_')] || 30;
76
+ let stable;
77
+ let decimals0;
78
+ let decimals1;
79
+ if (isAerodrome) {
80
+ stable = results[3];
81
+ decimals0 = results[4].toString();
82
+ decimals1 = results[5].toString();
83
+ if (opts?.aerodromePoolFactoryAddress) {
84
+ try {
85
+ const factory = new ethersLib.Contract(opts.aerodromePoolFactoryAddress, AERODROME_POOL_FACTORY_ABI, provider);
86
+ const feeOnChain = await factory.getFee(poolAddress, stable);
87
+ protocolFeeBps = Number(feeOnChain);
88
+ }
89
+ catch (e) {
90
+ (0, ttd_core_1.log_error)(`[PoolStateInitializer] Aerodrome getFee 失败,用默认 ${protocolFeeBps}bps`, e);
91
+ }
92
+ }
93
+ }
94
+ const state = {
95
+ address: poolAddress,
96
+ token0: token0.toLowerCase(),
97
+ token1: token1.toLowerCase(),
98
+ protocolFeeBps,
99
+ reserve0: reserves[0].toString(),
100
+ reserve1: reserves[1].toString(),
101
+ ...(stable !== undefined ? { stable, decimals0, decimals1 } : {}),
102
+ };
103
+ (0, ttd_core_1.log_info)(`[PoolStateInitializer] AMM pool initialized: ${poolAddress}`, {
104
+ token0: state.token0, token1: state.token1, protocolFeeBps: state.protocolFeeBps,
105
+ ...(stable !== undefined ? { stable, decimals0, decimals1 } : {}),
106
+ });
107
+ return state;
108
+ }
109
+ catch (error) {
110
+ (0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init AMM pool ${poolAddress}:`, error);
111
+ throw error;
112
+ }
113
+ }
114
+ static async initClmmPool(provider, poolAddress, ethersLib, poolAbi) {
115
+ try {
116
+ const abi = poolAbi || V3_POOL_ABI;
117
+ const poolContract = new ethersLib.Contract(poolAddress, abi, provider);
118
+ const [token0, token1, fee, tickSpacing, slot0, liquidity] = await Promise.all([
119
+ poolContract.token0(),
120
+ poolContract.token1(),
121
+ poolContract.fee(),
122
+ poolContract.tickSpacing(),
123
+ poolContract.slot0(),
124
+ poolContract.liquidity(),
125
+ ]);
126
+ const [sqrtPriceX96, tick] = slot0;
127
+ const state = {
128
+ address: poolAddress,
129
+ token0: token0.toLowerCase(),
130
+ token1: token1.toLowerCase(),
131
+ poolFee: Number(fee),
132
+ tickSpacing: Number(tickSpacing),
133
+ tick: Number(tick),
134
+ sqrtPriceX96: sqrtPriceX96.toString(),
135
+ liquidity: liquidity.toString(),
136
+ };
137
+ (0, ttd_core_1.log_info)(`[PoolStateInitializer] CLMM pool initialized: ${poolAddress}`, {
138
+ token0: state.token0, token1: state.token1,
139
+ poolFee: state.poolFee,
140
+ tickSpacing: state.tickSpacing, tick: state.tick,
141
+ });
142
+ return state;
143
+ }
144
+ catch (error) {
145
+ (0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init CLMM pool ${poolAddress}:`, error);
146
+ throw error;
147
+ }
148
+ }
149
+ static async initInfinityPool(provider, poolKey, poolManagerAddress, poolManagerAbi, ethersLib, decodeParameters) {
150
+ try {
151
+ const poolManagerContract = new ethersLib.Contract(poolManagerAddress, poolManagerAbi, provider);
152
+ const [slot0Result, liquidity, poolKeyInfo] = await Promise.all([
153
+ poolManagerContract.getSlot0(poolKey),
154
+ poolManagerContract.getLiquidity(poolKey),
155
+ poolManagerContract.poolIdToPoolKey(poolKey),
156
+ ]);
157
+ const [sqrtPriceX96, tick, protocolFee, lpFee] = slot0Result;
158
+ const [currency0, currency1, hooks, poolManager, fee, parameters] = poolKeyInfo;
159
+ const { tickSpacing } = decodeParameters(parameters);
160
+ const state = {
161
+ address: poolKey,
162
+ token0: currency0.toLowerCase ? currency0.toLowerCase() : currency0,
163
+ token1: currency1.toLowerCase ? currency1.toLowerCase() : currency1,
164
+ currency0,
165
+ currency1,
166
+ hooks,
167
+ poolManager,
168
+ parameters,
169
+ tickSpacing,
170
+ poolKeyFee: Number(fee),
171
+ tick: Number(tick),
172
+ sqrtPriceX96: sqrtPriceX96.toString(),
173
+ liquidity: liquidity.toString(),
174
+ slot0LpFee: Number(lpFee),
175
+ slot0ProtocolFee: Number(protocolFee),
176
+ };
177
+ (0, ttd_core_1.log_info)(`[PoolStateInitializer] Infinity pool initialized: ${poolKey}`, {
178
+ currency0: state.currency0, currency1: state.currency1,
179
+ poolKeyFee: state.poolKeyFee,
180
+ slot0LpFee: state.slot0LpFee, slot0ProtocolFee: state.slot0ProtocolFee,
181
+ tickSpacing: state.tickSpacing, tick: state.tick,
182
+ });
183
+ return state;
184
+ }
185
+ catch (error) {
186
+ (0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init Infinity pool ${poolKey}:`, error);
187
+ throw error;
188
+ }
189
+ }
190
+ }
191
+ exports.PoolStateInitializer = PoolStateInitializer;
@@ -0,0 +1,2 @@
1
+ import { StandardPoolInfoType } from "@clonegod/ttd-core";
2
+ export declare function buildSdkTokenMap<T>(chainId: number, poolInfoMap: Map<string, StandardPoolInfoType>, TokenClass: new (chainId: number, address: string, decimals: number, symbol?: string, name?: string) => T): Map<string, T>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSdkTokenMap = buildSdkTokenMap;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ function buildSdkTokenMap(chainId, poolInfoMap, TokenClass) {
6
+ const sdkTokens = new Map();
7
+ for (const poolInfo of poolInfoMap.values()) {
8
+ const tokens = [poolInfo.tokenA, poolInfo.tokenB];
9
+ for (const tokenInfo of tokens) {
10
+ const address = tokenInfo.address;
11
+ if (sdkTokens.has(address))
12
+ continue;
13
+ let symbol = tokenInfo.symbol || address.substring(0, 6);
14
+ let name = tokenInfo.name || `Token ${address.substring(0, 8)}`;
15
+ const token = new TokenClass(chainId, address, tokenInfo.decimals, symbol, name);
16
+ sdkTokens.set(address, token);
17
+ (0, ttd_core_1.log_debug)(`Created SDK Token: ${address}, symbol: ${symbol}, decimals: ${tokenInfo.decimals}`, '');
18
+ }
19
+ }
20
+ return sdkTokens;
21
+ }
@@ -0,0 +1,4 @@
1
+ import { StandardPoolInfoType } from '@clonegod/ttd-core';
2
+ import Decimal from 'decimal.js';
3
+ export declare function getQuoteAmountUsd(poolInfo: StandardPoolInfoType): number;
4
+ export declare function usdToTokenUiAmount(amountInUsd: number, tokenAddress: string): Promise<Decimal>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getQuoteAmountUsd = getQuoteAmountUsd;
7
+ exports.usdToTokenUiAmount = usdToTokenUiAmount;
8
+ const decimal_js_1 = __importDefault(require("decimal.js"));
9
+ const get_base_token_price_1 = require("./get_base_token_price");
10
+ function getQuoteAmountUsd(poolInfo) {
11
+ const envValue = Number(process.env.QUOTE_AMOUNT_USD);
12
+ if (envValue > 0) {
13
+ return envValue;
14
+ }
15
+ return poolInfo.quote_amount_usd;
16
+ }
17
+ async function usdToTokenUiAmount(amountInUsd, tokenAddress) {
18
+ const priceMap = await (0, get_base_token_price_1.get_base_token_price_info)([tokenAddress]);
19
+ const price = priceMap.get(tokenAddress)?.price;
20
+ if (!price || price === '0') {
21
+ throw new Error(`price not available for ${tokenAddress}`);
22
+ }
23
+ return new decimal_js_1.default(amountInUsd).div(new decimal_js_1.default(price));
24
+ }
@@ -0,0 +1,12 @@
1
+ import { ClmmTickCache } from './clmm_tick_cache';
2
+ export declare class CachedTickDataProvider {
3
+ private tickCache;
4
+ private poolAddress;
5
+ private tickSpacing;
6
+ constructor(tickCache: ClmmTickCache, poolAddress: string, tickSpacing: number);
7
+ getTick(tick: number): Promise<{
8
+ liquidityNet: string;
9
+ liquidityGross: string;
10
+ }>;
11
+ nextInitializedTickWithinOneWord(tick: number, lte: boolean, tickSpacing: number): Promise<[number, boolean]>;
12
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CachedTickDataProvider = void 0;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ const MIN_TICK = -887272;
6
+ const MAX_TICK = 887272;
7
+ class CachedTickDataProvider {
8
+ constructor(tickCache, poolAddress, tickSpacing) {
9
+ this.tickCache = tickCache;
10
+ this.poolAddress = poolAddress;
11
+ this.tickSpacing = tickSpacing;
12
+ }
13
+ async getTick(tick) {
14
+ const tickInfo = this.tickCache.getTickInfo(this.poolAddress, tick);
15
+ if (tickInfo) {
16
+ return {
17
+ liquidityNet: tickInfo.liquidityNet.toString(),
18
+ liquidityGross: tickInfo.liquidityGross.toString(),
19
+ };
20
+ }
21
+ (0, ttd_core_1.log_warn)(`[CachedTickDataProvider] tick ${tick} not in cache for pool ${this.poolAddress}`);
22
+ return {
23
+ liquidityNet: '0',
24
+ liquidityGross: '0',
25
+ };
26
+ }
27
+ async nextInitializedTickWithinOneWord(tick, lte, tickSpacing) {
28
+ const zeroForOne = !lte;
29
+ const result = this.tickCache.getNextInitializedTickWithinOneWord(this.poolAddress, tick, tickSpacing, zeroForOne);
30
+ if (result)
31
+ return result;
32
+ const compressed = Math.floor(tick / tickSpacing);
33
+ if (lte) {
34
+ const wordPos = compressed >> 8;
35
+ const minimum = (wordPos << 8) * tickSpacing;
36
+ return [Math.max(minimum, MIN_TICK), false];
37
+ }
38
+ else {
39
+ const wordPos = (compressed + 1) >> 8;
40
+ const maximum = (((wordPos + 1) << 8) - 1) * tickSpacing;
41
+ return [Math.min(maximum, MAX_TICK), false];
42
+ }
43
+ }
44
+ }
45
+ exports.CachedTickDataProvider = CachedTickDataProvider;