kricket-client 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.
- kricket_client-0.1.0/.gitignore +52 -0
- kricket_client-0.1.0/LICENSE +21 -0
- kricket_client-0.1.0/PKG-INFO +92 -0
- kricket_client-0.1.0/README.md +69 -0
- kricket_client-0.1.0/kricket_client/__init__.py +8 -0
- kricket_client-0.1.0/kricket_client/chirps.py +36 -0
- kricket_client-0.1.0/kricket_client/client.py +164 -0
- kricket_client-0.1.0/kricket_client/debugger.py +19 -0
- kricket_client-0.1.0/kricket_client/exceptions.py +11 -0
- kricket_client-0.1.0/kricket_client/firefly.py +32 -0
- kricket_client-0.1.0/kricket_client/http.py +53 -0
- kricket_client-0.1.0/kricket_client/mantis.py +32 -0
- kricket_client-0.1.0/kricket_client/pulse.py +28 -0
- kricket_client-0.1.0/kricket_client/types.py +253 -0
- kricket_client-0.1.0/pyproject.toml +31 -0
- kricket_client-0.1.0/tests/__init__.py +0 -0
- kricket_client-0.1.0/tests/test_client.py +94 -0
- kricket_client-0.1.0/tests/test_intel.py +120 -0
- kricket_client-0.1.0/tests/test_types.py +316 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
/core/target/
|
|
3
|
+
**/*.rs.bk
|
|
4
|
+
|
|
5
|
+
# Go
|
|
6
|
+
/services/*/vendor/
|
|
7
|
+
# Build artifacts — each service builds a binary matching its directory name
|
|
8
|
+
/services/gateway/gateway
|
|
9
|
+
/services/pulse-api/pulse-api
|
|
10
|
+
/services/mantis-api/mantis-api
|
|
11
|
+
/services/firefly-api/firefly-api
|
|
12
|
+
/services/chirps/chirps
|
|
13
|
+
/services/status/status
|
|
14
|
+
/services/hub-sidecar/cricket-hub-sidecar
|
|
15
|
+
# One-off CLI binaries — rebuild via `go build -o services/mint-backfill ./services/gateway/cmd/mint-backfill`
|
|
16
|
+
/services/mint-backfill
|
|
17
|
+
|
|
18
|
+
# TypeScript / Node
|
|
19
|
+
node_modules/
|
|
20
|
+
dist/
|
|
21
|
+
*.tsbuildinfo
|
|
22
|
+
|
|
23
|
+
# Environment
|
|
24
|
+
.env
|
|
25
|
+
.env.local
|
|
26
|
+
.env.production
|
|
27
|
+
|
|
28
|
+
# IDE
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
*.swp
|
|
32
|
+
*.swo
|
|
33
|
+
|
|
34
|
+
# Playwright MCP artifacts
|
|
35
|
+
.playwright-mcp/
|
|
36
|
+
|
|
37
|
+
# OS
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# Docker
|
|
42
|
+
docker-compose.override.yml
|
|
43
|
+
|
|
44
|
+
# Python
|
|
45
|
+
__pycache__/
|
|
46
|
+
*.pyc
|
|
47
|
+
*.egg-info/
|
|
48
|
+
|
|
49
|
+
# Secrets
|
|
50
|
+
*.pem
|
|
51
|
+
*.key
|
|
52
|
+
cricket-admin/.env
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VYLTH
|
|
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.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kricket-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for Kricket Protocol: DeFi intelligence and execution suite
|
|
5
|
+
Project-URL: Homepage, https://github.com/VYLTH/kricket
|
|
6
|
+
Project-URL: Repository, https://github.com/VYLTH/kricket
|
|
7
|
+
Author: VYLTH
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: api-client,defi,kricket,solana,web3
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: httpx>=0.27.0
|
|
21
|
+
Requires-Dist: pydantic>=2.0.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# kricket-client
|
|
25
|
+
|
|
26
|
+
Async Python client for the [Kricket Protocol](https://cricket.vylth.com) — DeFi intelligence APIs for rug detection, wallet profiling, price feeds, smart contract analysis, and notifications.
|
|
27
|
+
|
|
28
|
+
Requires Python 3.10+. All methods are async.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install kricket-client
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import asyncio
|
|
40
|
+
from kricket_client import KricketClient
|
|
41
|
+
|
|
42
|
+
async def main():
|
|
43
|
+
async with KricketClient(api_key="your-api-key") as kricket:
|
|
44
|
+
# Scan a token for rug-pull risk
|
|
45
|
+
result = await kricket.mantis.scan("TOKEN_ADDRESS")
|
|
46
|
+
print(result.risk_score.rating) # 'low' | 'moderate' | 'high' | 'critical'
|
|
47
|
+
print(result.risk_score.score) # 0–100
|
|
48
|
+
|
|
49
|
+
# Get a wallet intelligence profile
|
|
50
|
+
wallet = await kricket.firefly.get_wallet("WALLET_ADDRESS")
|
|
51
|
+
print(wallet.score, wallet.style, wallet.win_rate)
|
|
52
|
+
|
|
53
|
+
asyncio.run(main())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Clients
|
|
57
|
+
|
|
58
|
+
| Client | Description | Key Methods |
|
|
59
|
+
|--------|-------------|-------------|
|
|
60
|
+
| `kricket.mantis` | Rug-pull detection & token risk scoring | `scan(token)`, `get_score(token)`, `add_to_watchlist(tokens)`, `get_watchlist()` |
|
|
61
|
+
| `kricket.firefly` | Wallet intelligence & smart money signals | `get_wallet(address)`, `get_signals()`, `get_leaderboard()`, `track(address)` |
|
|
62
|
+
| `kricket.pulse` | Unified price feeds (CEX + DEX) | `get_price(symbol)`, `get_spread(symbol)` |
|
|
63
|
+
| `kricket.chirps` | Notification channel management | `list_channels()`, `create_channel(config)`, `delete_channel(id)` |
|
|
64
|
+
| `kricket.debugger` | Smart contract vulnerability analysis | `analyze(source, language)` |
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from kricket_client import KricketClient, KricketConfig
|
|
70
|
+
|
|
71
|
+
config = KricketConfig(
|
|
72
|
+
api_key="your-api-key",
|
|
73
|
+
base_url="https://api-cricket.vylth.com/api/kricket", # optional override
|
|
74
|
+
timeout=30.0, # seconds, default 30
|
|
75
|
+
)
|
|
76
|
+
kricket = KricketClient(config)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Error Handling
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from kricket_client import KricketError
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
result = await kricket.mantis.scan(token)
|
|
86
|
+
except KricketError as e:
|
|
87
|
+
print(e.code, e.message, e.status)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Full Docs
|
|
91
|
+
|
|
92
|
+
[cricket.vylth.com/docs](https://cricket.vylth.com/docs)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# kricket-client
|
|
2
|
+
|
|
3
|
+
Async Python client for the [Kricket Protocol](https://cricket.vylth.com) — DeFi intelligence APIs for rug detection, wallet profiling, price feeds, smart contract analysis, and notifications.
|
|
4
|
+
|
|
5
|
+
Requires Python 3.10+. All methods are async.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install kricket-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import asyncio
|
|
17
|
+
from kricket_client import KricketClient
|
|
18
|
+
|
|
19
|
+
async def main():
|
|
20
|
+
async with KricketClient(api_key="your-api-key") as kricket:
|
|
21
|
+
# Scan a token for rug-pull risk
|
|
22
|
+
result = await kricket.mantis.scan("TOKEN_ADDRESS")
|
|
23
|
+
print(result.risk_score.rating) # 'low' | 'moderate' | 'high' | 'critical'
|
|
24
|
+
print(result.risk_score.score) # 0–100
|
|
25
|
+
|
|
26
|
+
# Get a wallet intelligence profile
|
|
27
|
+
wallet = await kricket.firefly.get_wallet("WALLET_ADDRESS")
|
|
28
|
+
print(wallet.score, wallet.style, wallet.win_rate)
|
|
29
|
+
|
|
30
|
+
asyncio.run(main())
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Clients
|
|
34
|
+
|
|
35
|
+
| Client | Description | Key Methods |
|
|
36
|
+
|--------|-------------|-------------|
|
|
37
|
+
| `kricket.mantis` | Rug-pull detection & token risk scoring | `scan(token)`, `get_score(token)`, `add_to_watchlist(tokens)`, `get_watchlist()` |
|
|
38
|
+
| `kricket.firefly` | Wallet intelligence & smart money signals | `get_wallet(address)`, `get_signals()`, `get_leaderboard()`, `track(address)` |
|
|
39
|
+
| `kricket.pulse` | Unified price feeds (CEX + DEX) | `get_price(symbol)`, `get_spread(symbol)` |
|
|
40
|
+
| `kricket.chirps` | Notification channel management | `list_channels()`, `create_channel(config)`, `delete_channel(id)` |
|
|
41
|
+
| `kricket.debugger` | Smart contract vulnerability analysis | `analyze(source, language)` |
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from kricket_client import KricketClient, KricketConfig
|
|
47
|
+
|
|
48
|
+
config = KricketConfig(
|
|
49
|
+
api_key="your-api-key",
|
|
50
|
+
base_url="https://api-cricket.vylth.com/api/kricket", # optional override
|
|
51
|
+
timeout=30.0, # seconds, default 30
|
|
52
|
+
)
|
|
53
|
+
kricket = KricketClient(config)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Error Handling
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from kricket_client import KricketError
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
result = await kricket.mantis.scan(token)
|
|
63
|
+
except KricketError as e:
|
|
64
|
+
print(e.code, e.message, e.status)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Full Docs
|
|
68
|
+
|
|
69
|
+
[cricket.vylth.com/docs](https://cricket.vylth.com/docs)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Kricket Protocol Python Client: DeFi intelligence and execution suite."""
|
|
2
|
+
|
|
3
|
+
from kricket_client.client import KricketClient, KricketConfig
|
|
4
|
+
from kricket_client.types import * # noqa: F401,F403
|
|
5
|
+
from kricket_client.exceptions import KricketError
|
|
6
|
+
|
|
7
|
+
__all__ = ["KricketClient", "KricketConfig", "KricketError"]
|
|
8
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Chirps API client: notification channel management."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
from kricket_client.http import HttpClient
|
|
8
|
+
from kricket_client.types import ChirpChannel, ChirpRecord
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChirpsClient:
|
|
12
|
+
def __init__(self, http: HttpClient):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
async def create_channel(self, channel: dict) -> ChirpChannel:
|
|
16
|
+
"""Create a notification channel."""
|
|
17
|
+
data = await self._http.post("/chirps/channels", json=channel)
|
|
18
|
+
return ChirpChannel(**data)
|
|
19
|
+
|
|
20
|
+
async def list_channels(self) -> list[ChirpChannel]:
|
|
21
|
+
"""List all notification channels."""
|
|
22
|
+
data = await self._http.get("/chirps/channels")
|
|
23
|
+
return [ChirpChannel(**c) for c in data]
|
|
24
|
+
|
|
25
|
+
async def delete_channel(self, channel_id: str) -> None:
|
|
26
|
+
"""Delete a notification channel."""
|
|
27
|
+
await self._http.delete(f"/chirps/channels/{quote(channel_id)}")
|
|
28
|
+
|
|
29
|
+
async def test_channel(self, channel_id: str) -> dict:
|
|
30
|
+
"""Send a test chirp to verify channel configuration."""
|
|
31
|
+
return await self._http.post(f"/chirps/test/{quote(channel_id)}")
|
|
32
|
+
|
|
33
|
+
async def get_history(self) -> list[ChirpRecord]:
|
|
34
|
+
"""Get notification delivery history."""
|
|
35
|
+
data = await self._http.get("/chirps/history")
|
|
36
|
+
return [ChirpRecord(**r) for r in data]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Main Kricket client: unified entry point for all API modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import AsyncIterator, Optional
|
|
8
|
+
from urllib.parse import quote
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
from kricket_client.exceptions import KricketError
|
|
13
|
+
from kricket_client.http import HttpClient
|
|
14
|
+
from kricket_client.types import CheckResponse, WatchEvent
|
|
15
|
+
from kricket_client.pulse import PulseClient
|
|
16
|
+
from kricket_client.mantis import MantisClient
|
|
17
|
+
from kricket_client.firefly import FireflyClient
|
|
18
|
+
from kricket_client.chirps import ChirpsClient
|
|
19
|
+
from kricket_client.debugger import DebuggerClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class KricketConfig:
|
|
24
|
+
"""Configuration for the Kricket client."""
|
|
25
|
+
|
|
26
|
+
base_url: str = "https://api-cricket.vylth.com/api/cricket"
|
|
27
|
+
api_key: str = ""
|
|
28
|
+
timeout: float = 30.0
|
|
29
|
+
# Intel REST base for the one-call check()/guard()/watch() surface.
|
|
30
|
+
intel_url: str = "https://api-cricket.vylth.com"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class KricketClient:
|
|
34
|
+
"""Unified client for Kricket Protocol APIs."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, config: KricketConfig | None = None, **kwargs):
|
|
37
|
+
cfg = config or KricketConfig(**kwargs)
|
|
38
|
+
self._http = HttpClient(cfg.base_url, cfg.api_key, cfg.timeout)
|
|
39
|
+
self._intel_url = cfg.intel_url.rstrip("/")
|
|
40
|
+
self._api_key = cfg.api_key
|
|
41
|
+
self._timeout = cfg.timeout
|
|
42
|
+
self._intel: httpx.AsyncClient | None = None
|
|
43
|
+
self._pulse: PulseClient | None = None
|
|
44
|
+
self._mantis: MantisClient | None = None
|
|
45
|
+
self._firefly: FireflyClient | None = None
|
|
46
|
+
self._chirps: ChirpsClient | None = None
|
|
47
|
+
self._debugger: DebuggerClient | None = None
|
|
48
|
+
|
|
49
|
+
def _intel_client(self) -> httpx.AsyncClient:
|
|
50
|
+
if self._intel is None:
|
|
51
|
+
self._intel = httpx.AsyncClient(
|
|
52
|
+
base_url=self._intel_url,
|
|
53
|
+
headers={"X-API-Key": self._api_key},
|
|
54
|
+
timeout=self._timeout,
|
|
55
|
+
)
|
|
56
|
+
return self._intel
|
|
57
|
+
|
|
58
|
+
async def check(self, chain: str, address: str) -> CheckResponse:
|
|
59
|
+
"""One-call safety check for an address. The drop-in: is it safe to touch?
|
|
60
|
+
|
|
61
|
+
Returns the raw verdict; raises :class:`KricketError` with the server
|
|
62
|
+
reason on a non-ok response.
|
|
63
|
+
"""
|
|
64
|
+
resp = await self._intel_client().get(
|
|
65
|
+
f"/v1/check/{quote(chain, safe='')}/{quote(address, safe='')}"
|
|
66
|
+
)
|
|
67
|
+
if resp.status_code != 200:
|
|
68
|
+
reason = f"HTTP {resp.status_code}"
|
|
69
|
+
try:
|
|
70
|
+
body = resp.json()
|
|
71
|
+
if isinstance(body, dict) and body.get("reason"):
|
|
72
|
+
reason = body["reason"]
|
|
73
|
+
except Exception:
|
|
74
|
+
pass # non-JSON error body
|
|
75
|
+
raise KricketError("CHECK_FAILED", reason, resp.status_code)
|
|
76
|
+
return CheckResponse(**resp.json())
|
|
77
|
+
|
|
78
|
+
async def guard(
|
|
79
|
+
self, chain: str, address: str, fail_open: bool = False
|
|
80
|
+
) -> Optional[CheckResponse]:
|
|
81
|
+
"""Pre-sign guard — one line in a signing hook.
|
|
82
|
+
|
|
83
|
+
Returns the verdict when cleared; **raises** ``KricketError('BLOCKED')``
|
|
84
|
+
when not, so the transaction is never signed. Fail-closed by default: if
|
|
85
|
+
the engine is unreachable the error propagates (caller does not sign).
|
|
86
|
+
Pass ``fail_open=True`` to instead return ``None`` on an engine error —
|
|
87
|
+
an explicit danger verdict still raises.
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
verdict = await self.check(chain, address)
|
|
91
|
+
except KricketError:
|
|
92
|
+
if fail_open:
|
|
93
|
+
return None # engine unreachable — caller chose to allow
|
|
94
|
+
raise # fail closed: cannot verify -> do not sign
|
|
95
|
+
if not verdict.cleared:
|
|
96
|
+
raise KricketError("BLOCKED", verdict.reason, 403) # explicit danger always blocks
|
|
97
|
+
return verdict
|
|
98
|
+
|
|
99
|
+
async def watch(
|
|
100
|
+
self, addresses: Optional[list[str]] = None
|
|
101
|
+
) -> AsyncIterator[WatchEvent]:
|
|
102
|
+
"""Subscribe to live revocations — yields a :class:`WatchEvent` the instant
|
|
103
|
+
a watched address turns dangerous. Pass ``None`` to watch everything; break
|
|
104
|
+
out of the loop to unsubscribe.
|
|
105
|
+
"""
|
|
106
|
+
params = {"addresses": ",".join(addresses)} if addresses else None
|
|
107
|
+
async with self._intel_client().stream(
|
|
108
|
+
"GET", "/v1/watch", params=params, headers={"Accept": "text/event-stream"}
|
|
109
|
+
) as resp:
|
|
110
|
+
if resp.status_code != 200:
|
|
111
|
+
raise KricketError("WATCH_FAILED", f"HTTP {resp.status_code}", resp.status_code)
|
|
112
|
+
buf = ""
|
|
113
|
+
async for chunk in resp.aiter_text():
|
|
114
|
+
buf += chunk
|
|
115
|
+
while "\n\n" in buf:
|
|
116
|
+
block, buf = buf.split("\n\n", 1)
|
|
117
|
+
for line in block.split("\n"):
|
|
118
|
+
if line.startswith("data:"):
|
|
119
|
+
try:
|
|
120
|
+
yield WatchEvent(**json.loads(line[5:].strip()))
|
|
121
|
+
except Exception:
|
|
122
|
+
pass # skip malformed event
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def pulse(self) -> PulseClient:
|
|
126
|
+
if self._pulse is None:
|
|
127
|
+
self._pulse = PulseClient(self._http)
|
|
128
|
+
return self._pulse
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def mantis(self) -> MantisClient:
|
|
132
|
+
if self._mantis is None:
|
|
133
|
+
self._mantis = MantisClient(self._http)
|
|
134
|
+
return self._mantis
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def firefly(self) -> FireflyClient:
|
|
138
|
+
if self._firefly is None:
|
|
139
|
+
self._firefly = FireflyClient(self._http)
|
|
140
|
+
return self._firefly
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def chirps(self) -> ChirpsClient:
|
|
144
|
+
if self._chirps is None:
|
|
145
|
+
self._chirps = ChirpsClient(self._http)
|
|
146
|
+
return self._chirps
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def debugger(self) -> DebuggerClient:
|
|
150
|
+
if self._debugger is None:
|
|
151
|
+
self._debugger = DebuggerClient(self._http)
|
|
152
|
+
return self._debugger
|
|
153
|
+
|
|
154
|
+
async def close(self) -> None:
|
|
155
|
+
"""Close the underlying HTTP clients."""
|
|
156
|
+
await self._http.close()
|
|
157
|
+
if self._intel is not None:
|
|
158
|
+
await self._intel.aclose()
|
|
159
|
+
|
|
160
|
+
async def __aenter__(self) -> KricketClient:
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
async def __aexit__(self, *args) -> None:
|
|
164
|
+
await self.close()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Debugger API client: smart contract vulnerability scanning."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from kricket_client.http import HttpClient
|
|
6
|
+
from kricket_client.types import AnalysisResult, Language
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DebuggerClient:
|
|
10
|
+
def __init__(self, http: HttpClient):
|
|
11
|
+
self._http = http
|
|
12
|
+
|
|
13
|
+
async def analyze(self, source: str, language: Language | str) -> AnalysisResult:
|
|
14
|
+
"""Analyze source code for vulnerabilities and gas optimizations."""
|
|
15
|
+
data = await self._http.post(
|
|
16
|
+
"/debugger/analyze",
|
|
17
|
+
json={"source": source, "language": language.value if hasattr(language, "value") else str(language)},
|
|
18
|
+
)
|
|
19
|
+
return AnalysisResult(**data)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Kricket API exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class KricketError(Exception):
|
|
5
|
+
"""Raised when the Kricket API returns an error."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, code: str, message: str, status: int):
|
|
8
|
+
self.code = code
|
|
9
|
+
self.message = message
|
|
10
|
+
self.status = status
|
|
11
|
+
super().__init__(f"[{code}] {message} (HTTP {status})")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Firefly API client: wallet intelligence and smart money signals."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
from kricket_client.http import HttpClient
|
|
8
|
+
from kricket_client.types import WalletProfile, Signal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FireflyClient:
|
|
12
|
+
def __init__(self, http: HttpClient):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
async def get_wallet(self, address: str) -> WalletProfile:
|
|
16
|
+
"""Get a wallet's intelligence profile."""
|
|
17
|
+
data = await self._http.get(f"/firefly/wallet/{quote(address)}")
|
|
18
|
+
return WalletProfile(**data)
|
|
19
|
+
|
|
20
|
+
async def get_signals(self) -> list[Signal]:
|
|
21
|
+
"""Get active smart money signals."""
|
|
22
|
+
data = await self._http.get("/firefly/signals")
|
|
23
|
+
return [Signal(**s) for s in data]
|
|
24
|
+
|
|
25
|
+
async def get_leaderboard(self) -> list[WalletProfile]:
|
|
26
|
+
"""Get the top wallets leaderboard."""
|
|
27
|
+
data = await self._http.get("/firefly/leaderboard")
|
|
28
|
+
return [WalletProfile(**w) for w in data]
|
|
29
|
+
|
|
30
|
+
async def track(self, address: str) -> dict:
|
|
31
|
+
"""Start tracking a wallet."""
|
|
32
|
+
return await self._http.post("/firefly/track", json={"address": address})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Base HTTP client for Kricket API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, TypeVar
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from kricket_client.exceptions import KricketError
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HttpClient:
|
|
15
|
+
"""Async HTTP client with API key auth and error handling."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, base_url: str, api_key: str, timeout: float = 30.0):
|
|
18
|
+
self._client = httpx.AsyncClient(
|
|
19
|
+
base_url=base_url.rstrip("/"),
|
|
20
|
+
headers={
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
"X-API-Key": api_key,
|
|
23
|
+
},
|
|
24
|
+
timeout=timeout,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
async def get(self, path: str) -> Any:
|
|
28
|
+
return await self._request("GET", path)
|
|
29
|
+
|
|
30
|
+
async def post(self, path: str, json: Any = None) -> Any:
|
|
31
|
+
return await self._request("POST", path, json=json)
|
|
32
|
+
|
|
33
|
+
async def delete(self, path: str) -> None:
|
|
34
|
+
resp = await self._client.delete(path)
|
|
35
|
+
if resp.status_code not in (200, 204):
|
|
36
|
+
raise KricketError("REQUEST_FAILED", f"HTTP {resp.status_code}", resp.status_code)
|
|
37
|
+
|
|
38
|
+
async def _request(self, method: str, path: str, json: Any = None) -> Any:
|
|
39
|
+
resp = await self._client.request(method, path, json=json)
|
|
40
|
+
data = resp.json()
|
|
41
|
+
|
|
42
|
+
if not data.get("success") or data.get("error"):
|
|
43
|
+
err = data.get("error", {})
|
|
44
|
+
raise KricketError(
|
|
45
|
+
code=err.get("code", "UNKNOWN_ERROR"),
|
|
46
|
+
message=err.get("message", "Unknown error"),
|
|
47
|
+
status=resp.status_code,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return data.get("data")
|
|
51
|
+
|
|
52
|
+
async def close(self) -> None:
|
|
53
|
+
await self._client.aclose()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Mantis API client: rug-pull detection and token risk scoring."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
from kricket_client.http import HttpClient
|
|
8
|
+
from kricket_client.types import ScanResponse, RiskScore, WatchlistEntry
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MantisClient:
|
|
12
|
+
def __init__(self, http: HttpClient):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
async def scan(self, token: str) -> ScanResponse:
|
|
16
|
+
"""Run a full security scan on a token."""
|
|
17
|
+
data = await self._http.get(f"/mantis/scan/{quote(token)}")
|
|
18
|
+
return ScanResponse(**data)
|
|
19
|
+
|
|
20
|
+
async def get_score(self, token: str) -> RiskScore:
|
|
21
|
+
"""Get only the risk score for a token."""
|
|
22
|
+
data = await self._http.get(f"/mantis/score/{quote(token)}")
|
|
23
|
+
return RiskScore(**data)
|
|
24
|
+
|
|
25
|
+
async def add_to_watchlist(self, tokens: list[str]) -> dict:
|
|
26
|
+
"""Add tokens to the watchlist."""
|
|
27
|
+
return await self._http.post("/mantis/watchlist", json={"tokens": tokens})
|
|
28
|
+
|
|
29
|
+
async def get_watchlist(self) -> list[WatchlistEntry]:
|
|
30
|
+
"""Get all watched tokens."""
|
|
31
|
+
data = await self._http.get("/mantis/watchlist")
|
|
32
|
+
return [WatchlistEntry(**e) for e in data]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Pulse API client: unified price feeds across CEX and DEX venues."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
from kricket_client.http import HttpClient
|
|
8
|
+
from kricket_client.types import PriceQuote, SpreadAnalysis, Venue
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PulseClient:
|
|
12
|
+
def __init__(self, http: HttpClient):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
async def get_price(self, token: str) -> list[PriceQuote]:
|
|
16
|
+
"""Get price quotes for a token across all venues."""
|
|
17
|
+
data = await self._http.get(f"/pulse/price/{quote(token)}")
|
|
18
|
+
return [PriceQuote(**q) for q in data]
|
|
19
|
+
|
|
20
|
+
async def get_spread(self, token: str) -> SpreadAnalysis:
|
|
21
|
+
"""Get cross-venue spread analysis for a token."""
|
|
22
|
+
data = await self._http.get(f"/pulse/spread/{quote(token)}")
|
|
23
|
+
return SpreadAnalysis(**data)
|
|
24
|
+
|
|
25
|
+
async def get_venues(self) -> list[Venue]:
|
|
26
|
+
"""List all supported venues."""
|
|
27
|
+
data = await self._http.get("/pulse/venues")
|
|
28
|
+
return [Venue(**v) for v in data]
|