anaxer 0.1.0__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.
@@ -0,0 +1,9 @@
1
+ dist/
2
+ *.egg-info/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ .venv/
8
+ *.whl
9
+ *.tar.gz
anaxer-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anaxer
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.
anaxer-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: anaxer
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Anaxer Solana real-time data API.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: httpx>=0.27
9
+ Requires-Dist: pydantic>=2.6
10
+ Requires-Dist: websockets<14,>=12
11
+ Provides-Extra: dev
12
+ Requires-Dist: build>=1.2; extra == 'dev'
13
+ Requires-Dist: mypy>=1.9; extra == 'dev'
14
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
15
+ Requires-Dist: pytest>=8; extra == 'dev'
16
+ Requires-Dist: ruff>=0.4; extra == 'dev'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # anaxer
20
+
21
+ Official Python SDK for the [Anaxer](https://anaxer.com) Solana real-time data API.
22
+
23
+ ```bash
24
+ pip install anaxer
25
+ ```
26
+
27
+ Requires Python 3.10+.
28
+
29
+ ## Quickstart — async streaming
30
+
31
+ ```python
32
+ import asyncio
33
+ import anaxer
34
+
35
+ async def main():
36
+ async with anaxer.connect(api_key="...") as client:
37
+ # Plan limits from the server `connected` frame
38
+ print(client.connected.connections_per_stream)
39
+
40
+ async with client.stream("creations", exclude_mayhem=True) as sub:
41
+ async for event in sub:
42
+ print("new launch:", event.symbol, event.mint)
43
+ break
44
+
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ `stream()` returns a `Subscription[T]` handle (async-iterable + async context manager).
49
+ Prefer `async with client.stream(...) as sub:` so leaving the block always sends
50
+ `unsubscribe`. Bare `async for` / `break` also unsubscribes (via `GeneratorExit`).
51
+
52
+ ### Channels
53
+
54
+ | Channel | Yields | Filters (keyword-only, snake_case) |
55
+ |---|---|---|
56
+ | `trades` | `SwapV1` | `sources`, `mints`, `wallets`, `min_volume_usd`, `max_volume_usd`, `sol_only` |
57
+ | `creations` | `CreatedV1` | `sources`, `enriched`, `exclude_mayhem` |
58
+ | `graduations` | `GraduatedV1` | `sources`, `exclude_mayhem`, `min_liquidity_sol` |
59
+ | `prices` | `PriceUpdateV1` | `sources`, `mints` |
60
+
61
+ `transfers` is not wired in v1 (server ingestion deferred). `TransferV1` is still
62
+ exported as a type.
63
+
64
+ ### Side-channel errors
65
+
66
+ Reconnect-triggering / non-fatal codes (e.g. `slow_consumer`) are delivered on
67
+ `client.on_error(callback)` and/or `async for err in client.errors` — they never
68
+ interrupt an open `Subscription`'s `async for`. Subscription-fatal codes
69
+ (`invalid_filters`, …) raise from that subscription only. `unauthorized` is
70
+ terminal.
71
+
72
+ ## Quickstart — sync REST
73
+
74
+ ```python
75
+ from anaxer import Client
76
+
77
+ client = Client(api_key="...")
78
+ price = client.tokens.price("3PFaeFMXiRVYueDCnHHSKndKDDpKZhbhFjQ13JXYpump")
79
+ print(price.price.usd)
80
+ client.close()
81
+ ```
82
+
83
+ `Client` is REST-only — it has no `stream()` method. Use it when you do not want a
84
+ WebSocket. `AsyncClient` (from `anaxer.connect`) also exposes the same REST surface
85
+ via `await`.
86
+
87
+ ### REST surface
88
+
89
+ - `tokens.get` / `batch` / `price` / `batch_price` / `trades`
90
+ - `creations` / `iter_creations`
91
+ - `graduations` / `iter_graduations`
92
+ - `iter_trades(mint, ...)`
93
+ - `launchpads.stats`
94
+
95
+ `programs()` is omitted in v1 (response type not in the public schemas).
96
+
97
+ List endpoints return a `Page` (`data`, `next`, `window`). Use `iter_*` to walk
98
+ `next` across pages. Wire field `from` is exposed in Python as `from_`
99
+ (reserved word) with a Pydantic alias so round-trips keep the wire key.
100
+
101
+ Auth is always `Authorization: Bearer <api_key>` (same as the TypeScript SDK).
102
+
103
+ ## No gap recovery in v1
104
+
105
+ The WebSocket protocol has no sequence/cursor. After a disconnect the client
106
+ reconnects and re-subscribes, but events during the gap are not replayed.
107
+
108
+ ## Config
109
+
110
+ ```python
111
+ from anaxer import Reconnect, connect
112
+
113
+ async with connect(
114
+ api_key="...",
115
+ base_url="https://api.anaxer.com", # local: http://localhost:3010
116
+ reconnect=Reconnect(base_delay=0.5, max_delay=30.0, max_retries=None),
117
+ heartbeat_interval=15.0, # 0 disables
118
+ rest_max_retries=2,
119
+ ) as client:
120
+ ...
121
+ ```
122
+
123
+ ## Errors
124
+
125
+ All failures surface as `anaxer.AnaxerError` with `.code`, `.message`, `.status`
126
+ (HTTP only), and `.subscription_id` (subscription-fatal WS errors only).
127
+
128
+ SDK-local codes: `connection_closed`, `timeout`, `invalid_payload` (client-side
129
+ parse failure — distinct from the wire's `invalid_message`).
130
+
131
+ ## Links
132
+
133
+ - Wire contracts: [`docs/api/ws-v1.md`](../../docs/api/ws-v1.md), [`docs/api/rest-v1.md`](../../docs/api/rest-v1.md)
134
+ - System doc: [`docs/system/sdk-py.md`](../../docs/system/sdk-py.md)
135
+ - TypeScript sibling: [`@anaxer/sdk`](../../packages/sdk/README.md)
anaxer-0.1.0/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # anaxer
2
+
3
+ Official Python SDK for the [Anaxer](https://anaxer.com) Solana real-time data API.
4
+
5
+ ```bash
6
+ pip install anaxer
7
+ ```
8
+
9
+ Requires Python 3.10+.
10
+
11
+ ## Quickstart — async streaming
12
+
13
+ ```python
14
+ import asyncio
15
+ import anaxer
16
+
17
+ async def main():
18
+ async with anaxer.connect(api_key="...") as client:
19
+ # Plan limits from the server `connected` frame
20
+ print(client.connected.connections_per_stream)
21
+
22
+ async with client.stream("creations", exclude_mayhem=True) as sub:
23
+ async for event in sub:
24
+ print("new launch:", event.symbol, event.mint)
25
+ break
26
+
27
+ asyncio.run(main())
28
+ ```
29
+
30
+ `stream()` returns a `Subscription[T]` handle (async-iterable + async context manager).
31
+ Prefer `async with client.stream(...) as sub:` so leaving the block always sends
32
+ `unsubscribe`. Bare `async for` / `break` also unsubscribes (via `GeneratorExit`).
33
+
34
+ ### Channels
35
+
36
+ | Channel | Yields | Filters (keyword-only, snake_case) |
37
+ |---|---|---|
38
+ | `trades` | `SwapV1` | `sources`, `mints`, `wallets`, `min_volume_usd`, `max_volume_usd`, `sol_only` |
39
+ | `creations` | `CreatedV1` | `sources`, `enriched`, `exclude_mayhem` |
40
+ | `graduations` | `GraduatedV1` | `sources`, `exclude_mayhem`, `min_liquidity_sol` |
41
+ | `prices` | `PriceUpdateV1` | `sources`, `mints` |
42
+
43
+ `transfers` is not wired in v1 (server ingestion deferred). `TransferV1` is still
44
+ exported as a type.
45
+
46
+ ### Side-channel errors
47
+
48
+ Reconnect-triggering / non-fatal codes (e.g. `slow_consumer`) are delivered on
49
+ `client.on_error(callback)` and/or `async for err in client.errors` — they never
50
+ interrupt an open `Subscription`'s `async for`. Subscription-fatal codes
51
+ (`invalid_filters`, …) raise from that subscription only. `unauthorized` is
52
+ terminal.
53
+
54
+ ## Quickstart — sync REST
55
+
56
+ ```python
57
+ from anaxer import Client
58
+
59
+ client = Client(api_key="...")
60
+ price = client.tokens.price("3PFaeFMXiRVYueDCnHHSKndKDDpKZhbhFjQ13JXYpump")
61
+ print(price.price.usd)
62
+ client.close()
63
+ ```
64
+
65
+ `Client` is REST-only — it has no `stream()` method. Use it when you do not want a
66
+ WebSocket. `AsyncClient` (from `anaxer.connect`) also exposes the same REST surface
67
+ via `await`.
68
+
69
+ ### REST surface
70
+
71
+ - `tokens.get` / `batch` / `price` / `batch_price` / `trades`
72
+ - `creations` / `iter_creations`
73
+ - `graduations` / `iter_graduations`
74
+ - `iter_trades(mint, ...)`
75
+ - `launchpads.stats`
76
+
77
+ `programs()` is omitted in v1 (response type not in the public schemas).
78
+
79
+ List endpoints return a `Page` (`data`, `next`, `window`). Use `iter_*` to walk
80
+ `next` across pages. Wire field `from` is exposed in Python as `from_`
81
+ (reserved word) with a Pydantic alias so round-trips keep the wire key.
82
+
83
+ Auth is always `Authorization: Bearer <api_key>` (same as the TypeScript SDK).
84
+
85
+ ## No gap recovery in v1
86
+
87
+ The WebSocket protocol has no sequence/cursor. After a disconnect the client
88
+ reconnects and re-subscribes, but events during the gap are not replayed.
89
+
90
+ ## Config
91
+
92
+ ```python
93
+ from anaxer import Reconnect, connect
94
+
95
+ async with connect(
96
+ api_key="...",
97
+ base_url="https://api.anaxer.com", # local: http://localhost:3010
98
+ reconnect=Reconnect(base_delay=0.5, max_delay=30.0, max_retries=None),
99
+ heartbeat_interval=15.0, # 0 disables
100
+ rest_max_retries=2,
101
+ ) as client:
102
+ ...
103
+ ```
104
+
105
+ ## Errors
106
+
107
+ All failures surface as `anaxer.AnaxerError` with `.code`, `.message`, `.status`
108
+ (HTTP only), and `.subscription_id` (subscription-fatal WS errors only).
109
+
110
+ SDK-local codes: `connection_closed`, `timeout`, `invalid_payload` (client-side
111
+ parse failure — distinct from the wire's `invalid_message`).
112
+
113
+ ## Links
114
+
115
+ - Wire contracts: [`docs/api/ws-v1.md`](../../docs/api/ws-v1.md), [`docs/api/rest-v1.md`](../../docs/api/rest-v1.md)
116
+ - System doc: [`docs/system/sdk-py.md`](../../docs/system/sdk-py.md)
117
+ - TypeScript sibling: [`@anaxer/sdk`](../../packages/sdk/README.md)
@@ -0,0 +1,69 @@
1
+ [project]
2
+ name = "anaxer"
3
+ version = "0.1.0"
4
+ description = "Official Python SDK for the Anaxer Solana real-time data API."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.10"
9
+ dependencies = [
10
+ "websockets>=12,<14",
11
+ "httpx>=0.27",
12
+ "pydantic>=2.6",
13
+ ]
14
+
15
+ [project.optional-dependencies]
16
+ dev = [
17
+ "pytest>=8",
18
+ "pytest-asyncio>=0.23",
19
+ "mypy>=1.9",
20
+ "ruff>=0.4",
21
+ "build>=1.2",
22
+ ]
23
+
24
+ [build-system]
25
+ requires = ["hatchling"]
26
+ build-backend = "hatchling.build"
27
+
28
+ [tool.hatch.build.targets.wheel]
29
+ packages = ["src/anaxer"]
30
+
31
+ [tool.mypy]
32
+ strict = true
33
+ mypy_path = "src"
34
+ packages = ["anaxer"]
35
+ plugins = ["pydantic.mypy"]
36
+ warn_unused_ignores = true
37
+
38
+ [[tool.mypy.overrides]]
39
+ module = "websockets.*"
40
+ ignore_missing_imports = true
41
+
42
+ [[tool.mypy.overrides]]
43
+ module = [
44
+ "test_ws",
45
+ "test_reconnect",
46
+ "test_rest",
47
+ "test_contract_examples",
48
+ "test_models_compat",
49
+ "mock_ws",
50
+ ]
51
+ disallow_untyped_defs = false
52
+ check_untyped_defs = false
53
+ allow_untyped_defs = true
54
+ disable_error_code = ["type-arg", "no-untyped-def"]
55
+
56
+ # test_types.py intentionally uses type: ignore on negative cases — keep it under
57
+ # strict + warn_unused_ignores (do not loosen this module).
58
+
59
+ [tool.ruff]
60
+ line-length = 100
61
+ src = ["src", "test"]
62
+
63
+ [tool.ruff.lint]
64
+ ignore = ["S110", "PYI034"]
65
+
66
+ [tool.pytest.ini_options]
67
+ asyncio_mode = "auto"
68
+ testpaths = ["test"]
69
+ pythonpath = ["src"]
@@ -0,0 +1,37 @@
1
+ """Assert built wheel runtime Requires-Dist is exactly websockets/httpx/pydantic."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import glob
6
+ import re
7
+ import sys
8
+ import zipfile
9
+
10
+
11
+ def main() -> int:
12
+ wheels = sorted(glob.glob("dist/*.whl"))
13
+ if not wheels:
14
+ print("no wheel in dist/", file=sys.stderr)
15
+ return 1
16
+ wheel = wheels[-1]
17
+ zf = zipfile.ZipFile(wheel)
18
+ meta_name = next(n for n in zf.namelist() if n.endswith("METADATA"))
19
+ meta = zf.read(meta_name).decode()
20
+ deps: set[str] = set()
21
+ for line in meta.splitlines():
22
+ if not line.startswith("Requires-Dist:"):
23
+ continue
24
+ if "extra ==" in line:
25
+ continue
26
+ rest = line.split(":", 1)[1].strip()
27
+ deps.add(re.split(r"[><=!~; ]", rest)[0])
28
+ expected = {"websockets", "httpx", "pydantic"}
29
+ if deps != expected:
30
+ print(f"expected {expected}, got {deps}", file=sys.stderr)
31
+ return 1
32
+ print("OK", deps)
33
+ return 0
34
+
35
+
36
+ if __name__ == "__main__":
37
+ raise SystemExit(main())
@@ -0,0 +1,46 @@
1
+ """Official Python SDK for the Anaxer Solana real-time data API."""
2
+
3
+ from anaxer._client_async import AsyncClient
4
+ from anaxer._client_sync import Client
5
+ from anaxer._config import Reconnect
6
+ from anaxer._connect import connect
7
+ from anaxer.errors import AnaxerError, AnaxerErrorCode
8
+ from anaxer.models import (
9
+ WIRE_VERSION,
10
+ ConnectedInfo,
11
+ CreatedV1,
12
+ GraduatedV1,
13
+ LaunchpadStatsV1,
14
+ PriceUpdateV1,
15
+ SubscriptionFiltersV1,
16
+ SwapV1,
17
+ TokenMetadataV1,
18
+ TransferV1,
19
+ Window,
20
+ )
21
+ from anaxer.rest._pagination import Page
22
+ from anaxer.ws._subscription import Subscription
23
+
24
+ __all__ = [
25
+ "WIRE_VERSION",
26
+ "AnaxerError",
27
+ "AnaxerErrorCode",
28
+ "AsyncClient",
29
+ "Client",
30
+ "ConnectedInfo",
31
+ "CreatedV1",
32
+ "GraduatedV1",
33
+ "LaunchpadStatsV1",
34
+ "Page",
35
+ "PriceUpdateV1",
36
+ "Reconnect",
37
+ "Subscription",
38
+ "SubscriptionFiltersV1",
39
+ "SwapV1",
40
+ "TokenMetadataV1",
41
+ "TransferV1",
42
+ "Window",
43
+ "connect",
44
+ ]
45
+
46
+ __version__ = "0.1.0"
@@ -0,0 +1,175 @@
1
+ """AsyncClient: eager WS + REST namespaces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator, Callable
6
+ from typing import Any, Literal, overload
7
+
8
+ from anaxer._config import ClientConfig
9
+ from anaxer.errors import AnaxerError
10
+ from anaxer.models import (
11
+ ConnectedInfo,
12
+ CreatedV1,
13
+ GraduatedV1,
14
+ PriceUpdateV1,
15
+ SubscriptionFiltersV1,
16
+ SwapV1,
17
+ )
18
+ from anaxer.rest._endpoints import (
19
+ LaunchpadsAsyncApi,
20
+ TokensAsyncApi,
21
+ async_creations,
22
+ async_graduations,
23
+ iter_creations_async,
24
+ iter_graduations_async,
25
+ iter_trades_async,
26
+ )
27
+ from anaxer.rest._http import AsyncHttpClient
28
+ from anaxer.rest._pagination import Page
29
+ from anaxer.ws._connection import ErrorHub, WsConnection
30
+ from anaxer.ws._subscription import Subscription
31
+
32
+
33
+ class _ErrorsAsyncIterator:
34
+ def __init__(self, hub: ErrorHub) -> None:
35
+ self._q = hub.subscribe()
36
+
37
+ def __aiter__(self) -> _ErrorsAsyncIterator:
38
+ return self
39
+
40
+ async def __anext__(self) -> AnaxerError:
41
+ item = await self._q.get()
42
+ if item is None:
43
+ raise StopAsyncIteration
44
+ return item
45
+
46
+
47
+ class AsyncClient:
48
+ def __init__(
49
+ self,
50
+ config: ClientConfig,
51
+ *,
52
+ http_transport: Any = None,
53
+ open_transport_fn: Any = None,
54
+ ) -> None:
55
+ self._config = config
56
+ self._error_hub = ErrorHub()
57
+ self._conn = WsConnection(
58
+ config, self._error_hub, open_transport_fn=open_transport_fn
59
+ )
60
+ self._http = AsyncHttpClient(
61
+ base_url=config.base_url,
62
+ api_key=config.api_key,
63
+ max_retries=config.rest_max_retries,
64
+ transport=http_transport,
65
+ )
66
+ self.tokens = TokensAsyncApi(self._http)
67
+ self.launchpads = LaunchpadsAsyncApi(self._http)
68
+ self._entered = False
69
+
70
+ @property
71
+ def connected(self) -> ConnectedInfo:
72
+ info = self._conn.connected
73
+ if info is None:
74
+ raise AnaxerError("connection_closed", "Client is not connected")
75
+ return info
76
+
77
+ @property
78
+ def errors(self) -> AsyncIterator[AnaxerError]:
79
+ """Side-channel errors. Each access returns a new subscriber iterator."""
80
+ return _ErrorsAsyncIterator(self._error_hub)
81
+
82
+ def on_error(self, callback: Callable[[AnaxerError], None]) -> None:
83
+ self._error_hub.on_error(callback)
84
+
85
+ async def __aenter__(self) -> AsyncClient:
86
+ await self._conn.open()
87
+ self._entered = True
88
+ return self
89
+
90
+ async def __aexit__(self, *exc: object) -> None:
91
+ await self.aclose()
92
+
93
+ async def aclose(self) -> None:
94
+ await self._conn.close()
95
+ await self._http.aclose()
96
+ self._entered = False
97
+
98
+ @overload
99
+ def stream(
100
+ self,
101
+ channel: Literal["trades"],
102
+ *,
103
+ sources: list[str] | None = None,
104
+ mints: list[str] | None = None,
105
+ wallets: list[str] | None = None,
106
+ min_volume_usd: float | None = None,
107
+ max_volume_usd: float | None = None,
108
+ sol_only: bool | None = None,
109
+ ) -> Subscription[SwapV1]: ...
110
+
111
+ @overload
112
+ def stream(
113
+ self,
114
+ channel: Literal["creations"],
115
+ *,
116
+ sources: list[str] | None = None,
117
+ enriched: bool | None = None,
118
+ exclude_mayhem: bool | None = None,
119
+ ) -> Subscription[CreatedV1]: ...
120
+
121
+ @overload
122
+ def stream(
123
+ self,
124
+ channel: Literal["graduations"],
125
+ *,
126
+ sources: list[str] | None = None,
127
+ exclude_mayhem: bool | None = None,
128
+ min_liquidity_sol: float | None = None,
129
+ ) -> Subscription[GraduatedV1]: ...
130
+
131
+ @overload
132
+ def stream(
133
+ self,
134
+ channel: Literal["prices"],
135
+ *,
136
+ sources: list[str] | None = None,
137
+ mints: list[str] | None = None,
138
+ ) -> Subscription[PriceUpdateV1]: ...
139
+
140
+ def stream(self, channel: str, **filters: Any) -> Subscription[Any]:
141
+ if not self._entered and self._conn.state != "open":
142
+ # Allow stream() only after successful __aenter__ (eager open).
143
+ raise AnaxerError(
144
+ "connection_closed",
145
+ "Call stream() inside `async with anaxer.connect(...)`",
146
+ )
147
+ model = SubscriptionFiltersV1.model_validate(
148
+ {
149
+ "sources": filters.get("sources"),
150
+ "mints": filters.get("mints"),
151
+ "wallets": filters.get("wallets"),
152
+ "minVolumeUsd": filters.get("min_volume_usd"),
153
+ "maxVolumeUsd": filters.get("max_volume_usd"),
154
+ "enriched": filters.get("enriched"),
155
+ "excludeMayhem": filters.get("exclude_mayhem"),
156
+ "minLiquiditySol": filters.get("min_liquidity_sol"),
157
+ "solOnly": filters.get("sol_only"),
158
+ }
159
+ )
160
+ return self._conn.stream(channel, model) # type: ignore[arg-type]
161
+
162
+ async def creations(self, **opts: Any) -> Page[CreatedV1]:
163
+ return await async_creations(self._http, **opts)
164
+
165
+ def iter_creations(self, **opts: Any) -> AsyncIterator[CreatedV1]:
166
+ return iter_creations_async(self._http, **opts)
167
+
168
+ async def graduations(self, **opts: Any) -> Page[GraduatedV1]:
169
+ return await async_graduations(self._http, **opts)
170
+
171
+ def iter_graduations(self, **opts: Any) -> AsyncIterator[GraduatedV1]:
172
+ return iter_graduations_async(self._http, **opts)
173
+
174
+ def iter_trades(self, mint: str, **opts: Any) -> AsyncIterator[SwapV1]:
175
+ return iter_trades_async(self._http, mint, **opts)
@@ -0,0 +1,62 @@
1
+ """Sync Client — REST only."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from typing import Any
7
+
8
+ from anaxer.models import CreatedV1, GraduatedV1, SwapV1
9
+ from anaxer.rest._endpoints import (
10
+ LaunchpadsApi,
11
+ TokensApi,
12
+ iter_creations_sync,
13
+ iter_graduations_sync,
14
+ iter_trades_sync,
15
+ sync_creations,
16
+ sync_graduations,
17
+ )
18
+ from anaxer.rest._http import SyncHttpClient
19
+ from anaxer.rest._pagination import Page
20
+
21
+
22
+ class Client:
23
+ def __init__(
24
+ self,
25
+ api_key: str,
26
+ *,
27
+ base_url: str = "https://api.anaxer.com",
28
+ rest_max_retries: int = 2,
29
+ http_transport: Any = None,
30
+ ) -> None:
31
+ self._http = SyncHttpClient(
32
+ base_url=base_url,
33
+ api_key=api_key,
34
+ max_retries=rest_max_retries,
35
+ transport=http_transport,
36
+ )
37
+ self.tokens = TokensApi(self._http)
38
+ self.launchpads = LaunchpadsApi(self._http)
39
+
40
+ def close(self) -> None:
41
+ self._http.close()
42
+
43
+ def __enter__(self) -> Client:
44
+ return self
45
+
46
+ def __exit__(self, *exc: object) -> None:
47
+ self.close()
48
+
49
+ def creations(self, **opts: Any) -> Page[CreatedV1]:
50
+ return sync_creations(self._http, **opts)
51
+
52
+ def iter_creations(self, **opts: Any) -> Iterator[CreatedV1]:
53
+ return iter_creations_sync(self._http, **opts)
54
+
55
+ def graduations(self, **opts: Any) -> Page[GraduatedV1]:
56
+ return sync_graduations(self._http, **opts)
57
+
58
+ def iter_graduations(self, **opts: Any) -> Iterator[GraduatedV1]:
59
+ return iter_graduations_sync(self._http, **opts)
60
+
61
+ def iter_trades(self, mint: str, **opts: Any) -> Iterator[SwapV1]:
62
+ return iter_trades_sync(self._http, mint, **opts)