sol-parser-sdk-python 0.4.4__tar.gz → 0.5.5__tar.gz

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 (93) hide show
  1. sol_parser_sdk_python-0.5.5/PKG-INFO +307 -0
  2. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/README.md +17 -3
  3. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/README_CN.md +17 -3
  4. sol_parser_sdk_python-0.5.5/examples/meteora_damm_grpc.py +145 -0
  5. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/examples/multi_protocol_grpc.py +17 -36
  6. sol_parser_sdk_python-0.5.5/examples/pumpfun_quick_test.py +70 -0
  7. sol_parser_sdk_python-0.5.5/examples/pumpfun_trade_filter.py +160 -0
  8. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/examples/pumpfun_with_metrics.py +26 -74
  9. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/examples/pumpswap_low_latency.py +17 -41
  10. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/examples/pumpswap_with_metrics.py +28 -74
  11. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/pyproject.toml +2 -1
  12. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/__init__.py +39 -7
  13. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/account_dispatcher.py +6 -6
  14. sol_parser_sdk_python-0.5.5/sol_parser/account_fillers/__init__.py +5 -0
  15. sol_parser_sdk_python-0.5.5/sol_parser/account_fillers/pumpfun.py +160 -0
  16. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/account_fillers/pumpswap.py +40 -0
  17. sol_parser_sdk_python-0.5.5/sol_parser/account_fillers/raydium_launchlab.py +30 -0
  18. sol_parser_sdk_python-0.5.5/sol_parser/accounts/__init__.py +861 -0
  19. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/dex_parsers.py +115 -71
  20. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/event_types.py +143 -21
  21. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc_types.py +273 -122
  22. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/inner_instruction_parser.py +83 -45
  23. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/instr/__init__.py +4 -0
  24. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/instructions.py +720 -136
  25. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/log_instr_dedup.py +52 -13
  26. sol_parser_sdk_python-0.5.5/sol_parser/market.py +62 -0
  27. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/merger.py +78 -4
  28. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/pumpfun_fee_enrich.py +55 -2
  29. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/shredstream_client.py +94 -20
  30. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/shredstream_pumpfun.py +45 -13
  31. sol_parser_sdk_python-0.5.5/tests/test_filter_and_account_parity.py +341 -0
  32. sol_parser_sdk_python-0.5.5/tests/test_imports_and_inner.py +150 -0
  33. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/tests/test_log_instr_dedup.py +43 -0
  34. sol_parser_sdk_python-0.5.5/tests/test_market_helpers.py +19 -0
  35. sol_parser_sdk_python-0.5.5/tests/test_pumpfun_account_fill.py +72 -0
  36. sol_parser_sdk_python-0.5.5/tests/test_pumpfun_trade_tail.py +203 -0
  37. sol_parser_sdk_python-0.5.5/tests/test_pumpfun_v2_parity.py +245 -0
  38. sol_parser_sdk_python-0.5.5/tests/test_pumpswap_instruction.py +108 -0
  39. sol_parser_sdk_python-0.5.5/tests/test_raydium_clmm_instruction_parity.py +235 -0
  40. sol_parser_sdk_python-0.5.5/tests/test_shredstream.py +109 -0
  41. sol_parser_sdk_python-0.4.4/PKG-INFO +0 -14
  42. sol_parser_sdk_python-0.4.4/examples/meteora_damm_grpc.py +0 -237
  43. sol_parser_sdk_python-0.4.4/examples/pumpfun_quick_test.py +0 -112
  44. sol_parser_sdk_python-0.4.4/examples/pumpfun_trade_filter.py +0 -263
  45. sol_parser_sdk_python-0.4.4/sol_parser/account_fillers/__init__.py +0 -5
  46. sol_parser_sdk_python-0.4.4/sol_parser/account_fillers/bonk.py +0 -30
  47. sol_parser_sdk_python-0.4.4/sol_parser/account_fillers/pumpfun.py +0 -97
  48. sol_parser_sdk_python-0.4.4/sol_parser/accounts/__init__.py +0 -461
  49. sol_parser_sdk_python-0.4.4/tests/test_filter_and_account_parity.py +0 -89
  50. sol_parser_sdk_python-0.4.4/tests/test_imports_and_inner.py +0 -37
  51. sol_parser_sdk_python-0.4.4/tests/test_pumpfun_v2_parity.py +0 -83
  52. sol_parser_sdk_python-0.4.4/tests/test_raydium_clmm_instruction_parity.py +0 -107
  53. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/.env.example +0 -0
  54. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/.gitignore +0 -0
  55. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/examples/parse_tx_by_signature.py +0 -0
  56. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/examples/rust_example_utils.py +0 -0
  57. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/scripts/bench_parser.py +0 -0
  58. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/scripts/gen_golden_rust.sh +0 -0
  59. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/account_fillers/meteora.py +0 -0
  60. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/account_fillers/orca.py +0 -0
  61. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/account_fillers/raydium.py +0 -0
  62. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/accounts/rpc_wallet.py +0 -0
  63. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/accounts/utils.py +0 -0
  64. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/check_migration.py +0 -0
  65. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/clock.py +0 -0
  66. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/common/__init__.py +0 -0
  67. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/entries_decode.py +0 -0
  68. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/env_config.py +0 -0
  69. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/geyser_pb2.py +0 -0
  70. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/geyser_pb2_grpc.py +0 -0
  71. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc/__init__.py +0 -0
  72. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc/geyser_connect.py +0 -0
  73. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc/subscribe_builder.py +0 -0
  74. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc/transaction_meta.py +0 -0
  75. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc_client.py +0 -0
  76. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/grpc_instruction_parser.py +0 -0
  77. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/instr_account_utils.py +0 -0
  78. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/json_util.py +0 -0
  79. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/logs/__init__.py +0 -0
  80. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/order_buffer.py +0 -0
  81. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/parser.py +0 -0
  82. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/rpc_parser.py +0 -0
  83. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/rust_api_inventory.py +0 -0
  84. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/rust_event_json.py +0 -0
  85. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/shredstream_pb2.py +0 -0
  86. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/shredstream_pb2_grpc.py +0 -0
  87. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/solana_storage_pb2.py +0 -0
  88. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/solana_storage_pb2_grpc.py +0 -0
  89. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/u128_parity.py +0 -0
  90. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/sol_parser/verify_discriminators.py +0 -0
  91. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/tests/fixtures/golden_parse_log.json +0 -0
  92. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/tests/test_golden.py +0 -0
  93. {sol_parser_sdk_python-0.4.4 → sol_parser_sdk_python-0.5.5}/tests/test_order_buffer.py +0 -0
