sol-parser-sdk-python 0.5.7__py3-none-any.whl → 0.5.9__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 +34 -0
- sol_parser/account_dispatcher.py +98 -4
- sol_parser/account_fillers/meteora.py +10 -2
- sol_parser/account_fillers/raydium.py +9 -4
- sol_parser/account_fillers/raydium_launchlab.py +54 -0
- sol_parser/accounts/__init__.py +52 -2
- sol_parser/block_meta.py +29 -0
- sol_parser/dex_parsers.py +662 -69
- sol_parser/entries_decode.py +6 -80
- sol_parser/event_type_parity.py +4 -0
- sol_parser/event_types.py +269 -3
- sol_parser/grpc/subscribe_builder.py +8 -0
- sol_parser/grpc_client.py +114 -20
- sol_parser/grpc_instruction_parser.py +85 -26
- sol_parser/grpc_types.py +31 -0
- sol_parser/inner_instruction_parser.py +5 -7
- sol_parser/instructions.py +259 -53
- sol_parser/json_util.py +15 -1
- sol_parser/liquidity_snapshot.py +101 -0
- sol_parser/log_instr_dedup.py +56 -12
- sol_parser/merger.py +73 -85
- sol_parser/parser.py +11 -0
- sol_parser/pumpfun_fee_enrich.py +2 -0
- sol_parser/rpc_parser.py +30 -4
- sol_parser/simulation_route.py +233 -0
- sol_parser/stonkfun_registry.py +139 -0
- sol_parser/transaction_route.py +510 -0
- sol_parser/wire_transaction.py +172 -0
- {sol_parser_sdk_python-0.5.7.dist-info → sol_parser_sdk_python-0.5.9.dist-info}/METADATA +44 -9
- {sol_parser_sdk_python-0.5.7.dist-info → sol_parser_sdk_python-0.5.9.dist-info}/RECORD +32 -26
- {sol_parser_sdk_python-0.5.7.dist-info → sol_parser_sdk_python-0.5.9.dist-info}/WHEEL +1 -1
- {sol_parser_sdk_python-0.5.7.dist-info → sol_parser_sdk_python-0.5.9.dist-info}/entry_points.txt +0 -0
sol_parser/__init__.py
CHANGED
|
@@ -152,6 +152,11 @@ from .event_types import (
|
|
|
152
152
|
MeteoraDammV2AddLiquidityEvent,
|
|
153
153
|
MeteoraDammV2RemoveLiquidityEvent,
|
|
154
154
|
MeteoraDammV2InitializePoolEvent,
|
|
155
|
+
MeteoraDammV2DynamicFeeParameters,
|
|
156
|
+
MeteoraDammV2UpdateDelegatePermissionEvent,
|
|
157
|
+
MeteoraDammV2WithdrawDeadLiquidityRewardEvent,
|
|
158
|
+
MeteoraDammV2CreateConfigEvent,
|
|
159
|
+
MeteoraDammV2CreateDynamicConfigEvent,
|
|
155
160
|
MeteoraDbcCurveCompleteEvent,
|
|
156
161
|
MeteoraDbcInitializePoolEvent,
|
|
157
162
|
MeteoraDbcSwapEvent,
|
|
@@ -432,6 +437,11 @@ __all__ = [
|
|
|
432
437
|
"MeteoraDammV2AddLiquidityEvent",
|
|
433
438
|
"MeteoraDammV2RemoveLiquidityEvent",
|
|
434
439
|
"MeteoraDammV2InitializePoolEvent",
|
|
440
|
+
"MeteoraDammV2DynamicFeeParameters",
|
|
441
|
+
"MeteoraDammV2UpdateDelegatePermissionEvent",
|
|
442
|
+
"MeteoraDammV2WithdrawDeadLiquidityRewardEvent",
|
|
443
|
+
"MeteoraDammV2CreateConfigEvent",
|
|
444
|
+
"MeteoraDammV2CreateDynamicConfigEvent",
|
|
435
445
|
"MeteoraDbcCurveCompleteEvent",
|
|
436
446
|
"MeteoraDbcInitializePoolEvent",
|
|
437
447
|
"MeteoraDbcSwapEvent",
|
|
@@ -456,3 +466,27 @@ __all__ = [
|
|
|
456
466
|
"ShredStreamClient",
|
|
457
467
|
"ShredStreamConfig",
|
|
458
468
|
]
|
|
469
|
+
|
|
470
|
+
# Native route and subscription state APIs aligned with Rust 0.7.7.
|
|
471
|
+
from .transaction_route import (analyze_rpc_transaction_routes, TransactionRoute, RouteSwapLeg,
|
|
472
|
+
RouteTokenTransfer, RouteUnknownInvocation, RouteNativeTokenAction, InstructionPosition,
|
|
473
|
+
stonkfun_mode_from_platform_config)
|
|
474
|
+
from .stonkfun_registry import StonkFunPoolRegistry, StonkFunGraduatedPool
|
|
475
|
+
from .liquidity_snapshot import RawAccountSnapshotEvent, LiquidityAccountSnapshotEvent, parse_liquidity_account
|
|
476
|
+
from .wire_transaction import decode_wire_transaction
|
|
477
|
+
|
|
478
|
+
from .simulation_route import analyze_simulation_routes
|
|
479
|
+
|
|
480
|
+
__all__.append("analyze_simulation_routes")
|
|
481
|
+
|
|
482
|
+
from .block_meta import BlockMetaEvent, parse_block_meta_update
|
|
483
|
+
from .grpc.subscribe_builder import build_subscribe_request_with_event_filter
|
|
484
|
+
|
|
485
|
+
from .event_types import (
|
|
486
|
+
LaunchLabPoolCreateEvent, StonkFunPoolCreateEvent,
|
|
487
|
+
LaunchLabTradeEvent, StonkFunTradeEvent,
|
|
488
|
+
)
|
|
489
|
+
__all__ += ["LaunchLabPoolCreateEvent", "StonkFunPoolCreateEvent", "LaunchLabTradeEvent", "StonkFunTradeEvent"]
|
|
490
|
+
|
|
491
|
+
from .grpc_client import DexEventQueue, GrpcStreamStatus
|
|
492
|
+
__all__ += ["DexEventQueue", "GrpcStreamStatus"]
|
sol_parser/account_dispatcher.py
CHANGED
|
@@ -82,11 +82,26 @@ def fill_accounts_with_owned_keys(
|
|
|
82
82
|
w = [bytes(x) for x in meta_pb.loaded_writable_addresses]
|
|
83
83
|
r = [bytes(x) for x in meta_pb.loaded_readonly_addresses]
|
|
84
84
|
|
|
85
|
-
def run(program_b58: str, filler) -> None:
|
|
85
|
+
def run(program_b58: str, filler, anchor=None, unambiguous=False) -> None:
|
|
86
86
|
pid = base58.b58decode(program_b58)
|
|
87
87
|
inv = invokes.get(pid)
|
|
88
88
|
if not inv:
|
|
89
89
|
return
|
|
90
|
+
if anchor is not None or unambiguous:
|
|
91
|
+
pool, index = anchor or (None,1)
|
|
92
|
+
has_anchor=pool and pool != "11111111111111111111111111111111"
|
|
93
|
+
if not has_anchor and not unambiguous:return
|
|
94
|
+
selected = None
|
|
95
|
+
for invocation in inv:
|
|
96
|
+
candidate = get_instruction_account_getter(meta_pb, transaction_pb, static_keys, w, r, invocation)
|
|
97
|
+
if candidate is None or (has_anchor and candidate(index) != pool):
|
|
98
|
+
continue
|
|
99
|
+
if selected is not None and any(selected(i) != candidate(i) for i in range(18)):
|
|
100
|
+
return # Same pool, conflicting wallet/account context: do not guess.
|
|
101
|
+
selected = candidate
|
|
102
|
+
if selected is not None:
|
|
103
|
+
filler(selected)
|
|
104
|
+
return
|
|
90
105
|
ix = find_instruction_invoke(inv, meta_pb, transaction_pb)
|
|
91
106
|
if ix is None:
|
|
92
107
|
return
|
|
@@ -146,7 +161,7 @@ def fill_accounts_with_owned_keys(
|
|
|
146
161
|
elif et == EventType.RAYDIUM_CPMM_INITIALIZE:
|
|
147
162
|
run(RAYDIUM_CPMM_PROGRAM_ID, lambda g: raydium.fill_cpmm_initialize_accounts(data, g))
|
|
148
163
|
elif et == EventType.RAYDIUM_AMM_V4_SWAP:
|
|
149
|
-
run(RAYDIUM_AMM_V4_PROGRAM_ID, lambda g: raydium.fill_amm_v4_swap_accounts(data, g))
|
|
164
|
+
run(RAYDIUM_AMM_V4_PROGRAM_ID, lambda g: raydium.fill_amm_v4_swap_accounts(data, g), (data.amm,1), True)
|
|
150
165
|
elif et == EventType.RAYDIUM_AMM_V4_DEPOSIT:
|
|
151
166
|
run(RAYDIUM_AMM_V4_PROGRAM_ID, lambda g: raydium.fill_amm_v4_deposit_accounts(data, g))
|
|
152
167
|
elif et == EventType.RAYDIUM_AMM_V4_WITHDRAW:
|
|
@@ -182,9 +197,9 @@ def fill_accounts_with_owned_keys(
|
|
|
182
197
|
elif et == EventType.METEORA_DLMM_REMOVE_LIQUIDITY:
|
|
183
198
|
run(METEORA_DLMM_PROGRAM_ID, lambda g: meteora.fill_dlmm_remove_liquidity_accounts(data, g))
|
|
184
199
|
elif et == EventType.RAYDIUM_LAUNCHLAB_TRADE:
|
|
185
|
-
run(RAYDIUM_LAUNCHLAB_PROGRAM_ID, lambda g: raydium_launchlab.fill_trade_accounts(data, g))
|
|
200
|
+
run(RAYDIUM_LAUNCHLAB_PROGRAM_ID, lambda g: raydium_launchlab.fill_trade_accounts(data, g), (data.pool_state,4))
|
|
186
201
|
elif et == EventType.RAYDIUM_LAUNCHLAB_POOL_CREATE:
|
|
187
|
-
run(RAYDIUM_LAUNCHLAB_PROGRAM_ID, lambda g: raydium_launchlab.fill_pool_create_accounts(data, g))
|
|
202
|
+
run(RAYDIUM_LAUNCHLAB_PROGRAM_ID, lambda g: raydium_launchlab.fill_pool_create_accounts(data, g), (data.pool_state,5))
|
|
188
203
|
|
|
189
204
|
|
|
190
205
|
def fill_data(
|
|
@@ -209,3 +224,82 @@ def fill_data(
|
|
|
209
224
|
return
|
|
210
225
|
is_pump = raw[9] != 0
|
|
211
226
|
setattr(data, "is_pump_pool", is_pump)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _account_index(transaction_pb: Any, meta_pb: Any, account: str) -> Optional[int]:
|
|
230
|
+
if not account or account == "11111111111111111111111111111111":
|
|
231
|
+
return None
|
|
232
|
+
msg = getattr(transaction_pb, "message", None)
|
|
233
|
+
if msg is None:
|
|
234
|
+
return None
|
|
235
|
+
keys: List[str] = []
|
|
236
|
+
for b in getattr(msg, "account_keys", []) or []:
|
|
237
|
+
raw = bytes(b) if not isinstance(b, (bytes, bytearray)) else bytes(b)
|
|
238
|
+
if len(raw) == 32:
|
|
239
|
+
keys.append(base58.b58encode(raw).decode("ascii"))
|
|
240
|
+
for b in getattr(meta_pb, "loaded_writable_addresses", []) or []:
|
|
241
|
+
raw = bytes(b) if not isinstance(b, (bytes, bytearray)) else bytes(b)
|
|
242
|
+
if len(raw) == 32:
|
|
243
|
+
keys.append(base58.b58encode(raw).decode("ascii"))
|
|
244
|
+
for b in getattr(meta_pb, "loaded_readonly_addresses", []) or []:
|
|
245
|
+
raw = bytes(b) if not isinstance(b, (bytes, bytearray)) else bytes(b)
|
|
246
|
+
if len(raw) == 32:
|
|
247
|
+
keys.append(base58.b58encode(raw).decode("ascii"))
|
|
248
|
+
try:
|
|
249
|
+
return keys.index(account)
|
|
250
|
+
except ValueError:
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _token_balance_raw(t: Any) -> int:
|
|
255
|
+
ui = getattr(t, "ui_token_amount", None)
|
|
256
|
+
if ui is None:
|
|
257
|
+
return 0
|
|
258
|
+
amt = getattr(ui, "amount", None)
|
|
259
|
+
if amt is None:
|
|
260
|
+
return 0
|
|
261
|
+
try:
|
|
262
|
+
return int(str(amt))
|
|
263
|
+
except Exception:
|
|
264
|
+
return 0
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def fill_token_balances(event: DexEvent, meta_pb: Any, transaction_pb: Any) -> None:
|
|
268
|
+
"""对齐 Rust ``common_filler::fill_token_balances``(PumpFun trade 用户余额)。"""
|
|
269
|
+
if meta_pb is None or transaction_pb is None:
|
|
270
|
+
return
|
|
271
|
+
if event.type not in (
|
|
272
|
+
EventType.PUMP_FUN_TRADE,
|
|
273
|
+
EventType.PUMP_FUN_BUY,
|
|
274
|
+
EventType.PUMP_FUN_SELL,
|
|
275
|
+
EventType.PUMP_FUN_BUY_EXACT_SOL_IN,
|
|
276
|
+
):
|
|
277
|
+
return
|
|
278
|
+
trade = event.data
|
|
279
|
+
user = getattr(trade, "user", "") or ""
|
|
280
|
+
associated_user = getattr(trade, "associated_user", "") or ""
|
|
281
|
+
user_idx = _account_index(transaction_pb, meta_pb, user)
|
|
282
|
+
if user_idx is not None:
|
|
283
|
+
pre_balances = list(getattr(meta_pb, "pre_balances", []) or [])
|
|
284
|
+
post_balances = list(getattr(meta_pb, "post_balances", []) or [])
|
|
285
|
+
if user_idx < len(pre_balances):
|
|
286
|
+
trade.pre_sol_balance = int(pre_balances[user_idx])
|
|
287
|
+
if user_idx < len(post_balances):
|
|
288
|
+
trade.post_sol_balance = int(post_balances[user_idx])
|
|
289
|
+
token_idx = _account_index(transaction_pb, meta_pb, associated_user)
|
|
290
|
+
if token_idx is None:
|
|
291
|
+
return
|
|
292
|
+
token_idx_u32 = token_idx
|
|
293
|
+
pre = None
|
|
294
|
+
post = None
|
|
295
|
+
for bal in getattr(meta_pb, "pre_token_balances", []) or []:
|
|
296
|
+
if int(getattr(bal, "account_index", -1)) == token_idx_u32:
|
|
297
|
+
pre = _token_balance_raw(bal)
|
|
298
|
+
break
|
|
299
|
+
for bal in getattr(meta_pb, "post_token_balances", []) or []:
|
|
300
|
+
if int(getattr(bal, "account_index", -1)) == token_idx_u32:
|
|
301
|
+
post = _token_balance_raw(bal)
|
|
302
|
+
break
|
|
303
|
+
if pre is not None or post is not None:
|
|
304
|
+
trade.pre_token_balance = pre if pre is not None else 0
|
|
305
|
+
trade.post_token_balance = post if post is not None else 0
|
|
@@ -54,8 +54,16 @@ def fill_pools_remove_liquidity_accounts(_e: Any, _get: AccountGetter) -> None:
|
|
|
54
54
|
pass
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def fill_dlmm_swap_accounts(
|
|
58
|
-
|
|
57
|
+
def fill_dlmm_swap_accounts(e: Any, get: AccountGetter) -> None:
|
|
58
|
+
z = "11111111111111111111111111111111"
|
|
59
|
+
if not getattr(e, "user_token_in", "") or e.user_token_in == z:
|
|
60
|
+
e.user_token_in = get(4)
|
|
61
|
+
if not getattr(e, "user_token_out", "") or e.user_token_out == z:
|
|
62
|
+
e.user_token_out = get(5)
|
|
63
|
+
if not getattr(e, "token_x_mint", "") or e.token_x_mint == z:
|
|
64
|
+
e.token_x_mint = get(6)
|
|
65
|
+
if not getattr(e, "token_y_mint", "") or e.token_y_mint == z:
|
|
66
|
+
e.token_y_mint = get(7)
|
|
59
67
|
|
|
60
68
|
|
|
61
69
|
def fill_dlmm_add_liquidity_accounts(_e: Any, _get: AccountGetter) -> None:
|
|
@@ -77,8 +77,9 @@ def fill_clmm_close_position_accounts(e: RaydiumClmmClosePositionEvent, get: Acc
|
|
|
77
77
|
e.position_nft_mint = get(1)
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
def fill_cpmm_swap_accounts(
|
|
81
|
-
|
|
80
|
+
def fill_cpmm_swap_accounts(e: RaydiumCpmmSwapEvent, get: AccountGetter) -> None:
|
|
81
|
+
if _empty(e.pool_id):
|
|
82
|
+
e.pool_id = get(3)
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
def fill_cpmm_deposit_accounts(e: RaydiumCpmmDepositEvent, get: AccountGetter) -> None:
|
|
@@ -99,8 +100,12 @@ def fill_cpmm_initialize_accounts(e: RaydiumCpmmInitializeEvent, get: AccountGet
|
|
|
99
100
|
|
|
100
101
|
|
|
101
102
|
def fill_amm_v4_swap_accounts(e: RaydiumAmmV4SwapEvent, get: AccountGetter) -> None:
|
|
102
|
-
if _empty(e.amm):
|
|
103
|
-
|
|
103
|
+
if _empty(e.amm):e.amm=get(1)
|
|
104
|
+
modern=_empty(get(8)) and not _empty(get(7))
|
|
105
|
+
if modern:
|
|
106
|
+
for field,index in [('token_program',0),('amm_authority',2),('pool_coin_token_account',3),('pool_pc_token_account',4),('user_source_token_account',5),('user_destination_token_account',6),('user_source_owner',7)]:
|
|
107
|
+
if _empty(getattr(e,field)):setattr(e,field,get(index))
|
|
108
|
+
elif _empty(e.user_source_owner):e.user_source_owner=get(16) if _empty(get(17)) else get(17)
|
|
104
109
|
|
|
105
110
|
|
|
106
111
|
def fill_amm_v4_deposit_accounts(e: RaydiumAmmV4DepositEvent, get: AccountGetter) -> None:
|
|
@@ -7,6 +7,7 @@ from typing import Callable
|
|
|
7
7
|
from ..event_types import RaydiumLaunchlabPoolCreateEvent, RaydiumLaunchlabTradeEvent
|
|
8
8
|
|
|
9
9
|
Z = "11111111111111111111111111111111"
|
|
10
|
+
RAYDIUM_LAUNCHLAB_PROGRAM_ID = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def _empty(s: str) -> bool:
|
|
@@ -21,6 +22,30 @@ def fill_trade_accounts(e: RaydiumLaunchlabTradeEvent, get: AccountGetter) -> No
|
|
|
21
22
|
e.user = get(0)
|
|
22
23
|
if _empty(e.pool_state):
|
|
23
24
|
e.pool_state = get(4)
|
|
25
|
+
if _empty(e.global_config):
|
|
26
|
+
e.global_config = get(2)
|
|
27
|
+
if _empty(e.platform_config):
|
|
28
|
+
e.platform_config = get(3)
|
|
29
|
+
if _empty(e.user_base_token):
|
|
30
|
+
e.user_base_token = get(5)
|
|
31
|
+
if _empty(e.user_quote_token):
|
|
32
|
+
e.user_quote_token = get(6)
|
|
33
|
+
if _empty(e.base_vault):
|
|
34
|
+
e.base_vault = get(7)
|
|
35
|
+
if _empty(e.quote_vault):
|
|
36
|
+
e.quote_vault = get(8)
|
|
37
|
+
if _empty(e.base_mint):
|
|
38
|
+
e.base_mint = get(9)
|
|
39
|
+
if _empty(e.quote_mint):
|
|
40
|
+
e.quote_mint = get(10)
|
|
41
|
+
if _empty(e.base_token_program):
|
|
42
|
+
e.base_token_program = get(11)
|
|
43
|
+
if _empty(e.quote_token_program):
|
|
44
|
+
e.quote_token_program = get(12)
|
|
45
|
+
|
|
46
|
+
for name, index in [("system_program", 15), ("platform_associated_account", 16), ("creator_associated_account", 17)]:
|
|
47
|
+
if _empty(getattr(e,name,"")):
|
|
48
|
+
setattr(e,name,get(index))
|
|
24
49
|
|
|
25
50
|
|
|
26
51
|
def fill_pool_create_accounts(e: RaydiumLaunchlabPoolCreateEvent, get: AccountGetter) -> None:
|
|
@@ -28,3 +53,32 @@ def fill_pool_create_accounts(e: RaydiumLaunchlabPoolCreateEvent, get: AccountGe
|
|
|
28
53
|
e.pool_state = get(5)
|
|
29
54
|
if _empty(e.creator):
|
|
30
55
|
e.creator = get(1)
|
|
56
|
+
if _empty(e.payer):
|
|
57
|
+
e.payer = get(0)
|
|
58
|
+
if _empty(e.global_config):
|
|
59
|
+
e.global_config = get(2)
|
|
60
|
+
if _empty(e.platform_config):
|
|
61
|
+
e.platform_config = get(3)
|
|
62
|
+
if _empty(e.base_mint):
|
|
63
|
+
e.base_mint = get(6)
|
|
64
|
+
if _empty(e.quote_mint):
|
|
65
|
+
e.quote_mint = get(7)
|
|
66
|
+
if _empty(e.base_vault):
|
|
67
|
+
e.base_vault = get(8)
|
|
68
|
+
if _empty(e.quote_vault):
|
|
69
|
+
e.quote_vault = get(9)
|
|
70
|
+
# initialize / initialize_v2 use token-program indices 11-12;
|
|
71
|
+
# initialize_with_token_2022 uses 10-11 (detect via program id at end).
|
|
72
|
+
try:
|
|
73
|
+
if get(17) == RAYDIUM_LAUNCHLAB_PROGRAM_ID:
|
|
74
|
+
bi, qi = 11, 12
|
|
75
|
+
elif get(14) == RAYDIUM_LAUNCHLAB_PROGRAM_ID:
|
|
76
|
+
bi, qi = 10, 11
|
|
77
|
+
else:
|
|
78
|
+
return
|
|
79
|
+
except Exception:
|
|
80
|
+
return
|
|
81
|
+
if _empty(e.base_token_program):
|
|
82
|
+
e.base_token_program = get(bi)
|
|
83
|
+
if _empty(e.quote_token_program):
|
|
84
|
+
e.quote_token_program = get(qi)
|
sol_parser/accounts/__init__.py
CHANGED
|
@@ -42,10 +42,15 @@ TOKEN_ACCOUNT_SIZE = 165
|
|
|
42
42
|
NONCE_ACCOUNT_SIZE = 80
|
|
43
43
|
PUMPFUN_GLOBAL_BODY = 1037
|
|
44
44
|
PUMPFUN_BONDING_CURVE_BODY = 107
|
|
45
|
+
PUMPFUN_BONDING_CURVE_CREATOR_FEE_BODY = 116
|
|
46
|
+
PUMPFUN_BONDING_CURVE_HOLDER_REWARD_BODY = 117
|
|
45
47
|
PUMPFUN_GLOBAL_VOLUME_ACCUMULATOR_BODY = 536
|
|
46
48
|
PUMPFUN_USER_VOLUME_ACCUMULATOR_BODY = 98
|
|
47
49
|
GLOBAL_CONFIG_BODY = 634
|
|
48
|
-
|
|
50
|
+
POOL_LEGACY_BODY = 244
|
|
51
|
+
POOL_BOOST_BODY = 253
|
|
52
|
+
POOL_CREATOR_FEE_BODY = 262
|
|
53
|
+
POOL_BODY = 263
|
|
49
54
|
MAX_PUMPFUN_FEE_TIERS = 64
|
|
50
55
|
MAX_PUMPFUN_SHAREHOLDERS = 64
|
|
51
56
|
|
|
@@ -201,6 +206,11 @@ def parse_account_unified(
|
|
|
201
206
|
event_type_filter: Optional[EventTypeFilter] = None,
|
|
202
207
|
) -> Optional[DexEvent]:
|
|
203
208
|
"""对齐 Rust ``parse_account_unified``(含 ``Option<EventTypeFilter>`` 语义)。"""
|
|
209
|
+
from ..liquidity_snapshot import parse_liquidity_account
|
|
210
|
+
if event_type_filter is None or event_type_filter.should_include(EventType.ACCOUNT_LIQUIDITY_SNAPSHOT):
|
|
211
|
+
snapshot = parse_liquidity_account(account, metadata)
|
|
212
|
+
if snapshot is not None:
|
|
213
|
+
return DexEvent(type=EventType.ACCOUNT_LIQUIDITY_SNAPSHOT, data=snapshot)
|
|
204
214
|
data = account.data
|
|
205
215
|
if not data:
|
|
206
216
|
return None
|
|
@@ -483,6 +493,12 @@ def _parse_pumpfun_bonding_curve_fast(
|
|
|
483
493
|
is_cashback_coin = data[o] != 0
|
|
484
494
|
o += 1
|
|
485
495
|
quote_mint = read_pubkey_fast(data, o)
|
|
496
|
+
o += 32
|
|
497
|
+
creator_fee_bps = read_u64_fast(data, o) if o + 8 <= len(data) else 0
|
|
498
|
+
o += 8
|
|
499
|
+
can_edit_creator_fee = data[o] != 0 if o < len(data) else False
|
|
500
|
+
o += 1
|
|
501
|
+
is_holder_reward = data[o] != 0 if o < len(data) else False
|
|
486
502
|
return _account_event(
|
|
487
503
|
EventType.ACCOUNT_PUMP_FUN_BONDING_CURVE,
|
|
488
504
|
{
|
|
@@ -499,6 +515,9 @@ def _parse_pumpfun_bonding_curve_fast(
|
|
|
499
515
|
"is_mayhem_mode": is_mayhem_mode,
|
|
500
516
|
"is_cashback_coin": is_cashback_coin,
|
|
501
517
|
"quote_mint": quote_mint,
|
|
518
|
+
"creator_fee_bps": creator_fee_bps,
|
|
519
|
+
"can_edit_creator_fee": can_edit_creator_fee,
|
|
520
|
+
"is_holder_reward": is_holder_reward,
|
|
502
521
|
},
|
|
503
522
|
},
|
|
504
523
|
)
|
|
@@ -752,6 +771,18 @@ def _parse_pumpswap_pool_fast(account: AccountData, metadata: EventMetadata) ->
|
|
|
752
771
|
o += 32
|
|
753
772
|
is_mayhem = data[o] != 0
|
|
754
773
|
is_cashback = data[o + 1] != 0
|
|
774
|
+
o += 2
|
|
775
|
+
virtual_quote_reserves = (
|
|
776
|
+
int.from_bytes(data[o : o + 16], "little", signed=True)
|
|
777
|
+
if len(data) >= POOL_BOOST_BODY
|
|
778
|
+
else 0
|
|
779
|
+
)
|
|
780
|
+
o += 16
|
|
781
|
+
creator_fee_bps = read_u64_fast(data, o) if o + 8 <= len(data) else 0
|
|
782
|
+
o += 8
|
|
783
|
+
can_edit_creator_fee = data[o] != 0 if o < len(data) else False
|
|
784
|
+
o += 1
|
|
785
|
+
is_holder_reward = data[o] != 0 if o < len(data) else False
|
|
755
786
|
return _account_event(
|
|
756
787
|
EventType.ACCOUNT_PUMP_SWAP_POOL,
|
|
757
788
|
{
|
|
@@ -770,6 +801,10 @@ def _parse_pumpswap_pool_fast(account: AccountData, metadata: EventMetadata) ->
|
|
|
770
801
|
"coin_creator": coin_creator,
|
|
771
802
|
"is_mayhem_mode": is_mayhem,
|
|
772
803
|
"is_cashback_coin": is_cashback,
|
|
804
|
+
"virtual_quote_reserves": virtual_quote_reserves,
|
|
805
|
+
"creator_fee_bps": creator_fee_bps,
|
|
806
|
+
"can_edit_creator_fee": can_edit_creator_fee,
|
|
807
|
+
"is_holder_reward": is_holder_reward,
|
|
773
808
|
},
|
|
774
809
|
},
|
|
775
810
|
)
|
|
@@ -807,6 +842,13 @@ def parse_pumpfun_bonding_curve(
|
|
|
807
842
|
) -> Optional[DexEvent]:
|
|
808
843
|
if len(account.data) < 8 + PUMPFUN_BONDING_CURVE_BODY:
|
|
809
844
|
return None
|
|
845
|
+
body_len = len(account.data) - 8
|
|
846
|
+
if (
|
|
847
|
+
body_len != PUMPFUN_BONDING_CURVE_BODY
|
|
848
|
+
and body_len != PUMPFUN_BONDING_CURVE_CREATOR_FEE_BODY
|
|
849
|
+
and body_len < PUMPFUN_BONDING_CURVE_HOLDER_REWARD_BODY
|
|
850
|
+
):
|
|
851
|
+
return None
|
|
810
852
|
if not has_discriminator(account.data, _DISC_PUMPFUN_BONDING_CURVE):
|
|
811
853
|
return None
|
|
812
854
|
return _parse_pumpfun_bonding_curve_fast(account, metadata)
|
|
@@ -861,7 +903,15 @@ def parse_pumpswap_global_config(account: AccountData, metadata: EventMetadata)
|
|
|
861
903
|
|
|
862
904
|
|
|
863
905
|
def parse_pumpswap_pool(account: AccountData, metadata: EventMetadata) -> Optional[DexEvent]:
|
|
864
|
-
if len(account.data) < 8 +
|
|
906
|
+
if len(account.data) < 8 + POOL_LEGACY_BODY:
|
|
907
|
+
return None
|
|
908
|
+
body_len = len(account.data) - 8
|
|
909
|
+
if (
|
|
910
|
+
body_len != POOL_LEGACY_BODY
|
|
911
|
+
and body_len != POOL_BOOST_BODY
|
|
912
|
+
and body_len != POOL_CREATOR_FEE_BODY
|
|
913
|
+
and body_len < POOL_BODY
|
|
914
|
+
):
|
|
865
915
|
return None
|
|
866
916
|
if not has_discriminator(account.data, _DISC_POOL):
|
|
867
917
|
return None
|
sol_parser/block_meta.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Local BlockMeta adapter, matching Rust's saturated timestamp conversion."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from .grpc_types import EventType, EventMetadata
|
|
5
|
+
from .event_types import DexEvent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class BlockMetaEvent:
|
|
10
|
+
metadata: EventMetadata
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_block_meta_update(meta, grpc_recv_us, fallback_block_us=None):
|
|
14
|
+
block_us = fallback_block_us or 0
|
|
15
|
+
if meta.block_time is not None:
|
|
16
|
+
block_us = max(-(1 << 63), min((1 << 63) - 1, meta.block_time * 1_000_000))
|
|
17
|
+
return DexEvent(
|
|
18
|
+
EventType.BLOCK_META,
|
|
19
|
+
BlockMetaEvent(
|
|
20
|
+
EventMetadata(
|
|
21
|
+
signature="1" * 64,
|
|
22
|
+
slot=meta.slot,
|
|
23
|
+
tx_index=0,
|
|
24
|
+
block_time_us=block_us,
|
|
25
|
+
grpc_recv_us=grpc_recv_us,
|
|
26
|
+
recent_blockhash=meta.blockhash,
|
|
27
|
+
)
|
|
28
|
+
),
|
|
29
|
+
)
|