sol-parser-sdk-python 0.5.6__py3-none-any.whl → 0.5.8__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.
@@ -49,16 +49,19 @@ def _ix_lane(ix_name: Any) -> int:
49
49
  return 0
50
50
 
51
51
 
52
- PumpfunLaneBase = Tuple[str, str, bool, int]
52
+ OccurrenceBase = Tuple[Any, ...]
53
53
 
54
54
 
55
- def _next_occurrence(base: PumpfunLaneBase, counts: Dict[PumpfunLaneBase, int]) -> int:
55
+ def _next_occurrence(base: OccurrenceBase, counts: Dict[OccurrenceBase, int]) -> int:
56
56
  current = counts.get(base, 0)
57
57
  counts[base] = current + 1
58
58
  return current
59
59
 
60
60
 
61
- def _dedupe_key(ev: DexEvent, pumpfun_lane_counts: Dict[PumpfunLaneBase, int]) -> Optional[str]:
61
+ def _dedupe_key(
62
+ ev: DexEvent,
63
+ occurrence_counts: Dict[OccurrenceBase, int],
64
+ ) -> Optional[str]:
62
65
  data = ev.data
63
66
  if data is None:
64
67
  return None
@@ -71,14 +74,14 @@ def _dedupe_key(ev: DexEvent, pumpfun_lane_counts: Dict[PumpfunLaneBase, int]) -
71
74
  bool(getattr(data, "is_buy", False)),
72
75
  lane,
73
76
  )
74
- occ = _next_occurrence(base, pumpfun_lane_counts)
77
+ occ = _next_occurrence(("PumpFun", *base), occurrence_counts)
75
78
  return f"PumpFunTrade|{base[0]}|{base[1]}|{base[2]}|{base[3]}|{occ}"
76
79
 
77
80
  t = ev.type
78
81
  if t == EventType.PUMP_FUN_CREATE:
79
82
  return f"PumpFunCreate|{getattr(data, 'mint', '')}"
80
83
  if t == EventType.PUMP_FUN_CREATE_V2:
81
- return f"PumpFunCreateV2|{getattr(data, 'mint', '')}"
84
+ return f"PumpFunCreate|{getattr(data, 'mint', '')}"
82
85
  if t == EventType.PUMP_FUN_MIGRATE:
83
86
  return (
84
87
  f"PumpFunMigrate|{getattr(data, 'mint', '')}|"
@@ -110,13 +113,32 @@ def _dedupe_key(ev: DexEvent, pumpfun_lane_counts: Dict[PumpfunLaneBase, int]) -
110
113
  if t == EventType.PUMP_SWAP_LIQUIDITY_REMOVED:
111
114
  return f"PumpSwapLiquidityRemoved|{getattr(data, 'pool', '')}|{getattr(data, 'user', '')}"
112
115
  if t == EventType.RAYDIUM_CLMM_SWAP:
113
- return f"RaydiumClmmSwap|{getattr(data, 'pool_state', '')}|{bool(getattr(data, 'zero_for_one', False))}"
116
+ pool = getattr(data, "pool_state", "")
117
+ occurrence = _next_occurrence(("RaydiumClmm", pool), occurrence_counts)
118
+ return f"RaydiumClmmSwap|{pool}|{occurrence}"
119
+ if t == EventType.RAYDIUM_CPMM_SWAP:
120
+ pool = getattr(data, "pool_id", "")
121
+ occurrence = _next_occurrence(("RaydiumCpmm", pool), occurrence_counts)
122
+ return f"RaydiumCpmmSwap|{pool}|{occurrence}"
114
123
  if t == EventType.RAYDIUM_AMM_V4_SWAP:
115
- return f"RaydiumAmmV4Swap|{getattr(data, 'amm', '')}"
124
+ base_out = getattr(data, "max_amount_in", 0) != 0
125
+ amount = getattr(data, "amount_out" if base_out else "amount_in", 0)
126
+ base = (base_out, amount)
127
+ occurrence = _next_occurrence(("RaydiumAmmV4", *base), occurrence_counts)
128
+ return f"RaydiumAmmV4Swap|{base_out}|{amount}|{occurrence}"
129
+ if t == EventType.ORCA_WHIRLPOOL_SWAP:
130
+ whirlpool = getattr(data, "whirlpool", "")
131
+ occurrence = _next_occurrence(("OrcaWhirlpool", whirlpool), occurrence_counts)
132
+ return f"OrcaWhirlpoolSwap|{whirlpool}|{occurrence}"
116
133
  if t == EventType.METEORA_DLMM_SWAP:
134
+ base = (
135
+ getattr(data, "pool", ""),
136
+ getattr(data, "from_addr", ""),
137
+ bool(getattr(data, "swap_for_y", False)),
138
+ )
139
+ occurrence = _next_occurrence(("MeteoraDlmm", *base), occurrence_counts)
117
140
  return (
118
- f"MeteoraDlmmSwap|{getattr(data, 'pool', '')}|"
119
- f"{getattr(data, 'from_addr', '')}|{bool(getattr(data, 'swap_for_y', False))}"
141
+ f"MeteoraDlmmSwap|{base[0]}|{base[1]}|{base[2]}|{occurrence}"
120
142
  )
121
143
  return None
122
144
 
@@ -163,6 +185,8 @@ def _merge_pumpfun_trade(log: Any, ix: Any) -> None:
163
185
  "quote_amount",
164
186
  "virtual_quote_reserves",
165
187
  "real_quote_reserves",
188
+ "holder_rewards_bps",
189
+ "holder_rewards",
166
190
  ):
