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.
- sol_parser/__init__.py +400 -0
- sol_parser/account_dispatcher.py +209 -0
- sol_parser/account_fillers/__init__.py +5 -0
- sol_parser/account_fillers/bonk.py +30 -0
- sol_parser/account_fillers/meteora.py +51 -0
- sol_parser/account_fillers/orca.py +40 -0
- sol_parser/account_fillers/pumpfun.py +97 -0
- sol_parser/account_fillers/pumpswap.py +93 -0
- sol_parser/account_fillers/raydium.py +119 -0
- sol_parser/accounts/__init__.py +461 -0
- sol_parser/accounts/rpc_wallet.py +64 -0
- sol_parser/accounts/utils.py +71 -0
- sol_parser/check_migration.py +18 -0
- sol_parser/clock.py +10 -0
- sol_parser/common/__init__.py +27 -0
- sol_parser/dex_parsers.py +2576 -0
- sol_parser/entries_decode.py +186 -0
- sol_parser/env_config.py +215 -0
- sol_parser/event_types.py +1750 -0
- sol_parser/geyser_pb2.py +148 -0
- sol_parser/geyser_pb2_grpc.py +398 -0
- sol_parser/grpc/__init__.py +61 -0
- sol_parser/grpc/geyser_connect.py +42 -0
- sol_parser/grpc/subscribe_builder.py +133 -0
- sol_parser/grpc/transaction_meta.py +183 -0
- sol_parser/grpc_client.py +870 -0
- sol_parser/grpc_instruction_parser.py +318 -0
- sol_parser/grpc_types.py +919 -0
- sol_parser/inner_instruction_parser.py +281 -0
- sol_parser/instr/__init__.py +15 -0
- sol_parser/instr_account_utils.py +58 -0
- sol_parser/instructions.py +1026 -0
- sol_parser/json_util.py +41 -0
- sol_parser/log_instr_dedup.py +284 -0
- sol_parser/logs/__init__.py +15 -0
- sol_parser/merger.py +233 -0
- sol_parser/order_buffer.py +171 -0
- sol_parser/parser.py +300 -0
- sol_parser/pumpfun_fee_enrich.py +75 -0
- sol_parser/rpc_parser.py +655 -0
- sol_parser/rust_api_inventory.py +42 -0
- sol_parser/rust_event_json.py +50 -0
- sol_parser/shredstream_client.py +191 -0
- sol_parser/shredstream_pb2.py +40 -0
- sol_parser/shredstream_pb2_grpc.py +81 -0
- sol_parser/shredstream_pumpfun.py +296 -0
- sol_parser/solana_storage_pb2.py +75 -0
- sol_parser/solana_storage_pb2_grpc.py +24 -0
- sol_parser/u128_parity.py +115 -0
- sol_parser/verify_discriminators.py +85 -0
- sol_parser_sdk_python-0.4.4.dist-info/METADATA +14 -0
- sol_parser_sdk_python-0.4.4.dist-info/RECORD +54 -0
- sol_parser_sdk_python-0.4.4.dist-info/WHEEL +4 -0
- 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
|
+
]
|