sol-parser-sdk-python 0.5.10__py3-none-any.whl → 0.5.11__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.
sol_parser/__init__.py CHANGED
@@ -148,6 +148,9 @@ from .event_types import (
148
148
  # Meteora DAMM v2 events
149
149
  MeteoraDammV2SwapEvent,
150
150
  MeteoraDammV2CreatePositionEvent,
151
+ MeteoraDammV2ClaimPositionFeeEvent,
152
+ MeteoraDammV2ClaimRewardEvent,
153
+ MeteoraDlmmClaimRewardEvent,
151
154
  MeteoraDammV2ClosePositionEvent,
152
155
  MeteoraDammV2AddLiquidityEvent,
153
156
  MeteoraDammV2RemoveLiquidityEvent,
@@ -155,6 +158,11 @@ from .event_types import (
155
158
  MeteoraDammV2DynamicFeeParameters,
156
159
  MeteoraDammV2UpdateDelegatePermissionEvent,
157
160
  MeteoraDammV2WithdrawDeadLiquidityRewardEvent,
161
+ MeteoraDammV2WithdrawIneligibleRewardEvent,
162
+ MeteoraDammV2UpdateRewardFunderEvent,
163
+ MeteoraDammV2UpdateRewardDurationEvent,
164
+ MeteoraDammV2InitializeRewardEvent,
165
+ MeteoraDammV2FundRewardEvent,
158
166
  MeteoraDammV2CreateConfigEvent,
159
167
  MeteoraDammV2CreateDynamicConfigEvent,
160
168
  MeteoraDbcCurveCompleteEvent,
@@ -433,6 +441,9 @@ __all__ = [
433
441
  "MeteoraPoolsPoolCreatedEvent",
434
442
  "MeteoraDammV2SwapEvent",
435
443
  "MeteoraDammV2CreatePositionEvent",
444
+ "MeteoraDammV2ClaimPositionFeeEvent",
445
+ "MeteoraDammV2ClaimRewardEvent",
446
+ "MeteoraDlmmClaimRewardEvent",
436
447
  "MeteoraDammV2ClosePositionEvent",
437
448
  "MeteoraDammV2AddLiquidityEvent",
438
449
  "MeteoraDammV2RemoveLiquidityEvent",
@@ -440,6 +451,11 @@ __all__ = [
440
451
  "MeteoraDammV2DynamicFeeParameters",
441
452
  "MeteoraDammV2UpdateDelegatePermissionEvent",
442
453
  "MeteoraDammV2WithdrawDeadLiquidityRewardEvent",
454
+ "MeteoraDammV2WithdrawIneligibleRewardEvent",
455
+ "MeteoraDammV2UpdateRewardFunderEvent",
456
+ "MeteoraDammV2UpdateRewardDurationEvent",
457
+ "MeteoraDammV2InitializeRewardEvent",
458
+ "MeteoraDammV2FundRewardEvent",
443
459
  "MeteoraDammV2CreateConfigEvent",
444
460
  "MeteoraDammV2CreateDynamicConfigEvent",
445
461
  "MeteoraDbcCurveCompleteEvent",
@@ -490,3 +506,8 @@ __all__ += ["LaunchLabPoolCreateEvent", "StonkFunPoolCreateEvent", "LaunchLabTra
490
506
 
491
507
  from .grpc_client import DexEventQueue, GrpcStreamStatus
492
508
  __all__ += ["DexEventQueue", "GrpcStreamStatus"]
509
+
510
+ from .pump_upgrade import decode_pump_multi_hop_intent
511
+
512
+ from .pump_upgrade import PumpFunPostCompleteBuyEvent, PumpFunSweepBondingCurveFeeEvent, PumpFunCompleteEvent, PumpSwapSweepPoolFeeEvent
513
+ __all__ += ['decode_pump_multi_hop_intent','PumpFunPostCompleteBuyEvent','PumpFunSweepBondingCurveFeeEvent','PumpFunCompleteEvent','PumpSwapSweepPoolFeeEvent']
@@ -110,6 +110,94 @@ def fill_accounts_with_owned_keys(
110
110
  return
111
111
  filler(get)
112
112
 
113
+ def fill_clmm_liquidity(remove=False):
114
+ position_index = 2 if remove else 4
115
+ allowed = (bytes([58,127,188,62,79,82,196,96]), bytes([160,38,208,111,104,91,44,1])) if remove else (bytes([133,29,89,223,69,238,176,10]), bytes([46,156,243,118,13,205,251,178]))
116
+ selected = None
117
+ for invocation in invokes.get(base58.b58decode(RAYDIUM_CLMM_PROGRAM_ID), []):
118
+ raw = get_instruction_data(meta_pb, transaction_pb, invocation)
119
+ if not raw or raw[:8] not in allowed:
120
+ continue
121
+ get = get_instruction_account_getter(meta_pb, transaction_pb, static_keys, w, r, invocation)
122
+ if get is None or not raydium.clmm_position_matches(data.position_nft_mint, get(position_index)):
123
+ continue
124
+ data.personal_position = get(position_index)
125
+ if selected is not None and any(selected(i) != get(i) for i in range(18)):
126
+ return
127
+ selected = get
128
+ if selected is not None:
129
+ (raydium.fill_clmm_decrease_liquidity_accounts if remove else raydium.fill_clmm_increase_liquidity_accounts)(data, selected)
130
+
131
+ def fill_pump_trade():
132
+ if not data.mint or data.mint == "11111111111111111111111111111111":
133
+ return
134
+ layouts = {
135
+ bytes([102,6,61,18,1,218,235,234]): (2,6,True,16),
136
+ bytes([56,252,116,8,158,223,205,95]): (2,6,True,16),
137
+ bytes([51,230,133,164,1,127,131,173]): (2,6,False,14),
138
+ bytes([184,23,238,97,103,197,211,61]): (1,13,True,27),
139
+ bytes([194,171,28,70,104,77,91,47]): (1,13,True,27),
140
+ bytes([93,246,130,60,231,233,64,178]): (1,13,False,26),
141
+ bytes([7,5,29,196,245,23,101,80]): (1,8,True,17),
142
+ bytes([225,247,80,30,213,179,132,136]): (1,8,True,17),
143
+ bytes([28,146,222,119,38,196,105,213]): (1,8,False,17),
144
+ }
145
+ selected = None
146
+ for invocation in invokes.get(base58.b58decode(PUMPFUN_PROGRAM_ID), []):
147
+ raw = get_instruction_data(meta_pb, transaction_pb, invocation)
148
+ layout = layouts.get(raw[:8]) if raw else None
149
+ if layout is None:
150
+ continue
151
+ mint_index, user_index, buy, minimum = layout
152
+ oi, ii = invocation
153
+ instruction = (transaction_pb.message.instructions[oi] if ii < 0 else
154
+ next(g for g in meta_pb.inner_instructions if g.index == oi).instructions[ii])
155
+ if buy != data.is_buy or len(instruction.accounts) < minimum:
156
+ continue
157
+ get = get_instruction_account_getter(meta_pb, transaction_pb, static_keys, w, r, invocation)
158
+ if get is None or get(mint_index) != data.mint:
159
+ continue
160
+ if data.user and data.user != "11111111111111111111111111111111" and get(user_index) != data.user:
161
+ continue
162
+ if selected is not None:
163
+ return
164
+ selected = get
165
+ if selected is not None:
166
+ pumpfun.fill_trade_accounts(data, selected)
167
+
168
+ def fill_swap(buy):
169
+ # Match the event's own pool, user and direction, not the largest invoke.
170
+ if not data.pool or data.pool == "11111111111111111111111111111111":
171
+ return
172
+ selected = None
173
+ legacy = (bytes([102, 6, 61, 18, 1, 218, 235, 234]),
174
+ bytes([198, 46, 21, 82, 180, 217, 232, 112])) if buy else (bytes([51, 230, 133, 164, 1, 127, 131, 173]),)
175
+ compact = (bytes([184, 23, 238, 97, 103, 197, 211, 61]),
176
+ bytes([194, 171, 28, 70, 104, 77, 91, 47])) if buy else (bytes([93, 246, 130, 60, 231, 233, 64, 178]),)
177
+ for invocation in invokes.get(base58.b58decode(PUMPSWAP_PROGRAM_ID), []):
178
+ raw = get_instruction_data(meta_pb, transaction_pb, invocation)
179
+ boost = buy and raw is not None and raw[:8] == bytes([105, 68, 6, 175, 0, 7, 35, 162])
180
+ if raw is None or (not boost and raw[:8] not in legacy + compact):
181
+ continue
182
+ oi, ii = invocation
183
+ instruction = (transaction_pb.message.instructions[oi] if ii < 0 else
184
+ next(g for g in meta_pb.inner_instructions if g.index == oi).instructions[ii])
185
+ minimum = 13 if boost else (17 if raw[:8] in compact else (23 if buy else 21))
186
+ if len(instruction.accounts) < minimum:
187
+ continue
188
+ get = get_instruction_account_getter(meta_pb, transaction_pb, static_keys, w, r, invocation)
189
+ if get is None or get(0) != data.pool:
190
+ continue
191
+ if data.user and data.user != "11111111111111111111111111111111" and get(7 if boost else 1) != data.user:
192
+ continue
193
+ if selected is not None:
194
+ return # Invocation positions are unavailable; ambiguous context stays blank.
195
+ selected = (get, boost)
196
+ if selected is not None:
197
+ get, boost = selected
198
+ filler = pumpswap.fill_boost_buy_accounts if boost else (pumpswap.fill_buy_accounts if buy else pumpswap.fill_sell_accounts)
199
+ filler(data, get)
200
+
113
201
  def fill_create(v2_only=False):
114
202
  selected = None
115
203
  for ix in invokes.get(base58.b58decode(PUMPFUN_PROGRAM_ID), []):
@@ -144,7 +232,7 @@ def fill_accounts_with_owned_keys(
144
232
  EventType.PUMP_FUN_SELL,
145
233
  EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
146
234
  ):
147
- run(PUMPFUN_PROGRAM_ID, lambda g: pumpfun.fill_trade_accounts(data, g))
235
+ fill_pump_trade()
148
236
  elif et == EventType.PUMP_FUN_CREATE:
149
237
  fill_create()
150
238
  elif et == EventType.PUMP_FUN_CREATE_V2:
@@ -152,9 +240,9 @@ def fill_accounts_with_owned_keys(
152
240
  elif et == EventType.PUMP_FUN_MIGRATE:
153
241
  run(PUMPFUN_PROGRAM_ID, lambda g: pumpfun.fill_migrate_accounts(data, g))
154
242
  elif et == EventType.PUMP_SWAP_BUY:
155
- run(PUMPSWAP_PROGRAM_ID, lambda g: pumpswap.fill_buy_accounts(data, g))
243
+ fill_swap(True)
156
244
  elif et == EventType.PUMP_SWAP_SELL:
157
- run(PUMPSWAP_PROGRAM_ID, lambda g: pumpswap.fill_sell_accounts(data, g))
245
+ fill_swap(False)
158
246
  elif et == EventType.PUMP_SWAP_TRADE:
159
247
  run(PUMPSWAP_PROGRAM_ID, lambda g: pumpswap.fill_trade_accounts(data, g))
160
248
  elif et == EventType.PUMP_SWAP_CREATE_POOL:
@@ -174,9 +262,9 @@ def fill_accounts_with_owned_keys(
174
262
  elif et == EventType.RAYDIUM_CLMM_CLOSE_POSITION:
175
263
  run(RAYDIUM_CLMM_PROGRAM_ID, lambda g: raydium.fill_clmm_close_position_accounts(data, g))
176
264
  elif et == EventType.RAYDIUM_CLMM_INCREASE_LIQUIDITY:
177
- run(RAYDIUM_CLMM_PROGRAM_ID, lambda g: raydium.fill_clmm_increase_liquidity_accounts(data, g))
265
+ fill_clmm_liquidity()
178
266
  elif et == EventType.RAYDIUM_CLMM_DECREASE_LIQUIDITY:
179
- run(RAYDIUM_CLMM_PROGRAM_ID, lambda g: raydium.fill_clmm_decrease_liquidity_accounts(data, g))
267
+ fill_clmm_liquidity(True)
180
268
  elif et == EventType.RAYDIUM_CPMM_SWAP:
181
269
  run(RAYDIUM_CPMM_PROGRAM_ID, lambda g: raydium.fill_cpmm_swap_accounts(data, g))
182
270
  elif et == EventType.RAYDIUM_CPMM_DEPOSIT:
@@ -29,6 +29,25 @@ def fill_trade_accounts(e: PumpFunTradeEvent, get: AccountGetter) -> None:
29
29
  if _empty(getattr(e, name)):
30
30
  setattr(e, name, get(idx))
31
31
 
32
+ if get(16)=="6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" and account_at_matches_mint(1):
33
+ set_attr("global_account",0)
34
+ set_attr("mint",1)
35
+ set_attr("quote_mint",2)
36
+ set_attr("token_program",3)
37
+ set_attr("quote_token_program",4)
38
+ set_attr("bonding_curve",5)
39
+ set_attr("associated_bonding_curve",6)
40
+ set_attr("associated_quote_bonding_curve",7)
41
+ set_attr("user",8)
42
+ set_attr("associated_user",9)
43
+ set_attr("associated_quote_user",10)
44
+ set_attr("user_volume_accumulator",11)
45
+ set_attr("fee_config",12)
46
+ set_attr("buyback_fee_recipient",13)
47
+ set_attr("system_program",14)
48
+ set_attr("event_authority",15)
49
+ set_attr("program",16)
50
+ return
32
51
  is_v2 = e.ix_name in ("buy_v2", "sell_v2", "buy_exact_quote_in_v2") or account_at_matches_mint(1)
33
52
  if is_v2:
34
53
  set_attr("global_account", 0)
@@ -22,6 +22,21 @@ def _empty(s: str) -> bool:
22
22
  AccountGetter = Callable[[int], str]
23
23
 
24
24
 
25
+ TOKEN_PROGRAMS = {
26
+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
27
+ "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
28
+ }
29
+
30
+
31
+ def _is_compact_trade(get: AccountGetter) -> bool:
32
+ # Both legacy and compact layouts have the PumpSwap program at index 16.
33
+ return (
34
+ get(16) == "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"
35
+ and get(9) in TOKEN_PROGRAMS
36
+ and get(10) in TOKEN_PROGRAMS
37
+ )
38
+
39
+
25
40
  def _fill_trade_common(
26
41
  e: PumpSwapBuyEvent | PumpSwapSellEvent,
27
42
  get: AccountGetter,
@@ -42,6 +57,15 @@ def _fill_trade_common(
42
57
  e.pool_base_token_account = get(7)
43
58
  if _empty(e.pool_quote_token_account):
44
59
  e.pool_quote_token_account = get(8)
60
+ if _is_compact_trade(get):
61
+ for field, index in (
62
+ ("base_token_program", 9),
63
+ ("quote_token_program", 10),
64
+ ("fee_recipient_quote_token_account", 14),
65
+ ):
66
+ if _empty(getattr(e, field)):
67
+ setattr(e, field, get(index))
68
+ return
45
69
  if _empty(e.protocol_fee_recipient):
46
70
  e.protocol_fee_recipient = get(9)
47
71
  if _empty(e.protocol_fee_recipient_token_account):
@@ -56,8 +80,21 @@ def _fill_trade_common(
56
80
  e.coin_creator_vault_authority = get(18)
57
81
 
58
82
 
83
+ def fill_boost_buy_accounts(e: PumpSwapBuyEvent, get: AccountGetter) -> None:
84
+ """Boost has different vault slots and no user ATAs or fee accounts."""
85
+ for field, index in (
86
+ ("pool", 0), ("base_mint", 3), ("quote_mint", 4),
87
+ ("pool_base_token_account", 5), ("pool_quote_token_account", 6),
88
+ ("base_token_program", 9), ("quote_token_program", 10),
89
+ ):
90
+ if _empty(getattr(e, field)):
91
+ setattr(e, field, get(index))
92
+
93
+
59
94
  def fill_buy_accounts(e: PumpSwapBuyEvent, get: AccountGetter) -> None:
60
95
  _fill_trade_common(e, get)
96
+ if _is_compact_trade(get):
97
+ return
61
98
  a26 = get(26)
62
99
  if not _empty(a26):
63
100
  if _empty(e.pool_v2):
@@ -82,6 +119,8 @@ def fill_buy_accounts(e: PumpSwapBuyEvent, get: AccountGetter) -> None:
82
119
 
83
120
  def fill_sell_accounts(e: PumpSwapSellEvent, get: AccountGetter) -> None:
84
121
  _fill_trade_common(e, get)
122
+ if _is_compact_trade(get):
123
+ return
85
124
  a25 = get(25)
86
125
  if not _empty(a25):
87
126
  if _empty(e.pool_v2):
@@ -125,9 +164,13 @@ def fill_create_pool_accounts(e: PumpSwapCreatePoolEvent, get: AccountGetter) ->
125
164
  e.user_quote_token_account = get(7)
126
165
 
127
166
 
128
- def fill_liquidity_added_accounts(_e: PumpSwapLiquidityAddedEvent, _get: AccountGetter) -> None:
167
+ def fill_liquidity_added_accounts(
168
+ _e: PumpSwapLiquidityAddedEvent, _get: AccountGetter
169
+ ) -> None:
129
170
  pass
130
171
 
131
172
 
132
- def fill_liquidity_removed_accounts(_e: PumpSwapLiquidityRemovedEvent, _get: AccountGetter) -> None:
173
+ def fill_liquidity_removed_accounts(
174
+ _e: PumpSwapLiquidityRemovedEvent, _get: AccountGetter
175
+ ) -> None:
133
176
  pass
@@ -46,11 +46,15 @@ def fill_clmm_create_pool_accounts(e: RaydiumClmmCreatePoolEvent, get: AccountGe
46
46
  def fill_clmm_increase_liquidity_accounts(e: RaydiumClmmIncreaseLiquidityEvent, get: AccountGetter) -> None:
47
47
  if _empty(e.user):
48
48
  e.user = get(0)
49
+ if _empty(e.pool):
50
+ e.pool = get(2)
49
51
 
50
52
 
51
53
  def fill_clmm_decrease_liquidity_accounts(e: RaydiumClmmDecreaseLiquidityEvent, get: AccountGetter) -> None:
52
54
  if _empty(e.user):
53
55
  e.user = get(0)
56
+ if _empty(e.pool):
57
+ e.pool = get(3)
54
58
 
55
59
 
56
60
  def fill_clmm_open_position_accounts(e: RaydiumClmmOpenPositionEvent, get: AccountGetter) -> None:
@@ -122,3 +126,24 @@ def fill_amm_v4_withdraw_accounts(e: RaydiumAmmV4WithdrawEvent, get: AccountGett
122
126
  e.amm_authority = get(2)
123
127
  if _empty(e.amm_open_orders):
124
128
  e.amm_open_orders = get(3)
129
+
130
+
131
+ def clmm_position_matches(mint: str, position: str) -> bool:
132
+ """Match a candidate PDA to the log mint without a crypto dependency.
133
+
134
+ This checks seed hashes against an existing instruction address; it does
135
+ not create or validate a new PDA or prove transaction authorization.
136
+ """
137
+ import base58
138
+ import hashlib
139
+ if _empty(mint) or _empty(position):
140
+ return False
141
+ try:
142
+ raw_mint = base58.b58decode(mint)
143
+ target = base58.b58decode(position)
144
+ except ValueError:
145
+ return False
146
+ if len(raw_mint) != 32 or len(target) != 32:
147
+ return False
148
+ program = base58.b58decode("CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK")
149
+ return any(hashlib.sha256(b"position" + raw_mint + bytes([bump]) + program + b"ProgramDerivedAddress").digest() == target for bump in range(255, -1, -1))
@@ -464,6 +464,12 @@ def _parse_pumpfun_global_fast(account: AccountData, metadata: EventMetadata) ->
464
464
  "buyback_basis_points": buyback_basis_points,
465
465
  "initial_virtual_quote_reserves": initial_virtual_quote_reserves,
466
466
  "whitelisted_quote_mints": whitelisted_quote_mints,
467
+ "creator_fee_configurable": len(data)>1037 and data[1037]==1,
468
+ "max_configurable_creator_fee_bps": int.from_bytes(data[1038:1046],"little") if len(data)>=1046 else 0,
469
+ "holder_reward_claim_authority": read_pubkey_fast(data,1046) if len(data)>=1078 else "11111111111111111111111111111111",
470
+ "is_holder_reward_enabled": len(data)>1078 and data[1078]==1,
471
+ "max_curve_depth": data[1079] if len(data)>1079 else 0,
472
+
467
473
  },
468
474
  },
469
475
  )
@@ -505,6 +511,12 @@ def _parse_pumpfun_bonding_curve_fast(
505
511
  "metadata": metadata,
506
512
  "pubkey": account.pubkey,
507
513
  "bonding_curve": {
514
+ "creator_fee": read_u64_fast(data,117) if len(data)>=125 else 0,
515
+ "protocol_fees": read_u64_fast(data,125) if len(data)>=133 else 0,
516
+ "depth": data[133] if len(data)>133 else 0,
517
+ "initial_virtual_quote_reserves": read_u64_fast(data,134) if len(data)>=142 else 0,
518
+ "post_complete_base_out": read_u64_fast(data,142) if len(data)>=150 else 0,
519
+ "post_complete_quote_in": read_u64_fast(data,150) if len(data)>=158 else 0,
508
520
  "virtual_token_reserves": virtual_token_reserves,
509
521
  "virtual_quote_reserves": virtual_quote_reserves,
510
522
  "real_token_reserves": real_token_reserves,
@@ -543,7 +555,10 @@ def _parse_pumpfun_fee_config_fast(
543
555
  stable_fee_tiers_result = _read_pumpfun_fee_tiers(data, o)
544
556
  if stable_fee_tiers_result is None:
545
557
  return None
546
- stable_fee_tiers, _ = stable_fee_tiers_result
558
+ stable_fee_tiers, o = stable_fee_tiers_result
559
+ exotic_result=_read_pumpfun_fees(data,o) if len(data)>o else ({"lp_fee_bps":0,"protocol_fee_bps":0,"creator_fee_bps":0},o)
560
+ if exotic_result is None:return None
561
+ exotic_flat_fees,_=exotic_result
547
562
  return _account_event(
548
563
  EventType.ACCOUNT_PUMP_FUN_FEE_CONFIG,
549
564
  {
@@ -555,6 +570,7 @@ def _parse_pumpfun_fee_config_fast(
555
570
  "flat_fees": flat_fees,
556
571
  "fee_tiers": fee_tiers,
557
572
  "stable_fee_tiers": stable_fee_tiers,
573
+ "exotic_flat_fees":exotic_flat_fees,
558
574
  },
559
575
  },
560
576
  )
@@ -789,6 +805,8 @@ def _parse_pumpswap_pool_fast(account: AccountData, metadata: EventMetadata) ->
789
805
  "metadata": metadata,
790
806
  "pubkey": account.pubkey,
791
807
  "pool": {
808
+ "protocol_fees": read_u64_fast(data,263) if len(data)>=271 else 0,
809
+ "creator_fees": read_u64_fast(data,271) if len(data)>=279 else 0,
792
810
  "pool_bump": pool_bump,
793
811
  "index": index,
794
812
  "creator": creator,