167
191
  if getattr(log, attr, 0) == 0 and getattr(ix, attr, 0) != 0:
168
192
  setattr(log, attr, getattr(ix, attr))
@@ -174,10 +198,40 @@ def _merge_pumpfun_trade(log: Any, ix: Any) -> None:
174
198
 
175
199
 
176
200
  def _merge_pumpfun_create(log: Any, ix: Any) -> None:
177
- for attr in ("name", "symbol", "uri", "bonding_curve", "user", "creator", "token_program", "quote_mint"):
201
+ for attr in (
202
+ "name",
203
+ "symbol",
204
+ "uri",
205
+ "mint",
206
+ "bonding_curve",
207
+ "user",
208
+ "creator",
209
+ "token_program",
210
+ "quote_mint",
211
+ "quote_vault",
212
+ "quote_token_program",
213
+ ):
178
214
  _fill_attr(log, attr, ix)
179
- if getattr(log, "virtual_quote_reserves", 0) == 0 and getattr(ix, "virtual_quote_reserves", 0) != 0:
180
- log.virtual_quote_reserves = ix.virtual_quote_reserves
215
+ for attr in (
216
+ "timestamp",
217
+ "virtual_token_reserves",
218
+ "virtual_sol_reserves",
219
+ "real_token_reserves",
220
+ "token_total_supply",
221
+ "virtual_quote_reserves",
222
+ "creator_fee_bps",
223
+ ):
224
+ if getattr(log, attr, 0) == 0 and getattr(ix, attr, 0) != 0:
225
+ setattr(log, attr, getattr(ix, attr))
226
+ log.is_mayhem_mode = bool(getattr(log, "is_mayhem_mode", False)) or bool(
227
+ getattr(ix, "is_mayhem_mode", False)
228
+ )
229
+ log.is_cashback_enabled = bool(getattr(log, "is_cashback_enabled", False)) or bool(
230
+ getattr(ix, "is_cashback_enabled", False)
231
+ )
232
+ log.is_holder_reward = bool(getattr(log, "is_holder_reward", False)) or bool(
233
+ getattr(ix, "is_holder_reward", False)
234
+ )
181
235
 
182
236
 
183
237
  def _merge_pumpfun_create_v2(log: Any, ix: Any) -> None:
@@ -232,6 +286,10 @@ def _merge_grpc_instruction_into_log(log_ev: DexEvent, ix_ev: DexEvent) -> None:
232
286
 
233
287
  if log_ev.type in PUMPFUN_TRADE_TYPES and ix_ev.type in PUMPFUN_TRADE_TYPES:
234
288
  _merge_pumpfun_trade(log, ix)
289
+ elif log_ev.type == EventType.PUMP_FUN_CREATE and ix_ev.type == EventType.PUMP_FUN_CREATE_V2:
290
+ _merge_pumpfun_create(log, ix)
291
+ elif log_ev.type == EventType.PUMP_FUN_CREATE_V2 and ix_ev.type == EventType.PUMP_FUN_CREATE:
292
+ _merge_pumpfun_create_v2(log, ix)
235
293
  elif log_ev.type == EventType.PUMP_FUN_CREATE and ix_ev.type == EventType.PUMP_FUN_CREATE:
236
294
  _merge_pumpfun_create(log, ix)
237
295
  elif log_ev.type == EventType.PUMP_FUN_CREATE_V2 and ix_ev.type == EventType.PUMP_FUN_CREATE_V2:
