helius-python 0.0.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.0.1/.editorconfig +4 -0
- helius_python-0.0.1/.github/workflows/python-publish.yml +70 -0
- helius_python-0.0.1/.gitignore +220 -0
- helius_python-0.0.1/AGENTS.md +212 -0
- helius_python-0.0.1/CLAUDE.md +1 -0
- helius_python-0.0.1/CONTRIBUTING.md +116 -0
- helius_python-0.0.1/LICENSE +21 -0
- helius_python-0.0.1/PKG-INFO +231 -0
- helius_python-0.0.1/README.md +214 -0
- helius_python-0.0.1/pyproject.toml +38 -0
- helius_python-0.0.1/src/helius/__init__.py +0 -0
- helius_python-0.0.1/src/helius/client.py +941 -0
- helius_python-0.0.1/src/helius/models.py +206 -0
- helius_python-0.0.1/tests/fixtures/account.json +8 -0
- helius_python-0.0.1/tests/fixtures/supply.json +6 -0
- helius_python-0.0.1/tests/unit/test_helius_client.py +1325 -0
- helius_python-0.0.1/tests/unit/test_models.py +308 -0
- helius_python-0.0.1/tests/unit/test_rpc_request.py +46 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# This workflow will upload a Python Package to PyPI when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: Upload Python Package
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release-build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.x"
|
|
28
|
+
|
|
29
|
+
- name: Build release distributions
|
|
30
|
+
run: |
|
|
31
|
+
# NOTE: put your own distribution build steps here.
|
|
32
|
+
python -m pip install build
|
|
33
|
+
python -m build
|
|
34
|
+
|
|
35
|
+
- name: Upload distributions
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: release-dists
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
pypi-publish:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
needs:
|
|
44
|
+
- release-build
|
|
45
|
+
permissions:
|
|
46
|
+
# IMPORTANT: this permission is mandatory for trusted publishing
|
|
47
|
+
id-token: write
|
|
48
|
+
|
|
49
|
+
# Dedicated environments with protections for publishing are strongly recommended.
|
|
50
|
+
# For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
|
|
51
|
+
environment:
|
|
52
|
+
name: pypi
|
|
53
|
+
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
|
|
54
|
+
# url: https://pypi.org/p/YOURPROJECT
|
|
55
|
+
#
|
|
56
|
+
# ALTERNATIVE: if your GitHub Release name is the PyPI project version string
|
|
57
|
+
# ALTERNATIVE: exactly, uncomment the following line instead:
|
|
58
|
+
# url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- name: Retrieve release distributions
|
|
62
|
+
uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: release-dists
|
|
65
|
+
path: dist/
|
|
66
|
+
|
|
67
|
+
- name: Publish release distributions to PyPI
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
69
|
+
with:
|
|
70
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# UV
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
201
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
203
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
When implementing a function for a RPC method in the HeliusClient class, read the docs for the specific RPC method. For example, for the get_supply function (getSupply RPC method) you should read https://www.helius.dev/docs/rpc/guides/getsupply and https://www.helius.dev/docs/api-reference/rpc/http/getsupply.
|
|
2
|
+
|
|
3
|
+
# API Reference
|
|
4
|
+
|
|
5
|
+
The API reference is generated from **Google-style docstrings** on every public symbol in `src/helius/`. Whenever you add or modify a public method, model, or class, you MUST write or update its docstring following the rules below. The README's "Supported methods" table is a quick index only — the docstring is the source of truth.
|
|
6
|
+
|
|
7
|
+
## Where docstrings live
|
|
8
|
+
|
|
9
|
+
- **Client methods** (`HeliusClient.get_*`, `is_*`, `request_*`, etc.) — every public method gets a docstring. Private methods (`_send`, dunders) do not need one.
|
|
10
|
+
- **Models** (`src/helius/models.py`) — every public model class gets a docstring describing what it represents. Individual fields don't need per-field docstrings if their names and types are self-explanatory; only document fields where the meaning, units, or nullability isn't obvious from the type alone.
|
|
11
|
+
- **Builders / helpers** (`RpcRequest`) — class docstring describing purpose, plus one-line docstrings on each public method.
|
|
12
|
+
|
|
13
|
+
## Client method docstring format
|
|
14
|
+
|
|
15
|
+
Use Google-style sections. Order matters; omit any section that doesn't apply.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
def method_name(self, ...) -> ReturnType:
|
|
19
|
+
"""One-line summary in the imperative mood, ending with a period.
|
|
20
|
+
|
|
21
|
+
Optional longer description: a paragraph or two on what the method does,
|
|
22
|
+
when to use it, and any non-obvious behavior. Keep this short — link to
|
|
23
|
+
the upstream Helius docs rather than restating them.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
param_one: Description of the first parameter, including units
|
|
27
|
+
(lamports vs. SOL, slot vs. block height) when relevant. Mention
|
|
28
|
+
the upstream JSON field name only if it differs in a non-obvious
|
|
29
|
+
way from the snake_case Python name.
|
|
30
|
+
param_two: Description. Note constraints like "must be base-58
|
|
31
|
+
encoded" or "max 500_000 slots from `start_slot`".
|
|
32
|
+
commitment: Optional commitment level. Defaults to the node's default
|
|
33
|
+
when omitted.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Describe the return shape. For tuples, describe each element in
|
|
37
|
+
order. For models, name the model and what it represents. For
|
|
38
|
+
primitives, name the unit (e.g. "Balance in lamports.").
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: When the arguments violate the documented constraints
|
|
42
|
+
(e.g. mutually exclusive params, mismatched pairs).
|
|
43
|
+
|
|
44
|
+
Note:
|
|
45
|
+
Only available on Devnet and Testnet, not Mainnet Beta.
|
|
46
|
+
|
|
47
|
+
See Also:
|
|
48
|
+
- Helius guide: https://www.helius.dev/docs/rpc/guides/<method>
|
|
49
|
+
- Helius API reference: https://www.helius.dev/docs/api-reference/rpc/http/<method>
|
|
50
|
+
"""
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Rules:
|
|
54
|
+
|
|
55
|
+
- **Summary line** is one sentence, imperative mood, ≤ 88 chars, ends with a period. Example: `"Return the SOL balance of an account in lamports."`
|
|
56
|
+
- **`Args:`** documents every parameter, even `commitment` and `min_context_slot`. One entry per param. Don't re-state the type — basedpyright already shows it.
|
|
57
|
+
- **`Returns:`** is mandatory unless the method returns `None`.
|
|
58
|
+
- **`Raises:`** is required if the method calls `raise` directly. Don't document exceptions that bubble up from `httpx` — those are covered by the library-wide error-handling docs.
|
|
59
|
+
- **`Note:`** for network restrictions, version requirements, pagination limits, deprecation warnings, etc. One section per concern.
|
|
60
|
+
- **`See Also:`** ALWAYS includes the Helius guide and API reference URLs for the underlying RPC method. This is the contract for an RPC wrapper — the docstring tells you what we do, the upstream docs tell you what the network does.
|
|
61
|
+
- Keep prose tight. The docstring should be readable in `help(client.method)` without scrolling forever.
|
|
62
|
+
|
|
63
|
+
## Model docstring format
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
class Supply(BaseModel):
|
|
67
|
+
"""Total, circulating, and non-circulating SOL supply, in lamports.
|
|
68
|
+
|
|
69
|
+
Returned as the `value` field of `getSupply`. All amounts are in
|
|
70
|
+
lamports (1 SOL = 1_000_000_000 lamports).
|
|
71
|
+
|
|
72
|
+
See Also:
|
|
73
|
+
- Helius API reference: https://www.helius.dev/docs/api-reference/rpc/http/getsupply
|
|
74
|
+
"""
|
|
75
|
+
...
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Rules:
|
|
79
|
+
|
|
80
|
+
- One-line summary + optional paragraph + `See Also:` linking the upstream docs that define the shape.
|
|
81
|
+
- If a field has surprising semantics (e.g. `ui_amount` can be `None` when the mint has too many decimals, `non_circulating_accounts` is only populated when not excluded), document it inline with `Field(..., description="...")` or in the class docstring.
|
|
82
|
+
|
|
83
|
+
## Conventions
|
|
84
|
+
|
|
85
|
+
- Use **double-quoted triple strings** (`"""..."""`).
|
|
86
|
+
- Wrap docstring lines at 88 columns.
|
|
87
|
+
- Refer to parameters in backticks: `` `commitment` ``, `` `min_context_slot` ``.
|
|
88
|
+
- Refer to other client methods with their snake_case name in backticks: `` `get_balance` ``.
|
|
89
|
+
- Do NOT include the upstream JSON-RPC method name in the summary — that's already in the See Also URLs.
|
|
90
|
+
- Do NOT copy-paste large chunks from the Helius docs. Summarize and link.
|
|
91
|
+
- Examples (`Example:` section) are encouraged for methods with non-trivial argument combinations (e.g. `get_block_production`, `get_token_accounts_by_owner`), optional for everything else.
|
|
92
|
+
|
|
93
|
+
## Implementation conventions
|
|
94
|
+
|
|
95
|
+
- **If the RPC returns an `RpcResponse` wrapper (`{context, value}`), the Python method MUST return `(context, value)`** — never silently drop `context`. For methods whose `value` is itself a small composite, flatten the tuple (e.g. `get_latest_blockhash` returns `tuple[dict, str, int]`, not `tuple[dict, tuple[str, int]]`). Check the upstream Helius API reference page to see whether the response is wrapped.
|
|
96
|
+
- Type-annotate the return shape exactly. `tuple[dict, X]`, not bare `tuple`, and not the unwrapped `X`. Mismatches between annotation and runtime shape break the docstring contract.
|
|
97
|
+
- `value` can be `null` for some wrapped methods (e.g. `getAccountInfo` when the account doesn't exist, `getMultipleAccounts` entries for closed accounts). Reflect that in the return type with `| None` and handle it before calling `Model.model_validate(...)`.
|
|
98
|
+
|
|
99
|
+
## Checklist for adding a new client method
|
|
100
|
+
|
|
101
|
+
1. Read both Helius doc URLs for the RPC method (see the top of this file).
|
|
102
|
+
2. Implement the method, following the **Implementation conventions** above — in particular, return `(context, value)` if the upstream response is wrapped.
|
|
103
|
+
3. Add a docstring with `Args`, `Returns`, `Raises` (if any), `Note` (if any), and `See Also` with both Helius URLs.
|
|
104
|
+
4. Add the method to the "Supported methods" table in `README.md`.
|
|
105
|
+
5. Add tests per the Testing section.
|
|
106
|
+
|
|
107
|
+
# Testing
|
|
108
|
+
|
|
109
|
+
## Stack
|
|
110
|
+
|
|
111
|
+
- `pytest` as the test runner.
|
|
112
|
+
- `respx` for mocking the `httpx` transport. Do NOT use `unittest.mock` to patch `httpx` — always mock at the HTTP layer with `respx`.
|
|
113
|
+
- No live network calls. Do NOT make real requests to `mainnet.helius-rpc.com`, `devnet.helius-rpc.com`, or any other Helius endpoint in tests. Devnet integration tests are explicitly out of scope for now.
|
|
114
|
+
|
|
115
|
+
Add `pytest` and `respx` to a `[project.optional-dependencies] dev` table in `pyproject.toml` if not already present.
|
|
116
|
+
|
|
117
|
+
## Layout
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
tests/
|
|
121
|
+
conftest.py # shared fixtures (client factory, sample responses)
|
|
122
|
+
fixtures/ # captured JSON-RPC response bodies, one file per shape
|
|
123
|
+
unit/
|
|
124
|
+
test_rpc_request.py # RpcRequest builder
|
|
125
|
+
test_models.py # pydantic model validation against fixtures
|
|
126
|
+
test_client.py # one test (or small group) per client method
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Three Layers
|
|
130
|
+
|
|
131
|
+
### 1. `RpcRequest` builder — pure unit tests
|
|
132
|
+
|
|
133
|
+
`RpcRequest` is pure logic with no I/O. Cover:
|
|
134
|
+
|
|
135
|
+
- `add(value)` skips `None` unless `can_be_none=True`.
|
|
136
|
+
- `set(key, value)` skips `None` unless `can_be_none=True`.
|
|
137
|
+
- Positional params come before the config dict.
|
|
138
|
+
- The config dict is appended as the last element of `params` only when it's non-empty.
|
|
139
|
+
- `params` is omitted entirely from the payload when there are no positional or config values.
|
|
140
|
+
- `method`, `id`, and `jsonrpc` are passed through correctly.
|
|
141
|
+
|
|
142
|
+
These tests would have caught the current bug where `jsonrpc` is never added to the built payload. Fix bugs you find this way as separate commits, not as part of the test PR.
|
|
143
|
+
|
|
144
|
+
### 2. Pydantic models — fixture-based unit tests
|
|
145
|
+
|
|
146
|
+
For each model in `src/helius/models.py`:
|
|
147
|
+
|
|
148
|
+
- Capture a real JSON-RPC `result` body once (manually, from the Helius docs' example responses or a one-off live call) and save it under `tests/fixtures/`.
|
|
149
|
+
- Write a test that loads the fixture and calls `Model.model_validate(...)`. Assert a couple of representative fields parsed correctly (especially camelCase → snake_case via the alias generator).
|
|
150
|
+
|
|
151
|
+
One fixture per model is enough. The goal is to catch schema mismatches (missing fields, wrong optionality, typos like `foudnation`), not to exhaustively assert every field.
|
|
152
|
+
|
|
153
|
+
**Fixture keys must come from the Helius docs, not from the model.** If you write the fixture by reading the Python field names and camelCasing them, the test only proves the model agrees with itself — typos like `loaderScheduleSlotOffset` (should be `leaderScheduleSlotOffset`) will pass. Always copy the JSON keys verbatim from the upstream Helius example response.
|
|
154
|
+
|
|
155
|
+
### 3. Client methods — `respx`-mocked tests
|
|
156
|
+
|
|
157
|
+
For each method on `HeliusClient`, write at least one test that asserts **both** sides of the wire:
|
|
158
|
+
|
|
159
|
+
- **Outgoing request:** the JSON body sent to Helius matches the JSON-RPC payload you expect — correct `method`, correct positional `params`, correct config object with snake → camel mapping, and optional arguments omitted when `None`.
|
|
160
|
+
- **Return value:** the method correctly parses a canned response into the documented return shape (model, tuple, primitive, etc.).
|
|
161
|
+
|
|
162
|
+
Also assert that the `api-key` query parameter is present on the request URL.
|
|
163
|
+
|
|
164
|
+
**The mocked response body must mirror the real upstream shape.** Copy the `result` payload from the Helius docs' example response — do not invent a flatter shape by reading what the Python method indexes into. In particular, if the upstream method returns an `RpcResponse` wrapper (`{"context": {...}, "value": ...}`), the mock must include that wrapper, even if the client method only returns `value`. Otherwise a bug that reads `result["foo"]` instead of `result["value"]["foo"]` will pass the test and fail in production.
|
|
165
|
+
|
|
166
|
+
Skeleton:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import json
|
|
170
|
+
import httpx
|
|
171
|
+
import respx
|
|
172
|
+
from helius import HeliusClient
|
|
173
|
+
|
|
174
|
+
@respx.mock
|
|
175
|
+
def test_get_balance():
|
|
176
|
+
route = respx.post("https://mainnet.helius-rpc.com/").mock(
|
|
177
|
+
return_value=httpx.Response(
|
|
178
|
+
200,
|
|
179
|
+
json={
|
|
180
|
+
"jsonrpc": "2.0",
|
|
181
|
+
"id": 1,
|
|
182
|
+
"result": {"context": {"slot": 1}, "value": 42},
|
|
183
|
+
},
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
with HeliusClient(api_key="test") as c:
|
|
187
|
+
assert c.get_balance("So11...112", commitment="finalized") == 42
|
|
188
|
+
|
|
189
|
+
sent = route.calls.last.request
|
|
190
|
+
assert sent.url.params["api-key"] == "test"
|
|
191
|
+
body = json.loads(sent.content)
|
|
192
|
+
assert body["method"] == "getBalance"
|
|
193
|
+
assert body["params"] == ["So11...112", {"commitment": "finalized"}]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
For methods with branching logic (e.g. `get_block_production`, `get_token_accounts_by_owner`'s `mint` vs `program_id` validation, `get_account_info`'s `data_slice` pairing), add tests for each branch — including the `ValueError`s on invalid input combinations.
|
|
197
|
+
|
|
198
|
+
## Conventions
|
|
199
|
+
|
|
200
|
+
- Construct `HeliusClient` in tests with an explicit `api_key="test"` (or similar). Never rely on a real `.env` file.
|
|
201
|
+
- Use the context-manager form (`with HeliusClient(...) as c:`) in tests so the `httpx.Client` is closed cleanly.
|
|
202
|
+
- Group tests in `tests/unit/test_client.py` by method, but a single file is fine until it grows unwieldy.
|
|
203
|
+
- Test names should describe the behavior, not the implementation: `test_get_balance_includes_commitment_in_config`, not `test_get_balance_calls_set`.
|
|
204
|
+
- When adding a new client method, the PR must include: (a) a `respx` test asserting the request payload, and (b) a fixture-based model test if the method introduces or uses a model. This is enforced in `CONTRIBUTING.md`.
|
|
205
|
+
|
|
206
|
+
## Running
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pytest
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
All tests must pass and there must be no real network traffic. If a test fails because it tried to hit the network, that's a bug in the test — add the missing `@respx.mock` or `respx` route.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions to `helius-python` are very welcome! This project aims to be the
|
|
4
|
+
**complete, typed Python client for [Helius](https://helius.dev)** and there
|
|
5
|
+
is a lot of surface area still to cover. Every issue closed, RPC method added,
|
|
6
|
+
typo fixed, and example improved moves the library closer to that goal.
|
|
7
|
+
|
|
8
|
+
## Project Goals
|
|
9
|
+
|
|
10
|
+
Before contributing, please read the [Goals section of the README](README.md#goals).
|
|
11
|
+
Every change should serve at least one of:
|
|
12
|
+
|
|
13
|
+
1. **Completeness** — 1:1 coverage of the entire Helius API surface.
|
|
14
|
+
2. **Type safety** — fully typed parameters and responses.
|
|
15
|
+
3. **Pythonic ergonomics** — `snake_case` names, context managers, sensible
|
|
16
|
+
defaults.
|
|
17
|
+
4. **Zero magic** — thin, predictable wrappers that map directly to the
|
|
18
|
+
documented Helius API.
|
|
19
|
+
|
|
20
|
+
If a change doesn't clearly support one of these goals, it probably isn't a
|
|
21
|
+
good fit. When in doubt, open an issue first to discuss it.
|
|
22
|
+
|
|
23
|
+
## Ways to Contribute
|
|
24
|
+
|
|
25
|
+
There is no shortage of things to do. A few ideas, roughly ordered from
|
|
26
|
+
easiest to most involved:
|
|
27
|
+
|
|
28
|
+
- **Fix a bug.** Reproduce, write the fix, send the PR.
|
|
29
|
+
- **Add a missing standard Solana JSON-RPC method.** Check the
|
|
30
|
+
[supported methods table in the README](README.md#supported-methods) — anything
|
|
31
|
+
in the [Solana JSON-RPC spec](https://solana.com/docs/rpc) that isn't listed
|
|
32
|
+
is fair game.
|
|
33
|
+
- **Add a Helius-specific RPC extension** — enhanced transactions, DAS
|
|
34
|
+
(Digital Asset Standard) methods, priority fee estimation, etc.
|
|
35
|
+
- **Add a Helius REST endpoint** — Enhanced Transactions API, Webhooks API,
|
|
36
|
+
Mint API, token metadata, address lookups, and more.
|
|
37
|
+
- **Improve typing.** Tightening a `dict` return into a proper `pydantic`
|
|
38
|
+
model, narrowing a `str` into a `Literal`, or replacing `Any` with a real
|
|
39
|
+
type are all great contributions.
|
|
40
|
+
- **Improve error handling.** A consistent exception hierarchy for transport
|
|
41
|
+
errors and Helius/Solana RPC errors is a known gap.
|
|
42
|
+
- **Add streaming / websocket support** once the synchronous surface is
|
|
43
|
+
fleshed out.
|
|
44
|
+
- **Improve docs and examples.** Clearer docstrings, better README snippets,
|
|
45
|
+
more realistic examples.
|
|
46
|
+
|
|
47
|
+
If you're not sure where to start, browse the
|
|
48
|
+
[open issues](https://github.com/markosnarinian/helius-python/issues).
|
|
49
|
+
|
|
50
|
+
## Before You Start a Large Change
|
|
51
|
+
|
|
52
|
+
**Please open an issue before working on anything non-trivial.** This includes:
|
|
53
|
+
|
|
54
|
+
- Architectural changes or refactors that span multiple files.
|
|
55
|
+
- New public APIs that aren't just wrapping an existing Helius/Solana endpoint.
|
|
56
|
+
- Changes to the existing public method signatures or behavior.
|
|
57
|
+
- Pulling in new runtime dependencies.
|
|
58
|
+
- Anything you expect to take more than an afternoon.
|
|
59
|
+
|
|
60
|
+
A quick conversation up-front saves everyone time and avoids the awkward case
|
|
61
|
+
of a large PR that has to be redesigned or rejected. Small, focused PRs — one
|
|
62
|
+
new RPC method, one bug fix, one model improvement — usually don't need
|
|
63
|
+
prior discussion; just send them.
|
|
64
|
+
|
|
65
|
+
## Codebase Conventions
|
|
66
|
+
|
|
67
|
+
Please match the existing style of the codebase. Skim
|
|
68
|
+
[`src/helius/client.py`](src/helius/client.py) and
|
|
69
|
+
[`src/helius/models.py`](src/helius/models.py) before writing new code. In
|
|
70
|
+
particular:
|
|
71
|
+
|
|
72
|
+
- **Method names** are `snake_case` versions of the upstream RPC method
|
|
73
|
+
(`getAccountInfo` → `get_account_info`).
|
|
74
|
+
- **Parameter names** are `snake_case` versions of the upstream JSON field
|
|
75
|
+
names (`minContextSlot` → `min_context_slot`).
|
|
76
|
+
- **Use the `RpcRequest` builder** to construct request payloads — `add(...)`
|
|
77
|
+
for positional params, `set(key, value)` for fields inside the config
|
|
78
|
+
object. Don't build the JSON dict by hand.
|
|
79
|
+
- **Type everything.** Parameters get explicit types, including `Literal`s
|
|
80
|
+
for fixed string options (`commitment`, `encoding`, etc.). Return types are
|
|
81
|
+
always declared.
|
|
82
|
+
- **Model responses with `pydantic`.** Add a model in
|
|
83
|
+
[`src/helius/models.py`](src/helius/models.py) using the existing
|
|
84
|
+
`AliasGenerator(validation_alias=to_camel)` pattern instead of returning a
|
|
85
|
+
raw `dict`.
|
|
86
|
+
- **Follow the docs.** When implementing a new RPC method, read both the
|
|
87
|
+
Helius guide and API reference for that method (see
|
|
88
|
+
[`AGENTS.md`](AGENTS.md)) and mirror the documented parameters and response
|
|
89
|
+
shape.
|
|
90
|
+
- **Keep wrappers thin.** No surprise retries, caching, or transformations.
|
|
91
|
+
If the upstream API returns `{ context, value }`, return `(context, value)`
|
|
92
|
+
— don't flatten it.
|
|
93
|
+
|
|
94
|
+
## Format and Lint Before Opening a PR
|
|
95
|
+
|
|
96
|
+
Run the formatters and type checker before pushing. PRs that aren't formatted
|
|
97
|
+
will be asked to reformat before review.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
isort src
|
|
101
|
+
black src
|
|
102
|
+
basedpyright src
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Make sure your change at least imports cleanly and the method you added
|
|
106
|
+
actually works against a real Helius endpoint.
|
|
107
|
+
|
|
108
|
+
## Pull Requests
|
|
109
|
+
|
|
110
|
+
- Keep PRs focused. One method, one bug fix, or one refactor per PR.
|
|
111
|
+
- Write a clear PR description: what changed, why, and a link to the relevant
|
|
112
|
+
Helius/Solana docs for any new endpoint.
|
|
113
|
+
- Reference the issue your PR addresses (`Closes #123`).
|
|
114
|
+
- Be patient — reviews happen as time allows.
|
|
115
|
+
|
|
116
|
+
Thanks for contributing!
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Markos Narinian
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|