helius-python 0.4.1__tar.gz → 0.5.1__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.
- {helius_python-0.4.1 → helius_python-0.5.1}/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
- {helius_python-0.4.1 → helius_python-0.5.1}/AGENTS.md +1 -1
- {helius_python-0.4.1 → helius_python-0.5.1}/CONTRIBUTING.md +1 -1
- {helius_python-0.4.1 → helius_python-0.5.1}/PKG-INFO +34 -31
- {helius_python-0.4.1 → helius_python-0.5.1}/README.md +33 -30
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/address_transactions.py +3 -3
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/address_transfers.py +3 -3
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/block_explorer.py +4 -6
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/devnet_airdrop.py +5 -3
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/network_status.py +2 -2
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/priority_fees.py +3 -3
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/stake_overview.py +2 -2
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/token_inspector.py +3 -3
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/transaction_inspector.py +2 -2
- {helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/wallet_tracker.py +3 -3
- {helius_python-0.4.1 → helius_python-0.5.1}/pyproject.toml +1 -1
- {helius_python-0.4.1/src/helius/solana_rpc → helius_python-0.5.1/src/helius/rpc}/__init__.py +2 -2
- {helius_python-0.4.1/src/helius/solana_rpc → helius_python-0.5.1/src/helius/rpc}/client.py +1 -1
- {helius_python-0.4.1 → helius_python-0.5.1}/test_examples.py +107 -45
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/solana_rpc/test_client.py +3 -3
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/solana_rpc/test_models.py +1 -1
- {helius_python-0.4.1 → helius_python-0.5.1}/.editorconfig +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/.github/workflows/python-package.yml +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/.github/workflows/python-publish.yml +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/.gitignore +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/CLAUDE.md +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/LICENSE +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/TODO.md +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/examples/laserstream/websocket_logs.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/examples/webhooks/webhook_crud.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/examples/webhooks/webhook_receiver.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/requirements.txt +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/__init__.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/admin/__init__.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/admin/admin.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/laserstream/websockets.py +21 -21
- {helius_python-0.4.1/src/helius/solana_rpc → helius_python-0.5.1/src/helius/rpc}/models.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/utils/__init__.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/utils/json_rpc_request.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/webhooks/__init__.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/src/helius/webhooks/webhooks.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/fixtures/account.json +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/fixtures/supply.json +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/admin/test_admin.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/lasterstream/test_websockets.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/utils/test_json_rpc_request.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/webhooks/test_webhook.py +0 -0
- {helius_python-0.4.1 → helius_python-0.5.1}/tests/unit/webhooks/test_webhooks_api_client.py +0 -0
|
@@ -67,7 +67,7 @@ prior discussion; just send them.
|
|
|
67
67
|
## Codebase Conventions
|
|
68
68
|
|
|
69
69
|
Please match the existing style of the codebase. Skim
|
|
70
|
-
[`src/helius/
|
|
70
|
+
[`src/helius/rpc.py`](src/helius/rpc.py) and
|
|
71
71
|
[`src/helius/models.py`](src/helius/models.py) before writing new code. In
|
|
72
72
|
particular:
|
|
73
73
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: helius-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Typed Python client for the Helius API
|
|
5
5
|
Project-URL: Homepage, https://github.com/markosnarinian/helius-python
|
|
6
6
|
Project-URL: Issues, https://github.com/markosnarinian/helius-python/issues
|
|
@@ -123,7 +123,7 @@ Description-Content-Type: text/markdown
|
|
|
123
123
|
|
|
124
124
|
## Coverage
|
|
125
125
|
|
|
126
|
-
The goal of this library is **support every function, method, endpoint,
|
|
126
|
+
The goal of this library is to **support every function, method, endpoint,
|
|
127
127
|
and feature that Helius exposes.** If Helius ships it, this client
|
|
128
128
|
wraps it.
|
|
129
129
|
|
|
@@ -161,6 +161,7 @@ being worked on.
|
|
|
161
161
|
documented Helius API.
|
|
162
162
|
|
|
163
163
|
## Installation via PyPI
|
|
164
|
+
|
|
164
165
|
```bash
|
|
165
166
|
pip install helius-python
|
|
166
167
|
```
|
|
@@ -170,7 +171,7 @@ pip install helius-python
|
|
|
170
171
|
Pass your Helius API key explicitly:
|
|
171
172
|
|
|
172
173
|
```python
|
|
173
|
-
from helius.
|
|
174
|
+
from helius.rpc import SolanaRpcClient
|
|
174
175
|
|
|
175
176
|
client = SolanaRpcClient(api_key="YOUR_HELIUS_API_KEY")
|
|
176
177
|
```
|
|
@@ -189,7 +190,7 @@ HELIUS_API_KEY=your_helius_api_key
|
|
|
189
190
|
```
|
|
190
191
|
|
|
191
192
|
```python
|
|
192
|
-
from helius.
|
|
193
|
+
from helius.rpc import SolanaRpcClient
|
|
193
194
|
|
|
194
195
|
client = SolanaRpcClient() # reads HELIUS_API_KEY from the environment or .env
|
|
195
196
|
```
|
|
@@ -199,7 +200,7 @@ client = SolanaRpcClient() # reads HELIUS_API_KEY from the environment or .env
|
|
|
199
200
|
### As a context manager (recommended)
|
|
200
201
|
|
|
201
202
|
```python
|
|
202
|
-
from helius.
|
|
203
|
+
from helius.rpc import SolanaRpcClient
|
|
203
204
|
|
|
204
205
|
with SolanaRpcClient(api_key="YOUR_HELIUS_API_KEY") as client:
|
|
205
206
|
_ctx, balance = client.get_balance("So11111111111111111111111111111111111111112")
|
|
@@ -219,7 +220,7 @@ If a `with` block doesn't fit your code structure (e.g. the client lives
|
|
|
219
220
|
on a long-lived object), call `close()` yourself when you're done:
|
|
220
221
|
|
|
221
222
|
```python
|
|
222
|
-
from helius.
|
|
223
|
+
from helius.rpc import SolanaRpcClient
|
|
223
224
|
|
|
224
225
|
client = SolanaRpcClient(api_key="YOUR_HELIUS_API_KEY")
|
|
225
226
|
try:
|
|
@@ -267,7 +268,7 @@ The method names map 1:1 to the Solana JSON-RPC spec, just converted to
|
|
|
267
268
|
## Status
|
|
268
269
|
|
|
269
270
|
Actively expanding toward full coverage of the Helius API. See
|
|
270
|
-
[`src/helius/
|
|
271
|
+
[`src/helius/rpc/client.py`](src/helius/rpc/client.py) for the
|
|
271
272
|
current Solana JSON-RPC implementation; supported surfaces are listed below,
|
|
272
273
|
and missing endpoints are tracked as issues and added continuously.
|
|
273
274
|
|
|
@@ -360,18 +361,18 @@ Each `*_subscribe` call returns the integer subscription id. Use
|
|
|
360
361
|
them; both yield a `(context, notification, subscription)` tuple where
|
|
361
362
|
`notification` is the model below.
|
|
362
363
|
|
|
363
|
-
| Subscribe method
|
|
364
|
-
|
|
|
365
|
-
| `account_subscribe(...)`
|
|
366
|
-
| `block_subscribe(...)`
|
|
367
|
-
| `logs_subscribe(...)`
|
|
368
|
-
| `program_subscribe(...)`
|
|
369
|
-
| `root_subscribe()`
|
|
370
|
-
| `signature_subscribe(...)`
|
|
371
|
-
| `slot_subscribe()`
|
|
372
|
-
| `slots_updates_subscribe()`
|
|
373
|
-
| `vote_subscribe()`
|
|
374
|
-
| `transaction_subscribe(...)`
|
|
364
|
+
| Subscribe method | Unsubscribe method | Notification model | Helius docs |
|
|
365
|
+
| ---------------------------- | -------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------ |
|
|
366
|
+
| `account_subscribe(...)` | `account_unsubscribe(...)` | `AccountNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/accountsubscribe) |
|
|
367
|
+
| `block_subscribe(...)` | `block_unsubscribe(...)` | `BlockNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/blocksubscribe) |
|
|
368
|
+
| `logs_subscribe(...)` | `logs_unsubscribe(...)` | `LogsNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/logssubscribe) |
|
|
369
|
+
| `program_subscribe(...)` | `program_unsubscribe(...)` | `ProgramNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/programsubscribe) |
|
|
370
|
+
| `root_subscribe()` | `root_unsubscribe(...)` | `RootNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/rootsubscribe) |
|
|
371
|
+
| `signature_subscribe(...)` | `signature_unsubscribe(...)` | `SignatureNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/signaturesubscribe) |
|
|
372
|
+
| `slot_subscribe()` | `slot_unsubscribe(...)` | `SlotNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/slotsubscribe) |
|
|
373
|
+
| `slots_updates_subscribe()` | `slots_updates_unsubscribe(...)` | `SlotsUpdatesNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/slotsupdatessubscribe) |
|
|
374
|
+
| `vote_subscribe()` | `vote_unsubscribe(...)` | `VoteNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/votesubscribe) |
|
|
375
|
+
| `transaction_subscribe(...)` | `transaction_unsubscribe(...)` | `TransactionNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/transactionsubscribe) |
|
|
375
376
|
|
|
376
377
|
## Webhooks API
|
|
377
378
|
|
|
@@ -402,14 +403,14 @@ manual `close()`. The constructor defaults to
|
|
|
402
403
|
from the environment or `.env` when `api_key` is omitted; optional
|
|
403
404
|
`headers` and `proxy` arguments are also accepted.
|
|
404
405
|
|
|
405
|
-
| REST operation | Python method
|
|
406
|
-
| -------------- |
|
|
407
|
-
| Create webhook | `create_webhook(...)`
|
|
408
|
-
| Get webhook | `get_webhook(...)`
|
|
409
|
-
| List webhooks | `get_all_webhooks()`
|
|
410
|
-
| Update webhook | `update_webhook(...)`
|
|
411
|
-
| Toggle webhook | `toggle_webhook(...)`
|
|
412
|
-
| Delete webhook | `delete_webhook(...)`
|
|
406
|
+
| REST operation | Python method | Helius docs |
|
|
407
|
+
| -------------- | --------------------- | -------------------------------------------------------------------------------- |
|
|
408
|
+
| Create webhook | `create_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/create-webhook) |
|
|
409
|
+
| Get webhook | `get_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/get-webhook) |
|
|
410
|
+
| List webhooks | `get_all_webhooks()` | [reference](https://www.helius.dev/docs/api-reference/webhooks/get-all-webhooks) |
|
|
411
|
+
| Update webhook | `update_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/update-webhook) |
|
|
412
|
+
| Toggle webhook | `toggle_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/toggle-webhook) |
|
|
413
|
+
| Delete webhook | `delete_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/delete-webhook) |
|
|
413
414
|
|
|
414
415
|
## Admin API
|
|
415
416
|
|
|
@@ -432,14 +433,16 @@ supports the context-manager protocol, a manual `close()`, and reads
|
|
|
432
433
|
`HELIUS_API_KEY` from the environment or `.env`.
|
|
433
434
|
|
|
434
435
|
## AI
|
|
435
|
-
|
|
436
|
-
|
|
436
|
+
|
|
437
|
+
AI was used to assist in the development of this project, which was started because I needed a Helius client written in Python myself for another project. It has eliminated the need to perform repetitive tasks, such as writing functions that are similar to those written by hand before them. It has also been useful in drafting and updating the documentation (exclusively README.md at the time of writing), writing tests and examples.
|
|
438
|
+
That said, AI-generated code is reviewed in full, line-by-line by me against the Helius API specifications.
|
|
437
439
|
|
|
438
440
|
To roughly describe my approach to using AI in this project, I generally write the code by hand, which makes it easy to structure the code well and maintain an intuitive understanding of how the code works. Then, when I got to the part where I had to implement 100+ similar Python functions, I turned to AI to write the massive part of the codebase, while I reviewed the result at the same time.
|
|
439
441
|
|
|
440
442
|
To sum up:
|
|
441
|
-
|
|
442
|
-
- I
|
|
443
|
+
|
|
444
|
+
- I generally structure and write a good part of what I'm programming by hand, including boilerplate.
|
|
445
|
+
- I find it best to prompt agents to implement very limited functionality within narrow a scope, performing something closer to pair-programming.
|
|
443
446
|
- Even though at least half the LoC in this project are hand-typed, I find that AI is incredibly capable, can be enjoyable to use, but most importantly, using AI allows me to focus my time and energy on what matters.
|
|
444
447
|
|
|
445
448
|
By the way, this README section is completely brain-made and hand-typed.
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
|
|
102
102
|
## Coverage
|
|
103
103
|
|
|
104
|
-
The goal of this library is **support every function, method, endpoint,
|
|
104
|
+
The goal of this library is to **support every function, method, endpoint,
|
|
105
105
|
and feature that Helius exposes.** If Helius ships it, this client
|
|
106
106
|
wraps it.
|
|
107
107
|
|
|
@@ -139,6 +139,7 @@ being worked on.
|
|
|
139
139
|
documented Helius API.
|
|
140
140
|
|
|
141
141
|
## Installation via PyPI
|
|
142
|
+
|
|
142
143
|
```bash
|
|
143
144
|
pip install helius-python
|
|
144
145
|
```
|
|
@@ -148,7 +149,7 @@ pip install helius-python
|
|
|
148
149
|
Pass your Helius API key explicitly:
|
|
149
150
|
|
|
150
151
|
```python
|
|
151
|
-
from helius.
|
|
152
|
+
from helius.rpc import SolanaRpcClient
|
|
152
153
|
|
|
153
154
|
client = SolanaRpcClient(api_key="YOUR_HELIUS_API_KEY")
|
|
154
155
|
```
|
|
@@ -167,7 +168,7 @@ HELIUS_API_KEY=your_helius_api_key
|
|
|
167
168
|
```
|
|
168
169
|
|
|
169
170
|
```python
|
|
170
|
-
from helius.
|
|
171
|
+
from helius.rpc import SolanaRpcClient
|
|
171
172
|
|
|
172
173
|
client = SolanaRpcClient() # reads HELIUS_API_KEY from the environment or .env
|
|
173
174
|
```
|
|
@@ -177,7 +178,7 @@ client = SolanaRpcClient() # reads HELIUS_API_KEY from the environment or .env
|
|
|
177
178
|
### As a context manager (recommended)
|
|
178
179
|
|
|
179
180
|
```python
|
|
180
|
-
from helius.
|
|
181
|
+
from helius.rpc import SolanaRpcClient
|
|
181
182
|
|
|
182
183
|
with SolanaRpcClient(api_key="YOUR_HELIUS_API_KEY") as client:
|
|
183
184
|
_ctx, balance = client.get_balance("So11111111111111111111111111111111111111112")
|
|
@@ -197,7 +198,7 @@ If a `with` block doesn't fit your code structure (e.g. the client lives
|
|
|
197
198
|
on a long-lived object), call `close()` yourself when you're done:
|
|
198
199
|
|
|
199
200
|
```python
|
|
200
|
-
from helius.
|
|
201
|
+
from helius.rpc import SolanaRpcClient
|
|
201
202
|
|
|
202
203
|
client = SolanaRpcClient(api_key="YOUR_HELIUS_API_KEY")
|
|
203
204
|
try:
|
|
@@ -245,7 +246,7 @@ The method names map 1:1 to the Solana JSON-RPC spec, just converted to
|
|
|
245
246
|
## Status
|
|
246
247
|
|
|
247
248
|
Actively expanding toward full coverage of the Helius API. See
|
|
248
|
-
[`src/helius/
|
|
249
|
+
[`src/helius/rpc/client.py`](src/helius/rpc/client.py) for the
|
|
249
250
|
current Solana JSON-RPC implementation; supported surfaces are listed below,
|
|
250
251
|
and missing endpoints are tracked as issues and added continuously.
|
|
251
252
|
|
|
@@ -338,18 +339,18 @@ Each `*_subscribe` call returns the integer subscription id. Use
|
|
|
338
339
|
them; both yield a `(context, notification, subscription)` tuple where
|
|
339
340
|
`notification` is the model below.
|
|
340
341
|
|
|
341
|
-
| Subscribe method
|
|
342
|
-
|
|
|
343
|
-
| `account_subscribe(...)`
|
|
344
|
-
| `block_subscribe(...)`
|
|
345
|
-
| `logs_subscribe(...)`
|
|
346
|
-
| `program_subscribe(...)`
|
|
347
|
-
| `root_subscribe()`
|
|
348
|
-
| `signature_subscribe(...)`
|
|
349
|
-
| `slot_subscribe()`
|
|
350
|
-
| `slots_updates_subscribe()`
|
|
351
|
-
| `vote_subscribe()`
|
|
352
|
-
| `transaction_subscribe(...)`
|
|
342
|
+
| Subscribe method | Unsubscribe method | Notification model | Helius docs |
|
|
343
|
+
| ---------------------------- | -------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------ |
|
|
344
|
+
| `account_subscribe(...)` | `account_unsubscribe(...)` | `AccountNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/accountsubscribe) |
|
|
345
|
+
| `block_subscribe(...)` | `block_unsubscribe(...)` | `BlockNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/blocksubscribe) |
|
|
346
|
+
| `logs_subscribe(...)` | `logs_unsubscribe(...)` | `LogsNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/logssubscribe) |
|
|
347
|
+
| `program_subscribe(...)` | `program_unsubscribe(...)` | `ProgramNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/programsubscribe) |
|
|
348
|
+
| `root_subscribe()` | `root_unsubscribe(...)` | `RootNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/rootsubscribe) |
|
|
349
|
+
| `signature_subscribe(...)` | `signature_unsubscribe(...)` | `SignatureNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/signaturesubscribe) |
|
|
350
|
+
| `slot_subscribe()` | `slot_unsubscribe(...)` | `SlotNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/slotsubscribe) |
|
|
351
|
+
| `slots_updates_subscribe()` | `slots_updates_unsubscribe(...)` | `SlotsUpdatesNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/slotsupdatessubscribe) |
|
|
352
|
+
| `vote_subscribe()` | `vote_unsubscribe(...)` | `VoteNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/votesubscribe) |
|
|
353
|
+
| `transaction_subscribe(...)` | `transaction_unsubscribe(...)` | `TransactionNotification` | [reference](https://www.helius.dev/docs/api-reference/rpc/websocket/transactionsubscribe) |
|
|
353
354
|
|
|
354
355
|
## Webhooks API
|
|
355
356
|
|
|
@@ -380,14 +381,14 @@ manual `close()`. The constructor defaults to
|
|
|
380
381
|
from the environment or `.env` when `api_key` is omitted; optional
|
|
381
382
|
`headers` and `proxy` arguments are also accepted.
|
|
382
383
|
|
|
383
|
-
| REST operation | Python method
|
|
384
|
-
| -------------- |
|
|
385
|
-
| Create webhook | `create_webhook(...)`
|
|
386
|
-
| Get webhook | `get_webhook(...)`
|
|
387
|
-
| List webhooks | `get_all_webhooks()`
|
|
388
|
-
| Update webhook | `update_webhook(...)`
|
|
389
|
-
| Toggle webhook | `toggle_webhook(...)`
|
|
390
|
-
| Delete webhook | `delete_webhook(...)`
|
|
384
|
+
| REST operation | Python method | Helius docs |
|
|
385
|
+
| -------------- | --------------------- | -------------------------------------------------------------------------------- |
|
|
386
|
+
| Create webhook | `create_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/create-webhook) |
|
|
387
|
+
| Get webhook | `get_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/get-webhook) |
|
|
388
|
+
| List webhooks | `get_all_webhooks()` | [reference](https://www.helius.dev/docs/api-reference/webhooks/get-all-webhooks) |
|
|
389
|
+
| Update webhook | `update_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/update-webhook) |
|
|
390
|
+
| Toggle webhook | `toggle_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/toggle-webhook) |
|
|
391
|
+
| Delete webhook | `delete_webhook(...)` | [reference](https://www.helius.dev/docs/api-reference/webhooks/delete-webhook) |
|
|
391
392
|
|
|
392
393
|
## Admin API
|
|
393
394
|
|
|
@@ -410,14 +411,16 @@ supports the context-manager protocol, a manual `close()`, and reads
|
|
|
410
411
|
`HELIUS_API_KEY` from the environment or `.env`.
|
|
411
412
|
|
|
412
413
|
## AI
|
|
413
|
-
|
|
414
|
-
|
|
414
|
+
|
|
415
|
+
AI was used to assist in the development of this project, which was started because I needed a Helius client written in Python myself for another project. It has eliminated the need to perform repetitive tasks, such as writing functions that are similar to those written by hand before them. It has also been useful in drafting and updating the documentation (exclusively README.md at the time of writing), writing tests and examples.
|
|
416
|
+
That said, AI-generated code is reviewed in full, line-by-line by me against the Helius API specifications.
|
|
415
417
|
|
|
416
418
|
To roughly describe my approach to using AI in this project, I generally write the code by hand, which makes it easy to structure the code well and maintain an intuitive understanding of how the code works. Then, when I got to the part where I had to implement 100+ similar Python functions, I turned to AI to write the massive part of the codebase, while I reviewed the result at the same time.
|
|
417
419
|
|
|
418
420
|
To sum up:
|
|
419
|
-
|
|
420
|
-
- I
|
|
421
|
+
|
|
422
|
+
- I generally structure and write a good part of what I'm programming by hand, including boilerplate.
|
|
423
|
+
- I find it best to prompt agents to implement very limited functionality within narrow a scope, performing something closer to pair-programming.
|
|
421
424
|
- Even though at least half the LoC in this project are hand-typed, I find that AI is incredibly capable, can be enjoyable to use, but most importantly, using AI allows me to focus my time and energy on what matters.
|
|
422
425
|
|
|
423
426
|
By the way, this README section is completely brain-made and hand-typed.
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/address_transactions.py
RENAMED
|
@@ -6,8 +6,8 @@ either compact signature rows or full transaction payloads with pagination.
|
|
|
6
6
|
Usage:
|
|
7
7
|
|
|
8
8
|
export HELIUS_API_KEY=your_helius_api_key
|
|
9
|
-
python examples/
|
|
10
|
-
python examples/
|
|
9
|
+
python examples/rpc/address_transactions.py <ADDRESS> [--limit 10]
|
|
10
|
+
python examples/rpc/address_transactions.py <ADDRESS> --full --limit 5
|
|
11
11
|
|
|
12
12
|
Docs:
|
|
13
13
|
https://www.helius.dev/docs/getting-data/get-transactions-for-address
|
|
@@ -25,7 +25,7 @@ import sys
|
|
|
25
25
|
|
|
26
26
|
import httpx
|
|
27
27
|
|
|
28
|
-
from helius.
|
|
28
|
+
from helius.rpc import SolanaRpcClient
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def format_time(block_time: int | None) -> str:
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/address_transfers.py
RENAMED
|
@@ -6,8 +6,8 @@ Uses Helius's `getTransfersByAddress` RPC method exposed by this client as
|
|
|
6
6
|
Usage:
|
|
7
7
|
|
|
8
8
|
export HELIUS_API_KEY=your_helius_api_key
|
|
9
|
-
python examples/
|
|
10
|
-
python examples/
|
|
9
|
+
python examples/rpc/address_transfers.py <ADDRESS> [--limit 20]
|
|
10
|
+
python examples/rpc/address_transfers.py <ADDRESS> --direction in --mint <MINT>
|
|
11
11
|
|
|
12
12
|
Docs:
|
|
13
13
|
https://www.helius.dev/docs/getting-data/get-transfers-by-address
|
|
@@ -25,7 +25,7 @@ import sys
|
|
|
25
25
|
|
|
26
26
|
import httpx
|
|
27
27
|
|
|
28
|
-
from helius.
|
|
28
|
+
from helius.rpc import SolanaRpcClient
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def format_time(block_time: int) -> str:
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/block_explorer.py
RENAMED
|
@@ -8,8 +8,8 @@ breakdown of successful vs. failed transactions plus total fees paid.
|
|
|
8
8
|
Usage:
|
|
9
9
|
|
|
10
10
|
export HELIUS_API_KEY=your_helius_api_key
|
|
11
|
-
python examples/
|
|
12
|
-
python examples/
|
|
11
|
+
python examples/rpc/block_explorer.py
|
|
12
|
+
python examples/rpc/block_explorer.py --slot 250000000
|
|
13
13
|
|
|
14
14
|
Uses (with `with`):
|
|
15
15
|
get_slot, get_block.
|
|
@@ -21,7 +21,7 @@ import argparse
|
|
|
21
21
|
import datetime as dt
|
|
22
22
|
import sys
|
|
23
23
|
|
|
24
|
-
from helius.
|
|
24
|
+
from helius.rpc import SolanaRpcClient
|
|
25
25
|
|
|
26
26
|
LAMPORTS_PER_SOL = 1_000_000_000
|
|
27
27
|
|
|
@@ -73,9 +73,7 @@ def main() -> int:
|
|
|
73
73
|
print(f"\nTransactions : {total:,} total")
|
|
74
74
|
print(f" succeeded : {succeeded:,}")
|
|
75
75
|
print(f" failed : {failed:,}")
|
|
76
|
-
print(
|
|
77
|
-
f"Total fees : {fees / LAMPORTS_PER_SOL:.9f} SOL " f"({fees:,} lamports)"
|
|
78
|
-
)
|
|
76
|
+
print(f"Total fees : {fees / LAMPORTS_PER_SOL:.9f} SOL ({fees:,} lamports)")
|
|
79
77
|
|
|
80
78
|
if block.rewards:
|
|
81
79
|
print(f"\nRewards ({len(block.rewards)}):")
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/devnet_airdrop.py
RENAMED
|
@@ -7,7 +7,7 @@ polls `getSignatureStatuses` until the airdrop transaction reaches a
|
|
|
7
7
|
Usage:
|
|
8
8
|
|
|
9
9
|
export HELIUS_API_KEY=your_helius_api_key
|
|
10
|
-
python examples/
|
|
10
|
+
python examples/rpc/devnet_airdrop.py <WALLET_ADDRESS> [--sol 1.0]
|
|
11
11
|
|
|
12
12
|
Note:
|
|
13
13
|
`requestAirdrop` is only available on Devnet and Testnet — never on
|
|
@@ -26,7 +26,7 @@ import time
|
|
|
26
26
|
|
|
27
27
|
import httpx
|
|
28
28
|
|
|
29
|
-
from helius.
|
|
29
|
+
from helius.rpc import SolanaRpcClient
|
|
30
30
|
|
|
31
31
|
LAMPORTS_PER_SOL = 1_000_000_000
|
|
32
32
|
DEVNET_URL = "https://devnet.helius-rpc.com"
|
|
@@ -67,7 +67,9 @@ def main() -> int:
|
|
|
67
67
|
try:
|
|
68
68
|
print(f"Requesting {args.sol} SOL airdrop to {args.address} on devnet...")
|
|
69
69
|
try:
|
|
70
|
-
signature = client.request_airdrop(
|
|
70
|
+
signature = client.request_airdrop(
|
|
71
|
+
public_key=args.address, lamports=lamports
|
|
72
|
+
)
|
|
71
73
|
except httpx.HTTPStatusError as exc:
|
|
72
74
|
print(
|
|
73
75
|
f"HTTP {exc.response.status_code}: devnet airdrop request was rejected.",
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/network_status.py
RENAMED
|
@@ -7,7 +7,7 @@ recent-performance summary (avg TPS over the last samples).
|
|
|
7
7
|
Usage:
|
|
8
8
|
|
|
9
9
|
export HELIUS_API_KEY=your_helius_api_key
|
|
10
|
-
python examples/
|
|
10
|
+
python examples/rpc/network_status.py
|
|
11
11
|
|
|
12
12
|
Uses (with `with`):
|
|
13
13
|
get_health, get_version, get_slot, get_block_height, get_epoch_info,
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
import sys
|
|
20
20
|
|
|
21
|
-
from helius.
|
|
21
|
+
from helius.rpc import SolanaRpcClient
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def main() -> int:
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/priority_fees.py
RENAMED
|
@@ -12,8 +12,8 @@ sample only counts transactions that locked those accounts as writable
|
|
|
12
12
|
Usage:
|
|
13
13
|
|
|
14
14
|
export HELIUS_API_KEY=your_helius_api_key
|
|
15
|
-
python examples/
|
|
16
|
-
python examples/
|
|
15
|
+
python examples/rpc/priority_fees.py
|
|
16
|
+
python examples/rpc/priority_fees.py --account <PUBKEY> --account <PUBKEY>
|
|
17
17
|
|
|
18
18
|
Uses (with `with`):
|
|
19
19
|
get_recent_prioritization_fees.
|
|
@@ -25,7 +25,7 @@ import argparse
|
|
|
25
25
|
import statistics
|
|
26
26
|
import sys
|
|
27
27
|
|
|
28
|
-
from helius.
|
|
28
|
+
from helius.rpc import SolanaRpcClient
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def percentile(sorted_values: list[int], pct: float) -> int:
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/stake_overview.py
RENAMED
|
@@ -7,7 +7,7 @@ validator set (top 10 by stake).
|
|
|
7
7
|
Usage:
|
|
8
8
|
|
|
9
9
|
export HELIUS_API_KEY=your_helius_api_key
|
|
10
|
-
python examples/
|
|
10
|
+
python examples/rpc/stake_overview.py
|
|
11
11
|
|
|
12
12
|
Uses (with `try/finally`):
|
|
13
13
|
get_inflation_rate, get_inflation_governor, get_supply,
|
|
@@ -20,7 +20,7 @@ import sys
|
|
|
20
20
|
|
|
21
21
|
import httpx
|
|
22
22
|
|
|
23
|
-
from helius.
|
|
23
|
+
from helius.rpc import SolanaRpcClient
|
|
24
24
|
|
|
25
25
|
LAMPORTS_PER_SOL = 1_000_000_000
|
|
26
26
|
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/token_inspector.py
RENAMED
|
@@ -7,11 +7,11 @@ Given a mint address, prints:
|
|
|
7
7
|
Usage:
|
|
8
8
|
|
|
9
9
|
export HELIUS_API_KEY=your_helius_api_key
|
|
10
|
-
python examples/
|
|
10
|
+
python examples/rpc/token_inspector.py <MINT_ADDRESS>
|
|
11
11
|
|
|
12
12
|
Example with a small-holder-count mint:
|
|
13
13
|
|
|
14
|
-
python examples/
|
|
14
|
+
python examples/rpc/token_inspector.py J5iyNuTa6zqqA62Xe4h1VBvcBW5CTSNNva3QPh8DU5RV
|
|
15
15
|
|
|
16
16
|
Note:
|
|
17
17
|
Very large mints may be rejected by `getTokenLargestAccounts` if the
|
|
@@ -28,7 +28,7 @@ import sys
|
|
|
28
28
|
|
|
29
29
|
import httpx
|
|
30
30
|
|
|
31
|
-
from helius.
|
|
31
|
+
from helius.rpc import SolanaRpcClient
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def main() -> int:
|
|
@@ -7,7 +7,7 @@ involved, and any log messages emitted by the on-chain programs.
|
|
|
7
7
|
Usage:
|
|
8
8
|
|
|
9
9
|
export HELIUS_API_KEY=your_helius_api_key
|
|
10
|
-
python examples/
|
|
10
|
+
python examples/rpc/transaction_inspector.py <SIGNATURE>
|
|
11
11
|
|
|
12
12
|
Uses (with `with`):
|
|
13
13
|
get_transaction.
|
|
@@ -19,7 +19,7 @@ import argparse
|
|
|
19
19
|
import datetime as dt
|
|
20
20
|
import sys
|
|
21
21
|
|
|
22
|
-
from helius.
|
|
22
|
+
from helius.rpc import SolanaRpcClient
|
|
23
23
|
|
|
24
24
|
LAMPORTS_PER_SOL = 1_000_000_000
|
|
25
25
|
|
{helius_python-0.4.1/examples/solana_rpc → helius_python-0.5.1/examples/rpc}/wallet_tracker.py
RENAMED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
Usage:
|
|
4
4
|
|
|
5
5
|
export HELIUS_API_KEY=your_helius_api_key
|
|
6
|
-
python examples/
|
|
6
|
+
python examples/rpc/wallet_tracker.py <WALLET_ADDRESS> [--limit 20]
|
|
7
7
|
|
|
8
8
|
Example (Helius's own treasury-ish address, replace with any):
|
|
9
9
|
|
|
10
|
-
python examples/
|
|
10
|
+
python examples/rpc/wallet_tracker.py 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
|
|
11
11
|
|
|
12
12
|
It prints:
|
|
13
13
|
- SOL balance (in SOL, not lamports)
|
|
@@ -23,7 +23,7 @@ import argparse
|
|
|
23
23
|
import datetime as dt
|
|
24
24
|
import sys
|
|
25
25
|
|
|
26
|
-
from helius.
|
|
26
|
+
from helius.rpc import SolanaRpcClient
|
|
27
27
|
|
|
28
28
|
# SPL Token program ID — used to list every token account owned by a wallet.
|
|
29
29
|
TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
|
@@ -9,37 +9,47 @@ Usage:
|
|
|
9
9
|
.venv/bin/python test_examples.py
|
|
10
10
|
|
|
11
11
|
The runner expects `HELIUS_API_KEY` to be available in the environment or in
|
|
12
|
-
`.env`, matching the examples themselves.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
`.env`, matching the examples themselves. Missing/unauthorized API key failures
|
|
13
|
+
are reported as "auth" instead of as example runtime bugs. Some Helius endpoints
|
|
14
|
+
are plan-gated or network-gated; other failures in that class are reported as
|
|
15
|
+
"external".
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
from __future__ import annotations
|
|
18
19
|
|
|
20
|
+
import argparse
|
|
19
21
|
import os
|
|
20
22
|
import subprocess
|
|
21
23
|
import sys
|
|
22
24
|
from dataclasses import dataclass, field
|
|
23
25
|
from pathlib import Path
|
|
24
26
|
|
|
25
|
-
|
|
26
27
|
ROOT = Path(__file__).resolve().parent
|
|
27
28
|
PYTHONPATH = str(ROOT / "src")
|
|
28
29
|
USE_COLOR = "NO_COLOR" not in os.environ
|
|
29
30
|
|
|
30
31
|
GREEN = "\033[32m"
|
|
31
32
|
YELLOW = "\033[33m"
|
|
33
|
+
ORANGE = "\033[38;5;208m"
|
|
32
34
|
RED = "\033[31m"
|
|
33
35
|
BOLD = "\033[1m"
|
|
34
36
|
RESET = "\033[0m"
|
|
35
37
|
|
|
36
38
|
SYSTEM_PROGRAM = "11111111111111111111111111111111"
|
|
37
39
|
SMALL_MINT = "J5iyNuTa6zqqA62Xe4h1VBvcBW5CTSNNva3QPh8DU5RV"
|
|
38
|
-
KNOWN_SIGNATURE =
|
|
39
|
-
"eqRntqi1tjXv1zEGBM5btQGWoxWc73XXGDJXjxLE65Atj6T6qzNnJf5LyTbUoGXHS9TzeAnQniAre48SjcJft9f"
|
|
40
|
-
)
|
|
40
|
+
KNOWN_SIGNATURE = "eqRntqi1tjXv1zEGBM5btQGWoxWc73XXGDJXjxLE65Atj6T6qzNnJf5LyTbUoGXHS9TzeAnQniAre48SjcJft9f"
|
|
41
41
|
DEVNET_ADDRESS = "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
|
|
42
42
|
|
|
43
|
+
AUTH_FAILURE_MARKERS = (
|
|
44
|
+
"No API key provided.",
|
|
45
|
+
"HTTP 401",
|
|
46
|
+
"401 Unauthorized",
|
|
47
|
+
"HTTP 403",
|
|
48
|
+
"403 Forbidden",
|
|
49
|
+
"Unauthorized",
|
|
50
|
+
"Forbidden",
|
|
51
|
+
)
|
|
52
|
+
|
|
43
53
|
|
|
44
54
|
@dataclass(frozen=True)
|
|
45
55
|
class ExampleTest:
|
|
@@ -51,9 +61,9 @@ class ExampleTest:
|
|
|
51
61
|
|
|
52
62
|
TESTS = [
|
|
53
63
|
ExampleTest(
|
|
54
|
-
name="
|
|
64
|
+
name="rpc/address_transactions",
|
|
55
65
|
args=[
|
|
56
|
-
"examples/
|
|
66
|
+
"examples/rpc/address_transactions.py",
|
|
57
67
|
SYSTEM_PROGRAM,
|
|
58
68
|
"--limit",
|
|
59
69
|
"1",
|
|
@@ -61,9 +71,9 @@ TESTS = [
|
|
|
61
71
|
],
|
|
62
72
|
),
|
|
63
73
|
ExampleTest(
|
|
64
|
-
name="
|
|
74
|
+
name="rpc/address_transfers",
|
|
65
75
|
args=[
|
|
66
|
-
"examples/
|
|
76
|
+
"examples/rpc/address_transfers.py",
|
|
67
77
|
SYSTEM_PROGRAM,
|
|
68
78
|
"--limit",
|
|
69
79
|
"1",
|
|
@@ -71,14 +81,14 @@ TESTS = [
|
|
|
71
81
|
],
|
|
72
82
|
),
|
|
73
83
|
ExampleTest(
|
|
74
|
-
name="
|
|
75
|
-
args=["examples/
|
|
84
|
+
name="rpc/block_explorer",
|
|
85
|
+
args=["examples/rpc/block_explorer.py", "--slot", "423563000"],
|
|
76
86
|
timeout=90,
|
|
77
87
|
),
|
|
78
88
|
ExampleTest(
|
|
79
|
-
name="
|
|
89
|
+
name="rpc/devnet_airdrop",
|
|
80
90
|
args=[
|
|
81
|
-
"examples/
|
|
91
|
+
"examples/rpc/devnet_airdrop.py",
|
|
82
92
|
DEVNET_ADDRESS,
|
|
83
93
|
"--sol",
|
|
84
94
|
"0.000000001",
|
|
@@ -88,38 +98,40 @@ TESTS = [
|
|
|
88
98
|
],
|
|
89
99
|
),
|
|
90
100
|
ExampleTest(
|
|
91
|
-
name="
|
|
92
|
-
args=["examples/
|
|
101
|
+
name="rpc/network_status",
|
|
102
|
+
args=["examples/rpc/network_status.py"],
|
|
93
103
|
),
|
|
94
104
|
ExampleTest(
|
|
95
|
-
name="
|
|
96
|
-
args=["examples/
|
|
105
|
+
name="rpc/priority_fees",
|
|
106
|
+
args=["examples/rpc/priority_fees.py"],
|
|
97
107
|
),
|
|
98
108
|
ExampleTest(
|
|
99
|
-
name="
|
|
100
|
-
args=["examples/
|
|
109
|
+
name="rpc/stake_overview",
|
|
110
|
+
args=["examples/rpc/stake_overview.py"],
|
|
101
111
|
timeout=120,
|
|
102
112
|
),
|
|
103
113
|
ExampleTest(
|
|
104
|
-
name="
|
|
105
|
-
args=["examples/
|
|
114
|
+
name="rpc/token_inspector",
|
|
115
|
+
args=["examples/rpc/token_inspector.py", SMALL_MINT],
|
|
106
116
|
timeout=90,
|
|
107
117
|
),
|
|
108
118
|
ExampleTest(
|
|
109
|
-
name="
|
|
110
|
-
args=["examples/
|
|
119
|
+
name="rpc/transaction_inspector",
|
|
120
|
+
args=["examples/rpc/transaction_inspector.py", KNOWN_SIGNATURE],
|
|
111
121
|
timeout=90,
|
|
112
122
|
),
|
|
113
123
|
ExampleTest(
|
|
114
|
-
name="
|
|
115
|
-
args=["examples/
|
|
124
|
+
name="rpc/wallet_tracker",
|
|
125
|
+
args=["examples/rpc/wallet_tracker.py", SYSTEM_PROGRAM, "--limit", "1"],
|
|
116
126
|
timeout=120,
|
|
117
127
|
),
|
|
118
128
|
ExampleTest(
|
|
119
129
|
name="laserstream/websocket_logs",
|
|
120
130
|
args=["examples/laserstream/websocket_logs.py", "--count", "1"],
|
|
121
131
|
timeout=45,
|
|
122
|
-
external_failure_markers=(
|
|
132
|
+
external_failure_markers=(
|
|
133
|
+
"TimeoutError: timed out",
|
|
134
|
+
),
|
|
123
135
|
),
|
|
124
136
|
ExampleTest(
|
|
125
137
|
name="webhooks/webhook_crud",
|
|
@@ -140,6 +152,41 @@ def color(text: str, ansi_color: str) -> str:
|
|
|
140
152
|
return f"{ansi_color}{text}{RESET}"
|
|
141
153
|
|
|
142
154
|
|
|
155
|
+
def parse_args() -> argparse.Namespace:
|
|
156
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"-v",
|
|
159
|
+
action="count",
|
|
160
|
+
default=0,
|
|
161
|
+
dest="verbose_count",
|
|
162
|
+
help="increase verbosity; use -v for level 1 or -vv for level 2",
|
|
163
|
+
)
|
|
164
|
+
parser.add_argument(
|
|
165
|
+
"--verbose",
|
|
166
|
+
type=int,
|
|
167
|
+
choices=(0, 1, 2),
|
|
168
|
+
default=None,
|
|
169
|
+
help=(
|
|
170
|
+
"0: only results and failure names; "
|
|
171
|
+
"1: include script output for non-passing tests; "
|
|
172
|
+
"2: include script output for all tests"
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
args = parser.parse_args()
|
|
176
|
+
args.verbose = (
|
|
177
|
+
args.verbose if args.verbose is not None else min(args.verbose_count, 2)
|
|
178
|
+
)
|
|
179
|
+
return args
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def output_preview(output: str) -> str:
|
|
183
|
+
lines = output.strip().splitlines()
|
|
184
|
+
preview = "\n".join(lines[:30])
|
|
185
|
+
if len(lines) > 30:
|
|
186
|
+
preview += f"\n... ({len(lines) - 30} more lines)"
|
|
187
|
+
return preview
|
|
188
|
+
|
|
189
|
+
|
|
143
190
|
def run_example(test: ExampleTest) -> tuple[str, str]:
|
|
144
191
|
env = os.environ.copy()
|
|
145
192
|
env["PYTHONPATH"] = (
|
|
@@ -166,47 +213,60 @@ def run_example(test: ExampleTest) -> tuple[str, str]:
|
|
|
166
213
|
output = result.stdout + result.stderr
|
|
167
214
|
if result.returncode == 0:
|
|
168
215
|
return "passed", output
|
|
216
|
+
if any(marker in output for marker in AUTH_FAILURE_MARKERS):
|
|
217
|
+
return "auth", output
|
|
169
218
|
if any(marker in output for marker in test.external_failure_markers):
|
|
170
219
|
return "external", output
|
|
171
220
|
return "failed", output
|
|
172
221
|
|
|
173
222
|
|
|
174
223
|
def main() -> int:
|
|
224
|
+
args = parse_args()
|
|
175
225
|
passed: list[str] = []
|
|
226
|
+
auth: list[str] = []
|
|
176
227
|
external: list[str] = []
|
|
177
228
|
failed: list[tuple[str, str]] = []
|
|
178
229
|
|
|
179
230
|
for test in TESTS:
|
|
180
|
-
print(f"\n=== {test.name} ===", flush=True)
|
|
181
231
|
status, output = run_example(test)
|
|
182
232
|
if status == "passed":
|
|
183
233
|
passed.append(test.name)
|
|
184
|
-
|
|
234
|
+
result = color("PASS", GREEN + BOLD)
|
|
235
|
+
elif status == "auth":
|
|
236
|
+
auth.append(test.name)
|
|
237
|
+
result = (
|
|
238
|
+
color("AUTH", ORANGE + BOLD)
|
|
239
|
+
+ " - missing API key or endpoint is not authorized for this key"
|
|
240
|
+
)
|
|
185
241
|
elif status == "external":
|
|
186
242
|
external.append(test.name)
|
|
187
|
-
|
|
188
|
-
color(
|
|
189
|
-
|
|
190
|
-
YELLOW + BOLD,
|
|
191
|
-
)
|
|
192
|
-
+ ": endpoint, plan, or network prevented a live success"
|
|
243
|
+
result = (
|
|
244
|
+
color("EXTERNAL", YELLOW + BOLD)
|
|
245
|
+
+ " - endpoint, plan, or network prevented a live success"
|
|
193
246
|
)
|
|
194
247
|
else:
|
|
195
248
|
failed.append((test.name, output))
|
|
196
|
-
|
|
249
|
+
result = color("FAIL", RED + BOLD)
|
|
250
|
+
|
|
251
|
+
print(f"{result}: {test.name}", flush=True)
|
|
197
252
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
print(preview)
|
|
253
|
+
should_print_output = output.strip() and (
|
|
254
|
+
args.verbose == 2 or (args.verbose == 1 and status != "passed")
|
|
255
|
+
)
|
|
256
|
+
if should_print_output:
|
|
257
|
+
print(output_preview(output))
|
|
204
258
|
|
|
205
259
|
print("\n=== Summary ===")
|
|
206
260
|
print(f"{color('Passed', GREEN)} : {len(passed)}")
|
|
261
|
+
print(f"{color('Auth', ORANGE)} : {len(auth)}")
|
|
207
262
|
print(f"{color('External', YELLOW)} : {len(external)}")
|
|
208
263
|
print(f"{color('Failed', RED)} : {len(failed)}")
|
|
209
264
|
|
|
265
|
+
if auth:
|
|
266
|
+
print("\nAuth failures:")
|
|
267
|
+
for name in auth:
|
|
268
|
+
print(f" - {name}")
|
|
269
|
+
|
|
210
270
|
if external:
|
|
211
271
|
print("\nExternal failures:")
|
|
212
272
|
for name in external:
|
|
@@ -215,8 +275,10 @@ def main() -> int:
|
|
|
215
275
|
if failed:
|
|
216
276
|
print("\nUnexpected failures:")
|
|
217
277
|
for name, output in failed:
|
|
218
|
-
print(f"
|
|
219
|
-
|
|
278
|
+
print(f" - {name}")
|
|
279
|
+
if args.verbose >= 1:
|
|
280
|
+
print(f"\n--- {name} ---")
|
|
281
|
+
print(output.strip())
|
|
220
282
|
return 1
|
|
221
283
|
|
|
222
284
|
return 0
|
|
@@ -4,8 +4,8 @@ import httpx
|
|
|
4
4
|
import pytest
|
|
5
5
|
import respx
|
|
6
6
|
|
|
7
|
-
from helius.
|
|
8
|
-
from helius.
|
|
7
|
+
from helius.rpc import SolanaRpcClient
|
|
8
|
+
from helius.rpc.models import Account
|
|
9
9
|
|
|
10
10
|
ACCOUNT_VALUE = {
|
|
11
11
|
"lamports": 5_000_000_000,
|
|
@@ -797,7 +797,7 @@ def test_get_minimum_balance_for_rent_exemption():
|
|
|
797
797
|
@respx.mock
|
|
798
798
|
def test_get_multiple_accounts():
|
|
799
799
|
# Asserts correct upstream API shape {"dataSlice": {...}}.
|
|
800
|
-
# Will FAIL until
|
|
800
|
+
# Will FAIL until rpc.py:449-450 is fixed to send dataSlice as a nested object.
|
|
801
801
|
route = mock_rpc({"context": {"slot": 341197247}, "value": [ACCOUNT_VALUE]})
|
|
802
802
|
with SolanaRpcClient(api_key="test") as client:
|
|
803
803
|
context, accounts = client.get_multiple_accounts(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -163,6 +163,27 @@ class WebSocketClient:
|
|
|
163
163
|
response = self._websocket.recv()
|
|
164
164
|
return json.loads(response)
|
|
165
165
|
|
|
166
|
+
def receive(self) -> tuple[dict | None, Notification, int]:
|
|
167
|
+
response = json.loads(self._websocket.recv())
|
|
168
|
+
model = self.MODELS[response["method"]]
|
|
169
|
+
result = response["params"]["result"]
|
|
170
|
+
subscription = response["params"]["subscription"]
|
|
171
|
+
if isinstance(result, dict):
|
|
172
|
+
context = result.get("context")
|
|
173
|
+
value = result.get("value")
|
|
174
|
+
else:
|
|
175
|
+
context, value = None, None
|
|
176
|
+
if value is not None:
|
|
177
|
+
notification = model.model_validate(value)
|
|
178
|
+
else:
|
|
179
|
+
notification = model.model_validate(result)
|
|
180
|
+
return context, notification, subscription
|
|
181
|
+
|
|
182
|
+
def listen(self):
|
|
183
|
+
while True:
|
|
184
|
+
context, notification, subscription = self.receive()
|
|
185
|
+
yield context, notification, subscription
|
|
186
|
+
|
|
166
187
|
def _unsubscribe(self, subscription_type, subscription) -> bool:
|
|
167
188
|
request = (
|
|
168
189
|
JsonRpcRequest(method=f"{subscription_type}Unsubscribe")
|
|
@@ -376,24 +397,3 @@ class WebSocketClient:
|
|
|
376
397
|
|
|
377
398
|
def vote_unsubscribe(self, subscription) -> bool:
|
|
378
399
|
return self._unsubscribe("vote", subscription)
|
|
379
|
-
|
|
380
|
-
def receive(self) -> tuple[dict | None, Notification, int]:
|
|
381
|
-
response = json.loads(self._websocket.recv())
|
|
382
|
-
model = self.MODELS[response["method"]]
|
|
383
|
-
result = response["params"]["result"]
|
|
384
|
-
subscription = response["params"]["subscription"]
|
|
385
|
-
if isinstance(result, dict):
|
|
386
|
-
context = result.get("context")
|
|
387
|
-
value = result.get("value")
|
|
388
|
-
else:
|
|
389
|
-
context, value = None, None
|
|
390
|
-
if value is not None:
|
|
391
|
-
notification = model.model_validate(value)
|
|
392
|
-
else:
|
|
393
|
-
notification = model.model_validate(result)
|
|
394
|
-
return context, notification, subscription
|
|
395
|
-
|
|
396
|
-
def listen(self):
|
|
397
|
-
while True:
|
|
398
|
-
context, notification, subscription = self.receive()
|
|
399
|
-
yield context, notification, subscription
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|