@@ -253,6 +311,20 @@ def _merge_grpc_instruction_into_log(log_ev: DexEvent, ix_ev: DexEvent) -> None:
253
311
  "coin_creator",
254
312
  ):
255
313
  _fill_attr(log, attr, ix)
314
+ if getattr(log, "creator_fee_bps", 0) == 0 and getattr(ix, "creator_fee_bps", 0) != 0:
315
+ log.creator_fee_bps = ix.creator_fee_bps
316
+ log.is_mayhem_mode = bool(getattr(log, "is_mayhem_mode", False)) or bool(
317
+ getattr(ix, "is_mayhem_mode", False)
318
+ )
319
+ log.is_cashback_coin = bool(getattr(log, "is_cashback_coin", False)) or bool(
320
+ getattr(ix, "is_cashback_coin", False)
321
+ )
322
+ log.can_edit_creator_fee = bool(getattr(log, "can_edit_creator_fee", False)) or bool(
323
+ getattr(ix, "can_edit_creator_fee", False)
324
+ )
325
+ log.is_holder_reward = bool(getattr(log, "is_holder_reward", False)) or bool(
326
+ getattr(ix, "is_holder_reward", False)
327
+ )
256
328
  elif (
257
329
  log_ev.type == EventType.PUMP_SWAP_LIQUIDITY_ADDED
258
330
  and ix_ev.type == EventType.PUMP_SWAP_LIQUIDITY_ADDED
@@ -282,6 +354,8 @@ def _merge_grpc_instruction_into_log(log_ev: DexEvent, ix_ev: DexEvent) -> None:
282
354
  "serum_vault_signer",
283
355
  "user_source_token_account",
284
356
  "user_destination_token_account",
357
+ "user_source_owner",
358
+ "amm",
285
359
  ):
286
360
  _fill_attr(log, attr, ix)
287
361
  elif log_ev.type == EventType.RAYDIUM_LAUNCHLAB_POOL_CREATE and ix_ev.type == EventType.RAYDIUM_LAUNCHLAB_POOL_CREATE:
@@ -299,17 +373,17 @@ def dedupe_log_instruction_events(
299
373
  ) -> List[DexEvent]:
300
374
  out: List[DexEvent] = []
301
375
  index_by_key: Dict[str, int] = {}
302
- log_pumpfun_counts: Dict[PumpfunLaneBase, int] = {}
303
- ix_pumpfun_counts: Dict[PumpfunLaneBase, int] = {}
376
+ log_occurrences: Dict[OccurrenceBase, int] = {}
377
+ ix_occurrences: Dict[OccurrenceBase, int] = {}
304
378
 
305
379
  for ev in log_events:
306
- key = _dedupe_key(ev, log_pumpfun_counts)
380
+ key = _dedupe_key(ev, log_occurrences)
307
381
  if key is not None:
308
382
  index_by_key[key] = len(out)
309
383
  out.append(ev)
310
384
 
311
385
  for ev in instruction_events:
312
- key = _dedupe_key(ev, ix_pumpfun_counts)
386
+ key = _dedupe_key(ev, ix_occurrences)
313
387
  if key is None:
314
388
  out.append(ev)
315
389
  continue
