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,1026 @@
1
+ """指令解析器 - 对齐 Rust 实现"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import struct
6
+ from typing import Optional, List
7
+
8
+ from .grpc_types import EventTypeFilter, EventType, EventMetadata, IncludeOnlyFilter, ExcludeFilter
9
+ from .dex_parsers import Z, read_pump_fees_fee_tiers_vec, read_pump_fees_shareholders_vec
10
+ from .event_types import (
11
+ DexEvent,
12
+ PumpFeesCreateFeeSharingConfigEvent,
13
+ PumpFeesFees,
14
+ PumpFeesInitializeFeeConfigEvent,
15
+ PumpFeesResetFeeSharingConfigEvent,
16
+ PumpFeesRevokeFeeSharingAuthorityEvent,
17
+ PumpFeesTransferFeeSharingAuthorityEvent,
18
+ PumpFeesUpdateAdminEvent,
19
+ PumpFeesUpdateFeeConfigEvent,
20
+ PumpFeesUpdateFeeSharesEvent,
21
+ PumpFeesUpsertFeeTiersEvent,
22
+ PumpFunCreateEvent,
23
+ PumpFunCreateV2TokenEvent,
24
+ PumpFunTradeEvent,
25
+ legacy_dict_to_dex_event,
26
+ )
27
+
28
+ # 程序 ID 常量(与 Rust ``instr::program_ids`` 一致,用于 inner / outer 路由)
29
+ PUMPFUN_PROGRAM_ID = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
30
+ PUMPSWAP_PROGRAM_ID = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"
31
+ METEORA_DAMM_V2_PROGRAM_ID = "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"
32
+ RAYDIUM_CLMM_PROGRAM_ID = "CAMMCzo5YL8w4VFF8KVHrK22GGUQtcaMpgYqJPXBDvfE"
33
+ RAYDIUM_CPMM_PROGRAM_ID = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
34
+ RAYDIUM_AMM_V4_PROGRAM_ID = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
35
+ ORCA_WHIRLPOOL_PROGRAM_ID = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"
36
+ METEORA_POOLS_PROGRAM_ID = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"
37
+ METEORA_DLMM_PROGRAM_ID = "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo"
38
+ BONK_LAUNCHPAD_PROGRAM_ID = "DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1"
39
+ PUMPSWAP_FEES_PROGRAM_ID = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
40
+ PUMP_FEES_PROGRAM_ID = PUMPSWAP_FEES_PROGRAM_ID
41
+
42
+
43
+ def _d(*xs: int) -> int:
44
+ return struct.unpack("<Q", bytes(xs))[0]
45
+
46
+
47
+ # Discriminator 常量
48
+ _DISC_PUMPSWAP_BUY = _d(103, 244, 82, 31, 44, 245, 119, 119)
49
+ _DISC_PUMPSWAP_SELL = _d(62, 47, 55, 10, 165, 3, 220, 42)
50
+
51
+ _DISC_DAMM_SWAP = _d(27, 60, 21, 213, 138, 170, 187, 147)
52
+ _DISC_DAMM_SWAP2 = _d(189, 66, 51, 168, 38, 80, 117, 153)
53
+ _DISC_DAMM_ADD_LIQ = _d(175, 242, 8, 157, 30, 247, 185, 169)
54
+ _DISC_DAMM_REM_LIQ = _d(87, 46, 88, 98, 175, 96, 34, 91)
55
+ _DISC_DAMM_CREATE = _d(156, 15, 119, 198, 29, 181, 221, 55)
56
+ _DISC_DAMM_CLOSE = _d(20, 145, 144, 68, 143, 142, 214, 178)
57
+ _DISC_DAMM_INIT = _d(228, 50, 246, 85, 203, 66, 134, 37)
58
+
59
+ _DISC_CLMM_SWAP = _d(248, 198, 158, 145, 225, 117, 135, 200)
60
+ _DISC_CLMM_SWAP_V2 = _d(43, 4, 237, 11, 26, 201, 30, 98)
61
+ _DISC_CLMM_INC_LIQ = _d(133, 29, 89, 223, 69, 238, 176, 10)
62
+ _DISC_CLMM_DEC_LIQ = _d(58, 127, 188, 62, 79, 82, 196, 96)
63
+ _DISC_CLMM_CREATE = _d(233, 146, 209, 142, 207, 104, 64, 188)
64
+ _DISC_CLMM_OPEN_POSITION_V2 = _d(77, 184, 74, 214, 112, 86, 241, 199)
65
+ _DISC_CLMM_OPEN_POSITION_WITH_TOKEN_22_NFT = _d(77, 255, 174, 82, 125, 29, 201, 46)
66
+ _DISC_CLMM_CLOSE_POSITION = _d(123, 134, 81, 0, 49, 68, 98, 98)
67
+
68
+ _DISC_CPMM_SWAP = _d(143, 190, 90, 218, 196, 30, 51, 222)
69
+ _DISC_CPMM_DEP = _d(242, 35, 198, 137, 82, 225, 242, 182)
70
+ _DISC_CPMM_WIT = _d(183, 18, 70, 156, 148, 109, 161, 34)
71
+
72
+ _DISC_ORCA_SWAP = _d(225, 202, 73, 175, 147, 43, 160, 150)
73
+ _DISC_ORCA_INC_LIQ = _d(30, 7, 144, 181, 102, 254, 155, 161)
74
+ _DISC_ORCA_DEC_LIQ = _d(166, 1, 36, 71, 112, 202, 181, 171)
75
+
76
+ _DISC_BONK_TRADE = _d(2, 3, 4, 5, 6, 7, 8, 9)
77
+ _DISC_BONK_POOL_CREATE = _d(1, 2, 3, 4, 5, 6, 7, 8)
78
+
79
+ _DISC_PFEES_CREATE_FEE_SHARING = _d(195, 78, 86, 76, 111, 52, 251, 213)
80
+ _DISC_PFEES_INITIALIZE_FEE_CONFIG = _d(62, 162, 20, 133, 121, 65, 145, 27)
81
+ _DISC_PFEES_RESET_FEE_SHARING = _d(10, 2, 182, 95, 16, 127, 129, 186)
82
+ _DISC_PFEES_REVOKE_FEE_SHARING = _d(18, 233, 158, 39, 185, 207, 58, 104)
83
+ _DISC_PFEES_TRANSFER_FEE_SHARING = _d(202, 10, 75, 200, 164, 34, 210, 96)
84
+ _DISC_PFEES_UPDATE_ADMIN = _d(161, 176, 40, 213, 60, 184, 179, 228)
85
+ _DISC_PFEES_UPDATE_FEE_CONFIG = _d(104, 184, 103, 242, 88, 151, 107, 20)
86
+ _DISC_PFEES_UPDATE_FEE_SHARES = _d(189, 13, 136, 99, 187, 164, 237, 35)
87
+ _DISC_PFEES_UPSERT_FEE_TIERS = _d(227, 23, 150, 12, 77, 86, 94, 4)
88
+
89
+ _DISC_PUMPFUN_BUY_V2 = _d(184, 23, 238, 97, 103, 197, 211, 61)
90
+ _DISC_PUMPFUN_SELL_V2 = _d(93, 246, 130, 60, 231, 233, 64, 178)
91
+ _DISC_PUMPFUN_BUY_EXACT_QUOTE_IN_V2 = _d(194, 171, 28, 70, 104, 77, 91, 47)
92
+
93
+
94
+ def _get_account_safe(accounts: List[str], index: int) -> str:
95
+ """安全获取账户地址"""
96
+ if index < 0 or index >= len(accounts):
97
+ return Z
98
+ return accounts[index]
99
+
100
+
101
+ def _make_meta(
102
+ signature: str,
103
+ slot: int,
104
+ tx_index: int,
105
+ block_time_us: Optional[int],
106
+ grpc_recv_us: int,
107
+ ) -> EventMetadata:
108
+ return EventMetadata(
109
+ signature=signature,
110
+ slot=slot,
111
+ tx_index=tx_index,
112
+ block_time_us=block_time_us or 0,
113
+ grpc_recv_us=grpc_recv_us,
114
+ )
115
+
116
+
117
+ def parse_instruction_unified(
118
+ instruction_data: bytes,
119
+ accounts: List[str],
120
+ signature: str,
121
+ slot: int,
122
+ tx_index: int,
123
+ block_time_us: Optional[int],
124
+ grpc_recv_us: int,
125
+ filter: EventTypeFilter,
126
+ program_id: str,
127
+ ) -> Optional[DexEvent]:
128
+ """统一的指令解析入口
129
+
130
+ 对齐 Rust `parse_instruction_unified`
131
+ """
132
+ if not instruction_data:
133
+ return None
134
+
135
+ if program_id == PUMPFUN_PROGRAM_ID:
136
+ if not _filter_includes_pumpfun(filter):
137
+ return None
138
+ return parse_pumpfun_instruction(
139
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
140
+ )
141
+
142
+ elif program_id == PUMPSWAP_PROGRAM_ID:
143
+ if not _filter_includes_pumpswap(filter):
144
+ return None
145
+ return parse_pumpswap_instruction(
146
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
147
+ )
148
+
149
+ elif program_id == METEORA_DAMM_V2_PROGRAM_ID:
150
+ if not _filter_includes_meteora_damm_v2(filter):
151
+ return None
152
+ return parse_meteora_damm_instruction(
153
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
154
+ )
155
+
156
+ elif program_id == PUMP_FEES_PROGRAM_ID:
157
+ if not _filter_includes_pump_fees(filter):
158
+ return None
159
+ return parse_pump_fees_instruction(
160
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
161
+ )
162
+
163
+ elif program_id == RAYDIUM_CLMM_PROGRAM_ID:
164
+ if not _filter_includes_raydium_clmm(filter):
165
+ return None
166
+ return parse_raydium_clmm_instruction(
167
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
168
+ )
169
+
170
+ elif program_id == RAYDIUM_CPMM_PROGRAM_ID:
171
+ if not _filter_includes_raydium_cpmm(filter):
172
+ return None
173
+ return parse_raydium_cpmm_instruction(
174
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
175
+ )
176
+
177
+ elif program_id == RAYDIUM_AMM_V4_PROGRAM_ID:
178
+ if not _filter_includes_raydium_amm_v4(filter):
179
+ return None
180
+ return parse_raydium_amm_v4_instruction(
181
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
182
+ )
183
+
184
+ elif program_id == ORCA_WHIRLPOOL_PROGRAM_ID:
185
+ if not _filter_includes_orca_whirlpool(filter):
186
+ return None
187
+ return parse_orca_whirlpool_instruction(
188
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
189
+ )
190
+
191
+ elif program_id == BONK_LAUNCHPAD_PROGRAM_ID:
192
+ if not _filter_includes_bonk(filter):
193
+ return None
194
+ return parse_bonk_instruction(
195
+ instruction_data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us
196
+ )
197
+
198
+ return None
199
+
200
+
201
+ def parse_pumpfun_instruction(
202
+ data: bytes,
203
+ accounts: List[str],
204
+ signature: str,
205
+ slot: int,
206
+ tx_index: int,
207
+ block_time_us: Optional[int],
208
+ grpc_recv_us: int,
209
+ ) -> Optional[DexEvent]:
210
+ """解析 PumpFun 指令
211
+
212
+ 注意:PumpFun 的 Buy/Sell 操作通过统一的 TRADE 日志事件捕获,
213
+ 在 dex_parsers.py 的 parse_trade_from_data 中处理。
214
+ 这里只处理 Create 和 CreateV2 指令。
215
+
216
+ Discriminators(8字节小端):
217
+ - CREATE: [24, 30, 200, 40, 5, 28, 7, 119] = 8576854823835016728
218
+ - CREATE_V2: [214, 144, 76, 236, 95, 139, 49, 180] = 12992944682502211062
219
+ """
220
+ if len(data) < 8:
221
+ return None
222
+
223
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
224
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
225
+
226
+ # PumpFun Create: [24, 30, 200, 40, 5, 28, 7, 119]
227
+ if discriminator == 8576854823835016728:
228
+ return _parse_pumpfun_create(data, accounts, meta)
229
+
230
+ # PumpFun CreateV2: [214, 144, 76, 236, 95, 139, 49, 180]
231
+ if discriminator == 12992944682502211062:
232
+ return _parse_pumpfun_create_v2(data, accounts, meta)
233
+
234
+ if discriminator == _DISC_PUMPFUN_BUY_V2:
235
+ return _parse_pumpfun_trade_v2("buy_v2", data[8:], accounts, meta)
236
+
237
+ if discriminator == _DISC_PUMPFUN_BUY_EXACT_QUOTE_IN_V2:
238
+ return _parse_pumpfun_trade_v2("buy_exact_quote_in_v2", data[8:], accounts, meta)
239
+
240
+ if discriminator == _DISC_PUMPFUN_SELL_V2:
241
+ return _parse_pumpfun_trade_v2("sell_v2", data[8:], accounts, meta)
242
+
243
+ return None
244
+
245
+
246
+ def _u64_payload(data: bytes, offset: int) -> int:
247
+ if offset + 8 <= len(data):
248
+ return struct.unpack_from("<Q", data, offset)[0]
249
+ return 0
250
+
251
+
252
+ def _parse_pumpfun_trade_v2(
253
+ ix_name: str,
254
+ payload: bytes,
255
+ accounts: List[str],
256
+ meta: EventMetadata,
257
+ ) -> Optional[DexEvent]:
258
+ min_accounts = 26 if ix_name == "sell_v2" else 27
259
+ if len(accounts) < min_accounts:
260
+ return None
261
+ first = _u64_payload(payload, 0)
262
+ second = _u64_payload(payload, 8)
263
+ if ix_name == "buy_exact_quote_in_v2":
264
+ sol_amount, token_amount = first, second
265
+ else:
266
+ token_amount, sol_amount = first, second
267
+
268
+ return DexEvent(
269
+ type=EventType.PUMP_FUN_TRADE,
270
+ data=PumpFunTradeEvent(
271
+ metadata=meta,
272
+ mint=_get_account_safe(accounts, 1),
273
+ bonding_curve=_get_account_safe(accounts, 10),
274
+ user=_get_account_safe(accounts, 13),
275
+ sol_amount=sol_amount,
276
+ token_amount=token_amount,
277
+ fee_recipient=_get_account_safe(accounts, 6),
278
+ is_buy=ix_name != "sell_v2",
279
+ is_created_buy=False,
280
+ ix_name=ix_name,
281
+ associated_bonding_curve=_get_account_safe(accounts, 11),
282
+ token_program=_get_account_safe(accounts, 3),
283
+ creator_vault=_get_account_safe(accounts, 16),
284
+ ),
285
+ )
286
+
287
+
288
+ def _parse_pumpfun_create(data: bytes, accounts: List[str], meta: EventMetadata) -> Optional[DexEvent]:
289
+ """解析 PumpFun Create 指令"""
290
+ offset = 8 # Skip discriminator
291
+ try:
292
+ name_len = struct.unpack_from("<I", data, offset)[0]
293
+ offset += 4
294
+ name = data[offset:offset + name_len].decode('utf-8')
295
+ offset += name_len
296
+
297
+ symbol_len = struct.unpack_from("<I", data, offset)[0]
298
+ offset += 4
299
+ symbol = data[offset:offset + symbol_len].decode('utf-8')
300
+ offset += symbol_len
301
+
302
+ uri_len = struct.unpack_from("<I", data, offset)[0]
303
+ offset += 4
304
+ uri = data[offset:offset + uri_len].decode('utf-8')
305
+ offset += uri_len
306
+ except Exception:
307
+ return None
308
+
309
+ creator = Z
310
+ if offset + 32 <= len(data):
311
+ import base58
312
+ creator = base58.b58encode(data[offset:offset + 32]).decode('ascii')
313
+
314
+ return DexEvent(
315
+ type=EventType.PUMP_FUN_CREATE,
316
+ data=PumpFunCreateEvent(
317
+ metadata=meta,
318
+ name=name,
319
+ symbol=symbol,
320
+ uri=uri,
321
+ mint=_get_account_safe(accounts, 0),
322
+ bonding_curve=_get_account_safe(accounts, 2),
323
+ user=_get_account_safe(accounts, 7),
324
+ creator=creator,
325
+ token_program=Z,
326
+ timestamp=0,
327
+ virtual_token_reserves=0,
328
+ virtual_sol_reserves=0,
329
+ real_token_reserves=0,
330
+ token_total_supply=0,
331
+ is_mayhem_mode=False,
332
+ is_cashback_enabled=False,
333
+ ),
334
+ )
335
+
336
+
337
+ def _parse_pumpfun_create_v2(data: bytes, accounts: List[str], meta: EventMetadata) -> Optional[DexEvent]:
338
+ """解析 PumpFun CreateV2 指令
339
+
340
+ 对齐 TS pumpfun_ix.ts CREATE_V2 处理
341
+ """
342
+ if len(accounts) < 16:
343
+ return None
344
+
345
+ offset = 8 # Skip discriminator
346
+ try:
347
+ name_len = struct.unpack_from("<I", data, offset)[0]
348
+ offset += 4
349
+ name = data[offset:offset + name_len].decode('utf-8')
350
+ offset += name_len
351
+
352
+ symbol_len = struct.unpack_from("<I", data, offset)[0]
353
+ offset += 4
354
+ symbol = data[offset:offset + symbol_len].decode('utf-8')
355
+ offset += symbol_len
356
+
357
+ uri_len = struct.unpack_from("<I", data, offset)[0]
358
+ offset += 4
359
+ uri = data[offset:offset + uri_len].decode('utf-8')
360
+ offset += uri_len
361
+ except Exception:
362
+ return None
363
+
364
+ creator = Z
365
+ if offset + 32 <= len(data):
366
+ import base58
367
+ creator = base58.b58encode(data[offset:offset + 32]).decode('ascii')
368
+
369
+ return DexEvent(
370
+ type=EventType.PUMP_FUN_CREATE_V2,
371
+ data=PumpFunCreateV2TokenEvent(
372
+ metadata=meta,
373
+ name=name,
374
+ symbol=symbol,
375
+ uri=uri,
376
+ mint=_get_account_safe(accounts, 0),
377
+ mint_authority=_get_account_safe(accounts, 1),
378
+ bonding_curve=_get_account_safe(accounts, 2),
379
+ associated_bonding_curve=_get_account_safe(accounts, 3),
380
+ global_account=_get_account_safe(accounts, 4),
381
+ user=_get_account_safe(accounts, 5),
382
+ system_program=_get_account_safe(accounts, 6),
383
+ token_program=_get_account_safe(accounts, 7),
384
+ associated_token_program=_get_account_safe(accounts, 8),
385
+ mayhem_program_id=_get_account_safe(accounts, 9),
386
+ global_params=_get_account_safe(accounts, 10),
387
+ sol_vault=_get_account_safe(accounts, 11),
388
+ mayhem_state=_get_account_safe(accounts, 12),
389
+ mayhem_token_vault=_get_account_safe(accounts, 13),
390
+ event_authority=_get_account_safe(accounts, 14),
391
+ program=_get_account_safe(accounts, 15),
392
+ creator=creator,
393
+ timestamp=0,
394
+ virtual_token_reserves=0,
395
+ virtual_sol_reserves=0,
396
+ real_token_reserves=0,
397
+ token_total_supply=0,
398
+ is_mayhem_mode=False,
399
+ is_cashback_enabled=False,
400
+ observed_fee_recipient="",
401
+ ),
402
+ )
403
+
404
+
405
+ def parse_pumpswap_instruction(
406
+ data: bytes,
407
+ accounts: List[str],
408
+ signature: str,
409
+ slot: int,
410
+ tx_index: int,
411
+ block_time_us: Optional[int],
412
+ grpc_recv_us: int,
413
+ ) -> Optional[DexEvent]:
414
+ """解析 PumpSwap 指令"""
415
+ if len(data) < 8:
416
+ return None
417
+
418
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
419
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
420
+
421
+ if discriminator == _DISC_PUMPSWAP_BUY:
422
+ return legacy_dict_to_dex_event({"PumpSwapBuy": {"metadata": meta}})
423
+ if discriminator == _DISC_PUMPSWAP_SELL:
424
+ return legacy_dict_to_dex_event({"PumpSwapSell": {"metadata": meta}})
425
+
426
+ return None
427
+
428
+
429
+ def parse_meteora_damm_instruction(
430
+ data: bytes,
431
+ accounts: List[str],
432
+ signature: str,
433
+ slot: int,
434
+ tx_index: int,
435
+ block_time_us: Optional[int],
436
+ grpc_recv_us: int,
437
+ ) -> Optional[DexEvent]:
438
+ """解析 Meteora DAMM V2 指令"""
439
+ if len(data) < 8:
440
+ return None
441
+
442
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
443
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
444
+
445
+ if discriminator in (_DISC_DAMM_SWAP, _DISC_DAMM_SWAP2):
446
+ return legacy_dict_to_dex_event({"MeteoraDammV2Swap": {"metadata": meta}})
447
+ if discriminator == _DISC_DAMM_ADD_LIQ:
448
+ return legacy_dict_to_dex_event({"MeteoraDammV2AddLiquidity": {"metadata": meta}})
449
+ if discriminator == _DISC_DAMM_REM_LIQ:
450
+ return legacy_dict_to_dex_event({"MeteoraDammV2RemoveLiquidity": {"metadata": meta}})
451
+ if discriminator == _DISC_DAMM_CREATE:
452
+ return legacy_dict_to_dex_event({"MeteoraDammV2CreatePosition": {"metadata": meta}})
453
+ if discriminator == _DISC_DAMM_CLOSE:
454
+ return legacy_dict_to_dex_event({"MeteoraDammV2ClosePosition": {"metadata": meta}})
455
+ if discriminator == _DISC_DAMM_INIT:
456
+ return legacy_dict_to_dex_event({"MeteoraDammV2InitializePool": {"metadata": meta}})
457
+
458
+ return None
459
+
460
+
461
+ def _read_pump_fees_fees_at(data: bytes, o: List[int]) -> Optional[PumpFeesFees]:
462
+ if o[0] + 24 > len(data):
463
+ return None
464
+ lp_fee_bps = struct.unpack_from("<Q", data, o[0])[0]
465
+ o[0] += 8
466
+ protocol_fee_bps = struct.unpack_from("<Q", data, o[0])[0]
467
+ o[0] += 8
468
+ creator_fee_bps = struct.unpack_from("<Q", data, o[0])[0]
469
+ o[0] += 8
470
+ return PumpFeesFees(
471
+ lp_fee_bps=lp_fee_bps,
472
+ protocol_fee_bps=protocol_fee_bps,
473
+ creator_fee_bps=creator_fee_bps,
474
+ )
475
+
476
+
477
+ def parse_pump_fees_instruction(
478
+ data: bytes,
479
+ accounts: List[str],
480
+ signature: str,
481
+ slot: int,
482
+ tx_index: int,
483
+ block_time_us: Optional[int],
484
+ grpc_recv_us: int,
485
+ ) -> Optional[DexEvent]:
486
+ """解析 Pump Fees 外层指令(对齐 Rust `pump_fees::parse_instruction`)"""
487
+ if len(data) < 8:
488
+ return None
489
+
490
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
491
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
492
+
493
+ if discriminator == _DISC_PFEES_CREATE_FEE_SHARING:
494
+ admin = _get_account_safe(accounts, 2)
495
+ mint = _get_account_safe(accounts, 4)
496
+ if admin == Z or mint == Z:
497
+ return None
498
+ return DexEvent(
499
+ type=EventType.PUMP_FEES_CREATE_FEE_SHARING_CONFIG,
500
+ data=PumpFeesCreateFeeSharingConfigEvent(
501
+ metadata=meta,
502
+ timestamp=0,
503
+ mint=mint,
504
+ bonding_curve=_get_account_safe(accounts, 7),
505
+ pool=accounts[10] if len(accounts) > 10 else "",
506
+ sharing_config=_get_account_safe(accounts, 5),
507
+ admin=admin,
508
+ initial_shareholders=[],
509
+ status="Active",
510
+ ),
511
+ )
512
+
513
+ if discriminator == _DISC_PFEES_UPDATE_FEE_SHARES:
514
+ if len(accounts) < 8:
515
+ return None
516
+ o = [8]
517
+ shareholders = read_pump_fees_shareholders_vec(data, o)
518
+ if shareholders is None or o[0] != len(data):
519
+ return None
520
+ return DexEvent(
521
+ type=EventType.PUMP_FEES_UPDATE_FEE_SHARES,
522
+ data=PumpFeesUpdateFeeSharesEvent(
523
+ metadata=meta,
524
+ timestamp=0,
525
+ mint=_get_account_safe(accounts, 4),
526
+ sharing_config=_get_account_safe(accounts, 5),
527
+ admin=_get_account_safe(accounts, 2),
528
+ bonding_curve=_get_account_safe(accounts, 6),
529
+ pump_creator_vault=_get_account_safe(accounts, 7),
530
+ new_shareholders=shareholders,
531
+ ),
532
+ )
533
+
534
+ if discriminator == _DISC_PFEES_INITIALIZE_FEE_CONFIG:
535
+ if len(accounts) < 2:
536
+ return None
537
+ return DexEvent(
538
+ type=EventType.PUMP_FEES_INITIALIZE_FEE_CONFIG,
539
+ data=PumpFeesInitializeFeeConfigEvent(
540
+ metadata=meta,
541
+ timestamp=0,
542
+ admin=accounts[0],
543
+ fee_config=accounts[1],
544
+ ),
545
+ )
546
+
547
+ if discriminator == _DISC_PFEES_RESET_FEE_SHARING:
548
+ if len(accounts) < 5:
549
+ return None
550
+ return DexEvent(
551
+ type=EventType.PUMP_FEES_RESET_FEE_SHARING_CONFIG,
552
+ data=PumpFeesResetFeeSharingConfigEvent(
553
+ metadata=meta,
554
+ timestamp=0,
555
+ mint=accounts[3],
556
+ sharing_config=accounts[4],
557
+ old_admin=accounts[0],
558
+ old_shareholders=[],
559
+ new_admin=accounts[2],
560
+ new_shareholders=[],
561
+ ),
562
+ )
563
+
564
+ if discriminator == _DISC_PFEES_REVOKE_FEE_SHARING:
565
+ if len(accounts) < 4:
566
+ return None
567
+ return DexEvent(
568
+ type=EventType.PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY,
569
+ data=PumpFeesRevokeFeeSharingAuthorityEvent(
570
+ metadata=meta,
571
+ timestamp=0,
572
+ mint=accounts[2],
573
+ sharing_config=accounts[3],
574
+ admin=accounts[0],
575
+ ),
576
+ )
577
+
578
+ if discriminator == _DISC_PFEES_TRANSFER_FEE_SHARING:
579
+ if len(accounts) < 5:
580
+ return None
581
+ return DexEvent(
582
+ type=EventType.PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY,
583
+ data=PumpFeesTransferFeeSharingAuthorityEvent(
584
+ metadata=meta,
585
+ timestamp=0,
586
+ mint=accounts[2],
587
+ sharing_config=accounts[3],
588
+ old_admin=accounts[0],
589
+ new_admin=accounts[4],
590
+ ),
591
+ )
592
+
593
+ if discriminator == _DISC_PFEES_UPDATE_ADMIN:
594
+ if len(accounts) < 3:
595
+ return None
596
+ return DexEvent(
597
+ type=EventType.PUMP_FEES_UPDATE_ADMIN,
598
+ data=PumpFeesUpdateAdminEvent(
599
+ metadata=meta,
600
+ timestamp=0,
601
+ old_admin=accounts[0],
602
+ new_admin=accounts[2],
603
+ ),
604
+ )
605
+
606
+ if discriminator == _DISC_PFEES_UPDATE_FEE_CONFIG:
607
+ if len(accounts) < 2:
608
+ return None
609
+ o = [8]
610
+ fee_tiers = read_pump_fees_fee_tiers_vec(data, o)
611
+ if fee_tiers is None:
612
+ return None
613
+ flat_fees = _read_pump_fees_fees_at(data, o)
614
+ if flat_fees is None or o[0] != len(data):
615
+ return None
616
+ return DexEvent(
617
+ type=EventType.PUMP_FEES_UPDATE_FEE_CONFIG,
618
+ data=PumpFeesUpdateFeeConfigEvent(
619
+ metadata=meta,
620
+ timestamp=0,
621
+ admin=accounts[1],
622
+ fee_config=accounts[0],
623
+ fee_tiers=fee_tiers,
624
+ flat_fees=flat_fees,
625
+ ),
626
+ )
627
+
628
+ if discriminator == _DISC_PFEES_UPSERT_FEE_TIERS:
629
+ if len(accounts) < 2:
630
+ return None
631
+ o = [8]
632
+ fee_tiers = read_pump_fees_fee_tiers_vec(data, o)
633
+ if fee_tiers is None or o[0] >= len(data):
634
+ return None
635
+ offset = data[o[0]]
636
+ o[0] += 1
637
+ if o[0] != len(data):
638
+ return None
639
+ return DexEvent(
640
+ type=EventType.PUMP_FEES_UPSERT_FEE_TIERS,
641
+ data=PumpFeesUpsertFeeTiersEvent(
642
+ metadata=meta,
643
+ timestamp=0,
644
+ admin=accounts[1],
645
+ fee_config=accounts[0],
646
+ fee_tiers=fee_tiers,
647
+ offset=offset,
648
+ ),
649
+ )
650
+
651
+ return None
652
+
653
+
654
+ def parse_raydium_clmm_instruction(
655
+ data: bytes,
656
+ accounts: List[str],
657
+ signature: str,
658
+ slot: int,
659
+ tx_index: int,
660
+ block_time_us: Optional[int],
661
+ grpc_recv_us: int,
662
+ ) -> Optional[DexEvent]:
663
+ """解析 Raydium CLMM 指令"""
664
+ if len(data) < 8:
665
+ return None
666
+
667
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
668
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
669
+
670
+ if discriminator in (_DISC_CLMM_SWAP, _DISC_CLMM_SWAP_V2):
671
+ if len(data) < 8 + 8 + 8 + 8 + 1:
672
+ return None
673
+ sqrt_price_x64 = struct.unpack_from("<Q", data, 24)[0]
674
+ is_base_input = data[32] == 1
675
+ return legacy_dict_to_dex_event({"RaydiumClmmSwap": {
676
+ "metadata": meta,
677
+ "pool_state": _get_account_safe(accounts, 0),
678
+ "sender": _get_account_safe(accounts, 1),
679
+ "token_account_0": Z, "token_account_1": Z,
680
+ "amount_0": 0, "amount_1": 0, "zero_for_one": is_base_input,
681
+ "sqrt_price_x64": str(sqrt_price_x64), "liquidity": "0",
682
+ "transfer_fee_0": 0, "transfer_fee_1": 0, "tick": 0,
683
+ }})
684
+ if discriminator == _DISC_CLMM_INC_LIQ:
685
+ if len(data) < 8 + 8 + 8 + 8:
686
+ return None
687
+ liquidity, amount0_max, amount1_max = struct.unpack_from("<QQQ", data, 8)
688
+ return legacy_dict_to_dex_event({"RaydiumClmmIncreaseLiquidity": {
689
+ "metadata": meta,
690
+ "pool": _get_account_safe(accounts, 0),
691
+ "position_nft_mint": _get_account_safe(accounts, 1),
692
+ "user": _get_account_safe(accounts, 2),
693
+ "liquidity": str(liquidity), "amount0_max": amount0_max, "amount1_max": amount1_max,
694
+ }})
695
+ if discriminator == _DISC_CLMM_DEC_LIQ:
696
+ if len(data) < 8 + 8 + 8 + 8:
697
+ return None
698
+ liquidity, amount0_min, amount1_min = struct.unpack_from("<QQQ", data, 8)
699
+ return legacy_dict_to_dex_event({"RaydiumClmmDecreaseLiquidity": {
700
+ "metadata": meta,
701
+ "pool": _get_account_safe(accounts, 0),
702
+ "position_nft_mint": _get_account_safe(accounts, 1),
703
+ "user": _get_account_safe(accounts, 2),
704
+ "liquidity": str(liquidity), "amount0_min": amount0_min, "amount1_min": amount1_min,
705
+ }})
706
+ if discriminator == _DISC_CLMM_CREATE:
707
+ if len(data) < 8 + 8 + 8:
708
+ return None
709
+ sqrt_price_x64, open_time = struct.unpack_from("<QQ", data, 8)
710
+ return legacy_dict_to_dex_event({"RaydiumClmmCreatePool": {
711
+ "metadata": meta,
712
+ "pool": _get_account_safe(accounts, 0),
713
+ "creator": _get_account_safe(accounts, 1),
714
+ "token_0_mint": _get_account_safe(accounts, 2),
715
+ "token_1_mint": _get_account_safe(accounts, 3),
716
+ "tick_spacing": 0, "fee_rate": 0, "sqrt_price_x64": str(sqrt_price_x64), "open_time": open_time,
717
+ }})
718
+ if discriminator in (_DISC_CLMM_OPEN_POSITION_V2, _DISC_CLMM_OPEN_POSITION_WITH_TOKEN_22_NFT):
719
+ if len(data) < 8 + 4 + 4 + 4 + 4 + 8 + 8 + 8:
720
+ return None
721
+ tick_lower_index, tick_upper_index = struct.unpack_from("<ii", data, 8)
722
+ liquidity = struct.unpack_from("<Q", data, 24)[0]
723
+ return legacy_dict_to_dex_event({"RaydiumClmmOpenPosition": {
724
+ "metadata": meta,
725
+ "pool": _get_account_safe(accounts, 0),
726
+ "user": _get_account_safe(accounts, 1),
727
+ "position_nft_mint": _get_account_safe(accounts, 2),
728
+ "tick_lower_index": tick_lower_index,
729
+ "tick_upper_index": tick_upper_index,
730
+ "liquidity": str(liquidity),
731
+ }})
732
+ if discriminator == _DISC_CLMM_CLOSE_POSITION:
733
+ return legacy_dict_to_dex_event({"RaydiumClmmClosePosition": {
734
+ "metadata": meta,
735
+ "pool": _get_account_safe(accounts, 0),
736
+ "user": _get_account_safe(accounts, 1),
737
+ "position_nft_mint": _get_account_safe(accounts, 2),
738
+ }})
739
+
740
+ return None
741
+
742
+
743
+ def parse_raydium_cpmm_instruction(
744
+ data: bytes,
745
+ accounts: List[str],
746
+ signature: str,
747
+ slot: int,
748
+ tx_index: int,
749
+ block_time_us: Optional[int],
750
+ grpc_recv_us: int,
751
+ ) -> Optional[DexEvent]:
752
+ """解析 Raydium CPMM 指令"""
753
+ if len(data) < 8:
754
+ return None
755
+
756
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
757
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
758
+
759
+ if discriminator == _DISC_CPMM_SWAP:
760
+ return legacy_dict_to_dex_event({"RaydiumCpmmSwap": {
761
+ "metadata": meta,
762
+ "pool_id": _get_account_safe(accounts, 2),
763
+ "input_amount": 0, "output_amount": 0,
764
+ "input_vault_before": 0, "output_vault_before": 0,
765
+ "input_transfer_fee": 0, "output_transfer_fee": 0,
766
+ "base_input": True,
767
+ }})
768
+ if discriminator == _DISC_CPMM_DEP:
769
+ return legacy_dict_to_dex_event({"RaydiumCpmmDeposit": {
770
+ "metadata": meta,
771
+ "pool": _get_account_safe(accounts, 2),
772
+ "user": _get_account_safe(accounts, 0),
773
+ "lp_token_amount": 0, "token0_amount": 0, "token1_amount": 0,
774
+ }})
775
+ if discriminator == _DISC_CPMM_WIT:
776
+ return legacy_dict_to_dex_event({"RaydiumCpmmWithdraw": {
777
+ "metadata": meta,
778
+ "pool": _get_account_safe(accounts, 2),
779
+ "user": _get_account_safe(accounts, 0),
780
+ "lp_token_amount": 0, "token0_amount": 0, "token1_amount": 0,
781
+ }})
782
+
783
+ return None
784
+
785
+
786
+ def parse_raydium_amm_v4_instruction(
787
+ data: bytes,
788
+ accounts: List[str],
789
+ signature: str,
790
+ slot: int,
791
+ tx_index: int,
792
+ block_time_us: Optional[int],
793
+ grpc_recv_us: int,
794
+ ) -> Optional[DexEvent]:
795
+ """解析 Raydium AMM V4 指令(单字节 discriminator)"""
796
+ if len(data) < 1:
797
+ return None
798
+
799
+ instr_type = data[0]
800
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
801
+
802
+ if instr_type in (9, 11): # SwapBaseIn / SwapBaseOut
803
+ return legacy_dict_to_dex_event({"RaydiumAmmV4Swap": {
804
+ "metadata": meta,
805
+ "amm": _get_account_safe(accounts, 1),
806
+ "user_source_owner": _get_account_safe(accounts, 17),
807
+ "amount_in": 0, "minimum_amount_out": 0,
808
+ "max_amount_in": 0, "amount_out": 0,
809
+ "token_program": Z, "amm_authority": Z, "amm_open_orders": Z,
810
+ "pool_coin_token_account": Z, "pool_pc_token_account": Z,
811
+ "serum_program": Z, "serum_market": Z, "serum_bids": Z,
812
+ "serum_asks": Z, "serum_event_queue": Z,
813
+ "serum_coin_vault_account": Z, "serum_pc_vault_account": Z,
814
+ "serum_vault_signer": Z,
815
+ "user_source_token_account": Z,
816
+ "user_destination_token_account": Z,
817
+ }})
818
+
819
+ return None
820
+
821
+
822
+ def parse_orca_whirlpool_instruction(
823
+ data: bytes,
824
+ accounts: List[str],
825
+ signature: str,
826
+ slot: int,
827
+ tx_index: int,
828
+ block_time_us: Optional[int],
829
+ grpc_recv_us: int,
830
+ ) -> Optional[DexEvent]:
831
+ """解析 Orca Whirlpool 指令"""
832
+ if len(data) < 8:
833
+ return None
834
+
835
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
836
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
837
+
838
+ if discriminator == _DISC_ORCA_SWAP:
839
+ return legacy_dict_to_dex_event({"OrcaWhirlpoolSwap": {
840
+ "metadata": meta,
841
+ "whirlpool": _get_account_safe(accounts, 2),
842
+ "a_to_b": True,
843
+ "pre_sqrt_price": "0", "post_sqrt_price": "0",
844
+ "input_amount": 0, "output_amount": 0,
845
+ "input_transfer_fee": 0, "output_transfer_fee": 0,
846
+ "lp_fee": 0, "protocol_fee": 0,
847
+ }})
848
+ if discriminator == _DISC_ORCA_INC_LIQ:
849
+ return legacy_dict_to_dex_event({"OrcaWhirlpoolLiquidityIncreased": {
850
+ "metadata": meta,
851
+ "whirlpool": _get_account_safe(accounts, 1),
852
+ "position": _get_account_safe(accounts, 3),
853
+ "tick_lower_index": 0, "tick_upper_index": 0,
854
+ "liquidity": "0",
855
+ "token_a_amount": 0, "token_b_amount": 0,
856
+ "token_a_transfer_fee": 0, "token_b_transfer_fee": 0,
857
+ }})
858
+ if discriminator == _DISC_ORCA_DEC_LIQ:
859
+ return legacy_dict_to_dex_event({"OrcaWhirlpoolLiquidityDecreased": {
860
+ "metadata": meta,
861
+ "whirlpool": _get_account_safe(accounts, 1),
862
+ "position": _get_account_safe(accounts, 3),
863
+ "tick_lower_index": 0, "tick_upper_index": 0,
864
+ "liquidity": "0",
865
+ "token_a_amount": 0, "token_b_amount": 0,
866
+ "token_a_transfer_fee": 0, "token_b_transfer_fee": 0,
867
+ }})
868
+
869
+ return None
870
+
871
+
872
+ def parse_bonk_instruction(
873
+ data: bytes,
874
+ accounts: List[str],
875
+ signature: str,
876
+ slot: int,
877
+ tx_index: int,
878
+ block_time_us: Optional[int],
879
+ grpc_recv_us: int,
880
+ ) -> Optional[DexEvent]:
881
+ """解析 Bonk (Raydium Launchpad) 指令"""
882
+ if len(data) < 8:
883
+ return None
884
+
885
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
886
+ meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
887
+
888
+ if discriminator == _DISC_BONK_TRADE:
889
+ return legacy_dict_to_dex_event({"BonkTrade": {
890
+ "metadata": meta,
891
+ "pool_state": _get_account_safe(accounts, 1),
892
+ "user": _get_account_safe(accounts, 0),
893
+ "amount_in": 0, "amount_out": 0,
894
+ "is_buy": True, "trade_direction": "Buy", "exact_in": True,
895
+ }})
896
+ if discriminator == _DISC_BONK_POOL_CREATE:
897
+ return legacy_dict_to_dex_event({"BonkPoolCreate": {
898
+ "metadata": meta,
899
+ "base_mint_param": {"symbol": "BONK", "name": "Bonk Pool", "uri": "https://bonk.com", "decimals": 5},
900
+ "pool_state": _get_account_safe(accounts, 1),
901
+ "creator": _get_account_safe(accounts, 8),
902
+ }})
903
+
904
+ return None
905
+
906
+
907
+ # --- 过滤器辅助函数 ---
908
+
909
+ def _filter_includes_any(filter: Optional[EventTypeFilter], types: List[EventType]) -> bool:
910
+ if filter is None:
911
+ return True
912
+ if isinstance(filter, IncludeOnlyFilter):
913
+ return any(t in types for t in filter.include_only)
914
+ if isinstance(filter, ExcludeFilter):
915
+ return not any(t in types for t in filter.exclude_types)
916
+ return any(filter.should_include(t) for t in types)
917
+
918
+
919
+ def _filter_includes_pumpfun(filter: Optional[EventTypeFilter]) -> bool:
920
+ pumpfun_types = [
921
+ EventType.PUMP_FUN_TRADE, EventType.PUMP_FUN_BUY, EventType.PUMP_FUN_SELL,
922
+ EventType.PUMP_FUN_BUY_EXACT_SOL_IN, EventType.PUMP_FUN_CREATE,
923
+ EventType.PUMP_FUN_CREATE_V2, EventType.PUMP_FUN_COMPLETE, EventType.PUMP_FUN_MIGRATE,
924
+ EventType.PUMP_FEES_CREATE_FEE_SHARING_CONFIG,
925
+ EventType.PUMP_FEES_INITIALIZE_FEE_CONFIG,
926
+ EventType.PUMP_FEES_RESET_FEE_SHARING_CONFIG,
927
+ EventType.PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY,
928
+ EventType.PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY,
929
+ EventType.PUMP_FEES_UPDATE_ADMIN,
930
+ EventType.PUMP_FEES_UPDATE_FEE_CONFIG,
931
+ EventType.PUMP_FEES_UPDATE_FEE_SHARES,
932
+ EventType.PUMP_FEES_UPSERT_FEE_TIERS,
933
+ EventType.PUMP_FUN_MIGRATE_BONDING_CURVE_CREATOR,
934
+ EventType.ACCOUNT_PUMP_FUN_GLOBAL,
935
+ ]
936
+ return _filter_includes_any(filter, pumpfun_types)
937
+
938
+
939
+ def _filter_includes_pump_fees(filter: Optional[EventTypeFilter]) -> bool:
940
+ types = [
941
+ EventType.PUMP_FEES_CREATE_FEE_SHARING_CONFIG,
942
+ EventType.PUMP_FEES_INITIALIZE_FEE_CONFIG,
943
+ EventType.PUMP_FEES_RESET_FEE_SHARING_CONFIG,
944
+ EventType.PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY,
945
+ EventType.PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY,
946
+ EventType.PUMP_FEES_UPDATE_ADMIN,
947
+ EventType.PUMP_FEES_UPDATE_FEE_CONFIG,
948
+ EventType.PUMP_FEES_UPDATE_FEE_SHARES,
949
+ EventType.PUMP_FEES_UPSERT_FEE_TIERS,
950
+ ]
951
+ return _filter_includes_any(filter, types)
952
+
953
+
954
+ def _filter_includes_pumpswap(filter: Optional[EventTypeFilter]) -> bool:
955
+ pumpswap_types = [
956
+ EventType.PUMP_SWAP_BUY, EventType.PUMP_SWAP_SELL,
957
+ EventType.PUMP_SWAP_CREATE_POOL, EventType.PUMP_SWAP_LIQUIDITY_ADDED,
958
+ EventType.PUMP_SWAP_LIQUIDITY_REMOVED,
959
+ ]
960
+ return _filter_includes_any(filter, pumpswap_types)
961
+
962
+
963
+ def _filter_includes_meteora_damm_v2(filter: Optional[EventTypeFilter]) -> bool:
964
+ meteora_types = [
965
+ EventType.METEORA_DAMM_V2_SWAP, EventType.METEORA_DAMM_V2_ADD_LIQUIDITY,
966
+ EventType.METEORA_DAMM_V2_CREATE_POSITION, EventType.METEORA_DAMM_V2_CLOSE_POSITION,
967
+ EventType.METEORA_DAMM_V2_INITIALIZE_POOL, EventType.METEORA_DAMM_V2_REMOVE_LIQUIDITY,
968
+ ]
969
+ return _filter_includes_any(filter, meteora_types)
970
+
971
+
972
+ def _filter_includes_raydium_clmm(filter: Optional[EventTypeFilter]) -> bool:
973
+ types = [
974
+ EventType.RAYDIUM_CLMM_SWAP, EventType.RAYDIUM_CLMM_INCREASE_LIQUIDITY,
975
+ EventType.RAYDIUM_CLMM_DECREASE_LIQUIDITY, EventType.RAYDIUM_CLMM_CREATE_POOL,
976
+ EventType.RAYDIUM_CLMM_OPEN_POSITION, EventType.RAYDIUM_CLMM_OPEN_POSITION_WITH_TOKEN_EXT_NFT,
977
+ EventType.RAYDIUM_CLMM_CLOSE_POSITION,
978
+ EventType.RAYDIUM_CLMM_COLLECT_FEE,
979
+ ]
980
+ return _filter_includes_any(filter, types)
981
+
982
+
983
+ def _filter_includes_raydium_cpmm(filter: Optional[EventTypeFilter]) -> bool:
984
+ types = [
985
+ EventType.RAYDIUM_CPMM_SWAP, EventType.RAYDIUM_CPMM_DEPOSIT,
986
+ EventType.RAYDIUM_CPMM_WITHDRAW, EventType.RAYDIUM_CPMM_INITIALIZE,
987
+ ]
988
+ return _filter_includes_any(filter, types)
989
+
990
+
991
+ def _filter_includes_raydium_amm_v4(filter: Optional[EventTypeFilter]) -> bool:
992
+ types = [
993
+ EventType.RAYDIUM_AMM_V4_SWAP, EventType.RAYDIUM_AMM_V4_DEPOSIT,
994
+ EventType.RAYDIUM_AMM_V4_WITHDRAW, EventType.RAYDIUM_AMM_V4_WITHDRAW_PNL,
995
+ EventType.RAYDIUM_AMM_V4_INITIALIZE2,
996
+ ]
997
+ return _filter_includes_any(filter, types)
998
+
999
+
1000
+ def _filter_includes_orca_whirlpool(filter: Optional[EventTypeFilter]) -> bool:
1001
+ types = [
1002
+ EventType.ORCA_WHIRLPOOL_SWAP, EventType.ORCA_WHIRLPOOL_LIQUIDITY_INCREASED,
1003
+ EventType.ORCA_WHIRLPOOL_LIQUIDITY_DECREASED, EventType.ORCA_WHIRLPOOL_POOL_INITIALIZED,
1004
+ ]
1005
+ return _filter_includes_any(filter, types)
1006
+
1007
+
1008
+ def _filter_includes_bonk(filter: Optional[EventTypeFilter]) -> bool:
1009
+ types = [
1010
+ EventType.BONK_TRADE, EventType.BONK_POOL_CREATE, EventType.BONK_MIGRATE_AMM,
1011
+ ]
1012
+ return _filter_includes_any(filter, types)
1013
+
1014
+
1015
+ __all__ = [
1016
+ "parse_instruction_unified",
1017
+ "parse_pumpfun_instruction",
1018
+ "parse_pumpswap_instruction",
1019
+ "parse_meteora_damm_instruction",
1020
+ "parse_pump_fees_instruction",
1021
+ "parse_raydium_clmm_instruction",
1022
+ "parse_raydium_cpmm_instruction",
1023
+ "parse_raydium_amm_v4_instruction",
1024
+ "parse_orca_whirlpool_instruction",
1025
+ "parse_bonk_instruction",
1026
+ ]