sol-parser-sdk-python 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. sol_parser/__init__.py +400 -0
  2. sol_parser/account_dispatcher.py +209 -0
  3. sol_parser/account_fillers/__init__.py +5 -0
  4. sol_parser/account_fillers/bonk.py +30 -0
  5. sol_parser/account_fillers/meteora.py +51 -0
  6. sol_parser/account_fillers/orca.py +40 -0
  7. sol_parser/account_fillers/pumpfun.py +97 -0
  8. sol_parser/account_fillers/pumpswap.py +93 -0
  9. sol_parser/account_fillers/raydium.py +119 -0
  10. sol_parser/accounts/__init__.py +461 -0
  11. sol_parser/accounts/rpc_wallet.py +64 -0
  12. sol_parser/accounts/utils.py +71 -0
  13. sol_parser/check_migration.py +18 -0
  14. sol_parser/clock.py +10 -0
  15. sol_parser/common/__init__.py +27 -0
  16. sol_parser/dex_parsers.py +2576 -0
  17. sol_parser/entries_decode.py +186 -0
  18. sol_parser/env_config.py +215 -0
  19. sol_parser/event_types.py +1750 -0
  20. sol_parser/geyser_pb2.py +148 -0
  21. sol_parser/geyser_pb2_grpc.py +398 -0
  22. sol_parser/grpc/__init__.py +61 -0
  23. sol_parser/grpc/geyser_connect.py +42 -0
  24. sol_parser/grpc/subscribe_builder.py +133 -0
  25. sol_parser/grpc/transaction_meta.py +183 -0
  26. sol_parser/grpc_client.py +870 -0
  27. sol_parser/grpc_instruction_parser.py +318 -0
  28. sol_parser/grpc_types.py +919 -0
  29. sol_parser/inner_instruction_parser.py +281 -0
  30. sol_parser/instr/__init__.py +15 -0
  31. sol_parser/instr_account_utils.py +58 -0
  32. sol_parser/instructions.py +1026 -0
  33. sol_parser/json_util.py +41 -0
  34. sol_parser/log_instr_dedup.py +284 -0
  35. sol_parser/logs/__init__.py +15 -0
  36. sol_parser/merger.py +233 -0
  37. sol_parser/order_buffer.py +171 -0
  38. sol_parser/parser.py +300 -0
  39. sol_parser/pumpfun_fee_enrich.py +75 -0
  40. sol_parser/rpc_parser.py +655 -0
  41. sol_parser/rust_api_inventory.py +42 -0
  42. sol_parser/rust_event_json.py +50 -0
  43. sol_parser/shredstream_client.py +191 -0
  44. sol_parser/shredstream_pb2.py +40 -0
  45. sol_parser/shredstream_pb2_grpc.py +81 -0
  46. sol_parser/shredstream_pumpfun.py +296 -0
  47. sol_parser/solana_storage_pb2.py +75 -0
  48. sol_parser/solana_storage_pb2_grpc.py +24 -0
  49. sol_parser/u128_parity.py +115 -0
  50. sol_parser/verify_discriminators.py +85 -0
  51. sol_parser_sdk_python-0.4.4.dist-info/METADATA +14 -0
  52. sol_parser_sdk_python-0.4.4.dist-info/RECORD +54 -0
  53. sol_parser_sdk_python-0.4.4.dist-info/WHEEL +4 -0
  54. sol_parser_sdk_python-0.4.4.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,133 @@