sol_parser/merger.py CHANGED
@@ -32,6 +32,14 @@ from .event_types import (
32
32
  MeteoraDammV2CreatePositionEvent,
33
33
  MeteoraDammV2RemoveLiquidityEvent,
34
34
  MeteoraDammV2SwapEvent,
35
+ MeteoraDlmmAddLiquidityEvent,
36
+ MeteoraDlmmClaimFeeEvent,
37
+ MeteoraDlmmClosePositionEvent,
38
+ MeteoraDlmmCreatePositionEvent,
39
+ MeteoraDlmmInitializeBinArrayEvent,
40
+ MeteoraDlmmInitializePoolEvent,
41
+ MeteoraDlmmRemoveLiquidityEvent,
42
+ MeteoraDlmmSwapEvent,
35
43
  MeteoraPoolsAddLiquidityEvent,
36
44
  MeteoraPoolsRemoveLiquidityEvent,
37
45
  MeteoraPoolsSwapEvent,
@@ -44,6 +52,40 @@ from .grpc_types import EventType
44
52
 
45
53
  ZERO = "11111111111111111111111111111111"
46
54
 
55
+ GENERIC_MERGE_TYPES = (
56
+ PumpSwapCreatePoolEvent,
57
+ PumpSwapLiquidityAddedEvent,
58
+ PumpSwapLiquidityRemovedEvent,
59
+ RaydiumClmmSwapEvent,
60
+ RaydiumClmmIncreaseLiquidityEvent,
61
+ RaydiumClmmDecreaseLiquidityEvent,
62
+ RaydiumClmmCreatePoolEvent,
63
+ RaydiumClmmCollectFeeEvent,
64
+ RaydiumCpmmSwapEvent,
65
+ RaydiumCpmmDepositEvent,
66
+ RaydiumCpmmWithdrawEvent,
67
+ RaydiumAmmV4SwapEvent,
68
+ RaydiumAmmV4DepositEvent,
69
+ RaydiumAmmV4WithdrawEvent,
70
+ OrcaWhirlpoolSwapEvent,
71
+ OrcaWhirlpoolLiquidityIncreasedEvent,
72
+ OrcaWhirlpoolLiquidityDecreasedEvent,
73
+ MeteoraPoolsSwapEvent,
74
+ MeteoraPoolsAddLiquidityEvent,
75
+ MeteoraPoolsRemoveLiquidityEvent,
76
+ MeteoraDammV2SwapEvent,
77
+ MeteoraDammV2AddLiquidityEvent,
78
+ MeteoraDammV2RemoveLiquidityEvent,
79
+ MeteoraDammV2CreatePositionEvent,
80
+ MeteoraDammV2ClosePositionEvent,
81
+ MeteoraDlmmSwapEvent,
82
+ MeteoraDlmmAddLiquidityEvent,
83
+ MeteoraDlmmRemoveLiquidityEvent,
84
+ MeteoraDlmmInitializeBinArrayEvent,
85
+ MeteoraDlmmClaimFeeEvent,
86
+ RaydiumLaunchlabTradeEvent,
87
+ )
88
+
47
89
 
48
90
  def _merge_generic(base: Any, inner: Any) -> None:
49
91
  for f in dataclasses.fields(type(base)):
@@ -60,48 +102,125 @@ def _fill_attr_if_empty(base: Any, attr: str, source: Any) -> None:
60
102
  setattr(base, attr, value)
61
103
 
62
104
 
105
+ def _put_attr_if_set(base: Any, attr: str, source: Any) -> None:
106
+ value = getattr(source, attr, None)
107
+ if not _empty(value):
108
+ setattr(base, attr, value)
109
+
110
+
111
+ def _put_num_if_nonzero(base: Any, attr: str, source: Any) -> None:
112
+ value = getattr(source, attr, 0)
113
+ if value:
114
+ setattr(base, attr, value)
115
+
116
+
63
117
  def merge_pumpfun_trade(base: PumpFunTradeEvent, inner: PumpFunTradeEvent) -> None:
64
- base.mint = inner.mint
65
- base.sol_amount = inner.sol_amount
66
- base.token_amount = inner.token_amount
67
- base.is_buy = inner.is_buy
68
- base.user = inner.user
69
- base.timestamp = inner.timestamp
70
- base.virtual_sol_reserves = inner.virtual_sol_reserves
71
- base.virtual_token_reserves = inner.virtual_token_reserves
72
- base.real_sol_reserves = inner.real_sol_reserves
73
- base.real_token_reserves = inner.real_token_reserves
74
- base.fee_recipient = inner.fee_recipient
75
- base.fee_basis_points = inner.fee_basis_points
76
- base.fee = inner.fee
77
- base.creator = inner.creator
78
- base.creator_fee_basis_points = inner.creator_fee_basis_points
79
- base.creator_fee = inner.creator_fee
80
- base.track_volume = inner.track_volume
81
- base.total_unclaimed_tokens = inner.total_unclaimed_tokens
82
- base.total_claimed_tokens = inner.total_claimed_tokens
83
- base.current_sol_volume = inner.current_sol_volume
84
- base.last_update_timestamp = inner.last_update_timestamp
85
- base.ix_name = inner.ix_name
86
- base.is_created_buy = inner.is_created_buy
87
- base.mayhem_mode = inner.mayhem_mode
88
- base.cashback_fee_basis_points = inner.cashback_fee_basis_points
89
- base.cashback = inner.cashback
90
- if inner.buyback_fee_basis_points != 0:
91
- base.buyback_fee_basis_points = inner.buyback_fee_basis_points
92
- if inner.buyback_fee != 0:
93
- base.buyback_fee = inner.buyback_fee
94
- if inner.shareholders:
118
+ leg = inner.sol_amount != 0 or inner.token_amount != 0
119
+
120
+ for attr in ("mint", "user", "fee_recipient", "creator"):
121
+ _put_attr_if_set(base, attr, inner)
122
+
123
+ if leg:
124
+ for attr in (
125
+ "sol_amount",
126
+ "token_amount",
127
+ "timestamp",
128
+ "virtual_sol_reserves",
129
+ "virtual_token_reserves",
130
+ "real_sol_reserves",
131
+ "real_token_reserves",
132
+ "fee_basis_points",
133
+ "fee",
134
+ "creator_fee_basis_points",
135
+ "creator_fee",
136
+ "total_unclaimed_tokens",
137
+ "total_claimed_tokens",
138
+ "current_sol_volume",
139
+ "last_update_timestamp",
140
+ ):
141
+ setattr(base, attr, getattr(inner, attr))
142
+ base.is_buy = inner.is_buy
143
+ base.track_volume = bool(base.track_volume) or bool(inner.track_volume)
144
+ base.mayhem_mode = bool(base.mayhem_mode) or bool(inner.mayhem_mode)
145
+ if inner.ix_name:
146
+ base.ix_name = inner.ix_name
147
+ base.is_cashback_coin = bool(base.is_cashback_coin) or bool(inner.is_cashback_coin)
148
+ else:
149
+ for attr in (
150
+ "fee",
151
+ "creator_fee",
152
+ "fee_basis_points",
153
+ "creator_fee_basis_points",
154
+ "virtual_sol_reserves",
155
+ "virtual_token_reserves",
156
+ "real_sol_reserves",
157
+ "real_token_reserves",
158
+ "total_unclaimed_tokens",
159
+ "total_claimed_tokens",
160
+ "current_sol_volume",
161
+ "timestamp",
162
+ "last_update_timestamp",
163
+ ):
164
+ _put_num_if_nonzero(base, attr, inner)
165
+ base.track_volume = bool(base.track_volume) or bool(inner.track_volume)
166
+ base.mayhem_mode = bool(base.mayhem_mode) or bool(inner.mayhem_mode)
167
+ if inner.ix_name:
168
+ base.ix_name = inner.ix_name
169
+ base.is_cashback_coin = bool(base.is_cashback_coin) or bool(inner.is_cashback_coin)
170
+
171
+ for attr in (
172
+ "cashback_fee_basis_points",
173
+ "cashback",
174
+ "buyback_fee_basis_points",
175
+ "buyback_fee",
176
+ "quote_amount",
177
+ "virtual_quote_reserves",
178
+ "real_quote_reserves",
179
+ "holder_rewards_bps",
180
+ "holder_rewards",
181
+ "amount",
182
+ "max_sol_cost",
183
+ "min_sol_output",
184
+ "spendable_sol_in",
185
+ "spendable_quote_in",
186
+ "min_tokens_out",
187
+ ):
188
+ _put_num_if_nonzero(base, attr, inner)
189
+
190
+ if inner.shareholders and not base.shareholders:
95
191
  base.shareholders = inner.shareholders
96
- if not _empty(inner.quote_mint):
97
- base.quote_mint = inner.quote_mint
98
- if inner.quote_amount != 0:
99
- base.quote_amount = inner.quote_amount
100
- if inner.virtual_quote_reserves != 0:
101
- base.virtual_quote_reserves = inner.virtual_quote_reserves
102
- if inner.real_quote_reserves != 0:
103
- base.real_quote_reserves = inner.real_quote_reserves
104
- base.is_cashback_coin = inner.is_cashback_coin
192
+
193
+ for attr in (
194
+ "quote_mint",
195
+ "global_account",
196
+ "bonding_curve",
197
+ "bonding_curve_v2",
198
+ "associated_bonding_curve",
199
+ "associated_user",
200
+ "system_program",
201
+ "token_program",
202
+ "quote_token_program",
203
+ "associated_token_program",
204
+ "creator_vault",
205
+ "associated_quote_fee_recipient",
206
+ "buyback_fee_recipient",
207
+ "associated_quote_buyback_fee_recipient",
208
+ "associated_quote_bonding_curve",
209
+ "associated_quote_user",
210
+ "associated_creator_vault",
211
+ "sharing_config",
212
+ "event_authority",
213
+ "program",
214
+ "global_volume_accumulator",
215
+ "user_volume_accumulator",
216
+ "associated_user_volume_accumulator",
217
+ "fee_config",
218
+ "fee_program",
219
+ "extra_instruction_account",
220
+ ):
221
+ _put_attr_if_set(base, attr, inner)
222
+
223
+ base.is_created_buy = bool(base.is_created_buy) or bool(inner.is_created_buy)
105
224
 
106
225
 
107
226
  def merge_pumpswap_buy(base: PumpSwapBuyEvent, inner: PumpSwapBuyEvent) -> None:
@@ -168,6 +287,8 @@ def merge_pumpfun_create(base: PumpFunCreateEvent, inner: PumpFunCreateEvent) ->
168
287
  base.is_cashback_enabled = inner.is_cashback_enabled
169
288
  base.quote_mint = inner.quote_mint
170
289
  base.virtual_quote_reserves = inner.virtual_quote_reserves
290
+ base.creator_fee_bps = inner.creator_fee_bps
291
+ base.is_holder_reward = bool(base.is_holder_reward) or bool(inner.is_holder_reward)
171
292
 
172
293
 
173
294
  def merge_pumpfun_migrate(base: PumpFunMigrateEvent, inner: PumpFunMigrateEvent) -> None:
@@ -183,6 +304,11 @@ def merge_pumpfun_migrate(base: PumpFunMigrateEvent, inner: PumpFunMigrateEvent)
183
304
 
184
305
  def merge_dex_events(base: DexEvent, inner: DexEvent) -> None:
185
306
  """将 ``inner`` 合并进 ``base``(就地修改 ``base.data``)。"""
307
+ try_merge_dex_events(base, inner)
308
+
309
+
310
+ def try_merge_dex_events(base: DexEvent, inner: DexEvent) -> bool:
311
+ """Merge compatible events and report success so unmatched events are retained."""
186
312
  bd = base.data
187
313
  ind = inner.data
188
314
 
@@ -199,109 +325,45 @@ def merge_dex_events(base: DexEvent, inner: DexEvent) -> None:
199
325
  EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
200
326
  ):
