@clonegod/ttd-sol-common 2.0.67 → 2.0.69

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 (94) hide show
  1. package/dist/appconfig/SolanaQuoteAppConfig.d.ts +9 -0
  2. package/dist/appconfig/SolanaQuoteAppConfig.js +29 -0
  3. package/dist/appconfig/SolanaTradeAppConfig.d.ts +13 -0
  4. package/dist/{config → appconfig}/SolanaTradeAppConfig.js +25 -0
  5. package/dist/appconfig/ensure_core_env.d.ts +1 -0
  6. package/dist/appconfig/ensure_core_env.js +18 -0
  7. package/dist/appconfig/index.d.ts +5 -0
  8. package/dist/appconfig/index.js +21 -0
  9. package/dist/appconfig/sol_dex_env_args.d.ts +5 -0
  10. package/dist/appconfig/sol_dex_env_args.js +29 -0
  11. package/dist/appconfig/sol_env_args.d.ts +17 -0
  12. package/dist/appconfig/sol_env_args.js +37 -0
  13. package/dist/common/get_wallet_token_account.js +13 -16
  14. package/dist/grpc/grpc_provider_registry.d.ts +14 -0
  15. package/dist/grpc/grpc_provider_registry.js +70 -0
  16. package/dist/grpc/index.d.ts +1 -0
  17. package/dist/grpc/index.js +17 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +3 -0
  20. package/dist/quote/abstract_dex_quote.d.ts +68 -0
  21. package/dist/quote/abstract_dex_quote.js +208 -0
  22. package/dist/quote/chain_ops.d.ts +18 -0
  23. package/dist/quote/chain_ops.js +66 -0
  24. package/dist/quote/depth/amm_depth_calculator.d.ts +19 -0
  25. package/dist/quote/depth/amm_depth_calculator.js +55 -0
  26. package/dist/quote/depth/clmm_depth_calculator.d.ts +42 -0
  27. package/dist/quote/depth/clmm_depth_calculator.js +173 -0
  28. package/dist/quote/depth/index.d.ts +37 -0
  29. package/dist/quote/depth/index.js +164 -0
  30. package/dist/quote/index.d.ts +9 -0
  31. package/dist/quote/index.js +9 -0
  32. package/dist/quote/pool_event.d.ts +20 -0
  33. package/dist/quote/pool_event.js +22 -0
  34. package/dist/quote/pool_subscription_registry.d.ts +4 -0
  35. package/dist/quote/pool_subscription_registry.js +62 -0
  36. package/dist/quote/quote_amount.d.ts +4 -0
  37. package/dist/quote/quote_amount.js +24 -0
  38. package/dist/quote/quote_trace.d.ts +16 -0
  39. package/dist/quote/quote_trace.js +40 -0
  40. package/dist/quote/tick/clmm_tick_math.d.ts +5 -0
  41. package/dist/quote/tick/clmm_tick_math.js +61 -0
  42. package/dist/quote/tick/index.d.ts +1 -0
  43. package/dist/{config → quote/tick}/index.js +1 -1
  44. package/dist/quote/verify/index.d.ts +1 -0
  45. package/dist/quote/verify/index.js +17 -0
  46. package/dist/quote/verify/quote_price_verify.d.ts +30 -0
  47. package/dist/quote/verify/quote_price_verify.js +247 -0
  48. package/dist/trade/index.d.ts +0 -1
  49. package/dist/trade/index.js +0 -1
  50. package/dist/trade/tx_builder.d.ts +1 -1
  51. package/dist/trade/tx_result_parse.js +1 -0
  52. package/dist/types/index.d.ts +9 -1
  53. package/dist/types/index.js +2 -0
  54. package/dist/utils/index.d.ts +1 -0
  55. package/dist/utils/index.js +17 -0
  56. package/dist/utils/trade_direction.d.ts +14 -0
  57. package/dist/utils/trade_direction.js +23 -0
  58. package/package.json +4 -4
  59. package/src/appconfig/SolanaQuoteAppConfig.ts +55 -0
  60. package/src/appconfig/SolanaTradeAppConfig.ts +117 -0
  61. package/src/appconfig/ensure_core_env.ts +28 -0
  62. package/src/appconfig/index.ts +5 -0
  63. package/src/appconfig/sol_dex_env_args.ts +52 -0
  64. package/src/appconfig/sol_env_args.ts +79 -0
  65. package/src/common/get_wallet_token_account.ts +27 -33
  66. package/src/grpc/grpc_provider_registry.ts +103 -0
  67. package/src/grpc/index.ts +1 -0
  68. package/src/index.ts +3 -0
  69. package/src/quote/abstract_dex_quote.ts +337 -0
  70. package/src/quote/chain_ops.ts +91 -0
  71. package/src/quote/depth/amm_depth_calculator.ts +143 -0
  72. package/src/quote/depth/clmm_depth_calculator.ts +321 -0
  73. package/src/quote/depth/index.ts +272 -0
  74. package/src/quote/index.ts +9 -0
  75. package/src/quote/pool_event.ts +82 -0
  76. package/src/quote/pool_subscription_registry.ts +81 -0
  77. package/src/quote/quote_amount.ts +37 -0
  78. package/src/quote/quote_trace.ts +56 -0
  79. package/src/quote/tick/clmm_tick_math.ts +77 -0
  80. package/src/quote/tick/index.ts +1 -0
  81. package/src/quote/verify/index.ts +1 -0
  82. package/src/quote/verify/quote_price_verify.ts +508 -0
  83. package/src/trade/index.ts +0 -1
  84. package/src/trade/tx_builder.ts +1 -1
  85. package/src/trade/tx_result_parse.ts +1 -0
  86. package/src/types/index.ts +20 -2
  87. package/src/utils/index.ts +1 -0
  88. package/src/utils/trade_direction.ts +68 -0
  89. package/dist/config/SolanaTradeAppConfig.d.ts +0 -10
  90. package/dist/config/index.d.ts +0 -1
  91. package/dist/trade/SolanaTradeAppConfig.d.ts +0 -8
  92. package/dist/trade/SolanaTradeAppConfig.js +0 -26
  93. package/src/config/SolanaTradeAppConfig.ts +0 -70
  94. package/src/config/index.ts +0 -2