1
+ """对齐 Rust ``grpc/subscribe_builder.rs``:构造 Yellowstone ``SubscribeRequest``。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Tuple, Union
6
+
7
+ from ..grpc_types import AccountFilter, CommitmentLevel, TransactionFilter
8
+
9
+ try:
10
+ from .. import geyser_pb2
11
+ except ImportError:
12
+ geyser_pb2 = None # type: ignore
13
+
14
+
15
+ def _tx_filter_to_proto(f: TransactionFilter) -> Any:
16
+ if geyser_pb2 is None:
17
+ raise ImportError("需要 geyser_pb2")
18
+ tx = geyser_pb2.SubscribeRequestFilterTransactions(
19
+ vote=False,
20
+ failed=False,
21
+ account_include=list(f.account_include),
22
+ account_exclude=list(f.account_exclude),
23
+ account_required=list(f.account_required),
24
+ )
25
+ if f.vote is not None:
26
+ tx.vote = f.vote
27
+ if f.failed is not None:
28
+ tx.failed = f.failed
29
+ if f.signature:
30
+ tx.signature = f.signature
31
+ return tx
32
+
33
+
34
+ def _acc_filter_to_proto(f: AccountFilter) -> Any:
35
+ if geyser_pb2 is None:
36
+ raise ImportError("需要 geyser_pb2")
37
+ flist = []
38
+ for sub in f.filters:
39
+ one = geyser_pb2.SubscribeRequestFilterAccountsFilter()
40
+ if sub.memcmp is not None:
41
+ m = sub.memcmp
42
+ mc = geyser_pb2.SubscribeRequestFilterAccountsFilterMemcmp(offset=m.offset)
43
+ if m.bytes is not None:
44
+ mc.bytes = bytes(m.bytes)
45
+ elif m.base58:
46
+ mc.base58 = m.base58
47
+ elif m.base64:
48
+ mc.base64 = m.base64
49
+ one.memcmp.CopyFrom(mc)
50
+ if sub.datasize is not None:
51
+ one.datasize = sub.datasize
52
+ if sub.token_account_state is not None:
53
+ one.token_account_state = sub.token_account_state
54
+ if sub.lamports is not None:
55
+ lp = geyser_pb2.SubscribeRequestFilterAccountsFilterLamports()
56
+ if sub.lamports.eq is not None:
57
+ lp.eq = sub.lamports.eq
58
+ if sub.lamports.ne is not None:
59
+ lp.ne = sub.lamports.ne
60
+ if sub.lamports.lt is not None:
61
+ lp.lt = sub.lamports.lt
62
+ if sub.lamports.gt is not None:
63
+ lp.gt = sub.lamports.gt
64
+ one.lamports.CopyFrom(lp)
65
+ flist.append(one)
66
+ return geyser_pb2.SubscribeRequestFilterAccounts(
67
+ account=list(f.account),
68
+ owner=list(f.owner),
69
+ filters=flist,
70
+ )
71
+
72
+
73
+ def _finalize(
74
+ transactions: Dict[str, Any],
75
+ accounts: Dict[str, Any],
76
+ commitment: CommitmentLevel,
77
+ ) -> Any:
78
+ if geyser_pb2 is None:
79
+ raise ImportError("需要 geyser_pb2")
80
+ return geyser_pb2.SubscribeRequest(
81
+ slots={},
82
+ accounts=accounts,
83
+ transactions=transactions,
84
+ transactions_status={},
85
+ blocks={},
86
+ blocks_meta={},
87
+ entry={},
88
+ commitment=commitment.value,
89
+ accounts_data_slice=[],
90
+ ping=None,
91
+ )
92
+
93
+
94
+ def build_subscribe_request(
95
+ tx_filters: List[TransactionFilter],
96
+ acc_filters: List[AccountFilter],
97
+ ) -> Any:
98
+ """对齐 Rust ``build_subscribe_request``(commitment = Processed)。"""
99
+ return build_subscribe_request_with_commitment(
100
+ tx_filters, acc_filters, CommitmentLevel.PROCESSED
101
+ )
102
+
103
+
104
+ def build_subscribe_request_with_commitment(
105
+ tx_filters: List[TransactionFilter],
106
+ acc_filters: List[AccountFilter],
107
+ commitment: CommitmentLevel,
108
+ ) -> Any:
109
+ if geyser_pb2 is None:
110
+ raise ImportError("需要 geyser_pb2")
111
+ transactions = {f"tx_{i}": _tx_filter_to_proto(f) for i, f in enumerate(tx_filters)}
112
+ accounts = {f"acc_{i}": _acc_filter_to_proto(f) for i, f in enumerate(acc_filters)}
113
+ return _finalize(transactions, accounts, commitment)
114
+
115
+
116
+ def build_subscribe_transaction_filters_named(
117
+ named_tx_filters: List[Tuple[str, TransactionFilter]],
118
+ acc_filters: List[AccountFilter],
119
+ commitment: CommitmentLevel,
120
+ ) -> Any:
121
+ """对齐 Rust ``build_subscribe_transaction_filters_named``。"""
122
+ if geyser_pb2 is None:
123
+ raise ImportError("需要 geyser_pb2")
124
+ transactions = {name: _tx_filter_to_proto(f) for name, f in named_tx_filters}
125
+ accounts = {f"acc_{i}": _acc_filter_to_proto(f) for i, f in enumerate(acc_filters)}
126
+ return _finalize(transactions, accounts, commitment)
127
+
128
+
129
+ __all__ = [
130
+ "build_subscribe_request",
131
+ "build_subscribe_request_with_commitment",
132
+ "build_subscribe_transaction_filters_named",
133
+ ]
@@ -0,0 +1,183 @@
1
+ """对齐 Rust ``grpc/transaction_meta.rs``(不依赖 DEX 解析):账户 key、lamport delta、转账启发式。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Optional, Set, Tuple
6
+
7
+ import base58
8
+
9
+
10
+ def pubkey_bytes_to_bs58(bs: bytes) -> Optional[str]:
11
+ if len(bs) != 32:
12
+ return None
13
+ return base58.b58encode(bs).decode("ascii")
14
+
15
+
16
+ def collect_account_keys_bs58(tx: Any, meta: Any) -> Optional[List[str]]:
17
+ """对齐 ``collect_account_keys_bs58``:静态 keys + loaded writable/readonly。"""
18
+ msg = getattr(tx, "message", None) or getattr(tx, "Message", None)
19
+ if msg is None:
20
+ return None
21
+ keys: List[str] = []
22
+ for b in getattr(msg, "account_keys", []) or []:
23
+ raw = bytes(b) if not isinstance(b, (bytes, bytearray)) else bytes(b)
24
+ k = pubkey_bytes_to_bs58(raw)
25
+ if k:
26
+ keys.append(k)
27
+ for b in getattr(meta, "loaded_writable_addresses", []) or []:
28
+ raw = bytes(b) if not isinstance(b, (bytes, bytearray)) else bytes(b)
29
+ k = pubkey_bytes_to_bs58(raw)
30
+ if k:
31
+ keys.append(k)
32
+ for b in getattr(meta, "loaded_readonly_addresses", []) or []:
33
+ raw = bytes(b) if not isinstance(b, (bytes, bytearray)) else bytes(b)
34
+ k = pubkey_bytes_to_bs58(raw)
35
+ if k:
36
+ keys.append(k)
37
+ return keys
38
+
39
+
40
+ def lamport_balance_deltas(meta: Any) -> List[int]:
41
+ pre = list(getattr(meta, "pre_balances", []) or [])
42
+ post = list(getattr(meta, "post_balances", []) or [])
43
+ return [int(post[i]) - int(pre[i]) for i in range(min(len(pre), len(post)))]
44
+
45
+
46
+ def heuristic_sol_counterparties_for_watched_keys(
47
+ account_keys_bs58: List[str],
48
+ lamport_deltas: List[int],
49
+ watched_bs58: Set[str],
50
+ min_outflow_lamports: int,
51
+ ) -> List[Tuple[str, str]]:
52
+ min_l = int(min_outflow_lamports)
53
+ pairs: List[Tuple[str, str]] = []
54
+ for i, key in enumerate(account_keys_bs58):
55
+ if key not in watched_bs58:
56
+ continue
57
+ d = lamport_deltas[i] if i < len(lamport_deltas) else 0
58
+ if d >= -min_l:
59
+ continue
60
+ for j, dj in enumerate(lamport_deltas):
61
+ if i == j or dj <= min_l // 2:
62
+ continue
63
+ pairs.append((key, account_keys_bs58[j]))
64
+ return pairs
65
+
66
+
67
+ def token_balance_raw_amount(t: Any) -> int:
68
+ ui = getattr(t, "ui_token_amount", None)
69
+ if ui is None:
70
+ return 0
71
+ amt = getattr(ui, "amount", None)
72
+ if amt is None:
73
+ return 0
74
+ try:
75
+ return int(str(amt))
76
+ except Exception:
77
+ return 0
78
+
79
+
80
+ def spl_token_counterparty_by_owner(
81
+ meta: Any,
82
+ watch_owner_bs58: str,
83
+ min_watch_decrease_raw: int,
84
+ ) -> List[Tuple[str, str]]:
85
+ """对齐 ``spl_token_counterparty_by_owner``。"""
86
+ pre = list(getattr(meta, "pre_token_balances", []) or [])
87
+ post = list(getattr(meta, "post_token_balances", []) or [])
88
+ pre_m: Dict[Tuple[str, str], int] = {}
89
+ for b in pre:
90
+ owner = getattr(b, "owner", "") or ""
91
+ if not owner:
92
+ continue
93
+ mint = getattr(b, "mint", "") or ""
94
+ k = (mint, owner)
95
+ pre_m[k] = pre_m.get(k, 0) + token_balance_raw_amount(b)
96
+ post_m: Dict[Tuple[str, str], int] = {}
97
+ for b in post:
98
+ owner = getattr(b, "owner", "") or ""
99
+ if not owner:
100
+ continue
101
+ mint = getattr(b, "mint", "") or ""
102
+ k = (mint, owner)
103
+ post_m[k] = post_m.get(k, 0) + token_balance_raw_amount(b)
104
+
105
+ mints: Set[str] = set()
106
+ for (m, o) in list(pre_m.keys()) + list(post_m.keys()):
107
+ if o == watch_owner_bs58:
108
+ mints.add(m)
109
+
110
+ out: List[Tuple[str, str]] = []
111
+ min_l = max(int(min_watch_decrease_raw), 1)
112
+ for mint in mints:
113
+ w_pre = pre_m.get((mint, watch_owner_bs58), 0)
114
+ w_post = post_m.get((mint, watch_owner_bs58), 0)
115
+ lost = w_pre - w_post if w_pre >= w_post else 0
116
+ if lost < min_l:
117
+ continue
118
+ for (m, owner), po in post_m.items():
119
+ if m != mint or owner == watch_owner_bs58:
120
+ continue
121
+ pr = pre_m.get((mint, owner), 0)
122
+ if po > pr:
123
+ out.append((watch_owner_bs58, owner))
124
+ out.sort(key=lambda x: x[1])
125
+ seen = set()
126
+ dedup: List[Tuple[str, str]] = []
127
+ for a in out:
128
+ k = (a[0], a[1])
129
+ if k not in seen:
130
+ seen.add(k)
131
+ dedup.append(a)
132
+ return dedup
133
+
134
+
135
+ def collect_watch_transfer_counterparty_pairs(
136
+ tx: Any,
137
+ meta: Any,
138
+ watched_bs58: List[str],
139
+ min_native_outflow_lamports: int,
140
+ spl_min_watch_decrease_raw: int,
141
+ ) -> Optional[List[Tuple[str, str]]]:
142
+ keys = collect_account_keys_bs58(tx, meta)
143
+ if keys is None:
144
+ return None
145
+ n = len(keys)
146
+ pre = list(getattr(meta, "pre_balances", []) or [])
147
+ post = list(getattr(meta, "post_balances", []) or [])
148
+ if len(pre) != n or len(post) != n:
149
+ return None
150
+ deltas = lamport_balance_deltas(meta)
151
+ watched_h = set(watched_bs58)
152
+ pairs = heuristic_sol_counterparties_for_watched_keys(
153
+ keys, deltas, watched_h, min_native_outflow_lamports
154
+ )
155
+ for w in watched_bs58:
156
+ pairs.extend(spl_token_counterparty_by_owner(meta, w, spl_min_watch_decrease_raw))
157
+ pairs.sort(key=lambda x: x[1])
158
+ seen = set()
159
+ out: List[Tuple[str, str]] = []
160
+ for a in pairs:
161
+ k = (a[0], a[1])
162
+ if k not in seen:
163
+ seen.add(k)
164
+ out.append(a)
165
+ return out
166
+
167
+
168
+ def try_yellowstone_signature(sig: bytes) -> Optional[bytes]:
169
+ if len(sig) != 64:
170
+ return None
171
+ return bytes(sig)
172
+
173
+
174
+ __all__ = [
175
+ "pubkey_bytes_to_bs58",
176
+ "collect_account_keys_bs58",
177
+ "lamport_balance_deltas",
178
+ "heuristic_sol_counterparties_for_watched_keys",
179
+ "token_balance_raw_amount",
180
+ "spl_token_counterparty_by_owner",
181
+ "collect_watch_transfer_counterparty_pairs",
182
+ "try_yellowstone_signature",
183
+ ]