201
327
  merge_pumpfun_trade(bd, ind)
202
- base.type = inner.type
203
- return
328
+ return True
329
+ return False
204
330
 
205
331
  if isinstance(bd, PumpFunCreateEvent) and isinstance(ind, PumpFunCreateEvent):
206
332
  merge_pumpfun_create(bd, ind)
207
- return
333
+ return True
208
334
 
209
335
  if isinstance(bd, PumpFunCreateV2TokenEvent) and isinstance(ind, PumpFunCreateV2TokenEvent):
210
336
  _merge_generic(bd, ind)
211
- return
337
+ return True
212
338
 
213
339
  if isinstance(bd, PumpFunMigrateEvent) and isinstance(ind, PumpFunMigrateEvent):
214
340
  merge_pumpfun_migrate(bd, ind)
215
- return
341
+ return True
216
342
 
217
343
  if isinstance(bd, PumpSwapBuyEvent) and isinstance(ind, PumpSwapBuyEvent):
218
344
  merge_pumpswap_buy(bd, ind)
219
- return
345
+ return True
220
346
  if isinstance(bd, PumpSwapSellEvent) and isinstance(ind, PumpSwapSellEvent):
221
347
  merge_pumpswap_sell(bd, ind)
222
- return
223
- if isinstance(bd, PumpSwapCreatePoolEvent) and isinstance(ind, PumpSwapCreatePoolEvent):
224
- _merge_generic(bd, ind)
225
- return
226
- if isinstance(bd, PumpSwapLiquidityAddedEvent) and isinstance(ind, PumpSwapLiquidityAddedEvent):
227
- _merge_generic(bd, ind)
228
- return
229
- if isinstance(bd, PumpSwapLiquidityRemovedEvent) and isinstance(ind, PumpSwapLiquidityRemovedEvent):
230
- _merge_generic(bd, ind)
231
- return
232
-
233
- if isinstance(bd, RaydiumClmmSwapEvent) and isinstance(ind, RaydiumClmmSwapEvent):
234
- _merge_generic(bd, ind)
235
- return
236
- if isinstance(bd, RaydiumClmmIncreaseLiquidityEvent) and isinstance(ind, RaydiumClmmIncreaseLiquidityEvent):
237
- _merge_generic(bd, ind)
238
- return
239
- if isinstance(bd, RaydiumClmmDecreaseLiquidityEvent) and isinstance(ind, RaydiumClmmDecreaseLiquidityEvent):
348
+ return True
349
+ if isinstance(bd, MeteoraDlmmInitializePoolEvent) and isinstance(ind, MeteoraDlmmInitializePoolEvent):
350
+ creator, active_bin_id = bd.creator, bd.active_bin_id
240
351
  _merge_generic(bd, ind)
