keelcodes 1.0.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.
- keelcodes-1.0.0/PKG-INFO +55 -0
- keelcodes-1.0.0/README.md +41 -0
- keelcodes-1.0.0/keelcodes/__init__.py +24 -0
- keelcodes-1.0.0/keelcodes/_errors.py +11 -0
- keelcodes-1.0.0/keelcodes/_http.py +50 -0
- keelcodes-1.0.0/keelcodes/account.py +29 -0
- keelcodes-1.0.0/keelcodes/data.py +82 -0
- keelcodes-1.0.0/keelcodes/payment.py +78 -0
- keelcodes-1.0.0/keelcodes/wallet.py +63 -0
- keelcodes-1.0.0/keelcodes.egg-info/PKG-INFO +55 -0
- keelcodes-1.0.0/keelcodes.egg-info/SOURCES.txt +13 -0
- keelcodes-1.0.0/keelcodes.egg-info/dependency_links.txt +1 -0
- keelcodes-1.0.0/keelcodes.egg-info/top_level.txt +1 -0
- keelcodes-1.0.0/pyproject.toml +24 -0
- keelcodes-1.0.0/setup.cfg +4 -0
keelcodes-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keelcodes
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Keel SDK — unified access to payment / wallet / data / account modules over https://keel.codes
|
|
5
|
+
Author: Keel
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://keel.codes
|
|
8
|
+
Keywords: keel,payment,wallet,data,account,sdk
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Keel SDK (Python)
|
|
16
|
+
|
|
17
|
+
统一访问 Keel 四个模块:`payment / wallet / data / account`,基于 `https://keel.codes`。
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install keelcodes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 用法
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from keelcodes import Keel
|
|
29
|
+
|
|
30
|
+
keel = Keel(api_key="sk_payment_xxx")
|
|
31
|
+
|
|
32
|
+
# payment
|
|
33
|
+
res = keel.payment.charge(
|
|
34
|
+
payer="0x...", amount_wei="1000000000000000",
|
|
35
|
+
method="x402", reference="charge_1",
|
|
36
|
+
)
|
|
37
|
+
print(res)
|
|
38
|
+
|
|
39
|
+
# data
|
|
40
|
+
bars = keel.data.bars(symbol="BTC", timeframe="1d")
|
|
41
|
+
|
|
42
|
+
# account
|
|
43
|
+
key = keel.account.apikey_create(tenant_id="t1", module="data")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 模块
|
|
47
|
+
|
|
48
|
+
- `keel.payment` — 收费/付费/查账/核验/访问
|
|
49
|
+
- `keel.wallet` — 建钱包/签名/转账/链上读写/多签/会话
|
|
50
|
+
- `keel.data` — 取行情/K线/因子/预测/事件/知识
|
|
51
|
+
- `keel.account` — 租户/密钥/订阅
|
|
52
|
+
|
|
53
|
+
## 鉴权
|
|
54
|
+
|
|
55
|
+
统一 Bearer API key(按模块订阅签发 `sk_<module>_*`),传入 `Keel(api_key=...)`。
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Keel SDK (Python)
|
|
2
|
+
|
|
3
|
+
统一访问 Keel 四个模块:`payment / wallet / data / account`,基于 `https://keel.codes`。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install keelcodes
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 用法
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from keelcodes import Keel
|
|
15
|
+
|
|
16
|
+
keel = Keel(api_key="sk_payment_xxx")
|
|
17
|
+
|
|
18
|
+
# payment
|
|
19
|
+
res = keel.payment.charge(
|
|
20
|
+
payer="0x...", amount_wei="1000000000000000",
|
|
21
|
+
method="x402", reference="charge_1",
|
|
22
|
+
)
|
|
23
|
+
print(res)
|
|
24
|
+
|
|
25
|
+
# data
|
|
26
|
+
bars = keel.data.bars(symbol="BTC", timeframe="1d")
|
|
27
|
+
|
|
28
|
+
# account
|
|
29
|
+
key = keel.account.apikey_create(tenant_id="t1", module="data")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 模块
|
|
33
|
+
|
|
34
|
+
- `keel.payment` — 收费/付费/查账/核验/访问
|
|
35
|
+
- `keel.wallet` — 建钱包/签名/转账/链上读写/多签/会话
|
|
36
|
+
- `keel.data` — 取行情/K线/因子/预测/事件/知识
|
|
37
|
+
- `keel.account` — 租户/密钥/订阅
|
|
38
|
+
|
|
39
|
+
## 鉴权
|
|
40
|
+
|
|
41
|
+
统一 Bearer API key(按模块订阅签发 `sk_<module>_*`),传入 `Keel(api_key=...)`。
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Keel 聚合入口 — 4 个 namespace(payment/wallet/data/account)。"""
|
|
2
|
+
|
|
3
|
+
from ._http import HttpClient
|
|
4
|
+
from ._errors import KeelApiError
|
|
5
|
+
from .payment import PaymentAPI
|
|
6
|
+
from .wallet import WalletAPI
|
|
7
|
+
from .data import DataAPI
|
|
8
|
+
from .account import AccountAPI
|
|
9
|
+
|
|
10
|
+
DEFAULT_BASE_URL = "https://keel.codes"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Keel:
|
|
14
|
+
"""Keel 客户端:统一访问 4 模块。"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, base_url: str = DEFAULT_BASE_URL, api_key: str = None, timeout: float = 30.0):
|
|
17
|
+
http = HttpClient(base_url=base_url, api_key=api_key, timeout=timeout)
|
|
18
|
+
self.payment = PaymentAPI(http)
|
|
19
|
+
self.wallet = WalletAPI(http)
|
|
20
|
+
self.data = DataAPI(http)
|
|
21
|
+
self.account = AccountAPI(http)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = ["Keel", "PaymentAPI", "WalletAPI", "DataAPI", "AccountAPI", "KeelApiError"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""类型化错误(对齐契约 §3 错误体)。"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class KeelApiError(Exception):
|
|
5
|
+
def __init__(self, path: str, status: int, code: str, body):
|
|
6
|
+
self.path = path
|
|
7
|
+
self.status = status
|
|
8
|
+
self.code = code
|
|
9
|
+
self.body = body
|
|
10
|
+
message = (body or {}).get("message", "request failed") if isinstance(body, dict) else "request failed"
|
|
11
|
+
super().__init__(f"[keelcodes] {path} {status} ({code}): {message}")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""HTTP 客户端(零依赖,标准库 urllib;统一 Bearer key + 错误分级)。"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import urllib.request
|
|
5
|
+
import urllib.error
|
|
6
|
+
|
|
7
|
+
from ._errors import KeelApiError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _camelize(s: str) -> str:
|
|
11
|
+
head, *tail = s.split("_")
|
|
12
|
+
return head + "".join(t.capitalize() for t in tail)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def camel_dict(d: dict) -> dict:
|
|
16
|
+
"""snake_case -> camelCase,用于对齐 HTTP JSON 契约。"""
|
|
17
|
+
return {_camelize(k): v for k, v in d.items() if v is not None}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
SDK_VERSION = "1.0.0"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HttpClient:
|
|
24
|
+
def __init__(self, base_url: str, api_key: str = None, timeout: float = 30.0):
|
|
25
|
+
self.base_url = base_url.rstrip("/")
|
|
26
|
+
self.timeout = timeout
|
|
27
|
+
self._headers = {"Content-Type": "application/json", "X-Keel-Version": SDK_VERSION}
|
|
28
|
+
if api_key:
|
|
29
|
+
self._headers["Authorization"] = f"Bearer {api_key}"
|
|
30
|
+
|
|
31
|
+
def get(self, path: str):
|
|
32
|
+
return self._request("GET", path)
|
|
33
|
+
|
|
34
|
+
def post(self, path: str, body=None):
|
|
35
|
+
return self._request("POST", path, body)
|
|
36
|
+
|
|
37
|
+
def _request(self, method: str, path: str, body=None):
|
|
38
|
+
data = json.dumps(body).encode("utf-8") if body is not None else None
|
|
39
|
+
req = urllib.request.Request(self.base_url + path, data=data, headers=self._headers, method=method)
|
|
40
|
+
try:
|
|
41
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as res:
|
|
42
|
+
return json.loads(res.read().decode("utf-8"))
|
|
43
|
+
except urllib.error.HTTPError as e:
|
|
44
|
+
raw = e.read().decode("utf-8") or "{}"
|
|
45
|
+
try:
|
|
46
|
+
payload = json.loads(raw)
|
|
47
|
+
except json.JSONDecodeError:
|
|
48
|
+
payload = {}
|
|
49
|
+
code = payload.get("code", "INTERNAL") if isinstance(payload, dict) else "INTERNAL"
|
|
50
|
+
raise KeelApiError(path, e.code, code, payload)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""account 模块 — 租户/密钥/订阅(用户管自己的账户)。"""
|
|
2
|
+
|
|
3
|
+
from ._http import HttpClient, camel_dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AccountAPI:
|
|
7
|
+
def __init__(self, http: HttpClient):
|
|
8
|
+
self._http = http
|
|
9
|
+
|
|
10
|
+
def tenant_create(self, name: str, metadata: dict = None) -> dict:
|
|
11
|
+
return self._http.post("/v1/account/tenant", camel_dict({"name": name, "metadata": metadata}))
|
|
12
|
+
|
|
13
|
+
def tenant_get(self, tenant_id: str) -> dict:
|
|
14
|
+
return self._http.get(f"/v1/account/tenant/{tenant_id}")
|
|
15
|
+
|
|
16
|
+
def apikey_create(self, tenant_id: str, module: str, reference: str = None) -> dict:
|
|
17
|
+
return self._http.post("/v1/account/apikey", {"tenantId": tenant_id, "module": module, "reference": reference})
|
|
18
|
+
|
|
19
|
+
def apikey_revoke(self, api_key: str) -> dict:
|
|
20
|
+
return self._http.post("/v1/account/apikey/revoke", {"apiKey": api_key})
|
|
21
|
+
|
|
22
|
+
def subscription_get(self, module: str) -> dict:
|
|
23
|
+
return self._http.get(f"/v1/account/subscription?module={module}")
|
|
24
|
+
|
|
25
|
+
def subscription_subscribe(self, module: str, plan_id: str, reference: str = None) -> dict:
|
|
26
|
+
return self._http.post("/v1/account/subscription", {"module": module, "planId": plan_id, "reference": reference})
|
|
27
|
+
|
|
28
|
+
def subscription_cancel(self, module: str) -> dict:
|
|
29
|
+
return self._http.post("/v1/account/subscription/cancel", {"module": module})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""data 模块 — 取行情/K线/因子/预测/事件/知识。"""
|
|
2
|
+
|
|
3
|
+
from ._http import HttpClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DataAPI:
|
|
7
|
+
def __init__(self, http: HttpClient):
|
|
8
|
+
self._http = http
|
|
9
|
+
|
|
10
|
+
def bars(self, symbol: str, timeframe: str = None, market_type: str = None,
|
|
11
|
+
start: int = None, end: int = None, limit: int = None) -> dict:
|
|
12
|
+
q = f"symbol={symbol}"
|
|
13
|
+
if timeframe:
|
|
14
|
+
q += f"&timeframe={timeframe}"
|
|
15
|
+
if market_type:
|
|
16
|
+
q += f"&market_type={market_type}"
|
|
17
|
+
if start is not None:
|
|
18
|
+
q += f"&start={start}"
|
|
19
|
+
if end is not None:
|
|
20
|
+
q += f"&end={end}"
|
|
21
|
+
if limit is not None:
|
|
22
|
+
q += f"&limit={limit}"
|
|
23
|
+
return self._http.get(f"/v1/data/bars?{q}")
|
|
24
|
+
|
|
25
|
+
def tickers(self, symbols, market_type: str = None) -> dict:
|
|
26
|
+
syms = ",".join(symbols) if isinstance(symbols, (list, tuple)) else symbols
|
|
27
|
+
q = f"symbols={syms}"
|
|
28
|
+
if market_type:
|
|
29
|
+
q += f"&market_type={market_type}"
|
|
30
|
+
return self._http.get(f"/v1/data/tickers?{q}")
|
|
31
|
+
|
|
32
|
+
def factors(self, symbols: str = None, category: str = None) -> dict:
|
|
33
|
+
parts = []
|
|
34
|
+
if symbols:
|
|
35
|
+
parts.append(f"symbols={symbols}")
|
|
36
|
+
if category:
|
|
37
|
+
parts.append(f"category={category}")
|
|
38
|
+
q = "&".join(parts)
|
|
39
|
+
return self._http.get("/v1/data/factors" + (f"?{q}" if q else ""))
|
|
40
|
+
|
|
41
|
+
def graph(self, symbol: str = None, entity: str = None) -> dict:
|
|
42
|
+
parts = []
|
|
43
|
+
if symbol:
|
|
44
|
+
parts.append(f"symbol={symbol}")
|
|
45
|
+
if entity:
|
|
46
|
+
parts.append(f"entity={entity}")
|
|
47
|
+
q = "&".join(parts)
|
|
48
|
+
return self._http.get("/v1/data/graph" + (f"?{q}" if q else ""))
|
|
49
|
+
|
|
50
|
+
def predictions(self, model: str, symbol: str, start: int = None, end: int = None, limit: int = None) -> dict:
|
|
51
|
+
q = f"model={model}&symbol={symbol}"
|
|
52
|
+
if start is not None:
|
|
53
|
+
q += f"&start={start}"
|
|
54
|
+
if end is not None:
|
|
55
|
+
q += f"&end={end}"
|
|
56
|
+
if limit is not None:
|
|
57
|
+
q += f"&limit={limit}"
|
|
58
|
+
return self._http.get(f"/v1/data/predictions?{q}")
|
|
59
|
+
|
|
60
|
+
def events(self, chain: str = None, address: str = None, contract: str = None,
|
|
61
|
+
event_type: str = None, from_block: str = None, limit: int = None) -> dict:
|
|
62
|
+
parts = []
|
|
63
|
+
if chain:
|
|
64
|
+
parts.append(f"chain={chain}")
|
|
65
|
+
if address:
|
|
66
|
+
parts.append(f"address={address}")
|
|
67
|
+
if contract:
|
|
68
|
+
parts.append(f"contract={contract}")
|
|
69
|
+
if event_type:
|
|
70
|
+
parts.append(f"event_type={event_type}")
|
|
71
|
+
if from_block:
|
|
72
|
+
parts.append(f"from_block={from_block}")
|
|
73
|
+
if limit is not None:
|
|
74
|
+
parts.append(f"limit={limit}")
|
|
75
|
+
q = "&".join(parts)
|
|
76
|
+
return self._http.get("/v1/data/events" + (f"?{q}" if q else ""))
|
|
77
|
+
|
|
78
|
+
def rag(self, query: str, top_k: int = None) -> dict:
|
|
79
|
+
body = {"query": query}
|
|
80
|
+
if top_k is not None:
|
|
81
|
+
body["topK"] = top_k
|
|
82
|
+
return self._http.post("/v1/data/rag", body)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""payment 模块 — 收费/付费/查账/核验/访问。"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field, asdict
|
|
4
|
+
from typing import Optional, Any, Dict
|
|
5
|
+
|
|
6
|
+
from ._http import HttpClient, camel_dict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ChargeParams:
|
|
11
|
+
payer: str
|
|
12
|
+
amount_wei: str
|
|
13
|
+
method: str # fiat | x402 | a2a | batch | invite
|
|
14
|
+
reference: str
|
|
15
|
+
asset: Optional[str] = None
|
|
16
|
+
chain: Optional[str] = None
|
|
17
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class PayParams:
|
|
22
|
+
payee: str
|
|
23
|
+
amount_wei: str
|
|
24
|
+
reference: str
|
|
25
|
+
asset: Optional[str] = None
|
|
26
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SettlementRecord:
|
|
31
|
+
reference: str
|
|
32
|
+
payer: str
|
|
33
|
+
amount_wei: str
|
|
34
|
+
asset: str
|
|
35
|
+
chain_id: int
|
|
36
|
+
settled_at: str
|
|
37
|
+
method: Optional[str] = None
|
|
38
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PaymentAPI:
|
|
42
|
+
def __init__(self, http: HttpClient):
|
|
43
|
+
self._http = http
|
|
44
|
+
|
|
45
|
+
def charge(self, params: ChargeParams) -> dict:
|
|
46
|
+
return self._http.post("/v1/payment/charge", camel_dict(asdict(params)))
|
|
47
|
+
|
|
48
|
+
def pay(self, params: PayParams) -> dict:
|
|
49
|
+
return self._http.post("/v1/payment/pay", camel_dict(asdict(params)))
|
|
50
|
+
|
|
51
|
+
def verify(self, tx_hash: str, chain: Optional[str] = None) -> dict:
|
|
52
|
+
return self._http.post("/v1/payment/verify", camel_dict({"tx_hash": tx_hash, "chain": chain}))
|
|
53
|
+
|
|
54
|
+
def balance(self, address: str, asset: Optional[str] = None) -> dict:
|
|
55
|
+
q = f"address={address}"
|
|
56
|
+
if asset:
|
|
57
|
+
q += f"&asset={asset}"
|
|
58
|
+
return self._http.get(f"/v1/payment/balance?{q}")
|
|
59
|
+
|
|
60
|
+
def orders(self, limit: Optional[int] = None, offset: Optional[int] = None,
|
|
61
|
+
status: Optional[str] = None, subscriber: Optional[str] = None) -> dict:
|
|
62
|
+
parts = []
|
|
63
|
+
if limit is not None:
|
|
64
|
+
parts.append(f"limit={limit}")
|
|
65
|
+
if offset is not None:
|
|
66
|
+
parts.append(f"offset={offset}")
|
|
67
|
+
if status is not None:
|
|
68
|
+
parts.append(f"status={status}")
|
|
69
|
+
if subscriber is not None:
|
|
70
|
+
parts.append(f"subscriber={subscriber}")
|
|
71
|
+
q = "&".join(parts)
|
|
72
|
+
return self._http.get("/v1/payment/orders" + (f"?{q}" if q else ""))
|
|
73
|
+
|
|
74
|
+
def settlement(self, reference: str) -> dict:
|
|
75
|
+
return self._http.get(f"/v1/payment/settlement/{reference}")
|
|
76
|
+
|
|
77
|
+
def access(self, subscriber: str, resource) -> dict:
|
|
78
|
+
return self._http.post("/v1/payment/access", {"subscriber": subscriber, "resource": resource})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""wallet 模块 — 建钱包/签名/转账/链上读写/多签/会话。"""
|
|
2
|
+
|
|
3
|
+
from ._http import HttpClient, camel_dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WalletAPI:
|
|
7
|
+
def __init__(self, http: HttpClient):
|
|
8
|
+
self._http = http
|
|
9
|
+
|
|
10
|
+
def create(self, type: str, chain: str = None, reference: str = None) -> dict:
|
|
11
|
+
return self._http.post("/v1/wallet/create", camel_dict({"type": type, "chain": chain, "reference": reference}))
|
|
12
|
+
|
|
13
|
+
def import_wallet(self, type: str, seed: str = None, private_key: str = None) -> dict:
|
|
14
|
+
return self._http.post("/v1/wallet/import", camel_dict({"type": type, "seed": seed, "private_key": private_key}))
|
|
15
|
+
|
|
16
|
+
def sign(self, wallet_id: str, message: str = None, typed_data: dict = None,
|
|
17
|
+
digest: str = None, chain: str = None) -> dict:
|
|
18
|
+
return self._http.post("/v1/wallet/sign", camel_dict({
|
|
19
|
+
"wallet_id": wallet_id, "message": message, "typed_data": typed_data,
|
|
20
|
+
"digest": digest, "chain": chain,
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
def send(self, wallet_id: str, to: str, amount: str, chain: str, token_address: str = None,
|
|
24
|
+
reference: str = None) -> dict:
|
|
25
|
+
return self._http.post("/v1/wallet/send", camel_dict({
|
|
26
|
+
"wallet_id": wallet_id, "to": to, "amount": amount,
|
|
27
|
+
"chain": chain, "token_address": token_address, "reference": reference,
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
def chain(self, chain: str, method: str, params: list = None, raw_transaction: str = None) -> dict:
|
|
31
|
+
return self._http.post("/v1/wallet/chain", camel_dict({
|
|
32
|
+
"chain": chain, "method": method, "params": params, "raw_transaction": raw_transaction,
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
def balance(self, address: str, chain: str = None, token: str = None) -> dict:
|
|
36
|
+
q = f"address={address}"
|
|
37
|
+
if chain:
|
|
38
|
+
q += f"&chain={chain}"
|
|
39
|
+
if token:
|
|
40
|
+
q += f"&token={token}"
|
|
41
|
+
return self._http.get(f"/v1/wallet/balance?{q}")
|
|
42
|
+
|
|
43
|
+
def safe_propose(self, safe_address: str, to: str, value: str = None, data: str = None) -> dict:
|
|
44
|
+
return self._http.post("/v1/wallet/safe/propose", camel_dict({
|
|
45
|
+
"safe_address": safe_address, "to": to, "value": value, "data": data,
|
|
46
|
+
}))
|
|
47
|
+
|
|
48
|
+
def safe_confirm(self, safe_address: str, safe_tx_hash: str, signature: str) -> dict:
|
|
49
|
+
return self._http.post("/v1/wallet/safe/confirm", camel_dict({
|
|
50
|
+
"safe_address": safe_address, "safe_tx_hash": safe_tx_hash, "signature": signature,
|
|
51
|
+
}))
|
|
52
|
+
|
|
53
|
+
def safe_execute(self, safe_tx_hash: str) -> dict:
|
|
54
|
+
return self._http.post("/v1/wallet/safe/execute", {"safeTxHash": safe_tx_hash})
|
|
55
|
+
|
|
56
|
+
def session_create(self, **kwargs) -> dict:
|
|
57
|
+
return self._http.post("/v1/wallet/session/create", kwargs)
|
|
58
|
+
|
|
59
|
+
def session_revoke(self, session_id: str) -> dict:
|
|
60
|
+
return self._http.post("/v1/wallet/session/revoke", {"sessionId": session_id})
|
|
61
|
+
|
|
62
|
+
def session_execute(self, **kwargs) -> dict:
|
|
63
|
+
return self._http.post("/v1/wallet/session/execute", kwargs)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keelcodes
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Keel SDK — unified access to payment / wallet / data / account modules over https://keel.codes
|
|
5
|
+
Author: Keel
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://keel.codes
|
|
8
|
+
Keywords: keel,payment,wallet,data,account,sdk
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Keel SDK (Python)
|
|
16
|
+
|
|
17
|
+
统一访问 Keel 四个模块:`payment / wallet / data / account`,基于 `https://keel.codes`。
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install keelcodes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 用法
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from keelcodes import Keel
|
|
29
|
+
|
|
30
|
+
keel = Keel(api_key="sk_payment_xxx")
|
|
31
|
+
|
|
32
|
+
# payment
|
|
33
|
+
res = keel.payment.charge(
|
|
34
|
+
payer="0x...", amount_wei="1000000000000000",
|
|
35
|
+
method="x402", reference="charge_1",
|
|
36
|
+
)
|
|
37
|
+
print(res)
|
|
38
|
+
|
|
39
|
+
# data
|
|
40
|
+
bars = keel.data.bars(symbol="BTC", timeframe="1d")
|
|
41
|
+
|
|
42
|
+
# account
|
|
43
|
+
key = keel.account.apikey_create(tenant_id="t1", module="data")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 模块
|
|
47
|
+
|
|
48
|
+
- `keel.payment` — 收费/付费/查账/核验/访问
|
|
49
|
+
- `keel.wallet` — 建钱包/签名/转账/链上读写/多签/会话
|
|
50
|
+
- `keel.data` — 取行情/K线/因子/预测/事件/知识
|
|
51
|
+
- `keel.account` — 租户/密钥/订阅
|
|
52
|
+
|
|
53
|
+
## 鉴权
|
|
54
|
+
|
|
55
|
+
统一 Bearer API key(按模块订阅签发 `sk_<module>_*`),传入 `Keel(api_key=...)`。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
keelcodes/__init__.py
|
|
4
|
+
keelcodes/_errors.py
|
|
5
|
+
keelcodes/_http.py
|
|
6
|
+
keelcodes/account.py
|
|
7
|
+
keelcodes/data.py
|
|
8
|
+
keelcodes/payment.py
|
|
9
|
+
keelcodes/wallet.py
|
|
10
|
+
keelcodes.egg-info/PKG-INFO
|
|
11
|
+
keelcodes.egg-info/SOURCES.txt
|
|
12
|
+
keelcodes.egg-info/dependency_links.txt
|
|
13
|
+
keelcodes.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
keelcodes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "keelcodes"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Keel SDK — unified access to payment / wallet / data / account modules over https://keel.codes"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Keel" }]
|
|
13
|
+
keywords = ["keel", "payment", "wallet", "data", "account", "sdk"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://keel.codes"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
include = ["keelcodes*"]
|