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.
- sol_parser/__init__.py +28 -0
- sol_parser/account_dispatcher.py +81 -0
- sol_parser/account_fillers/meteora.py +25 -2
- sol_parser/account_fillers/pumpfun.py +11 -0
- sol_parser/account_fillers/raydium.py +3 -2
- sol_parser/account_fillers/raydium_launchlab.py +50 -0
- sol_parser/accounts/__init__.py +47 -2
- sol_parser/check_migration.py +3 -0
- sol_parser/dex_parsers.py +1521 -124
- sol_parser/event_type_parity.py +133 -0
- sol_parser/event_types.py +473 -3
- sol_parser/grpc_client.py +3 -4
- sol_parser/grpc_instruction_parser.py +98 -28
- sol_parser/grpc_types.py +61 -21
- sol_parser/inner_instruction_parser.py +142 -18
- sol_parser/instructions.py +501 -101
- sol_parser/log_instr_dedup.py +90 -16
- sol_parser/merger.py +188 -126
- sol_parser/parser.py +37 -6
- sol_parser/pumpfun_fee_enrich.py +14 -1
- sol_parser/rpc_parser.py +82 -11
- {sol_parser_sdk_python-0.5.6.dist-info → sol_parser_sdk_python-0.5.8.dist-info}/METADATA +26 -9
- {sol_parser_sdk_python-0.5.6.dist-info → sol_parser_sdk_python-0.5.8.dist-info}/RECORD +25 -24
- {sol_parser_sdk_python-0.5.6.dist-info → sol_parser_sdk_python-0.5.8.dist-info}/WHEEL +1 -1
- {sol_parser_sdk_python-0.5.6.dist-info → sol_parser_sdk_python-0.5.8.dist-info}/entry_points.txt +0 -0
sol_parser/log_instr_dedup.py
CHANGED
|
@@ -49,16 +49,19 @@ def _ix_lane(ix_name: Any) -> int:
|
|
|
49
49
|
return 0
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
OccurrenceBase = Tuple[Any, ...]
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def _next_occurrence(base:
|
|
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(
|
|
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,
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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|{
|
|
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 (
|
|
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
|
-
|
|
180
|
-
|
|
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
|
-
|
|
303
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
base.
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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,
|
|
224
|
-
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
245
|
-
|
|
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
|
-
|
|
362
|
+
bd.pool = pool
|
|
363
|
+
return True
|
|
248
364
|
|
|
249
|
-
if
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|