241
- return
242
- if isinstance(bd, RaydiumClmmCreatePoolEvent) and isinstance(ind, RaydiumClmmCreatePoolEvent):
352
+ bd.creator, bd.active_bin_id = creator, active_bin_id
353
+ return True
354
+ if isinstance(bd, MeteoraDlmmCreatePositionEvent) and isinstance(ind, MeteoraDlmmCreatePositionEvent):
355
+ lower_bin_id, width = bd.lower_bin_id, bd.width
243
356
  _merge_generic(bd, ind)
244
- return
245
- if isinstance(bd, RaydiumClmmCollectFeeEvent) and isinstance(ind, RaydiumClmmCollectFeeEvent):
357
+ bd.lower_bin_id, bd.width = lower_bin_id, width
358
+ return True
359
+ if isinstance(bd, MeteoraDlmmClosePositionEvent) and isinstance(ind, MeteoraDlmmClosePositionEvent):
360
+ pool = bd.pool
246
361
  _merge_generic(bd, ind)
247
- return
362
+ bd.pool = pool
363
+ return True
248
364
 
249
- if isinstance(bd, RaydiumCpmmSwapEvent) and isinstance(ind, RaydiumCpmmSwapEvent):
365
+ if base.type == inner.type and type(bd) is type(ind) and isinstance(bd, GENERIC_MERGE_TYPES):
250
366
  _merge_generic(bd, ind)
