sol-parser-sdk-python 0.4.4__py3-none-any.whl

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 (54) hide show
  1. sol_parser/__init__.py +400 -0
  2. sol_parser/account_dispatcher.py +209 -0
  3. sol_parser/account_fillers/__init__.py +5 -0
  4. sol_parser/account_fillers/bonk.py +30 -0
  5. sol_parser/account_fillers/meteora.py +51 -0
  6. sol_parser/account_fillers/orca.py +40 -0
  7. sol_parser/account_fillers/pumpfun.py +97 -0
  8. sol_parser/account_fillers/pumpswap.py +93 -0
  9. sol_parser/account_fillers/raydium.py +119 -0
  10. sol_parser/accounts/__init__.py +461 -0
  11. sol_parser/accounts/rpc_wallet.py +64 -0
  12. sol_parser/accounts/utils.py +71 -0
  13. sol_parser/check_migration.py +18 -0
  14. sol_parser/clock.py +10 -0
  15. sol_parser/common/__init__.py +27 -0
  16. sol_parser/dex_parsers.py +2576 -0
  17. sol_parser/entries_decode.py +186 -0
  18. sol_parser/env_config.py +215 -0
  19. sol_parser/event_types.py +1750 -0
  20. sol_parser/geyser_pb2.py +148 -0
  21. sol_parser/geyser_pb2_grpc.py +398 -0
  22. sol_parser/grpc/__init__.py +61 -0
  23. sol_parser/grpc/geyser_connect.py +42 -0
  24. sol_parser/grpc/subscribe_builder.py +133 -0
  25. sol_parser/grpc/transaction_meta.py +183 -0
  26. sol_parser/grpc_client.py +870 -0
  27. sol_parser/grpc_instruction_parser.py +318 -0
  28. sol_parser/grpc_types.py +919 -0
  29. sol_parser/inner_instruction_parser.py +281 -0
  30. sol_parser/instr/__init__.py +15 -0
  31. sol_parser/instr_account_utils.py +58 -0
  32. sol_parser/instructions.py +1026 -0
  33. sol_parser/json_util.py +41 -0
  34. sol_parser/log_instr_dedup.py +284 -0
  35. sol_parser/logs/__init__.py +15 -0
  36. sol_parser/merger.py +233 -0
  37. sol_parser/order_buffer.py +171 -0
  38. sol_parser/parser.py +300 -0
  39. sol_parser/pumpfun_fee_enrich.py +75 -0
  40. sol_parser/rpc_parser.py +655 -0
  41. sol_parser/rust_api_inventory.py +42 -0
  42. sol_parser/rust_event_json.py +50 -0
  43. sol_parser/shredstream_client.py +191 -0
  44. sol_parser/shredstream_pb2.py +40 -0
  45. sol_parser/shredstream_pb2_grpc.py +81 -0
  46. sol_parser/shredstream_pumpfun.py +296 -0
  47. sol_parser/solana_storage_pb2.py +75 -0
  48. sol_parser/solana_storage_pb2_grpc.py +24 -0
  49. sol_parser/u128_parity.py +115 -0
  50. sol_parser/verify_discriminators.py +85 -0
  51. sol_parser_sdk_python-0.4.4.dist-info/METADATA +14 -0
  52. sol_parser_sdk_python-0.4.4.dist-info/RECORD +54 -0
  53. sol_parser_sdk_python-0.4.4.dist-info/WHEEL +4 -0
  54. sol_parser_sdk_python-0.4.4.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,919 @@