@@ -0,0 +1,9 @@
1
+ import { AppConfig } from '@clonegod/ttd-core';
2
+ import { Connection } from '@solana/web3.js';
3
+ import { SolanaDexEnvArgs } from './sol_dex_env_args';
4
+ export declare class SolanaQuoteAppConfig extends AppConfig {
5
+ env_args: SolanaDexEnvArgs;
6
+ connection: Connection;
7
+ constructor();
8
+ init(): Promise<void>;
9
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaQuoteAppConfig = void 0;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ const constants_1 = require("../common/constants");
7
+ const ensure_core_env_1 = require("./ensure_core_env");
8
+ class SolanaQuoteAppConfig extends ttd_core_1.AppConfig {
9
+ constructor() {
10
+ (0, ensure_core_env_1.ensureCoreEnv)();
11
+ super();
12
+ this.env_args = (0, ttd_core_1.getCoreEnv)();
13
+ }
14
+ async init() {
15
+ await super.init();
16
+ if (!this.arb_event_subscriber) {
17
+ this.arb_event_subscriber = (0, ttd_core_1.getArbEventSubscriber)(this.arb_cache);
18
+ }
19
+ const commitment = process.env.CONNECTION_COMMITMENT_LEVEL || constants_1.COMMITMENT_LEVEL.PROCESSED;
20
+ this.connection = new web3_js_1.Connection(this.env_args.rpc_endpoint, { commitment });
21
+ (0, ttd_core_1.log_info)('SolanaQuoteAppConfig initialized', {
22
+ chain_id: this.env_args.chain_id,
23
+ dex_id: this.env_args.dex_id,
24
+ pair: this.env_args.pair,
25
+ rpc_endpoint: this.env_args.rpc_endpoint,
26
+ });
27
+ }
28
+ }
29
+ exports.SolanaQuoteAppConfig = SolanaQuoteAppConfig;
@@ -0,0 +1,13 @@
1
+ import { AbstractTradeAppConfig } from '@clonegod/ttd-core/dist';
2
+ import { Connection, Keypair } from '@solana/web3.js';
3
+ import { SolanaDexEnvArgs } from './sol_dex_env_args';
4
+ export declare class SolanaTradeAppConfig extends AbstractTradeAppConfig {
5
+ env_args: SolanaDexEnvArgs;
6
+ connection: Connection;
7
+ keypair: Keypair;
8
+ private ws_client;
9
+ constructor();
10
+ init(): Promise<void>;
11
+ init_trade_runtime(): Promise<void>;
12
+ subscribe_wallet_raw_txn_event(): void;
13
+ }
@@ -1,17 +1,42 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.SolanaTradeAppConfig = void 0;
4
7
  const dist_1 = require("@clonegod/ttd-core/dist");
8
+ const web3_js_1 = require("@solana/web3.js");
9
+ const bs58_1 = __importDefault(require("bs58"));
10
+ const constants_1 = require("../common/constants");
11
+ const ensure_core_env_1 = require("./ensure_core_env");
5
12
  class SolanaTradeAppConfig extends dist_1.AbstractTradeAppConfig {
6
13
  constructor() {
14
+ (0, ensure_core_env_1.ensureCoreEnv)();
7
15
  super();
8
16
  this.ws_client = null;
17
+ this.env_args = (0, dist_1.getCoreEnv)();
9
18
  }
10
19
  async init() {
11
20
  await super.init();
21
+ const commitment = process.env.CONNECTION_COMMITMENT_LEVEL || constants_1.COMMITMENT_LEVEL.PROCESSED;
22
+ this.connection = new web3_js_1.Connection(this.env_args.rpc_endpoint, { commitment });
12
23
  this.subscribe_wallet_raw_txn_event();
13
24
  (0, dist_1.log_info)('SolanaTradeAppConfig init ...');
14
25
  }
26
+ async init_trade_runtime() {
27
+ try {
28
+ const { chain_id, dex_id, group_id, pair } = this.env_args;
29
+ this.trade_runtime = await this.arb_cache.create_trade_runtime(chain_id, group_id, dex_id, pair);
30
+ this.trade_runtime.wallet_token_accounts = new Map();
31
+ const secret_key = new Uint8Array(bs58_1.default.decode(this.trade_runtime.wallet.private_key));
32
+ this.keypair = web3_js_1.Keypair.fromSecretKey(secret_key);
33
+ }
34
+ catch (err) {
35
+ (0, dist_1.log_error)(`SolanaTradeAppConfig create_trade_runtime error!`, err);
36
+ await (0, dist_1.sleep)(1000);
37
+ process.exit(0);
38
+ }
39
+ }
15
40
  subscribe_wallet_raw_txn_event() {
16
41
  if (this.is_already_subscribe_wallet_raw_txn) {
17
42
  if (this.ws_client && this.ws_client.isConnected()) {
@@ -0,0 +1 @@
1
+ export declare function ensureCoreEnv(): void;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureCoreEnv = ensureCoreEnv;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ const sol_dex_env_args_1 = require("./sol_dex_env_args");
6
+ let _ensured = false;
7
+ function ensureCoreEnv() {
8
+ if (_ensured)
9
+ return;
10
+ try {
11
+ (0, ttd_core_1.getCoreEnv)();
12
+ _ensured = true;
13
+ }
14
+ catch {
15
+ (0, ttd_core_1.setCoreEnv)(new sol_dex_env_args_1.SolanaDexEnvArgs());
16
+ _ensured = true;
17
+ }
18
+ }
@@ -0,0 +1,5 @@
1
+ export * from './sol_env_args';
2
+ export * from './sol_dex_env_args';
3
+ export * from './SolanaQuoteAppConfig';
4
+ export * from './SolanaTradeAppConfig';
5
+ export * from './ensure_core_env';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./sol_env_args"), exports);
18
+ __exportStar(require("./sol_dex_env_args"), exports);
19
+ __exportStar(require("./SolanaQuoteAppConfig"), exports);
20
+ __exportStar(require("./SolanaTradeAppConfig"), exports);
21
+ __exportStar(require("./ensure_core_env"), exports);
@@ -0,0 +1,5 @@
1
+ import { SolanaEnvArgs } from './sol_env_args';
2
+ export declare class SolanaDexEnvArgs extends SolanaEnvArgs {
3
+ constructor();
4
+ print(moduleName?: string): void;
5
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaDexEnvArgs = void 0;
4
+ const dist_1 = require("@clonegod/ttd-core/dist");
5
+ const sol_env_args_1 = require("./sol_env_args");
6
+ class SolanaDexEnvArgs extends sol_env_args_1.SolanaEnvArgs {
7
+ constructor() {
8
+ super();
9
+ const cfg = this._cfg;
10
+ this.dex_id = (cfg.dex_id || '').toString().toUpperCase();
11
+ this.rpc_endpoint = cfg.rpc_endpoint;
12
+ this.ws_endpoint = cfg.ws_endpoint;
13
+ this.grpc_endpoint = cfg.grpc_endpoint;
14
+ this.grpc_token = cfg.grpc_token;
15
+ this.pair = cfg.pair ?? '';
16
+ this.group_id = cfg.group_id ?? '';
17
+ this.quote_pool_address = cfg.quote_pool_address;
18
+ this.quote_pool_name = cfg.quote_pool_name;
19
+ this.quote_pool_fee_rate = cfg.quote_pool_fee_rate;
20
+ this.quote_amount_usd = cfg.quote_amount_usd;
21
+ this.wallet_dir = cfg.wallet_dir;
22
+ this.encryption_key = cfg.encryption_key ?? '';
23
+ this.namespace = cfg.namespace ?? '';
24
+ }
25
+ print(moduleName) {
26
+ (0, dist_1.printEnvConfig)(moduleName || this.app_name || 'sol-dex', this);
27
+ }
28
+ }
29
+ exports.SolanaDexEnvArgs = SolanaDexEnvArgs;
@@ -0,0 +1,17 @@
1
+ import { EnvArgs } from '@clonegod/ttd-core';
2
+ export declare class SolanaEnvArgs extends EnvArgs {
3
+ gecko_network: string;
4
+ chain_id_num: number;
5
+ native_token_symbol: string;
6
+ native_token_address: string;
7
+ wrapped_native_address: string;
8
+ sync_pool_interval_ms: number;
9
+ fetch_api_wait_ms: number;
10
+ fetch_min_tvl: number;
11
+ fetch_min_vol: number;
12
+ pool_default_tvl: number;
13
+ fetch_max_page_no: number;
14
+ token_batch_size: number;
15
+ fetch_on_startup: boolean;
16
+ constructor();
17
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaEnvArgs = void 0;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ const WSOL_MINT = 'So11111111111111111111111111111111111111112';
6
+ (0, ttd_core_1.registerEnvVars)({
7
+ gecko_network: { env: 'GECKO_NETWORK', type: 'string', default: 'solana', desc: 'GeckoTerminal 网络标识' },
8
+ chain_id_num: { env: 'CHAIN_ID_NUM', type: 'number', default: 101, desc: 'Solana mainnet 链 ID 数字(SPL token list chainId)' },
9
+ native_token_symbol: { env: 'NATIVE_TOKEN_SYMBOL', type: 'string', default: 'SOL', desc: '原生代币符号' },
10
+ native_token_address: { env: 'NATIVE_TOKEN_ADDRESS', type: 'string', default: WSOL_MINT, desc: '原生代币地址(wrapped SOL mint)' },
11
+ wrapped_native_address: { env: 'WRAPPED_NATIVE_ADDRESS', type: 'string', default: WSOL_MINT, desc: 'Wrapped SOL mint' },
12
+ sync_pool_interval_ms: { env: 'SYNC_POOL_INTERVAL_MS', type: 'number', default: 900000, desc: '定时池子全量同步间隔(毫秒,默认 15 分钟)' },
13
+ fetch_api_wait_ms: { env: 'FETCH_API_WAIT_MS', type: 'number', default: 1000, desc: '各 DEX/Gecko API 调用间隔(毫秒)' },
14
+ fetch_min_tvl: { env: 'FETCH_MIN_TVL', type: 'number', default: 50000, desc: '最小 TVL(USD)' },
15
+ fetch_min_vol: { env: 'FETCH_MIN_VOL', type: 'number', default: 50000, desc: '最小 24h 成交量(USD)' },
16
+ pool_default_tvl: { env: 'POOL_DEFAULT_TVL', type: 'number', default: 100000, desc: '默认 TVL(无 API 数据来源的池子兜底,如 pumpswap)' },
17
+ fetch_max_page_no: { env: 'FETCH_MAX_PAGE_NO', type: 'number', default: 10, desc: '最大翻页数' },
18
+ token_batch_size: { env: 'TOKEN_BATCH_SIZE', type: 'number', default: 10, desc: 'token 批量查询大小' },
19
+ fetch_on_startup: { env: 'FETCH_ON_STARTUP', type: 'boolean', default: true, desc: '启动时立即执行一次全量同步' },
20
+ });
21
+ class SolanaEnvArgs extends ttd_core_1.EnvArgs {
22
+ constructor() {
23
+ super();
24
+ const cfg = this._cfg;
25
+ this.app_name = (cfg.app_name || '').toLowerCase();
26
+ this.chain_id = (cfg.chain_id || ttd_core_1.CHAIN_ID.SOLANA).toString().toUpperCase();
27
+ this.server_id = cfg.server_id ?? '';
28
+ this.redis_host = cfg.redis_host;
29
+ this.redis_port = String(cfg.redis_port);
30
+ this.server_ip_list = cfg.server_ip_list ?? '';
31
+ this.ip_exclude_prefix = cfg.ip_exclude_prefix ?? '';
32
+ this.config_center_host = cfg.config_center_host;
33
+ this.token_price_refresh_interval_seconds = cfg.token_price_refresh_interval_seconds;
34
+ this.trade_analyze_host = cfg.trade_analyze_host ?? '';
35
+ }
36
+ }
37
+ exports.SolanaEnvArgs = SolanaEnvArgs;
@@ -16,7 +16,7 @@ const get_token_program_id = (token) => {
16
16
  exports.get_token_program_id = get_token_program_id;
17
17
  const wallet_token_accounts_cache = new Map();
18
18
  function getWalletTokenAccountCacheKey(wallet_pubkey, token_address) {
19
- return `${wallet_pubkey}_${token_address}}`;
19
+ return `${wallet_pubkey}_${token_address}`;
20
20
  }
21
21
  function getCachedAtaAddress(wallet_pubkey, token_address, programId) {
22
22
  const cacheKey = getWalletTokenAccountCacheKey(wallet_pubkey, token_address);
@@ -38,46 +38,43 @@ function get_wallet_token_account(wallet_pubkey, pool_list) {
38
38
  return account_map;
39
39
  }
40
40
  const create_token_account_if_not_exist = async (connection, owner, token_list) => {
41
- token_list.forEach(async (token) => {
41
+ await Promise.all(token_list.map(async (token) => {
42
42
  if (['SOL', 'WSOL', 'USDC', 'USDT'].includes(token.symbol?.toUpperCase())) {
43
43
  return;
44
44
  }
45
45
  try {
46
46
  const mint = new web3_js_1.PublicKey(token.address);
47
- let ata = await (0, spl_token_1.getAssociatedTokenAddress)(new web3_js_1.PublicKey(mint), owner.publicKey);
47
+ const programId = (0, exports.get_token_program_id)(token);
48
+ let ata = await (0, spl_token_1.getAssociatedTokenAddress)(mint, owner.publicKey, false, programId);
48
49
  const account_data = await connection.getAccountInfo(ata);
49
50
  if (account_data) {
50
51
  (0, dist_1.log_info)(`token account already exist, skip!`, {
51
52
  owner: owner.publicKey.toBase58(),
52
53
  token,
53
- ata: ata.toBase58()
54
+ ata: ata.toBase58(),
55
+ program: programId.toBase58(),
54
56
  });
55
57
  }
56
58
  else {
57
59
  (0, dist_1.log_info)(`token account not exist, create new one!`, {
58
60
  owner: owner.publicKey.toBase58(),
59
61
  token,
62
+ program: programId.toBase58(),
60
63
  });
61
- if (token.is_token2022) {
62
- ata = await (0, spl_token_1.createAssociatedTokenAccountIdempotent)(connection, owner, mint, owner.publicKey, {
63
- commitment: constants_1.COMMITMENT_LEVEL.CONFIRMED,
64
- }, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
65
- }
66
- else {
67
- ata = await (0, spl_token_1.createAssociatedTokenAccountIdempotent)(connection, owner, mint, owner.publicKey, {
68
- commitment: constants_1.COMMITMENT_LEVEL.CONFIRMED,
69
- }, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
70
- }
64
+ ata = await (0, spl_token_1.createAssociatedTokenAccountIdempotent)(connection, owner, mint, owner.publicKey, {
65
+ commitment: constants_1.COMMITMENT_LEVEL.CONFIRMED,
66
+ }, programId, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
71
67
  (0, dist_1.log_info)(`Create New Token Account`, {
72
68
  owner: owner.publicKey.toBase58(),
73
69
  token,
74
- ata: ata.toBase58()
70
+ ata: ata.toBase58(),
71
+ program: programId.toBase58(),
75
72
  });
76
73
  }
77
74
  }
78
75
  catch (err) {
79
76
  (0, dist_1.log_error)(`create_token_account_if_not_exist error!`, err);
80
77
  }
81
- });
78
+ }));
82
79
  };
83
80
  exports.create_token_account_if_not_exist = create_token_account_if_not_exist;
@@ -0,0 +1,14 @@
1
+ export interface GrpcProviderConfig {
2
+ id: string;
3
+ endpoint: string;
4
+ token: string;
5
+ }
6
+ export declare function getRpcProvidersKey(chainId: string): string;
7
+ export declare function getRpcConfigChangeChannel(chainId: string): string;
8
+ interface RedisLike {
9
+ hgetall(key: string): Promise<Record<string, string> | null>;
10
+ subscribe(channel: string, listener: (message: string) => void): Promise<void> | void;
11
+ }
12
+ export declare function resolveGrpcProvider(redis: RedisLike, chainId: string, preferId?: string): Promise<GrpcProviderConfig>;
13
+ export declare function watchGrpcProviderChange(redis: RedisLike, chainId: string, active: GrpcProviderConfig, onChange: (next: GrpcProviderConfig | null) => void, preferId?: string): void;
14
+ export {};
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRpcProvidersKey = getRpcProvidersKey;
4
+ exports.getRpcConfigChangeChannel = getRpcConfigChangeChannel;
5
+ exports.resolveGrpcProvider = resolveGrpcProvider;
6
+ exports.watchGrpcProviderChange = watchGrpcProviderChange;
7
+ const dist_1 = require("@clonegod/ttd-core/dist");
8
+ function getRpcProvidersKey(chainId) {
9
+ return `${chainId.toLowerCase()}:rpc:providers`;
10
+ }
11
+ function getRpcConfigChangeChannel(chainId) {
12
+ return `${chainId.toLowerCase()}:rpc:config:change`;
13
+ }
14
+ async function resolveGrpcProvider(redis, chainId, preferId) {
15
+ const key = getRpcProvidersKey(chainId);
16
+ const map = (await redis.hgetall(key)) || {};
17
+ const candidates = [];
18
+ for (const [id, json] of Object.entries(map)) {
19
+ try {
20
+ const p = JSON.parse(json);
21
+ if (p.enabled === true && p.type === 'grpc' && p.grpc_endpoint)
22
+ candidates.push({ ...p, id });
23
+ }
24
+ catch {
25
+ (0, dist_1.log_warn)(`[grpc-registry] ${key} 字段 ${id} JSON 非法,跳过`);
26
+ }
27
+ }
28
+ if (candidates.length === 0) {
29
+ throw new Error(`[grpc-registry] ${key} 无 enabled 的 grpc provider —— 请在 trade-analyze Config/RPC 页新增 type=grpc 的 Provider(Solana: grpc_endpoint 填 Helius laserstream URL,auth_token 填 API key)`);
30
+ }
31
+ if (preferId) {
32
+ const preferred = candidates.find(c => c.id === preferId);
33
+ if (!preferred) {
34
+ throw new Error(`[grpc-registry] 指定的 grpc provider '${preferId}' 不存在或未 enabled(${key} 可用: ${candidates.map(c => c.id).join(',')})`);
35
+ }
36
+ if (!preferred.auth_token)
37
+ (0, dist_1.log_warn)(`[grpc-registry] provider ${preferred.id} auth_token 为空,按无鉴权端点连接`);
38
+ (0, dist_1.log_info)(`[grpc-registry] 使用指定 provider: ${preferred.id}`);
39
+ return { id: preferred.id, endpoint: preferred.grpc_endpoint, token: preferred.auth_token || '' };
40
+ }
41
+ candidates.sort((a, b) => (b.default_for_quote === true ? 1 : 0) - (a.default_for_quote === true ? 1 : 0)
42
+ || a.id.localeCompare(b.id));
43
+ const picked = candidates[0];
44
+ if (candidates.length > 1) {
45
+ (0, dist_1.log_warn)(`[grpc-registry] ${candidates.length} 个 grpc provider 可用,选用 ${picked.id}(default_for_quote 优先)`);
46
+ }
47
+ if (!picked.auth_token) {
48
+ (0, dist_1.log_warn)(`[grpc-registry] provider ${picked.id} auth_token 为空,按无鉴权端点连接`);
49
+ }
50
+ return { id: picked.id, endpoint: picked.grpc_endpoint, token: picked.auth_token || '' };
51
+ }
52
+ function watchGrpcProviderChange(redis, chainId, active, onChange, preferId) {
53
+ const channel = getRpcConfigChangeChannel(chainId);
54
+ redis.subscribe(channel, (message) => {
55
+ void (async () => {
56
+ let next;
57
+ try {
58
+ next = await resolveGrpcProvider(redis, chainId, preferId);
59
+ }
60
+ catch {
61
+ next = null;
62
+ }
63
+ const same = next && next.id === active.id && next.endpoint === active.endpoint && next.token === active.token;
64
+ if (same)
65
+ return;
66
+ (0, dist_1.log_info)(`[grpc-registry] provider 配置变化: ${message} → ${next ? `${next.id} ${next.endpoint}` : '<无可用>'}`);
67
+ onChange(next);
68
+ })();
69
+ });
70
+ }
@@ -0,0 +1 @@
1
+ export * from './grpc_provider_registry';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./grpc_provider_registry"), exports);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
+ export * from './appconfig';
2
+ export * from './grpc';
1
3
  export * from './types';
2
4
  export * from './common';
3
5
  export * from './quote';
4
6
  export * from './trade';
7
+ export * from './utils';
package/dist/index.js CHANGED
@@ -14,7 +14,10 @@ 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("./appconfig"), exports);
18
+ __exportStar(require("./grpc"), exports);
17
19
  __exportStar(require("./types"), exports);
18
20
  __exportStar(require("./common"), exports);
19
21
  __exportStar(require("./quote"), exports);
20
22
  __exportStar(require("./trade"), exports);
23
+ __exportStar(require("./utils"), exports);
@@ -0,0 +1,68 @@
1
+ import { AppConfig, QuoteDepthOutput, QuoteResultType, StandardPoolInfoType } from '@clonegod/ttd-core/dist';
2
+ import { SolanaChainOps } from './chain_ops';
3
+ import { SolPoolEvent } from './pool_event';
4
+ import { QuotePriceVerify, CheckSwapParams } from './verify/quote_price_verify';
5
+ import { QuoteTrace } from './quote_trace';
6
+ export type ConsistencyPolicy = 'snapshot' | 'slot-gate' | 'tx-driven';
7
+ export interface PoolInitSummary {
8
+ protocol_type: string;
9
+ fee_bps: number;
10
+ tick_spacing?: number;
11
+ bin_step?: number;
12
+ token0: string;
13
+ token1: string;
14
+ verifyProgram?: string;
15
+ }
16
+ export interface QuoteBundle {
17
+ poolInfo: StandardPoolInfoType;
18
+ quote_amount_usd: number;
19
+ streamTimestamp: number;
20
+ quoteStartTime: number;
21
+ blockNumber: number;
22
+ txIndex?: number;
23
+ askQuote: QuoteResultType;
24
+ bidQuote: QuoteResultType;
25
+ txid: string;
26
+ source?: string;
27
+ depth?: QuoteDepthOutput;
28
+ priceMap?: Map<string, {
29
+ price: string;
30
+ } | undefined>;
31
+ trace?: QuoteTrace;
32
+ }
33
+ export declare abstract class AbstractDexQuote<C extends SolanaChainOps = SolanaChainOps> {
34
+ protected appConfig: AppConfig;
35
+ protected chain: C;
36
+ protected poolInfoMap: Map<string, StandardPoolInfoType>;
37
+ private poolLastQuoteTimeMap;
38
+ private lastPublished;
39
+ private appliedStateWatermark;
40
+ protected quotePriceVerify: QuotePriceVerify;
41
+ protected latestBlockSlot: number;
42
+ private readonly MIN_QUOTE_INTERVAL_MS;
43
+ constructor(appConfig: AppConfig, chain: C);
44
+ protected abstract readonly dexId: string;
45
+ protected readonly consistencyPolicy: ConsistencyPolicy;
46
+ protected abstract loadPool(poolInfo: StandardPoolInfoType): Promise<PoolInitSummary>;
47
+ protected abstract quoteV1(poolInfo: StandardPoolInfoType, isBuy: boolean): Promise<QuoteResultType>;
48
+ protected abstract quoteV2(poolInfo: StandardPoolInfoType, isBuy: boolean): Promise<QuoteResultType>;
49
+ protected refreshStateFromEvent(_poolInfo: StandardPoolInfoType, _evt: SolPoolEvent): Promise<void>;
50
+ protected calculateDepth(_poolInfo: StandardPoolInfoType, _poolAddress: string, _priceMap: Map<string, {
51
+ price: string;
52
+ } | undefined>): Promise<QuoteDepthOutput | undefined>;
53
+ protected buildSwapVerify(_poolInfo: StandardPoolInfoType, _evt: SolPoolEvent): CheckSwapParams | null;
54
+ protected isStateConsistentForQuote(_poolInfo: StandardPoolInfoType): boolean;
55
+ protected accountSlots: Map<string, number>;
56
+ protected recordAccountSlot(accountAddr: string, slot: number): void;
57
+ init(poolList: StandardPoolInfoType[]): Promise<void>;
58
+ protected beforeLoadPools(): Promise<void>;
59
+ protected getQuoteAmountUsd(poolInfo: StandardPoolInfoType): number;
60
+ private registerEventHandlers;
61
+ private assertValidBlockNumber;
62
+ private isStaleUpdate;
63
+ private shouldApplyState;
64
+ private handleBlockUpdateEvent;
65
+ private calculateQuote;
66
+ private calculateQuoteForPool;
67
+ protected publishQuote(result: QuoteBundle): void;
68
+ }