@@ -0,0 +1,307 @@
1
+ Metadata-Version: 2.4
2
+ Name: sol-parser-sdk-python
3
+ Version: 0.5.5
4
+ Summary: Solana DEX program log parsing (pure Python, API aligned with sol-parser-sdk)
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: base58>=2.0
7
+ Requires-Dist: grpcio-tools>=1.64.0
8
+ Requires-Dist: grpcio>=1.64.0
9
+ Requires-Dist: python-dotenv>=1.0.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest-benchmark>=4.0; extra == 'dev'
12
+ Requires-Dist: pytest>=8.0; extra == 'dev'
13
+ Provides-Extra: shredstream
14
+ Requires-Dist: solders>=0.21.0; extra == 'shredstream'
15
+ Description-Content-Type: text/markdown
16
+
17
+ <div align="center">
18
+ <h1>⚡ Sol Parser SDK - Python</h1>
19
+ <h3><em>High-performance Solana DEX event parser for Python</em></h3>
20
+ </div>
21
+
22
+ <p align="center">
23
+ <strong>High-performance Python library for parsing Solana DEX events in real-time via Yellowstone gRPC</strong>
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="https://pypi.org/project/sol-parser-sdk-python/">
28
+ <img src="https://img.shields.io/pypi/v/sol-parser-sdk-python.svg" alt="PyPI">
29
+ </a>
30
+ <a href="https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/LICENSE">
31
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
32
+ </a>
33
+ </p>
34
+
35
+ <p align="center">
36
+ <img src="https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white" alt="Python">
37
+ <img src="https://img.shields.io/badge/Solana-9945FF?style=for-the-badge&logo=solana&logoColor=white" alt="Solana">
38
+ <img src="https://img.shields.io/badge/gRPC-4285F4?style=for-the-badge&logo=grpc&logoColor=white" alt="gRPC">
39
+ </p>
40
+
41
+ <p align="center">
42
+ <a href="./README_CN.md">中文</a> |
43
+ <a href="./README.md">English</a> |
44
+ <a href="https://fnzero.dev/">Website</a> |
45
+ <a href="https://t.me/fnzero_group">Telegram</a> |
46
+ <a href="https://discord.gg/vuazbGkqQE">Discord</a>
47
+ </p>
48
+
49
+ > ☕ **Support This Project**
50
+ >
51
+ > This SDK is completely free and open source. However, maintaining and continuously updating it requires significant AI computing resources and token consumption. If this SDK helps with your development, consider making a monthly SOL donation — any amount is appreciated and helps keep this project alive!
52
+ >
53
+ > **Donation Wallet:** `6oW7AXz1yRb57pYSxysuXnMs2aR1ha5rzGzReZ1MjPV8`
54
+
55
+ ---
56
+
57
+ ## Other language SDKs
58
+
59
+ | Language | Repository |
60
+ |----------|------------|
61
+ | Rust | [sol-parser-sdk](https://github.com/0xfnzero/sol-parser-sdk) |
62
+ | Node.js | [sol-parser-sdk-nodejs](https://github.com/0xfnzero/sol-parser-sdk-nodejs) |
63
+ | Python | [sol-parser-sdk-python](https://github.com/0xfnzero/sol-parser-sdk-python) |
64
+ | Go | [sol-parser-sdk-golang](https://github.com/0xfnzero/sol-parser-sdk-golang) |
65
+
66
+ ---
67
+
68
+ ## Release notes
69
+
70
+ ### v0.5.5
71
+
72
+ - Aligns ShredStream parsing with Rust/Node.js/Go for low-latency static-account paths.
73
+ - Uses default pubkey placeholders for V0 ALT-loaded instruction accounts instead of dropping the instruction.
74
+ - Adds discriminator fallback when the ShredStream outer program id is ALT-loaded.
75
+ - Fixes Pump.fun ShredStream create/create_v2 fallback to use instruction-order accounts.
76
+ - Improves Pump.fun v2 short-account parsing, event-type filtering, and multi-protocol routing parity.
77
+
78
+ ---
79
+
80
+ ## How to use
81
+
82
+ ### 1. Install
83
+
84
+ **From PyPI**
85
+
86
+ ```bash
87
+ pip install sol-parser-sdk-python==0.5.5
88
+ ```
89
+
90
+ **From source**
91
+
92
+ ```bash
93
+ git clone https://github.com/0xfnzero/sol-parser-sdk-python
94
+ cd sol-parser-sdk-python
95
+ pip install -e .
96
+ pip install grpcio grpcio-tools protobuf base58 python-dotenv
97
+ ```
98
+
99
+ ### 2. Environment (Yellowstone gRPC examples)
100
+
101
+ At the **package root** (next to `pyproject.toml`):
102
+
103
+ ```bash
104
+ cp .env.example .env
105
+ # Set GRPC_URL (or GRPC_ENDPOINT) and GRPC_AUTH_TOKEN or GRPC_TOKEN
106
+ ```
107
+
108
+ Run examples from that directory so `.env` is picked up (same idea as the Node.js package).
109
+
110
+ **CLI overrides:** `--grpc-url` / `-g`, `--grpc-token` / `--token` (also `--grpc-url=https://host:443`). **Rust-compatible names:** `GRPC_AUTH_TOKEN` (same as [sol-parser-sdk](https://github.com/0xfnzero/sol-parser-sdk) examples), `GRPC_ENDPOINT` (alias for URL). **Legacy env:** `GEYSER_ENDPOINT` / `GEYSER_API_TOKEN`. **Precedence:** CLI > `GRPC_URL` / `GRPC_ENDPOINT` > `GEYSER_*` > defaults; token: CLI > `GRPC_AUTH_TOKEN` > `GRPC_TOKEN` > `GEYSER_API_TOKEN`. Explicit shell `export` wins over `.env`; `python-dotenv` does not overwrite existing variables.
111
+
112
+ Helpers: `sol_parser.env_config` (`parse_grpc_credentials`, `require_grpc_env`, `parse_shredstream_url`, …), re-exported from `sol_parser`.
113
+
114
+ ### 3. Smoke test
115
+
116
+ ```bash
117
+ python examples/pumpfun_quick_test.py
118
+ ```
119
+
120
+ Requires `GRPC_URL` (or `GRPC_ENDPOINT`) and (for most providers) `GRPC_AUTH_TOKEN` or `GRPC_TOKEN` in `.env` or the environment. You can pass credentials on the command line instead; see step 2.
121
+
122
+ ### 4. Minimal gRPC subscribe + parse
123
+
124
+ ```python
125
+ import asyncio
126
+ import os
127
+
128
+ import base58
129
+
130
+ from sol_parser import parse_logs_only
131
+ from sol_parser.grpc_client import YellowstoneGrpc
132
+ from sol_parser.grpc_types import Protocol, SubscribeCallbacks, transaction_filter_for_protocols
133
+
134
+ async def main():
135
+ endpoint = (
136
+ os.environ.get("GRPC_URL", "").strip()
137
+ or os.environ.get("GRPC_ENDPOINT", "").strip()
138
+ or os.environ.get("GEYSER_ENDPOINT", "solana-yellowstone-grpc.publicnode.com:443")
139
+ )
140
+ token = (
141
+ os.environ.get("GRPC_AUTH_TOKEN", "").strip()
142
+ or os.environ.get("GRPC_TOKEN", "").strip()
143
+ or os.environ.get("GEYSER_API_TOKEN", "")
144
+ )
145
+
146
+ client = YellowstoneGrpc(endpoint)
147
+ if token:
148
+ client.set_x_token(token)
149
+ await client.connect()
150
+
151
+ filter_ = transaction_filter_for_protocols([Protocol.PUMP_FUN, Protocol.PUMP_SWAP])
152
+ filter_.vote = False
153
+ filter_.failed = False
154
+
155
+ def on_update(update):
156
+ if update.transaction is None or update.transaction.transaction is None:
157
+ return
158
+ tx_info = update.transaction.transaction
159
+ slot = update.transaction.slot
160
+ logs = tx_info.log_messages
161
+ if not logs:
162
+ return
163
+ sb = bytes(tx_info.signature) if tx_info.signature else b""
164
+ sig = base58.b58encode(sb).decode("ascii") if len(sb) == 64 else ""
165
+ events = parse_logs_only(
166
+ logs, sig, slot, None, subscribe_tx_info=tx_info
167
+ ) # tx_info from gRPC update — fills accounts from instruction keys
168
+ for ev in events:
169
+ print(ev)
170
+
171
+ sub = await client.subscribe_transactions(
172
+ filter_,
173
+ SubscribeCallbacks(on_update=on_update, on_error=print, on_end=lambda: None),
174
+ )
175
+ print("subscribed", sub.id)
176
+
177
+ try:
178
+ await asyncio.Event().wait()
179
+ except KeyboardInterrupt:
180
+ pass
181
+ await client.disconnect()
182
+
183
+ asyncio.run(main())
184
+ ```
185
+
186
+ **Lighter path:** `parse_logs_only(logs, …)` only needs log messages from the update (no full wire transaction).
187
+
188
+ ### 5. ShredStream (HTTP — not Yellowstone gRPC)
189
+
190
+ Python includes a native ShredStream client (`ShredStreamClient`, `ShredStreamConfig`) plus the same env/CLI helpers as Node (`parse_shredstream_url`: `SHREDSTREAM_URL` / `SHRED_URL`, `--url` / `-u` / `--endpoint=`). Install the optional extra with `pip install 'sol-parser-sdk-python[shredstream]'`. ShredStream uses its own endpoint, **not** `GRPC_URL`.
191
+
192
+ The Python ShredStream hot path uses static account keys only. V0 ALT-loaded instruction accounts are represented with default pubkey placeholders, and ALT-loaded outer program ids are parsed best-effort by discriminator. Inner CPI/log-only events still require the Yellowstone/RPC paths.
193
+
194
+ ---
195
+
196
+ ## Examples
197
+
198
+ From the **package root** after `pip install -e .`. Run with `python examples/<file>.py`. Scripts are written to mirror the **output layout and env names** of [sol-parser-sdk/examples](https://github.com/0xfnzero/sol-parser-sdk/tree/main/examples) (Rust); streaming uses `subscribe_transactions` + `parse_logs_only` with the same program IDs as `TransactionFilter::for_protocols` in Rust.
199
+
200
+ | Description | Run command | Source |
201
+ |-------------|-------------|--------|
202
+ | **PumpFun** | | |
203
+ | PumpFun trade filtering | `python examples/pumpfun_trade_filter.py` | [pumpfun_trade_filter.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/pumpfun_trade_filter.py) |
204
+ | PumpFun events + metrics | `python examples/pumpfun_with_metrics.py` | [pumpfun_with_metrics.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/pumpfun_with_metrics.py) |
205
+ | Quick PumpFun connection test | `python examples/pumpfun_quick_test.py` | [pumpfun_quick_test.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/pumpfun_quick_test.py) |
206
+ | **PumpSwap** | | |
207
+ | PumpSwap ultra-low latency | `python examples/pumpswap_low_latency.py` | [pumpswap_low_latency.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/pumpswap_low_latency.py) |
208
+ | PumpSwap events + metrics | `python examples/pumpswap_with_metrics.py` | [pumpswap_with_metrics.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/pumpswap_with_metrics.py) |
209
+ | **Meteora DAMM** | | |
210
+ | Meteora DAMM V2 events | `python examples/meteora_damm_grpc.py` | [meteora_damm_grpc.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/meteora_damm_grpc.py) |
211
+ | **Multi-protocol** | | |
212
+ | Subscribe to every program in Rust `Protocol` / `program_ids` | `python examples/multi_protocol_grpc.py` | [multi_protocol_grpc.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/multi_protocol_grpc.py) |
213
+ | **Utility** | | |
214
+ | Parse tx by signature (HTTP RPC; not gRPC). `TX_SIGNATURE` / `RPC_URL` or `SOLANA_RPC_URL` in `.env` or `--sig` / `--rpc`. | `python examples/parse_tx_by_signature.py` | [parse_tx_by_signature.py](https://github.com/0xfnzero/sol-parser-sdk-python/blob/main/examples/parse_tx_by_signature.py) |
215
+
216
+ **Env:** gRPC examples need **`GRPC_URL`** or **`GRPC_ENDPOINT`**, plus **`GRPC_AUTH_TOKEN`** or **`GRPC_TOKEN`** (or legacy `GEYSER_*`). See **`.env.example`**.
217
+
218
+ ---
219
+
220
+ ## Protocols
221
+
222
+ PumpFun, PumpSwap, Raydium AMM V4 / CLMM / CPMM, Orca Whirlpool, Meteora DAMM V2 / DLMM, Raydium LaunchLab (see `sol_parser/`).
223
+
224
+ ---
225
+
226
+ ## Useful exports
227
+
228
+ - `parse_logs_only` — log-based DEX events from gRPC log messages.
229
+ - `format_dex_event_json` / `dex_event_to_jsonable` — pretty-print `DexEvent` as indented JSON (one field per line); `repr(ev)` is the default dataclass single-line form, not JSON.
230
+ - `YellowstoneGrpc` — async Yellowstone gRPC client (`connect`, `subscribe_transactions`, `disconnect`).
231
+ - `now_micros` — microsecond clock (same role as Rust `sol_parser_sdk::core::now_micros`).
232
+ - `transaction_filter_for_protocols` / `program_ids_for_protocols` / `account_filter_for_protocols` — same program IDs as Rust `Protocol` + `for_protocols` in `grpc/filter.rs`.
233
+ - `parse_grpc_credentials` / `require_grpc_env` — load `.env` + env + CLI (aligned with [sol-parser-sdk-nodejs](https://github.com/0xfnzero/sol-parser-sdk-nodejs) `grpc_env`).
234
+
235
+ ---
236
+
237
+ ## Advanced
238
+
239
+ ### Custom gRPC endpoint
240
+
241
+ ```python
242
+ import os
243
+
244
+ from sol_parser.grpc_client import YellowstoneGrpc
245
+
246
+ endpoint = os.environ.get("GRPC_URL") or os.environ.get("GRPC_ENDPOINT") or os.environ.get(
247
+ "GEYSER_ENDPOINT", "solana-yellowstone-grpc.publicnode.com:443"
248
+ )
249
+ token = (
250
+ os.environ.get("GRPC_AUTH_TOKEN")
251
+ or os.environ.get("GRPC_TOKEN")
252
+ or os.environ.get("GEYSER_API_TOKEN", "")
253
+ )
254
+ client = YellowstoneGrpc(endpoint)
255
+ if token:
256
+ client.set_x_token(token)
257
+ ```
258
+
259
+ ### Create + buy detection
260
+
261
+ `parse_logs_only` can detect create-and-buy patterns from program logs; see the PumpFun examples.
262
+
263
+ ---
264
+
265
+ ## Project structure
266
+
267
+ ```
268
+ sol-parser-sdk-python/
269
+ ├── sol_parser/
270
+ │ ├── grpc_client.py # YellowstoneGrpc (async connect / subscribe)
271
+ │ ├── env_config.py # GRPC_URL, .env, CLI helpers (Node parity)
272
+ │ ├── clock.py # now_micros()
273
+ │ ├── grpc_types.py # TransactionFilter, SubscribeCallbacks, for_protocols helpers, …
274
+ │ ├── parser.py # parse_logs_only, …
275
+ │ ├── geyser_pb2.py # Generated proto (Yellowstone)
276
+ │ └── …
277
+ ├── examples/
278
+ │ ├── pumpfun_trade_filter.py
279
+ │ ├── pumpfun_quick_test.py
280
+ │ └── …
281
+ ├── .env.example
282
+ └── pyproject.toml
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ pip install -e ".[dev]"
291
+ pytest tests/
292
+ ```
293
+
294
+ ---
295
+
296
+ ## License
297
+
298
+ MIT — https://github.com/0xfnzero/sol-parser-sdk-python
299
+
300
+ ---
301
+
302
+ ## Contact
303
+
304
+ - **Repository**: https://github.com/0xfnzero/sol-parser-sdk-python
305
+ - **Website**: https://fnzero.dev/
306
+ - **Telegram**: https://t.me/fnzero_group
307
+ - **Discord**: https://discord.gg/vuazbGkqQE
@@ -49,6 +49,18 @@
49
49
 
50
50
  ---
51
51
 
52
+ ## Release notes
53
+
54
+ ### v0.5.5
55
+
56
+ - Aligns ShredStream parsing with Rust/Node.js/Go for low-latency static-account paths.
57
+ - Uses default pubkey placeholders for V0 ALT-loaded instruction accounts instead of dropping the instruction.
58
+ - Adds discriminator fallback when the ShredStream outer program id is ALT-loaded.
59
+ - Fixes Pump.fun ShredStream create/create_v2 fallback to use instruction-order accounts.
60
+ - Improves Pump.fun v2 short-account parsing, event-type filtering, and multi-protocol routing parity.
61
+
62
+ ---
63
+
52
64
  ## How to use
53
65
 
54
66
  ### 1. Install
@@ -56,7 +68,7 @@
56
68
  **From PyPI**
57
69
 
58
70
  ```bash
59
- pip install sol-parser-sdk-python
71
+ pip install sol-parser-sdk-python==0.5.5
60
72
  ```
61
73
 
62
74
  **From source**
@@ -159,7 +171,9 @@ asyncio.run(main())
159
171
 
160
172
  ### 5. ShredStream (HTTP — not Yellowstone gRPC)
161
173
 
162
- The Node.js SDK includes a ShredStream HTTP client. **Python** provides the same env/CLI helpers as Node (`parse_shredstream_url`: `SHREDSTREAM_URL` / `SHRED_URL`, `--url` / `-u` / `--endpoint=`) for configuration parity; a native ShredStream client may be added later. **Not** `GRPC_URL`.
174
+ Python includes a native ShredStream client (`ShredStreamClient`, `ShredStreamConfig`) plus the same env/CLI helpers as Node (`parse_shredstream_url`: `SHREDSTREAM_URL` / `SHRED_URL`, `--url` / `-u` / `--endpoint=`). Install the optional extra with `pip install 'sol-parser-sdk-python[shredstream]'`. ShredStream uses its own endpoint, **not** `GRPC_URL`.
175
+
176
+ The Python ShredStream hot path uses static account keys only. V0 ALT-loaded instruction accounts are represented with default pubkey placeholders, and ALT-loaded outer program ids are parsed best-effort by discriminator. Inner CPI/log-only events still require the Yellowstone/RPC paths.
163
177
 
164
178
  ---
165
179
 
@@ -189,7 +203,7 @@ From the **package root** after `pip install -e .`. Run with `python examples/<f
189
203
 
190
204
  ## Protocols
191
205
 
192
- PumpFun, PumpSwap, Raydium AMM V4 / CLMM / CPMM, Orca Whirlpool, Meteora DAMM V2 / DLMM, Bonk Launchpad (see `sol_parser/`).
206
+ PumpFun, PumpSwap, Raydium AMM V4 / CLMM / CPMM, Orca Whirlpool, Meteora DAMM V2 / DLMM, Raydium LaunchLab (see `sol_parser/`).
193
207
 
194
208
  ---
195
209
 
@@ -49,6 +49,18 @@
49
49
 
50
50
  ---
51
51
 
52
+ ## 发布说明
53
+
54
+ ### v0.5.5
55
+
56
+ - ShredStream 低延迟静态账户路径与 Rust/Node.js/Go 对齐。
57
+ - V0 ALT-loaded 指令账户不再整条跳过,而是用默认 pubkey 占位继续 best-effort 解析。
58
+ - 当 ShredStream 外层 program id 来自 ALT 时,按 discriminator 做候选 program id fallback。
59
+ - 修复 Pump.fun ShredStream create/create_v2 fallback,确保按 instruction-order 账户解析。
60
+ - 改进 Pump.fun v2 短账户解析、事件类型过滤和多协议路由一致性。
61
+
62
+ ---
63
+
52
64
  ## 怎么用
53
65
 
54
66
  ### 1. 安装
@@ -56,7 +68,7 @@
56
68
  **PyPI**
57
69
 
58
70
  ```bash
59
- pip install sol-parser-sdk-python
71
+ pip install sol-parser-sdk-python==0.5.5
60
72
  ```
61
73
 
62
74
  **源码**
@@ -159,7 +171,9 @@ asyncio.run(main())
159
171
 
160
172
  ### 5. ShredStream(HTTP,不是 Yellowstone gRPC)
161
173
 
162
- Node 版提供 ShredStream HTTP 客户端。**Python** 侧提供与 Node 相同的配置方式(`parse_shredstream_url`:`SHREDSTREAM_URL` / `SHRED_URL`,`--url` / `-u` / `--endpoint=`);原生 ShredStream 客户端后续可能补充。**不要**用 `GRPC_URL` 配 ShredStream。
174
+ Python 已提供原生 ShredStream 客户端(`ShredStreamClient`、`ShredStreamConfig`),并保留与 Node 相同的配置方式(`parse_shredstream_url`:`SHREDSTREAM_URL` / `SHRED_URL`,`--url` / `-u` / `--endpoint=`)。安装可选依赖:`pip install 'sol-parser-sdk-python[shredstream]'`。ShredStream 使用独立端点,**不要**用 `GRPC_URL`。
175
+
176
+ Python ShredStream 热路径只使用静态账户表。V0 ALT-loaded 指令账户会用默认 pubkey 占位,外层 program id 若来自 ALT 会按 discriminator best-effort 解析。Inner CPI / 仅日志事件仍需 Yellowstone/RPC 路径。
163
177
 
164
178
  ---
165
179
 
@@ -189,7 +203,7 @@ Node 版提供 ShredStream HTTP 客户端。**Python** 侧提供与 Node 相同
189
203
 
190
204
  ## 协议
191
205
 
192
- PumpFun、PumpSwap、Raydium AMM V4 / CLMM / CPMM、Orca Whirlpool、Meteora DAMM V2 / DLMM、Bonk Launchpad(见 `sol_parser/`)。
206
+ PumpFun、PumpSwap、Raydium AMM V4 / CLMM / CPMM、Orca Whirlpool、Meteora DAMM V2 / DLMM、Raydium LaunchLab(见 `sol_parser/`)。
193
207
 
194
208
  ---
195
209
 
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+ """Meteora DAMM gRPC — latest ``subscribe_dex_events`` queue API.
3
+
4
+ Run: ``python examples/meteora_damm_grpc.py``
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import os
11
+ import sys
12
+
13
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
14
+
15
+ from sol_parser import now_micros
16
+ from sol_parser.env_config import load_dotenv_silent, parse_grpc_credentials
17
+ from sol_parser.event_types import DexEvent
18
+ from sol_parser.grpc_client import YellowstoneGrpc
19
+ from sol_parser.grpc_types import (
20
+ ClientConfig,
21
+ EventType,
22
+ OrderMode,
23
+ Protocol,
24
+ event_type_filter_include_only,
25
+ transaction_filter_for_protocols,
26
+ )
27
+
28
+ event_count = 0
29
+ swap_count = 0
30
+ add_liquidity_count = 0
31
+ remove_liquidity_count = 0
32
+ create_position_count = 0
33
+ close_position_count = 0
34
+
35
+
36
+ def _metadata(ev: DexEvent):
37
+ return getattr(ev.data, "metadata", None) if ev.data is not None else None
38
+
39
+
40
+ def _latency_us(ev: DexEvent) -> int:
41
+ meta = _metadata(ev)
42
+ grpc_recv_us = int(getattr(meta, "grpc_recv_us", 0) or 0) if meta else 0
43
+ return max(0, now_micros() - grpc_recv_us) if grpc_recv_us else 0
44
+
45
+
46
+ async def main() -> None:
47
+ global event_count, swap_count, add_liquidity_count, remove_liquidity_count
48
+ global create_position_count, close_position_count
49
+
50
+ load_dotenv_silent()
51
+ endpoint, token = parse_grpc_credentials(
52
+ sys.argv[1:],
53
+ default_endpoint="solana-yellowstone-grpc.publicnode.com:443",
54
+ )
55
+
56
+ print("🚀 Meteora DAMM gRPC Streaming Example")
57
+ print("========================================\n")
58
+
59
+ config = ClientConfig.default()
60
+ config.enable_metrics = True
61
+ config.connection_timeout_ms = 10000
62
+ config.request_timeout_ms = 30000
63
+ config.enable_tls = True
64
+ config.order_mode = OrderMode.UNORDERED
65
+
66
+ client = YellowstoneGrpc.new_with_config(endpoint, token or None, config)
67
+ protocols = [Protocol.METEORA_DAMM_V2]
68
+ print(f"📊 Protocols: {[p.value for p in protocols]}")
69
+
70
+ tx_filter = transaction_filter_for_protocols(protocols)
71
+ tx_filter.vote = False
72
+ tx_filter.failed = False
73
+ event_filter = event_type_filter_include_only(
74
+ [
75
+ EventType.METEORA_DAMM_V2_SWAP,
76
+ EventType.METEORA_DAMM_V2_ADD_LIQUIDITY,
77
+ EventType.METEORA_DAMM_V2_REMOVE_LIQUIDITY,
78
+ EventType.METEORA_DAMM_V2_CREATE_POSITION,
79
+ EventType.METEORA_DAMM_V2_CLOSE_POSITION,
80
+ ]
81
+ )
82
+
83
+ queue: asyncio.Queue[DexEvent] = await client.subscribe_dex_events(
84
+ [tx_filter],
85
+ [],
86
+ event_filter,
87
+ )
88
+
89
+ print("🎯 Event Filter: Swap, AddLiquidity, RemoveLiquidity, CreatePosition, ClosePosition")
90
+ print("🛑 Press Ctrl+C to stop...\n")
91
+
92
+ try:
93
+ while True:
94
+ ev = await queue.get()
95
+ if not isinstance(ev, DexEvent) or ev.data is None:
96
+ continue
97
+ event_count += 1
98
+ d = ev.data
99
+ meta = _metadata(ev)
100
+ sig = getattr(meta, "signature", "") if meta else ""
101
+ slot = int(getattr(meta, "slot", 0) or 0) if meta else 0
102
+ latency_us = _latency_us(ev)
103
+
104
+ if ev.type == EventType.METEORA_DAMM_V2_SWAP:
105
+ swap_count += 1
106
+ direction = "A->B" if getattr(d, "trade_direction", 0) == 0 else "B->A"
107
+ print("┌─────────────────────────────────────────────────────────────")
108
+ print(f"│ 🔄 Meteora DAMM SWAP (V2) #{event_count}")
109
+ print("├─────────────────────────────────────────────────────────────")
110
+ print(f"│ Signature : {sig}")
111
+ print(f"│ Slot : {slot}")
112
+ print("├─────────────────────────────────────────────────────────────")
113
+ print(f"│ Pool : {getattr(d, 'pool', '')}")
114
+ print(f"│ Direction : {direction}")
115
+ print(f"│ Amount In : {getattr(d, 'amount_in', 0)}")
116
+ print(f"│ Actual Out : {getattr(d, 'output_amount', 0)}")
117
+ print(f"│ Protocol : {getattr(d, 'protocol_fee', 0)}")
118
+ print("├─────────────────────────────────────────────────────────────")
119
+ print(f"│ 📊 Latency : {latency_us} μs")
120
+ print(
121
+ f"│ 📊 Stats : Swap={swap_count} AddLiq={add_liquidity_count} RemLiq={remove_liquidity_count}"
122
+ )
123
+ print("└─────────────────────────────────────────────────────────────\n")
124
+ elif ev.type == EventType.METEORA_DAMM_V2_ADD_LIQUIDITY:
125
+ add_liquidity_count += 1
126
+ print(f"➕ ADD_LIQUIDITY #{add_liquidity_count} sig={sig} slot={slot} latency={latency_us}μs")
127
+ elif ev.type == EventType.METEORA_DAMM_V2_REMOVE_LIQUIDITY:
128
+ remove_liquidity_count += 1
129
+ print(f"➖ REMOVE_LIQUIDITY #{remove_liquidity_count} sig={sig} slot={slot} latency={latency_us}μs")
130
+ elif ev.type == EventType.METEORA_DAMM_V2_CREATE_POSITION:
131
+ create_position_count += 1
132
+ print(f"📌 CREATE_POSITION #{create_position_count} sig={sig} slot={slot} latency={latency_us}μs")
133
+ elif ev.type == EventType.METEORA_DAMM_V2_CLOSE_POSITION:
134
+ close_position_count += 1
135
+ print(f"❌ CLOSE_POSITION #{close_position_count} sig={sig} slot={slot} latency={latency_us}μs")
136
+ finally:
137
+ await client.disconnect()
138
+ print("\n👋 Shutting down gracefully...")
139
+
140
+
141
+ if __name__ == "__main__":
142
+ try:
143
+ asyncio.run(main())
144
+ except KeyboardInterrupt:
145
+ pass
@@ -10,18 +10,17 @@ import asyncio
10
10
  import os
11
11
  import sys
12
12
 
13
- import base58
14
-
15
13
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
16
14
 
17
- from sol_parser import format_dex_event_json, parse_logs_only
15
+ from sol_parser import format_dex_event_json
18
16
  from sol_parser.env_config import load_dotenv_silent, parse_grpc_credentials
19
17
  from sol_parser.event_types import DexEvent
20
18
  from sol_parser.grpc_client import YellowstoneGrpc
21
19
  from sol_parser.grpc_types import (
22
20
  ClientConfig,
21
+ OrderMode,
23
22
  Protocol,
24
- SubscribeCallbacks,
23
+ account_filter_for_protocols,
25
24
  transaction_filter_for_protocols,
26
25
  )
27
26
 
@@ -29,7 +28,7 @@ from sol_parser.grpc_types import (
29
28
  PROTOCOLS = [
30
29
  Protocol.PUMP_FUN,
31
30
  Protocol.PUMP_SWAP,
32
- Protocol.BONK,
31
+ Protocol.RAYDIUM_LAUNCHLAB,
33
32
  Protocol.RAYDIUM_CPMM,
34
33
  Protocol.RAYDIUM_CLMM,
35
34
  Protocol.RAYDIUM_AMM_V4,
@@ -64,50 +63,32 @@ async def main() -> None:
64
63
 
65
64
  cfg = ClientConfig.default()
66
65
  cfg.enable_metrics = True
66
+ cfg.order_mode = OrderMode.UNORDERED
67
67
  client = YellowstoneGrpc.new_with_config(endpoint, token or None, cfg)
68
68
 
69
- await client.connect()
70
69
  asyncio.create_task(stats_reporter())
71
70
 
72
- def on_update(update):
73
- if update.transaction is None or update.transaction.transaction is None:
74
- return
75
- tx_info = update.transaction.transaction
76
- slot = update.transaction.slot
77
- logs = tx_info.log_messages
78
- if not logs:
79
- return
80
-
81
- sb = bytes(tx_info.signature) if tx_info.signature else b""
82
- sig_b58 = base58.b58encode(sb).decode("ascii") if len(sb) == 64 else ""
83
-
84
- for ev in parse_logs_only(
85
- logs, sig_b58, slot, None, subscribe_tx_info=tx_info
86
- ):
87
- if not isinstance(ev, DexEvent):
88
- continue
89
- key = str(ev.type.value)
90
- stats[key] = stats.get(key, 0) + 1
91
- print(format_dex_event_json(ev))
92
-
93
71
  tx_filter = transaction_filter_for_protocols(PROTOCOLS)
94
72
  tx_filter.vote = False
95
73
  tx_filter.failed = False
74
+ account_filter = account_filter_for_protocols(PROTOCOLS)
96
75
 
97
- await client.subscribe_transactions(
98
- tx_filter,
99
- SubscribeCallbacks(
100
- on_update=on_update,
101
- on_error=lambda e: print(f"Stream error: {e}", file=sys.stderr),
102
- on_end=lambda: print("Stream ended"),
103
- ),
76
+ queue: asyncio.Queue[DexEvent] = await client.subscribe_dex_events(
77
+ [tx_filter],
78
+ [account_filter],
104
79
  )
105
80
 
106
81
  print(f"✅ Subscribed")
107
82
  print("🛑 Press Ctrl+C to stop...\n")
108
83
 
109
84
  try:
110
- await asyncio.Event().wait()
85
+ while True:
86
+ ev = await queue.get()
87
+ if not isinstance(ev, DexEvent):
88
+ continue
89
+ key = str(ev.type.value)
90
+ stats[key] = stats.get(key, 0) + 1
91
+ print(format_dex_event_json(ev))
111
92
  except KeyboardInterrupt:
112
93
  pass
113
94
  finally:
@@ -118,4 +99,4 @@ async def main() -> None:
118
99
 
119
100
 
120
101
  if __name__ == "__main__":
121
- asyncio.run(main())
102
+ asyncio.run(main())