251
- return
252
- if isinstance(bd, RaydiumCpmmDepositEvent) and isinstance(ind, RaydiumCpmmDepositEvent):
253
- _merge_generic(bd, ind)
254
- return
255
- if isinstance(bd, RaydiumCpmmWithdrawEvent) and isinstance(ind, RaydiumCpmmWithdrawEvent):
256
- _merge_generic(bd, ind)
257
- return
258
-
259
- if isinstance(bd, RaydiumAmmV4SwapEvent) and isinstance(ind, RaydiumAmmV4SwapEvent):
260
- _merge_generic(bd, ind)
261
- return
262
- if isinstance(bd, RaydiumAmmV4DepositEvent) and isinstance(ind, RaydiumAmmV4DepositEvent):
263
- _merge_generic(bd, ind)
264
- return
265
- if isinstance(bd, RaydiumAmmV4WithdrawEvent) and isinstance(ind, RaydiumAmmV4WithdrawEvent):
266
- _merge_generic(bd, ind)
267
- return
367
+ return True
268
368
 
269
- if isinstance(bd, OrcaWhirlpoolSwapEvent) and isinstance(ind, OrcaWhirlpoolSwapEvent):
270
- _merge_generic(bd, ind)
271
- return
272
- if isinstance(bd, OrcaWhirlpoolLiquidityIncreasedEvent) and isinstance(ind, OrcaWhirlpoolLiquidityIncreasedEvent):
273
- _merge_generic(bd, ind)
274
- return
275
- if isinstance(bd, OrcaWhirlpoolLiquidityDecreasedEvent) and isinstance(ind, OrcaWhirlpoolLiquidityDecreasedEvent):
276
- _merge_generic(bd, ind)
277
- return
278
-
279
- if isinstance(bd, MeteoraPoolsSwapEvent) and isinstance(ind, MeteoraPoolsSwapEvent):
280
- _merge_generic(bd, ind)
281
- return
282
- if isinstance(bd, MeteoraPoolsAddLiquidityEvent) and isinstance(ind, MeteoraPoolsAddLiquidityEvent):
283
- _merge_generic(bd, ind)
284
- return
285
- if isinstance(bd, MeteoraPoolsRemoveLiquidityEvent) and isinstance(ind, MeteoraPoolsRemoveLiquidityEvent):
286
- _merge_generic(bd, ind)
287
- return
288
-
289
- if isinstance(bd, MeteoraDammV2SwapEvent) and isinstance(ind, MeteoraDammV2SwapEvent):
290
- _merge_generic(bd, ind)
291
- return
292
- if isinstance(bd, MeteoraDammV2AddLiquidityEvent) and isinstance(ind, MeteoraDammV2AddLiquidityEvent):
293
- _merge_generic(bd, ind)
294
- return
295
- if isinstance(bd, MeteoraDammV2RemoveLiquidityEvent) and isinstance(ind, MeteoraDammV2RemoveLiquidityEvent):
296
- _merge_generic(bd, ind)
297
- return
298
- if isinstance(bd, MeteoraDammV2CreatePositionEvent) and isinstance(ind, MeteoraDammV2CreatePositionEvent):
299
- _merge_generic(bd, ind)
300
- return
301
- if isinstance(bd, MeteoraDammV2ClosePositionEvent) and isinstance(ind, MeteoraDammV2ClosePositionEvent):
302
- _merge_generic(bd, ind)
303
- return
304
-
305
- if isinstance(bd, RaydiumLaunchlabTradeEvent) and isinstance(ind, RaydiumLaunchlabTradeEvent):
306
- _merge_generic(bd, ind)
307
- return
369
+ return False
sol_parser/parser.py CHANGED
@@ -8,12 +8,16 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
8
8
 
