synapse-filecoin-sdk 0.1.0__py3-none-any.whl
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.
- pynapse/__init__.py +6 -0
- pynapse/_version.py +1 -0
- pynapse/contracts/__init__.py +34 -0
- pynapse/contracts/abi_registry.py +11 -0
- pynapse/contracts/addresses.json +30 -0
- pynapse/contracts/erc20_abi.json +92 -0
- pynapse/contracts/errorsAbi.json +933 -0
- pynapse/contracts/filecoinPayV1Abi.json +2424 -0
- pynapse/contracts/filecoinWarmStorageServiceAbi.json +2363 -0
- pynapse/contracts/filecoinWarmStorageServiceStateViewAbi.json +651 -0
- pynapse/contracts/generated.py +35 -0
- pynapse/contracts/payments_abi.json +205 -0
- pynapse/contracts/pdpVerifierAbi.json +1266 -0
- pynapse/contracts/providerIdSetAbi.json +161 -0
- pynapse/contracts/serviceProviderRegistryAbi.json +1479 -0
- pynapse/contracts/sessionKeyRegistryAbi.json +147 -0
- pynapse/core/__init__.py +68 -0
- pynapse/core/abis.py +25 -0
- pynapse/core/chains.py +97 -0
- pynapse/core/constants.py +27 -0
- pynapse/core/errors.py +22 -0
- pynapse/core/piece.py +263 -0
- pynapse/core/rand.py +14 -0
- pynapse/core/typed_data.py +320 -0
- pynapse/core/utils.py +30 -0
- pynapse/evm/__init__.py +3 -0
- pynapse/evm/client.py +26 -0
- pynapse/filbeam/__init__.py +3 -0
- pynapse/filbeam/service.py +39 -0
- pynapse/payments/__init__.py +17 -0
- pynapse/payments/service.py +826 -0
- pynapse/pdp/__init__.py +21 -0
- pynapse/pdp/server.py +331 -0
- pynapse/pdp/types.py +38 -0
- pynapse/pdp/verifier.py +82 -0
- pynapse/retriever/__init__.py +12 -0
- pynapse/retriever/async_chain.py +227 -0
- pynapse/retriever/chain.py +209 -0
- pynapse/session/__init__.py +12 -0
- pynapse/session/key.py +30 -0
- pynapse/session/permissions.py +57 -0
- pynapse/session/registry.py +90 -0
- pynapse/sp_registry/__init__.py +11 -0
- pynapse/sp_registry/capabilities.py +25 -0
- pynapse/sp_registry/pdp_capabilities.py +102 -0
- pynapse/sp_registry/service.py +446 -0
- pynapse/sp_registry/types.py +52 -0
- pynapse/storage/__init__.py +57 -0
- pynapse/storage/async_context.py +682 -0
- pynapse/storage/async_manager.py +757 -0
- pynapse/storage/context.py +680 -0
- pynapse/storage/manager.py +758 -0
- pynapse/synapse.py +191 -0
- pynapse/utils/__init__.py +25 -0
- pynapse/utils/constants.py +25 -0
- pynapse/utils/errors.py +3 -0
- pynapse/utils/metadata.py +35 -0
- pynapse/utils/piece_url.py +16 -0
- pynapse/warm_storage/__init__.py +13 -0
- pynapse/warm_storage/service.py +513 -0
- synapse_filecoin_sdk-0.1.0.dist-info/METADATA +74 -0
- synapse_filecoin_sdk-0.1.0.dist-info/RECORD +64 -0
- synapse_filecoin_sdk-0.1.0.dist-info/WHEEL +4 -0
- synapse_filecoin_sdk-0.1.0.dist-info/licenses/LICENSE.md +228 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
6
|
+
|
|
7
|
+
from eth_abi import encode as abi_encode
|
|
8
|
+
from eth_account import Account
|
|
9
|
+
from eth_account.messages import encode_structured_data
|
|
10
|
+
from eth_utils import to_bytes
|
|
11
|
+
|
|
12
|
+
from .chains import Chain
|
|
13
|
+
from .rand import rand_u256
|
|
14
|
+
|
|
15
|
+
EIP712_TYPES: Dict[str, List[Dict[str, str]]] = {
|
|
16
|
+
"EIP712Domain": [
|
|
17
|
+
{"name": "name", "type": "string"},
|
|
18
|
+
{"name": "version", "type": "string"},
|
|
19
|
+
{"name": "chainId", "type": "uint256"},
|
|
20
|
+
{"name": "verifyingContract", "type": "address"},
|
|
21
|
+
],
|
|
22
|
+
"MetadataEntry": [
|
|
23
|
+
{"name": "key", "type": "string"},
|
|
24
|
+
{"name": "value", "type": "string"},
|
|
25
|
+
],
|
|
26
|
+
"CreateDataSet": [
|
|
27
|
+
{"name": "clientDataSetId", "type": "uint256"},
|
|
28
|
+
{"name": "payee", "type": "address"},
|
|
29
|
+
{"name": "metadata", "type": "MetadataEntry[]"},
|
|
30
|
+
],
|
|
31
|
+
"Cid": [
|
|
32
|
+
{"name": "data", "type": "bytes"},
|
|
33
|
+
],
|
|
34
|
+
"PieceMetadata": [
|
|
35
|
+
{"name": "pieceIndex", "type": "uint256"},
|
|
36
|
+
{"name": "metadata", "type": "MetadataEntry[]"},
|
|
37
|
+
],
|
|
38
|
+
"AddPieces": [
|
|
39
|
+
{"name": "clientDataSetId", "type": "uint256"},
|
|
40
|
+
{"name": "nonce", "type": "uint256"},
|
|
41
|
+
{"name": "pieceData", "type": "Cid[]"},
|
|
42
|
+
{"name": "pieceMetadata", "type": "PieceMetadata[]"},
|
|
43
|
+
],
|
|
44
|
+
"SchedulePieceRemovals": [
|
|
45
|
+
{"name": "clientDataSetId", "type": "uint256"},
|
|
46
|
+
{"name": "pieceIds", "type": "uint256[]"},
|
|
47
|
+
],
|
|
48
|
+
"DeleteDataSet": [
|
|
49
|
+
{"name": "clientDataSetId", "type": "uint256"},
|
|
50
|
+
],
|
|
51
|
+
"Permit": [
|
|
52
|
+
{"name": "owner", "type": "address"},
|
|
53
|
+
{"name": "spender", "type": "address"},
|
|
54
|
+
{"name": "value", "type": "uint256"},
|
|
55
|
+
{"name": "nonce", "type": "uint256"},
|
|
56
|
+
{"name": "deadline", "type": "uint256"},
|
|
57
|
+
],
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_storage_domain(chain: Chain, verifying_contract: Optional[str] = None) -> Dict[str, Any]:
|
|
62
|
+
return {
|
|
63
|
+
"name": "FilecoinWarmStorageService",
|
|
64
|
+
"version": "1",
|
|
65
|
+
"chainId": chain.id,
|
|
66
|
+
"verifyingContract": verifying_contract or chain.contracts.warm_storage,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _sign_typed_data(private_key: str, domain: Dict[str, Any], primary_type: str, message: Dict[str, Any]) -> str:
|
|
71
|
+
typed = {
|
|
72
|
+
"types": EIP712_TYPES,
|
|
73
|
+
"primaryType": primary_type,
|
|
74
|
+
"domain": domain,
|
|
75
|
+
"message": message,
|
|
76
|
+
}
|
|
77
|
+
msg = encode_structured_data(typed)
|
|
78
|
+
signed = Account.sign_message(msg, private_key=private_key)
|
|
79
|
+
return signed.signature.hex()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _piece_cid_bytes(piece_cid: str) -> bytes:
|
|
83
|
+
"""Convert a piece CID string to its raw bytes.
|
|
84
|
+
|
|
85
|
+
Handles both PieceCIDv1 (baga...) and PieceCIDv2 (bafk...) formats
|
|
86
|
+
by decoding the base32lower multibase encoding directly.
|
|
87
|
+
"""
|
|
88
|
+
if not (piece_cid.startswith('baga') or piece_cid.startswith('bafk')):
|
|
89
|
+
raise ValueError(f"Unsupported CID format: {piece_cid}")
|
|
90
|
+
|
|
91
|
+
# Remove 'b' prefix (base32lower multibase prefix)
|
|
92
|
+
raw = piece_cid[1:]
|
|
93
|
+
# base32lower uses lowercase RFC 4648 alphabet
|
|
94
|
+
# Python's base64.b32decode expects uppercase
|
|
95
|
+
raw_upper = raw.upper()
|
|
96
|
+
# Add padding
|
|
97
|
+
padding = (8 - len(raw_upper) % 8) % 8
|
|
98
|
+
raw_padded = raw_upper + '=' * padding
|
|
99
|
+
return base64.b32decode(raw_padded)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def sign_create_dataset(
|
|
103
|
+
private_key: str,
|
|
104
|
+
chain: Chain,
|
|
105
|
+
client_data_set_id: int,
|
|
106
|
+
payee: str,
|
|
107
|
+
metadata: Sequence[Dict[str, str]],
|
|
108
|
+
verifying_contract: Optional[str] = None,
|
|
109
|
+
) -> str:
|
|
110
|
+
domain = get_storage_domain(chain, verifying_contract)
|
|
111
|
+
message = {
|
|
112
|
+
"clientDataSetId": int(client_data_set_id),
|
|
113
|
+
"payee": payee,
|
|
114
|
+
"metadata": list(metadata),
|
|
115
|
+
}
|
|
116
|
+
return _sign_typed_data(private_key, domain, "CreateDataSet", message)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def sign_schedule_piece_removals(
|
|
120
|
+
private_key: str,
|
|
121
|
+
chain: Chain,
|
|
122
|
+
client_data_set_id: int,
|
|
123
|
+
piece_ids: Sequence[int],
|
|
124
|
+
verifying_contract: Optional[str] = None,
|
|
125
|
+
) -> str:
|
|
126
|
+
domain = get_storage_domain(chain, verifying_contract)
|
|
127
|
+
message = {
|
|
128
|
+
"clientDataSetId": int(client_data_set_id),
|
|
129
|
+
"pieceIds": [int(pid) for pid in piece_ids],
|
|
130
|
+
}
|
|
131
|
+
return _sign_typed_data(private_key, domain, "SchedulePieceRemovals", message)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def sign_add_pieces_extra_data(
|
|
135
|
+
private_key: str,
|
|
136
|
+
chain: Chain,
|
|
137
|
+
client_data_set_id: int,
|
|
138
|
+
pieces: Sequence[Tuple[str, Sequence[Dict[str, str]]]],
|
|
139
|
+
nonce: Optional[int] = None,
|
|
140
|
+
verifying_contract: Optional[str] = None,
|
|
141
|
+
) -> str:
|
|
142
|
+
use_nonce = nonce if nonce is not None else rand_u256()
|
|
143
|
+
|
|
144
|
+
piece_data = [{"data": _piece_cid_bytes(piece_cid)} for piece_cid, _ in pieces]
|
|
145
|
+
piece_metadata = []
|
|
146
|
+
for idx, (_, metadata) in enumerate(pieces):
|
|
147
|
+
piece_metadata.append(
|
|
148
|
+
{
|
|
149
|
+
"pieceIndex": int(idx),
|
|
150
|
+
"metadata": list(metadata),
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
domain = get_storage_domain(chain, verifying_contract)
|
|
155
|
+
message = {
|
|
156
|
+
"clientDataSetId": int(client_data_set_id),
|
|
157
|
+
"nonce": int(use_nonce),
|
|
158
|
+
"pieceData": piece_data,
|
|
159
|
+
"pieceMetadata": piece_metadata,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
signature = _sign_typed_data(private_key, domain, "AddPieces", message)
|
|
163
|
+
|
|
164
|
+
metadata_keys = [[entry["key"] for entry in metadata] for _, metadata in pieces]
|
|
165
|
+
metadata_values = [[entry["value"] for entry in metadata] for _, metadata in pieces]
|
|
166
|
+
|
|
167
|
+
encoded = abi_encode(
|
|
168
|
+
["uint256", "string[][]", "string[][]", "bytes"],
|
|
169
|
+
[use_nonce, metadata_keys, metadata_values, bytes.fromhex(signature[2:])],
|
|
170
|
+
)
|
|
171
|
+
return "0x" + encoded.hex()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def sign_delete_dataset(
|
|
175
|
+
private_key: str,
|
|
176
|
+
chain: Chain,
|
|
177
|
+
client_data_set_id: int,
|
|
178
|
+
verifying_contract: Optional[str] = None,
|
|
179
|
+
) -> str:
|
|
180
|
+
"""
|
|
181
|
+
Sign a DeleteDataSet typed data message.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
private_key: Private key to sign with
|
|
185
|
+
chain: Chain configuration
|
|
186
|
+
client_data_set_id: Client-side dataset identifier
|
|
187
|
+
verifying_contract: Optional override for verifying contract address
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Hex-encoded signature
|
|
191
|
+
"""
|
|
192
|
+
domain = get_storage_domain(chain, verifying_contract)
|
|
193
|
+
message = {
|
|
194
|
+
"clientDataSetId": int(client_data_set_id),
|
|
195
|
+
}
|
|
196
|
+
return _sign_typed_data(private_key, domain, "DeleteDataSet", message)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def sign_delete_dataset_extra_data(
|
|
200
|
+
private_key: str,
|
|
201
|
+
chain: Chain,
|
|
202
|
+
client_data_set_id: int,
|
|
203
|
+
verifying_contract: Optional[str] = None,
|
|
204
|
+
) -> str:
|
|
205
|
+
"""
|
|
206
|
+
Create encoded extra data for delete dataset operation.
|
|
207
|
+
|
|
208
|
+
This is the ABI-encoded format expected by the warm storage contract.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
private_key: Private key to sign with
|
|
212
|
+
chain: Chain configuration
|
|
213
|
+
client_data_set_id: Client-side dataset identifier
|
|
214
|
+
verifying_contract: Optional override for verifying contract address
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Hex-encoded extra data (ABI encoded signature)
|
|
218
|
+
"""
|
|
219
|
+
signature = sign_delete_dataset(private_key, chain, client_data_set_id, verifying_contract)
|
|
220
|
+
encoded = abi_encode(["bytes"], [bytes.fromhex(signature[2:])])
|
|
221
|
+
return "0x" + encoded.hex()
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def sign_create_dataset_extra_data(
|
|
225
|
+
private_key: str,
|
|
226
|
+
chain: Chain,
|
|
227
|
+
client_data_set_id: int,
|
|
228
|
+
payee: str,
|
|
229
|
+
metadata: Optional[Sequence[Dict[str, str]]] = None,
|
|
230
|
+
payer: Optional[str] = None,
|
|
231
|
+
verifying_contract: Optional[str] = None,
|
|
232
|
+
) -> str:
|
|
233
|
+
"""
|
|
234
|
+
Create encoded extra data for create dataset operation.
|
|
235
|
+
|
|
236
|
+
This is the ABI-encoded format expected by the warm storage contract.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
private_key: Private key to sign with
|
|
240
|
+
chain: Chain configuration
|
|
241
|
+
client_data_set_id: Client-side dataset identifier
|
|
242
|
+
payee: Address to receive payments
|
|
243
|
+
metadata: Optional dataset metadata as list of {key, value} dicts
|
|
244
|
+
payer: Address that will pay for storage (defaults to signer's address)
|
|
245
|
+
verifying_contract: Optional override for verifying contract address
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Hex-encoded extra data
|
|
249
|
+
"""
|
|
250
|
+
metadata = metadata or []
|
|
251
|
+
signature = sign_create_dataset(private_key, chain, client_data_set_id, payee, metadata, verifying_contract)
|
|
252
|
+
|
|
253
|
+
# Derive payer address from private key if not provided
|
|
254
|
+
if payer is None:
|
|
255
|
+
acct = Account.from_key(private_key)
|
|
256
|
+
payer = acct.address
|
|
257
|
+
|
|
258
|
+
metadata_keys = [entry["key"] for entry in metadata]
|
|
259
|
+
metadata_values = [entry["value"] for entry in metadata]
|
|
260
|
+
|
|
261
|
+
# ABI encoding must match TypeScript SDK:
|
|
262
|
+
# [payer (address), clientDataSetId (uint256), keys (string[]), values (string[]), signature (bytes)]
|
|
263
|
+
encoded = abi_encode(
|
|
264
|
+
["address", "uint256", "string[]", "string[]", "bytes"],
|
|
265
|
+
[payer, client_data_set_id, metadata_keys, metadata_values, bytes.fromhex(signature[2:])],
|
|
266
|
+
)
|
|
267
|
+
return "0x" + encoded.hex()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def sign_schedule_removals_extra_data(
|
|
271
|
+
private_key: str,
|
|
272
|
+
chain: Chain,
|
|
273
|
+
client_data_set_id: int,
|
|
274
|
+
piece_ids: Sequence[int],
|
|
275
|
+
verifying_contract: Optional[str] = None,
|
|
276
|
+
) -> str:
|
|
277
|
+
"""
|
|
278
|
+
Create encoded extra data for schedule piece removals operation.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
private_key: Private key to sign with
|
|
282
|
+
chain: Chain configuration
|
|
283
|
+
client_data_set_id: Client-side dataset identifier
|
|
284
|
+
piece_ids: List of piece IDs to schedule for removal
|
|
285
|
+
verifying_contract: Optional override for verifying contract address
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Hex-encoded extra data
|
|
289
|
+
"""
|
|
290
|
+
signature = sign_schedule_piece_removals(private_key, chain, client_data_set_id, piece_ids, verifying_contract)
|
|
291
|
+
encoded = abi_encode(["bytes"], [bytes.fromhex(signature[2:])])
|
|
292
|
+
return "0x" + encoded.hex()
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def sign_erc20_permit(
|
|
296
|
+
private_key: str,
|
|
297
|
+
name: str,
|
|
298
|
+
version: str,
|
|
299
|
+
chain_id: int,
|
|
300
|
+
verifying_contract: str,
|
|
301
|
+
owner: str,
|
|
302
|
+
spender: str,
|
|
303
|
+
value: int,
|
|
304
|
+
nonce: int,
|
|
305
|
+
deadline: int,
|
|
306
|
+
) -> str:
|
|
307
|
+
domain = {
|
|
308
|
+
"name": name,
|
|
309
|
+
"version": version,
|
|
310
|
+
"chainId": chain_id,
|
|
311
|
+
"verifyingContract": verifying_contract,
|
|
312
|
+
}
|
|
313
|
+
message = {
|
|
314
|
+
"owner": owner,
|
|
315
|
+
"spender": spender,
|
|
316
|
+
"value": int(value),
|
|
317
|
+
"nonce": int(nonce),
|
|
318
|
+
"deadline": int(deadline),
|
|
319
|
+
}
|
|
320
|
+
return _sign_typed_data(private_key, domain, "Permit", message)
|
pynapse/core/utils.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal, ROUND_DOWN, getcontext
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
NumberLike = Union[int, float, str, Decimal]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def parse_units(value: NumberLike, decimals: int = 18) -> int:
|
|
10
|
+
if decimals < 0:
|
|
11
|
+
raise ValueError("decimals must be >= 0")
|
|
12
|
+
|
|
13
|
+
if isinstance(value, Decimal):
|
|
14
|
+
dec = value
|
|
15
|
+
else:
|
|
16
|
+
dec = Decimal(str(value))
|
|
17
|
+
|
|
18
|
+
scale = Decimal(10) ** decimals
|
|
19
|
+
scaled = (dec * scale).quantize(Decimal(1), rounding=ROUND_DOWN)
|
|
20
|
+
return int(scaled)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_units(value: int, decimals: int = 18) -> str:
|
|
24
|
+
if decimals < 0:
|
|
25
|
+
raise ValueError("decimals must be >= 0")
|
|
26
|
+
|
|
27
|
+
getcontext().prec = max(28, decimals + 10)
|
|
28
|
+
scale = Decimal(10) ** decimals
|
|
29
|
+
dec = Decimal(value) / scale
|
|
30
|
+
return format(dec.normalize(), "f")
|
pynapse/evm/__init__.py
ADDED
pynapse/evm/client.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from web3 import AsyncWeb3, Web3
|
|
7
|
+
from web3.providers.async_rpc import AsyncHTTPProvider
|
|
8
|
+
from web3.providers.rpc import HTTPProvider
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class SyncEVMClient:
|
|
13
|
+
web3: Web3
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_rpc_url(cls, rpc_url: str) -> "SyncEVMClient":
|
|
17
|
+
return cls(web3=Web3(HTTPProvider(rpc_url)))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class AsyncEVMClient:
|
|
22
|
+
web3: AsyncWeb3
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_rpc_url(cls, rpc_url: str) -> "AsyncEVMClient":
|
|
26
|
+
return cls(web3=AsyncWeb3(AsyncHTTPProvider(rpc_url)))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from pynapse.core.chains import Chain, as_chain
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class DataSetStats:
|
|
12
|
+
cdn_egress_quota: int
|
|
13
|
+
cache_miss_egress_quota: int
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FilBeamService:
|
|
17
|
+
def __init__(self, chain: Chain, client: httpx.Client | None = None) -> None:
|
|
18
|
+
self._chain = as_chain(chain)
|
|
19
|
+
self._client = client or httpx.Client(timeout=30)
|
|
20
|
+
|
|
21
|
+
def _stats_base_url(self) -> str:
|
|
22
|
+
if self._chain.id == 314159:
|
|
23
|
+
return "https://calibration.stats.filbeam.com"
|
|
24
|
+
return "https://stats.filbeam.com"
|
|
25
|
+
|
|
26
|
+
def get_data_set_stats(self, data_set_id: str | int) -> DataSetStats:
|
|
27
|
+
url = f"{self._stats_base_url()}/data-set/{data_set_id}"
|
|
28
|
+
resp = self._client.get(url)
|
|
29
|
+
if resp.status_code == 404:
|
|
30
|
+
raise RuntimeError(f"Data set not found: {data_set_id}")
|
|
31
|
+
if resp.status_code != 200:
|
|
32
|
+
raise RuntimeError(f"HTTP {resp.status_code}: {resp.text}")
|
|
33
|
+
data = resp.json()
|
|
34
|
+
if not isinstance(data, dict):
|
|
35
|
+
raise RuntimeError("Invalid response from FilBeam")
|
|
36
|
+
return DataSetStats(
|
|
37
|
+
cdn_egress_quota=int(data.get("cdnEgressQuota", "0")),
|
|
38
|
+
cache_miss_egress_quota=int(data.get("cacheMissEgressQuota", "0")),
|
|
39
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .service import (
|
|
2
|
+
AccountInfo,
|
|
3
|
+
AsyncPaymentsService,
|
|
4
|
+
RailInfo,
|
|
5
|
+
ServiceApproval,
|
|
6
|
+
SettlementResult,
|
|
7
|
+
SyncPaymentsService,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"AccountInfo",
|
|
12
|
+
"AsyncPaymentsService",
|
|
13
|
+
"RailInfo",
|
|
14
|
+
"ServiceApproval",
|
|
15
|
+
"SettlementResult",
|
|
16
|
+
"SyncPaymentsService",
|
|
17
|
+
]
|