1
+ """gRPC 类型定义,对齐 yellowstone-grpc 和 TypeScript SDK"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import Enum, IntEnum
6
+ from typing import Any, Dict, List, Optional, Callable
7
+ from dataclasses import dataclass, field
8
+
9
+
10
+ class OrderMode(str, Enum):
11
+ """gRPC 订阅顺序模式"""
12
+ UNORDERED = "Unordered"
13
+ ORDERED = "Ordered"
14
+ STREAMING_ORDERED = "StreamingOrdered"
15
+ MICRO_BATCH = "MicroBatch"
16
+
17
+
18
+ class CommitmentLevel(IntEnum):
19
+ """Solana 确认级别"""
20
+ PROCESSED = 0
21
+ CONFIRMED = 1
22
+ FINALIZED = 2
23
+
24
+
25
+ class SlotStatus(IntEnum):
26
+ """Slot 状态"""
27
+ PROCESSED = 0
28
+ CONFIRMED = 1
29
+ FINALIZED = 2
30
+ FIRST_SHRED_RECEIVED = 3
31
+ COMPLETED = 4
32
+ CREATED_BANK = 5
33
+ DEAD = 6
34
+
35
+
36
+ class EventType(str, Enum):
37
+ """事件类型"""
38
+ # Block
39
+ BLOCK_META = "BlockMeta"
40
+ # PumpFun
41
+ BONK_TRADE = "BonkTrade"
42
+ BONK_POOL_CREATE = "BonkPoolCreate"
43
+ BONK_MIGRATE_AMM = "BonkMigrateAmm"
44
+ PUMP_FUN_TRADE = "PumpFunTrade"
45
+ PUMP_FUN_BUY = "PumpFunBuy"
46
+ PUMP_FUN_SELL = "PumpFunSell"
47
+ PUMP_FUN_BUY_EXACT_SOL_IN = "PumpFunBuyExactSolIn"
48
+ PUMP_FUN_CREATE = "PumpFunCreate"
49
+ PUMP_FUN_CREATE_V2 = "PumpFunCreateV2"
50
+ PUMP_FUN_COMPLETE = "PumpFunComplete"
51
+ PUMP_FUN_MIGRATE = "PumpFunMigrate"
52
+ PUMP_FEES_CREATE_FEE_SHARING_CONFIG = "PumpFeesCreateFeeSharingConfig"
53
+ PUMP_FEES_INITIALIZE_FEE_CONFIG = "PumpFeesInitializeFeeConfig"
54
+ PUMP_FEES_RESET_FEE_SHARING_CONFIG = "PumpFeesResetFeeSharingConfig"
55
+ PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY = "PumpFeesRevokeFeeSharingAuthority"
56
+ PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY = "PumpFeesTransferFeeSharingAuthority"
57
+ PUMP_FEES_UPDATE_ADMIN = "PumpFeesUpdateAdmin"
58
+ PUMP_FEES_UPDATE_FEE_CONFIG = "PumpFeesUpdateFeeConfig"
59
+ PUMP_FEES_UPDATE_FEE_SHARES = "PumpFeesUpdateFeeShares"
60
+ PUMP_FEES_UPSERT_FEE_TIERS = "PumpFeesUpsertFeeTiers"
61
+ PUMP_FUN_MIGRATE_BONDING_CURVE_CREATOR = "PumpFunMigrateBondingCurveCreator"
62
+ # PumpSwap
63
+ PUMP_SWAP_TRADE = "PumpSwapTrade"
64
+ PUMP_SWAP_BUY = "PumpSwapBuy"
65
+ PUMP_SWAP_SELL = "PumpSwapSell"
66
+ PUMP_SWAP_CREATE_POOL = "PumpSwapCreatePool"
67
+ PUMP_SWAP_LIQUIDITY_ADDED = "PumpSwapLiquidityAdded"
68
+ PUMP_SWAP_LIQUIDITY_REMOVED = "PumpSwapLiquidityRemoved"
69
+ # Raydium CLMM
70
+ RAYDIUM_CLMM_SWAP = "RaydiumClmmSwap"
71
+ RAYDIUM_CLMM_INCREASE_LIQUIDITY = "RaydiumClmmIncreaseLiquidity"
72
+ RAYDIUM_CLMM_DECREASE_LIQUIDITY = "RaydiumClmmDecreaseLiquidity"
73
+ RAYDIUM_CLMM_CREATE_POOL = "RaydiumClmmCreatePool"
74
+ RAYDIUM_CLMM_OPEN_POSITION = "RaydiumClmmOpenPosition"
75
+ RAYDIUM_CLMM_OPEN_POSITION_WITH_TOKEN_EXT_NFT = "RaydiumClmmOpenPositionWithTokenExtNft"
76
+ RAYDIUM_CLMM_CLOSE_POSITION = "RaydiumClmmClosePosition"
77
+ RAYDIUM_CLMM_COLLECT_FEE = "RaydiumClmmCollectFee"
78
+ # Raydium CPMM
79
+ RAYDIUM_CPMM_SWAP = "RaydiumCpmmSwap"
80
+ RAYDIUM_CPMM_DEPOSIT = "RaydiumCpmmDeposit"
81
+ RAYDIUM_CPMM_WITHDRAW = "RaydiumCpmmWithdraw"
82
+ RAYDIUM_CPMM_INITIALIZE = "RaydiumCpmmInitialize"
83
+ # Raydium AMM V4
84
+ RAYDIUM_AMM_V4_SWAP = "RaydiumAmmV4Swap"
85
+ RAYDIUM_AMM_V4_DEPOSIT = "RaydiumAmmV4Deposit"
86
+ RAYDIUM_AMM_V4_WITHDRAW = "RaydiumAmmV4Withdraw"
87
+ RAYDIUM_AMM_V4_WITHDRAW_PNL = "RaydiumAmmV4WithdrawPnl"
88
+ RAYDIUM_AMM_V4_INITIALIZE2 = "RaydiumAmmV4Initialize2"
89
+ # Orca Whirlpool
90
+ ORCA_WHIRLPOOL_SWAP = "OrcaWhirlpoolSwap"
91
+ ORCA_WHIRLPOOL_LIQUIDITY_INCREASED = "OrcaWhirlpoolLiquidityIncreased"
92
+ ORCA_WHIRLPOOL_LIQUIDITY_DECREASED = "OrcaWhirlpoolLiquidityDecreased"
93
+ ORCA_WHIRLPOOL_POOL_INITIALIZED = "OrcaWhirlpoolPoolInitialized"
94
+ # Meteora Pools
95
+ METEORA_POOLS_SWAP = "MeteoraPoolsSwap"
96
+ METEORA_POOLS_ADD_LIQUIDITY = "MeteoraPoolsAddLiquidity"
97
+ METEORA_POOLS_REMOVE_LIQUIDITY = "MeteoraPoolsRemoveLiquidity"
98
+ METEORA_POOLS_BOOTSTRAP_LIQUIDITY = "MeteoraPoolsBootstrapLiquidity"
99
+ METEORA_POOLS_POOL_CREATED = "MeteoraPoolsPoolCreated"
100
+ METEORA_POOLS_SET_POOL_FEES = "MeteoraPoolsSetPoolFees"
101
+ # Meteora DAMM V2
102
+ METEORA_DAMM_V2_SWAP = "MeteoraDammV2Swap"
103
+ METEORA_DAMM_V2_ADD_LIQUIDITY = "MeteoraDammV2AddLiquidity"
104
+ METEORA_DAMM_V2_REMOVE_LIQUIDITY = "MeteoraDammV2RemoveLiquidity"
105
+ METEORA_DAMM_V2_CREATE_POSITION = "MeteoraDammV2CreatePosition"
106
+ METEORA_DAMM_V2_CLOSE_POSITION = "MeteoraDammV2ClosePosition"
107
+ METEORA_DAMM_V2_INITIALIZE_POOL = "MeteoraDammV2InitializePool"
108
+ # Meteora DLMM
109
+ METEORA_DLMM_SWAP = "MeteoraDlmmSwap"
110
+ METEORA_DLMM_ADD_LIQUIDITY = "MeteoraDlmmAddLiquidity"
111
+ METEORA_DLMM_REMOVE_LIQUIDITY = "MeteoraDlmmRemoveLiquidity"
112
+ METEORA_DLMM_INITIALIZE_POOL = "MeteoraDlmmInitializePool"
113
+ METEORA_DLMM_INITIALIZE_BIN_ARRAY = "MeteoraDlmmInitializeBinArray"
114
+ METEORA_DLMM_CREATE_POSITION = "MeteoraDlmmCreatePosition"
115
+ METEORA_DLMM_CLOSE_POSITION = "MeteoraDlmmClosePosition"
116
+ METEORA_DLMM_CLAIM_FEE = "MeteoraDlmmClaimFee"
117
+ # Account types
118
+ TOKEN_ACCOUNT = "TokenAccount"
119
+ TOKEN_INFO = "TokenInfo"
120
+ NONCE_ACCOUNT = "NonceAccount"
121
+ ACCOUNT_PUMP_FUN_GLOBAL = "AccountPumpFunGlobal"
122
+ ACCOUNT_PUMP_SWAP_GLOBAL_CONFIG = "AccountPumpSwapGlobalConfig"
123
+ ACCOUNT_PUMP_SWAP_POOL = "AccountPumpSwapPool"
124
+
125
+
126
+ def all_event_types() -> List[EventType]:
127
+ """返回所有支持的事件类型列表"""
128
+ return [
129
+ # Block
130
+ EventType.BLOCK_META,
131
+ # Bonk
132
+ EventType.BONK_TRADE,
133
+ EventType.BONK_POOL_CREATE,
134
+ EventType.BONK_MIGRATE_AMM,
135
+ # PumpFun
136
+ EventType.PUMP_FUN_TRADE,
137
+ EventType.PUMP_FUN_BUY,
138
+ EventType.PUMP_FUN_SELL,
139
+ EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
140
+ EventType.PUMP_FUN_CREATE,
141
+ EventType.PUMP_FUN_CREATE_V2,
142
+ EventType.PUMP_FUN_COMPLETE,
143
+ EventType.PUMP_FUN_MIGRATE,
144
+ EventType.PUMP_FEES_CREATE_FEE_SHARING_CONFIG,
145
+ EventType.PUMP_FEES_INITIALIZE_FEE_CONFIG,
146
+ EventType.PUMP_FEES_RESET_FEE_SHARING_CONFIG,
147
+ EventType.PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY,
148
+ EventType.PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY,
149
+ EventType.PUMP_FEES_UPDATE_ADMIN,
150
+ EventType.PUMP_FEES_UPDATE_FEE_CONFIG,
151
+ EventType.PUMP_FEES_UPDATE_FEE_SHARES,
152
+ EventType.PUMP_FEES_UPSERT_FEE_TIERS,
153
+ EventType.PUMP_FUN_MIGRATE_BONDING_CURVE_CREATOR,
154
+ # PumpSwap
155
+ EventType.PUMP_SWAP_TRADE,
156
+ EventType.PUMP_SWAP_BUY,
157
+ EventType.PUMP_SWAP_SELL,
158
+ EventType.PUMP_SWAP_CREATE_POOL,
159
+ EventType.PUMP_SWAP_LIQUIDITY_ADDED,
160
+ EventType.PUMP_SWAP_LIQUIDITY_REMOVED,
161
+ # Raydium CLMM
162
+ EventType.RAYDIUM_CLMM_SWAP,
163
+ EventType.RAYDIUM_CLMM_INCREASE_LIQUIDITY,
164
+ EventType.RAYDIUM_CLMM_DECREASE_LIQUIDITY,
165
+ EventType.RAYDIUM_CLMM_CREATE_POOL,
166
+ EventType.RAYDIUM_CLMM_OPEN_POSITION,
167
+ EventType.RAYDIUM_CLMM_OPEN_POSITION_WITH_TOKEN_EXT_NFT,
168
+ EventType.RAYDIUM_CLMM_CLOSE_POSITION,
169
+ EventType.RAYDIUM_CLMM_COLLECT_FEE,
170
+ # Raydium CPMM
171
+ EventType.RAYDIUM_CPMM_SWAP,
172
+ EventType.RAYDIUM_CPMM_DEPOSIT,
173
+ EventType.RAYDIUM_CPMM_WITHDRAW,
174
+ EventType.RAYDIUM_CPMM_INITIALIZE,
175
+ # Raydium AMM V4
176
+ EventType.RAYDIUM_AMM_V4_SWAP,
177
+ EventType.RAYDIUM_AMM_V4_DEPOSIT,
178
+ EventType.RAYDIUM_AMM_V4_WITHDRAW,
179
+ EventType.RAYDIUM_AMM_V4_WITHDRAW_PNL,
180
+ EventType.RAYDIUM_AMM_V4_INITIALIZE2,
181
+ # Orca Whirlpool
182
+ EventType.ORCA_WHIRLPOOL_SWAP,
183
+ EventType.ORCA_WHIRLPOOL_LIQUIDITY_INCREASED,
184
+ EventType.ORCA_WHIRLPOOL_LIQUIDITY_DECREASED,
185
+ EventType.ORCA_WHIRLPOOL_POOL_INITIALIZED,
186
+ # Meteora Pools
187
+ EventType.METEORA_POOLS_SWAP,
188
+ EventType.METEORA_POOLS_ADD_LIQUIDITY,
189
+ EventType.METEORA_POOLS_REMOVE_LIQUIDITY,
190
+ EventType.METEORA_POOLS_BOOTSTRAP_LIQUIDITY,
191
+ EventType.METEORA_POOLS_POOL_CREATED,
192
+ EventType.METEORA_POOLS_SET_POOL_FEES,
193
+ # Meteora DAMM V2
194
+ EventType.METEORA_DAMM_V2_SWAP,
195
+ EventType.METEORA_DAMM_V2_ADD_LIQUIDITY,
196
+ EventType.METEORA_DAMM_V2_REMOVE_LIQUIDITY,
197
+ EventType.METEORA_DAMM_V2_CREATE_POSITION,
198
+ EventType.METEORA_DAMM_V2_CLOSE_POSITION,
199
+ EventType.METEORA_DAMM_V2_INITIALIZE_POOL,
200
+ # Meteora DLMM
201
+ EventType.METEORA_DLMM_SWAP,
202
+ EventType.METEORA_DLMM_ADD_LIQUIDITY,
203
+ EventType.METEORA_DLMM_REMOVE_LIQUIDITY,
204
+ EventType.METEORA_DLMM_INITIALIZE_POOL,
205
+ EventType.METEORA_DLMM_INITIALIZE_BIN_ARRAY,
206
+ EventType.METEORA_DLMM_CREATE_POSITION,
207
+ EventType.METEORA_DLMM_CLOSE_POSITION,
208
+ EventType.METEORA_DLMM_CLAIM_FEE,
209
+ # Account types
210
+ EventType.TOKEN_ACCOUNT,
211
+ EventType.TOKEN_INFO,
212
+ EventType.NONCE_ACCOUNT,
213
+ EventType.ACCOUNT_PUMP_FUN_GLOBAL,
214
+ EventType.ACCOUNT_PUMP_SWAP_GLOBAL_CONFIG,
215
+ EventType.ACCOUNT_PUMP_SWAP_POOL,
216
+ ]
217
+
218
+
219
+ @dataclass
220
+ class EventMetadata:
221
+ """事件元数据"""
222
+
223
+ signature: str = ""
224
+ slot: int = 0
225
+ #: 区块内交易序号(Yellowstone ``SubscribeUpdateTransactionInfo.index`` / RPC ``transactionIndex``)
226
+ tx_index: int = 0
227
+ block_time_us: int = 0
228
+ grpc_recv_us: int = 0
229
+ recent_blockhash: str = ""
230
+ is_created_buy: bool = False
231
+
232
+
233
+ @dataclass
234
+ class ClientConfig:
235
+ """gRPC 客户端配置"""
236
+ enable_metrics: bool = False
237
+ connection_timeout_ms: int = 8000
238
+ request_timeout_ms: int = 15000
239
+ enable_tls: bool = True
240
+ max_retries: int = 3
241
+ retry_delay_ms: int = 1000
242
+ max_concurrent_streams: int = 100
243
+ keep_alive_interval_ms: int = 30000
244
+ keep_alive_timeout_ms: int = 5000
245
+ buffer_size: int = 8192
246
+ order_mode: OrderMode = OrderMode.UNORDERED
247
+ order_timeout_ms: int = 100
248
+ micro_batch_us: int = 100
249
+
250
+ @staticmethod
251
+ def default() -> ClientConfig:
252
+ """返回默认客户端配置"""
253
+ return ClientConfig()
254
+
255
+ @staticmethod
256
+ def low_latency() -> ClientConfig:
257
+ """对齐 Rust ``ClientConfig::low_latency``"""
258
+ return ClientConfig(
259
+ enable_metrics=False,
260
+ connection_timeout_ms=5000,
261
+ request_timeout_ms=10000,
262
+ enable_tls=True,
263
+ max_retries=1,
264
+ retry_delay_ms=100,
265
+ max_concurrent_streams=200,
266
+ keep_alive_interval_ms=10000,
267
+ keep_alive_timeout_ms=2000,
268
+ buffer_size=16384,
269
+ order_mode=OrderMode.UNORDERED,
270
+ order_timeout_ms=50,
271
+ micro_batch_us=50,
272
+ )
273
+
274
+ @staticmethod
275
+ def high_throughput() -> ClientConfig:
276
+ """对齐 Rust ``ClientConfig::high_throughput``"""
277
+ return ClientConfig(
278
+ enable_metrics=True,
279
+ connection_timeout_ms=10000,
280
+ request_timeout_ms=30000,
281
+ enable_tls=True,
282
+ max_retries=5,
283
+ retry_delay_ms=2000,
284
+ max_concurrent_streams=500,
285
+ keep_alive_interval_ms=60000,
286
+ keep_alive_timeout_ms=10000,
287
+ buffer_size=32768,
288
+ order_mode=OrderMode.UNORDERED,
289
+ order_timeout_ms=200,
290
+ micro_batch_us=200,
291
+ )
292
+
293
+
294
+ @dataclass
295
+ class TransactionFilter:
296
+ """交易过滤器"""
297
+ account_include: List[str] = field(default_factory=list)
298
+ account_exclude: List[str] = field(default_factory=list)
299
+ account_required: List[str] = field(default_factory=list)
300
+ vote: Optional[bool] = None
301
+ failed: Optional[bool] = None
302
+ signature: str = ""
303
+
304
+ @staticmethod
305
+ def new() -> TransactionFilter:
306
+ """创建新的交易过滤器"""
307
+ return TransactionFilter()
308
+
309
+ @staticmethod
310
+ def from_program_ids(program_ids: List[str]) -> TransactionFilter:
311
+ """对齐 Rust ``TransactionFilter::from_program_ids``"""
312
+ return TransactionFilter(account_include=list(program_ids))
313
+
314
+ def include_account(self, account: str) -> TransactionFilter:
315
+ self.account_include.append(account)
316
+ return self
317
+
318
+ def exclude_account(self, account: str) -> TransactionFilter:
319
+ self.account_exclude.append(account)
320
+ return self
321
+
322
+ def require_account(self, account: str) -> TransactionFilter:
323
+ self.account_required.append(account)
324
+ return self
325
+
326
+
327
+ class EventTypeFilter:
328
+ """事件类型过滤器接口"""
329
+
330
+ def should_include(self, event_type: EventType) -> bool:
331
+ raise NotImplementedError
332
+
333
+
334
+ class IncludeOnlyFilter(EventTypeFilter):
335
+ """仅包含指定类型的事件过滤器"""
336
+
337
+ def __init__(self, include_only: List[EventType]):
338
+ self.include_only = include_only
339
+
340
+ def should_include(self, event_type: EventType) -> bool:
341
+ if event_type in self.include_only:
342
+ return True
343
+ # PumpFunTrade 包含 PumpFunBuy, PumpFunSell, PumpFunBuyExactSolIn
344
+ if event_type == EventType.PUMP_FUN_TRADE:
345
+ pumpfun_types = [
346
+ EventType.PUMP_FUN_BUY,
347
+ EventType.PUMP_FUN_SELL,
348
+ EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
349
+ ]
350
+ if any(t in self.include_only for t in pumpfun_types):
351
+ return True
352
+ return False
353
+
354
+
355
+ class ExcludeFilter(EventTypeFilter):
356
+ """排除指定类型的事件过滤器"""
357
+
358
+ def __init__(self, exclude_types: List[EventType]):
359
+ self.exclude_types = exclude_types
360
+
361
+ def should_include(self, event_type: EventType) -> bool:
362
+ return event_type not in self.exclude_types
363
+
364
+
365
+ def event_type_filter_include_only(types: List[EventType]) -> EventTypeFilter:
366
+ """创建仅包含指定类型的事件过滤器"""
367
+ return IncludeOnlyFilter(types)
368
+
369
+
370
+ def event_type_filter_exclude(types: List[EventType]) -> EventTypeFilter:
371
+ """创建排除指定类型的事件过滤器"""
372
+ return ExcludeFilter(types)
373
+
374
+
375
+ def _event_type_filter_includes_any(filter: EventTypeFilter, types: List[EventType]) -> bool:
376
+ if isinstance(filter, IncludeOnlyFilter):
377
+ return any(t in types for t in filter.include_only)
378
+ if isinstance(filter, ExcludeFilter):
379
+ return not any(t in types for t in filter.exclude_types)
380
+ return any(filter.should_include(t) for t in types)
381
+
382
+
383
+ PUMP_FEES_EVENT_TYPES = [
384
+ EventType.PUMP_FEES_CREATE_FEE_SHARING_CONFIG,
385
+ EventType.PUMP_FEES_INITIALIZE_FEE_CONFIG,
386
+ EventType.PUMP_FEES_RESET_FEE_SHARING_CONFIG,
387
+ EventType.PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY,
388
+ EventType.PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY,
389
+ EventType.PUMP_FEES_UPDATE_ADMIN,
390
+ EventType.PUMP_FEES_UPDATE_FEE_CONFIG,
391
+ EventType.PUMP_FEES_UPDATE_FEE_SHARES,
392
+ EventType.PUMP_FEES_UPSERT_FEE_TIERS,
393
+ ]
394
+
395
+
396
+ def event_type_filter_includes_pumpfun(filter: EventTypeFilter) -> bool:
397
+ """判断过滤器是否包含 PumpFun 相关类型"""
398
+ pumpfun_types = [
399
+ EventType.PUMP_FUN_TRADE,
400
+ EventType.PUMP_FUN_BUY,
401
+ EventType.PUMP_FUN_SELL,
402
+ EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
403
+ EventType.PUMP_FUN_CREATE,
404
+ EventType.PUMP_FUN_CREATE_V2,
405
+ EventType.PUMP_FUN_COMPLETE,
406
+ EventType.PUMP_FUN_MIGRATE,
407
+ EventType.PUMP_FEES_CREATE_FEE_SHARING_CONFIG,
408
+ EventType.PUMP_FEES_INITIALIZE_FEE_CONFIG,
409
+ EventType.PUMP_FEES_RESET_FEE_SHARING_CONFIG,
410
+ EventType.PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY,
411
+ EventType.PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY,
412
+ EventType.PUMP_FEES_UPDATE_ADMIN,
413
+ EventType.PUMP_FEES_UPDATE_FEE_CONFIG,
414
+ EventType.PUMP_FEES_UPDATE_FEE_SHARES,
415
+ EventType.PUMP_FEES_UPSERT_FEE_TIERS,
416
+ EventType.PUMP_FUN_MIGRATE_BONDING_CURVE_CREATOR,
417
+ EventType.ACCOUNT_PUMP_FUN_GLOBAL,
418
+ ]
419
+ return _event_type_filter_includes_any(filter, pumpfun_types)
420
+
421
+
422
+ def event_type_filter_includes_pump_fees(filter: EventTypeFilter) -> bool:
423
+ """判断过滤器是否包含 Pump Fees 相关类型"""
424
+ return _event_type_filter_includes_any(filter, PUMP_FEES_EVENT_TYPES)
425
+
426
+
427
+ def event_type_filter_includes_pumpswap(filter: EventTypeFilter) -> bool:
428
+ """判断过滤器是否包含 PumpSwap 相关类型"""
429
+ pumpswap_types = [
430
+ EventType.PUMP_SWAP_BUY,
431
+ EventType.PUMP_SWAP_SELL,
432
+ EventType.PUMP_SWAP_CREATE_POOL,
433
+ EventType.PUMP_SWAP_LIQUIDITY_ADDED,
434
+ EventType.PUMP_SWAP_LIQUIDITY_REMOVED,
435
+ ]
436
+ return _event_type_filter_includes_any(filter, pumpswap_types)
437
+
438
+
439
+ def event_type_filter_includes_meteora_damm_v2(filter: EventTypeFilter) -> bool:
440
+ """判断过滤器是否包含 Meteora DAMM V2 相关类型"""
441
+ meteora_types = [
442
+ EventType.METEORA_DAMM_V2_SWAP,
443
+ EventType.METEORA_DAMM_V2_ADD_LIQUIDITY,
444
+ EventType.METEORA_DAMM_V2_CREATE_POSITION,
445
+ EventType.METEORA_DAMM_V2_CLOSE_POSITION,
446
+ EventType.METEORA_DAMM_V2_INITIALIZE_POOL,
447
+ EventType.METEORA_DAMM_V2_REMOVE_LIQUIDITY,
448
+ ]
449
+ return _event_type_filter_includes_any(filter, meteora_types)
450
+
451
+
452
+ def event_type_filter_allows_instruction_parsing(include_only: List[EventType]) -> bool:
453
+ """判断过滤器是否允许指令解析"""
454
+ ix_types = [
455
+ EventType.PUMP_FUN_TRADE,
456
+ EventType.PUMP_FUN_BUY,
457
+ EventType.PUMP_FUN_SELL,
458
+ EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
459
+ EventType.PUMP_FUN_CREATE,
460
+ EventType.PUMP_FUN_CREATE_V2,
461
+ EventType.PUMP_FUN_MIGRATE,
462
+ EventType.PUMP_FUN_MIGRATE_BONDING_CURVE_CREATOR,
463
+ EventType.ACCOUNT_PUMP_FUN_GLOBAL,
464
+ *PUMP_FEES_EVENT_TYPES,
465
+ EventType.PUMP_SWAP_BUY,
466
+ EventType.PUMP_SWAP_SELL,
467
+ EventType.PUMP_SWAP_CREATE_POOL,
468
+ EventType.PUMP_SWAP_LIQUIDITY_ADDED,
469
+ EventType.PUMP_SWAP_LIQUIDITY_REMOVED,
470
+ EventType.METEORA_DAMM_V2_SWAP,
471
+ EventType.METEORA_DAMM_V2_ADD_LIQUIDITY,
472
+ EventType.METEORA_DAMM_V2_CREATE_POSITION,
473
+ EventType.METEORA_DAMM_V2_CLOSE_POSITION,
474
+ EventType.METEORA_DAMM_V2_INITIALIZE_POOL,
475
+ EventType.METEORA_DAMM_V2_REMOVE_LIQUIDITY,
476
+ EventType.RAYDIUM_CLMM_SWAP,
477
+ EventType.RAYDIUM_CLMM_INCREASE_LIQUIDITY,
478
+ EventType.RAYDIUM_CLMM_DECREASE_LIQUIDITY,
479
+ EventType.RAYDIUM_CLMM_CREATE_POOL,
480
+ EventType.RAYDIUM_CLMM_OPEN_POSITION,
481
+ EventType.RAYDIUM_CLMM_OPEN_POSITION_WITH_TOKEN_EXT_NFT,
482
+ EventType.RAYDIUM_CLMM_CLOSE_POSITION,
483
+ EventType.RAYDIUM_CLMM_COLLECT_FEE,
484
+ EventType.RAYDIUM_CPMM_SWAP,
485
+ EventType.RAYDIUM_CPMM_DEPOSIT,
486
+ EventType.RAYDIUM_CPMM_WITHDRAW,
487
+ EventType.RAYDIUM_CPMM_INITIALIZE,
488
+ EventType.RAYDIUM_AMM_V4_SWAP,
489
+ EventType.RAYDIUM_AMM_V4_DEPOSIT,
490
+ EventType.RAYDIUM_AMM_V4_WITHDRAW,
491
+ EventType.RAYDIUM_AMM_V4_WITHDRAW_PNL,
492
+ EventType.RAYDIUM_AMM_V4_INITIALIZE2,
493
+ EventType.ORCA_WHIRLPOOL_SWAP,
494
+ EventType.ORCA_WHIRLPOOL_LIQUIDITY_INCREASED,
495
+ EventType.ORCA_WHIRLPOOL_LIQUIDITY_DECREASED,
496
+ EventType.ORCA_WHIRLPOOL_POOL_INITIALIZED,
497
+ EventType.BONK_TRADE,
498
+ EventType.BONK_POOL_CREATE,
499
+ EventType.BONK_MIGRATE_AMM,
500
+ ]
501
+ return any(t in include_only for t in ix_types)
502
+
503
+
504
+ # Subscribe 请求/响应类型
505
+
506
+ @dataclass
507
+ class SubscribeRequestFilterAccountsFilterMemcmp:
508
+ """Memcmp 过滤器"""
509
+ offset: int
510
+ bytes: Optional[bytes] = None
511
+ base58: str = ""
512
+ base64: str = ""
513
+
514
+
515
+ @dataclass
516
+ class SubscribeRequestFilterAccountsFilterLamports:
517
+ """Lamports 过滤器"""
518
+ eq: Optional[int] = None
519
+ ne: Optional[int] = None
520
+ lt: Optional[int] = None
521
+ gt: Optional[int] = None
522
+
523
+
524
+ @dataclass
525
+ class SubscribeRequestFilterAccountsFilter:
526
+ """账户过滤条件"""
527
+ memcmp: Optional[SubscribeRequestFilterAccountsFilterMemcmp] = None
528
+ datasize: Optional[int] = None
529
+ token_account_state: Optional[bool] = None
530
+ lamports: Optional[SubscribeRequestFilterAccountsFilterLamports] = None
531
+
532
+
533
+ @dataclass
534
+ class AccountFilter:
535
+ """账户订阅过滤器(对齐 Rust ``grpc/types::AccountFilter``,用于 ``subscribe_builder``)"""
536
+
537
+ account: List[str] = field(default_factory=list)
538
+ owner: List[str] = field(default_factory=list)
539
+ filters: List[SubscribeRequestFilterAccountsFilter] = field(default_factory=list)
540
+
541
+ @staticmethod
542
+ def new() -> AccountFilter:
543
+ return AccountFilter()
544
+
545
+ def add_account(self, account: str) -> AccountFilter:
546
+ self.account.append(account)
547
+ return self
548
+
549
+ def add_owner(self, owner: str) -> AccountFilter:
550
+ self.owner.append(owner)
551
+ return self
552
+
553
+ def add_filter(self, f: SubscribeRequestFilterAccountsFilter) -> AccountFilter:
554
+ self.filters.append(f)
555
+ return self
556
+
557
+ @staticmethod
558
+ def from_program_owners(program_ids: List[str]) -> AccountFilter:
559
+ return AccountFilter(owner=list(program_ids))
560
+
561
+
562
+ class Protocol(str, Enum):
563
+ """DEX 协议枚举(对齐 Rust ``grpc/types::Protocol``)"""
564
+
565
+ PUMP_FUN = "PumpFun"
566
+ PUMP_SWAP = "PumpSwap"
567
+ BONK = "Bonk"
568
+ RAYDIUM_CPMM = "RaydiumCpmm"
569
+ RAYDIUM_CLMM = "RaydiumClmm"
570
+ RAYDIUM_AMM_V4 = "RaydiumAmmV4"
571
+ METEORA_DAMM_V2 = "MeteoraDammV2"
572
+
573
+
574
+ # 与 Rust ``grpc/program_ids::PROTOCOL_PROGRAM_IDS`` 一致
575
+ _PROTOCOL_PROGRAM_IDS: Dict[Protocol, List[str]] = {
576
+ Protocol.PUMP_FUN: ["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"],
577
+ Protocol.PUMP_SWAP: ["pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"],
578
+ Protocol.BONK: ["BSwp6bEBihVLdqJRKS58NaebUBSDNjN7MdpFwNaR6gn3"],
579
+ Protocol.RAYDIUM_CPMM: ["CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"],
580
+ Protocol.RAYDIUM_CLMM: ["CAMMCzo5YL8w4VFF8KVHrK22GGUQtcaMpgYqJPXBDvfE"],
581
+ Protocol.RAYDIUM_AMM_V4: ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"],
582
+ Protocol.METEORA_DAMM_V2: ["cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"],
583
+ }
584
+
585
+
586
+ def program_ids_for_protocols(protocols: List[Protocol]) -> List[str]:
587
+ """对齐 Rust ``get_program_ids_for_protocols``。"""
588
+ out: List[str] = []
589
+ for p in protocols:
590
+ out.extend(_PROTOCOL_PROGRAM_IDS.get(p, []))
591
+ return sorted(set(out))
592
+
593
+
594
+ def transaction_filter_for_protocols(protocols: List[Protocol]) -> TransactionFilter:
595
+ """对齐 Rust ``TransactionFilter::for_protocols``。"""
596
+ ids = program_ids_for_protocols(protocols)
597
+ return TransactionFilter(account_include=ids, account_exclude=[], account_required=[])
598
+
599
+
600
+ def account_filter_for_protocols(protocols: List[Protocol]) -> AccountFilter:
601
+ """对齐 Rust ``AccountFilter::for_protocols``(owner = 程序 ID 列表)。"""
602
+ ids = program_ids_for_protocols(protocols)
603
+ return AccountFilter(account=[], owner=ids, filters=[])
604
+
605
+
606
+ @dataclass
607
+ class SlotFilter:
608
+ """Slot 范围过滤(对齐 Rust ``SlotFilter``)"""
609
+
610
+ min_slot: Optional[int] = None
611
+ max_slot: Optional[int] = None
612
+
613
+ @staticmethod
614
+ def new() -> SlotFilter:
615
+ return SlotFilter()
616
+
617
+
618
+ def account_filter_memcmp(offset: int, bs: bytes) -> SubscribeRequestFilterAccountsFilter:
619
+ """对齐 Rust ``account_filter_memcmp``:用于 ``AccountFilter.filters``。"""
620
+ return SubscribeRequestFilterAccountsFilter(
621
+ memcmp=SubscribeRequestFilterAccountsFilterMemcmp(offset=offset, bytes=bs)
622
+ )
623
+
624
+
625
+ @dataclass
626
+ class SubscribeRequestFilterAccounts:
627
+ """账户过滤器"""
628
+ account: List[str] = field(default_factory=list)
629
+ owner: List[str] = field(default_factory=list)
630
+ filters: List[SubscribeRequestFilterAccountsFilter] = field(default_factory=list)
631
+ nonempty_txn_signature: Optional[bool] = None
632
+
633
+
634
+ @dataclass
635
+ class SubscribeRequestFilterSlots:
636
+ """Slot 过滤器"""
637
+ filter_by_commitment: Optional[bool] = None
638
+ interslot_updates: Optional[bool] = None
639
+
640
+
641
+ @dataclass
642
+ class SubscribeRequestFilterTransactions:
643
+ """交易过滤器(proto 定义)"""
644
+ vote: Optional[bool] = None
645
+ failed: Optional[bool] = None
646
+ signature: str = ""
647
+ account_include: List[str] = field(default_factory=list)
648
+ account_exclude: List[str] = field(default_factory=list)
649
+ account_required: List[str] = field(default_factory=list)
650
+
651
+
652
+ @dataclass
653
+ class SubscribeRequestFilterBlocks:
654
+ """区块过滤器"""
655
+ account_include: List[str] = field(default_factory=list)
656
+ include_transactions: Optional[bool] = None
657
+ include_accounts: Optional[bool] = None
658
+ include_entries: Optional[bool] = None
659
+
660
+
661
+ @dataclass
662
+ class SubscribeRequestFilterBlocksMeta:
663
+ """区块元数据过滤器"""
664
+ pass
665
+
666
+
667
+ @dataclass
668
+ class SubscribeRequestFilterEntry:
669
+ """Entry 过滤器"""
670
+ pass
671
+
672
+
673
+ @dataclass
674
+ class SubscribeRequestAccountsDataSlice:
675
+ """账户数据切片"""
676
+ offset: int = 0
677
+ length: int = 0
678
+
679
+
680
+ @dataclass
681
+ class SubscribeRequestPing:
682
+ """Ping 请求"""
683
+ id: int = 0
684
+
685
+
686
+ @dataclass
687
+ class SubscribeRequest:
688
+ """订阅请求"""
689
+ accounts: Dict[str, SubscribeRequestFilterAccounts] = field(default_factory=dict)
690
+ slots: Dict[str, SubscribeRequestFilterSlots] = field(default_factory=dict)
691
+ transactions: Dict[str, SubscribeRequestFilterTransactions] = field(default_factory=dict)
692
+ transactions_status: Dict[str, SubscribeRequestFilterTransactions] = field(default_factory=dict)
693
+ blocks: Dict[str, SubscribeRequestFilterBlocks] = field(default_factory=dict)
694
+ blocks_meta: Dict[str, SubscribeRequestFilterBlocksMeta] = field(default_factory=dict)
695
+ entry: Dict[str, SubscribeRequestFilterEntry] = field(default_factory=dict)
696
+ commitment: Optional[CommitmentLevel] = None
697
+ accounts_data_slice: List[SubscribeRequestAccountsDataSlice] = field(default_factory=list)
698
+ ping: Optional[SubscribeRequestPing] = None
699
+ from_slot: Optional[int] = None
700
+
701
+
702
+ # Subscribe 更新类型
703
+
704
+ @dataclass
705
+ class SubscribeUpdateAccountInfo:
706
+ """账户信息"""
707
+ pubkey: bytes = b""
708
+ lamports: int = 0
709
+ owner: bytes = b""
710
+ executable: bool = False
711
+ rent_epoch: int = 0
712
+ data: bytes = b""
713
+ write_version: int = 0
714
+ txn_signature: Optional[bytes] = None
715
+
716
+
717
+ @dataclass
718
+ class SubscribeUpdateAccount:
719
+ """账户更新"""
720
+ account: Optional[SubscribeUpdateAccountInfo] = None
721
+ slot: int = 0
722
+ is_startup: bool = False
723
+
724
+
725
+ @dataclass
726
+ class SubscribeUpdateSlot:
727
+ """Slot 更新"""
728
+ slot: int = 0
729
+ parent: Optional[int] = None
730
+ status: SlotStatus = SlotStatus.PROCESSED
731
+ dead_error: Optional[str] = None
732
+
733
+
734
+ @dataclass
735
+ class SubscribeUpdateTransactionInfo:
736
+ """交易信息"""
737
+ signature: bytes = b""
738
+ is_vote: bool = False
739
+ transaction_raw: bytes = b""
740
+ meta_raw: bytes = b""
741
+ index: int = 0
742
+ log_messages: List[str] = field(default_factory=list)
743
+
744
+
745
+ @dataclass
746
+ class SubscribeUpdateTransaction:
747
+ """交易更新"""
748
+ transaction: Optional[SubscribeUpdateTransactionInfo] = None
749
+ slot: int = 0
750
+
751
+
752
+ @dataclass
753
+ class SubscribeUpdateTransactionStatus:
754
+ """交易状态更新"""
755
+ slot: int = 0
756
+ signature: bytes = b""
757
+ is_vote: bool = False
758
+ index: int = 0
759
+ err: bytes = b""
760
+
761
+
762
+ @dataclass
763
+ class SubscribeUpdateBlock:
764
+ """区块更新"""
765
+ slot: int = 0
766
+ blockhash: str = ""
767
+ parent_slot: int = 0
768
+ parent_blockhash: str = ""
769
+ executed_transaction_count: int = 0
770
+ transactions: List[SubscribeUpdateTransactionInfo] = field(default_factory=list)
771
+
772
+
773
+ @dataclass
774
+ class SubscribeUpdatePing:
775
+ """Ping 更新"""
776
+ pass
777
+
778
+
779
+ @dataclass
780
+ class SubscribeUpdatePong:
781
+ """Pong 更新"""
782
+ id: int = 0
783
+
784
+
785
+ @dataclass
786
+ class SubscribeUpdateBlockMeta:
787
+ """区块元数据更新"""
788
+ slot: int = 0
789
+ blockhash: str = ""
790
+ parent_slot: int = 0
791
+ parent_blockhash: str = ""
792
+ executed_transaction_count: int = 0
793
+
794
+
795
+ @dataclass
796
+ class SubscribeUpdateEntry:
797
+ """Entry 更新"""
798
+ slot: int = 0
799
+ index: int = 0
800
+ num_hashes: int = 0
801
+ hash: bytes = b""
802
+ executed_transaction_count: int = 0
803
+ starting_transaction_index: int = 0
804
+
805
+
806
+ @dataclass
807
+ class SubscribeUpdate:
808
+ """订阅更新"""
809
+ filters: List[str] = field(default_factory=list)
810
+ account: Optional[SubscribeUpdateAccount] = None
811
+ slot: Optional[SubscribeUpdateSlot] = None
812
+ transaction: Optional[SubscribeUpdateTransaction] = None
813
+ transaction_status: Optional[SubscribeUpdateTransactionStatus] = None
814
+ block: Optional[SubscribeUpdateBlock] = None
815
+ ping: Optional[SubscribeUpdatePing] = None
816
+ pong: Optional[SubscribeUpdatePong] = None
817
+ block_meta: Optional[SubscribeUpdateBlockMeta] = None
818
+ entry: Optional[SubscribeUpdateEntry] = None
819
+ created_at: Optional[int] = None # Unix timestamp in microseconds
820
+
821
+
822
+ # RPC 请求/响应类型
823
+
824
+ @dataclass
825
+ class GetLatestBlockhashRequest:
826
+ """获取最新区块哈希请求"""
827
+ commitment: Optional[CommitmentLevel] = None
828
+
829
+
830
+ @dataclass
831
+ class GetLatestBlockhashResponse:
832
+ """获取最新区块哈希响应"""
833
+ slot: int = 0
834
+ blockhash: str = ""
835
+ last_valid_block_height: int = 0
836
+
837
+
838
+ @dataclass
839
+ class GetBlockHeightRequest:
840
+ """获取区块高度请求"""
841
+ commitment: Optional[CommitmentLevel] = None
842
+
843
+
844
+ @dataclass
845
+ class GetBlockHeightResponse:
846
+ """获取区块高度响应"""
847
+ block_height: int = 0
848
+
849
+
850
+ @dataclass
851
+ class GetSlotRequest:
852
+ """获取 Slot 请求"""
853
+ commitment: Optional[CommitmentLevel] = None
854
+
855
+
856
+ @dataclass
857
+ class GetSlotResponse:
858
+ """获取 Slot 响应"""
859
+ slot: int = 0
860
+
861
+
862
+ @dataclass
863
+ class GetVersionRequest:
864
+ """获取版本请求"""
865
+ pass
866
+
867
+
868
+ @dataclass
869
+ class GetVersionResponse:
870
+ """获取版本响应"""
871
+ version: str = ""
872
+
873
+
874
+ @dataclass
875
+ class IsBlockhashValidRequest:
876
+ """验证区块哈希请求"""
877
+ blockhash: str = ""
878
+ commitment: Optional[CommitmentLevel] = None
879
+
880
+
881
+ @dataclass
882
+ class IsBlockhashValidResponse:
883
+ """验证区块哈希响应"""
884
+ slot: int = 0
885
+ valid: bool = False
886
+
887
+
888
+ @dataclass
889
+ class PingRequest:
890
+ """Ping 请求"""
891
+ count: int = 0
892
+
893
+
894
+ @dataclass
895
+ class PongResponse:
896
+ """Pong 响应"""
897
+ count: int = 0
898
+
899
+
900
+ @dataclass
901
+ class SubscribeReplayInfoRequest:
902
+ """订阅重放信息请求"""
903
+ pass
904
+
905
+
906
+ @dataclass
907
+ class SubscribeReplayInfoResponse:
908
+ """订阅重放信息响应"""
909
+ first_available: Optional[int] = None
910
+
911
+
912
+ # SubscribeCallbacks 类型
913
+
914
+ @dataclass
915
+ class SubscribeCallbacks:
916
+ """订阅回调函数"""
917
+ on_update: Optional[Callable[[SubscribeUpdate], None]] = None
918
+ on_error: Optional[Callable[[Exception], None]] = None
919
+ on_end: Optional[Callable[[], None]] = None