9
9
  from .dex_parsers import (
10
10
  DexEvent,
11
+ PUMPFUN_PROGRAM_ID,
12
+ PUMP_TRADE,
11
13
  apply_event_type_filter,
12
14
  dispatch_program_data,
13
15
  event_type_for_discriminator,
14
16
  event_type_for_program_discriminator,
15
17
  filter_allows_unknown_log_event,
18
+ filter_allows_unscoped_discriminator,
16
19
  filter_includes_program,
20
+ filter_wants_pumpfun_trade,
17
21
  parse_trade_from_data,
18
22
  )
19
23
 
@@ -32,13 +36,11 @@ def decode_program_data_line(log: str) -> Optional[bytes]:
32
36
  if i < 0:
33
37
  return None
34
38
  s = log[i + len(p) :].strip()
35
- if len(s) > 2700:
36
- return None
37
39
  try:
38
40
  raw = base64.standard_b64decode(s)
39
41
  except Exception:
40
42
  return None
41
- if len(raw) < 8 or len(raw) > 2048:
43
+ if len(raw) < 8:
42
44
  return None
43
45
  return raw
44
46
 
@@ -77,24 +79,53 @@ def parse_log_optimized(
77
79
  ) -> Optional[DexEvent]:
78
80
  """单次 base64 decode 后按 discriminator 做 early filter,再按实际事件类型二次过滤。"""
79
81
  grpc = int(time.time() * 1_000_000) if grpc_recv_us is None else grpc_recv_us
82
+ if program_id == "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" and "ray_log: " in log:
83
+ from .dex_parsers import parse_amm_ray_log_swap
84
+ from .grpc_types import EventType
85
+
86
+ if event_type_filter is not None and not event_type_filter.should_include(
87
+ EventType.RAYDIUM_AMM_V4_SWAP
88
+ ):
89
+ return None
90
+ return parse_amm_ray_log_swap(
91
+ log, _meta(signature, slot, tx_index, block_time_us, grpc, recent_blockhash)
92
+ )
80
93
  buf = decode_program_data_line(log)
81
94
  if not buf:
82
95
  return None
83
96
  disc = _disc8(buf[:8])
84
97
  if event_type_filter is not None:
98
+ is_unscoped_shared_discriminator = program_id is None and disc in (
99
+ _disc8(bytes([189, 219, 127, 211, 78, 230, 97, 238])),
100
+ _disc8(bytes([143, 190, 90, 218, 196, 30, 51, 222])),
101
+ )
85
102
  event_type = event_type_for_program_discriminator(program_id, disc)
86
- if event_type is not None:
103
+ if is_unscoped_shared_discriminator:
104
+ if not filter_allows_unscoped_discriminator(event_type_filter, disc):
105
+ return None
106
+ elif program_id == PUMPFUN_PROGRAM_ID and disc == PUMP_TRADE:
107
+ if not filter_wants_pumpfun_trade(event_type_filter):
108
+ return None
109
+ elif event_type is not None:
87
110
  if not event_type_filter.should_include(event_type):
88
111
  return None
89
112
  elif program_id:
90
113
  if not filter_includes_program(event_type_filter, program_id):
91
114
  return None
92
- elif not filter_allows_unknown_log_event(event_type_filter):
115
+ elif not filter_allows_unscoped_discriminator(event_type_filter, disc):
93
116
  return None
94
117
  data = buf[8:]
95
118
  meta = _meta(signature, slot, tx_index, block_time_us, grpc, recent_blockhash)
96
119
  return apply_event_type_filter(
97
- dispatch_program_data(disc, data, buf, meta, is_created_buy, program_id),
120
+ dispatch_program_data(
121
+ disc,
122
+ data,
123
+ buf,
124
+ meta,
125
+ is_created_buy,
126
+ program_id,
127
+ event_type_filter,
128
+ ),
98
129
  event_type_filter,
99
130
  )
100
131