chipi-stack 2.0.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.
- chipi_sdk/__init__.py +342 -0
- chipi_sdk/client.py +505 -0
- chipi_sdk/constants.py +171 -0
- chipi_sdk/encryption.py +179 -0
- chipi_sdk/errors.py +130 -0
- chipi_sdk/execute_paymaster.py +434 -0
- chipi_sdk/formatters.py +154 -0
- chipi_sdk/models/__init__.py +145 -0
- chipi_sdk/models/core.py +96 -0
- chipi_sdk/models/session.py +119 -0
- chipi_sdk/models/sku.py +28 -0
- chipi_sdk/models/sku_transaction.py +30 -0
- chipi_sdk/models/transaction.py +192 -0
- chipi_sdk/models/user.py +31 -0
- chipi_sdk/models/wallet.py +178 -0
- chipi_sdk/models/x402.py +117 -0
- chipi_sdk/py.typed +1 -0
- chipi_sdk/sdk.py +1021 -0
- chipi_sdk/sessions.py +836 -0
- chipi_sdk/sku_transactions.py +58 -0
- chipi_sdk/skus.py +93 -0
- chipi_sdk/transactions.py +447 -0
- chipi_sdk/users.py +92 -0
- chipi_sdk/validators.py +75 -0
- chipi_sdk/wallets.py +465 -0
- chipi_sdk/x402_client.py +207 -0
- chipi_sdk/x402_facilitator.py +200 -0
- chipi_sdk/x402_middleware.py +280 -0
- chipi_stack-2.0.0.dist-info/METADATA +366 -0
- chipi_stack-2.0.0.dist-info/RECORD +33 -0
- chipi_stack-2.0.0.dist-info/WHEEL +5 -0
- chipi_stack-2.0.0.dist-info/licenses/LICENSE +21 -0
- chipi_stack-2.0.0.dist-info/top_level.txt +1 -0
chipi_sdk/client.py
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
"""HTTP client for Chipi API interactions."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional, TypeVar
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from .models.core import ChipiSDKConfig
|
|
7
|
+
from .errors import ChipiAuthError, ChipiApiError, handle_api_error
|
|
8
|
+
from .validators import is_valid_api_key, validate_error_response
|
|
9
|
+
from .constants import API_VERSION, API_VERSION_DATE, STARKNET_NETWORKS
|
|
10
|
+
|
|
11
|
+
T = TypeVar('T')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ChipiClient:
|
|
15
|
+
"""HTTP client with sync and async support."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, config: ChipiSDKConfig):
|
|
18
|
+
"""
|
|
19
|
+
Initialize the Chipi HTTP client.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config: SDK configuration
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ChipiAuthError: If API key is invalid
|
|
26
|
+
"""
|
|
27
|
+
if not is_valid_api_key(config.api_public_key):
|
|
28
|
+
raise ChipiAuthError("Invalid API key format")
|
|
29
|
+
|
|
30
|
+
self.api_public_key = config.api_public_key
|
|
31
|
+
self.custom_alpha_url = config.alpha_url
|
|
32
|
+
self.base_url = self._get_base_url()
|
|
33
|
+
self.node_url = config.node_url or STARKNET_NETWORKS["MAINNET"]
|
|
34
|
+
self.sdk_version = "2.0.0"
|
|
35
|
+
|
|
36
|
+
# Initialize HTTP clients
|
|
37
|
+
self._sync_client = httpx.Client(timeout=30.0)
|
|
38
|
+
self._async_client = httpx.AsyncClient(timeout=30.0)
|
|
39
|
+
|
|
40
|
+
def get_api_public_key(self) -> str:
|
|
41
|
+
"""Get the API public key (for internal SDK use)."""
|
|
42
|
+
return self.api_public_key
|
|
43
|
+
|
|
44
|
+
def _get_base_url(self) -> str:
|
|
45
|
+
"""Construct the base API URL."""
|
|
46
|
+
version = f"v{API_VERSION}"
|
|
47
|
+
|
|
48
|
+
if self.custom_alpha_url:
|
|
49
|
+
# Remove any existing version suffix and add the current one
|
|
50
|
+
clean_url = self.custom_alpha_url.rstrip('/')
|
|
51
|
+
# Remove existing version if present
|
|
52
|
+
if clean_url.endswith(f'/v{API_VERSION}'):
|
|
53
|
+
clean_url = clean_url[:-len(f'/v{API_VERSION}')]
|
|
54
|
+
return f"{clean_url}/{version}"
|
|
55
|
+
|
|
56
|
+
return f"https://api.chipipay.com/{version}"
|
|
57
|
+
|
|
58
|
+
def _add_version_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
59
|
+
"""
|
|
60
|
+
Add API version query parameters.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
params: Existing parameters
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Parameters with version info added
|
|
67
|
+
"""
|
|
68
|
+
if params is None:
|
|
69
|
+
params = {}
|
|
70
|
+
params["__chipi_api_version"] = API_VERSION_DATE
|
|
71
|
+
return params
|
|
72
|
+
|
|
73
|
+
def _get_headers(self, bearer_token: Optional[str] = None) -> Dict[str, str]:
|
|
74
|
+
"""
|
|
75
|
+
Get HTTP headers for requests.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
bearer_token: Optional bearer token for authentication
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dictionary of headers
|
|
82
|
+
"""
|
|
83
|
+
headers = {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"x-api-key": self.api_public_key,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if bearer_token:
|
|
89
|
+
headers["Authorization"] = f"Bearer {bearer_token}"
|
|
90
|
+
|
|
91
|
+
return headers
|
|
92
|
+
|
|
93
|
+
def get(
|
|
94
|
+
self,
|
|
95
|
+
endpoint: str,
|
|
96
|
+
params: Optional[Dict[str, Any]] = None,
|
|
97
|
+
bearer_token: Optional[str] = None,
|
|
98
|
+
) -> Any:
|
|
99
|
+
"""
|
|
100
|
+
Synchronous GET request.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
endpoint: API endpoint path
|
|
104
|
+
params: Query parameters
|
|
105
|
+
bearer_token: Optional bearer token
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Response data
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
ChipiApiError: If request fails
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
url = f"{self.base_url}{endpoint}"
|
|
115
|
+
|
|
116
|
+
# Add version tracking parameters
|
|
117
|
+
params = self._add_version_params(params or {})
|
|
118
|
+
|
|
119
|
+
# Filter out None values
|
|
120
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
121
|
+
|
|
122
|
+
response = self._sync_client.get(
|
|
123
|
+
url,
|
|
124
|
+
params=params,
|
|
125
|
+
headers=self._get_headers(bearer_token),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
data = response.json()
|
|
129
|
+
|
|
130
|
+
if not response.is_success:
|
|
131
|
+
error_data = validate_error_response(data)
|
|
132
|
+
raise ChipiApiError(
|
|
133
|
+
error_data["message"],
|
|
134
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
135
|
+
response.status_code,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return data
|
|
139
|
+
except ChipiApiError:
|
|
140
|
+
raise
|
|
141
|
+
except Exception as error:
|
|
142
|
+
raise handle_api_error(error)
|
|
143
|
+
|
|
144
|
+
async def aget(
|
|
145
|
+
self,
|
|
146
|
+
endpoint: str,
|
|
147
|
+
params: Optional[Dict[str, Any]] = None,
|
|
148
|
+
bearer_token: Optional[str] = None,
|
|
149
|
+
) -> Any:
|
|
150
|
+
"""
|
|
151
|
+
Async GET request.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
endpoint: API endpoint path
|
|
155
|
+
params: Query parameters
|
|
156
|
+
bearer_token: Optional bearer token
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Response data
|
|
160
|
+
|
|
161
|
+
Raises:
|
|
162
|
+
ChipiApiError: If request fails
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
url = f"{self.base_url}{endpoint}"
|
|
166
|
+
|
|
167
|
+
# Add version tracking parameters
|
|
168
|
+
params = self._add_version_params(params or {})
|
|
169
|
+
|
|
170
|
+
# Filter out None values
|
|
171
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
172
|
+
|
|
173
|
+
response = await self._async_client.get(
|
|
174
|
+
url,
|
|
175
|
+
params=params,
|
|
176
|
+
headers=self._get_headers(bearer_token),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
data = response.json()
|
|
180
|
+
|
|
181
|
+
if not response.is_success:
|
|
182
|
+
error_data = validate_error_response(data)
|
|
183
|
+
raise ChipiApiError(
|
|
184
|
+
error_data["message"],
|
|
185
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
186
|
+
response.status_code,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return data
|
|
190
|
+
except ChipiApiError:
|
|
191
|
+
raise
|
|
192
|
+
except Exception as error:
|
|
193
|
+
raise handle_api_error(error)
|
|
194
|
+
|
|
195
|
+
def post(
|
|
196
|
+
self,
|
|
197
|
+
endpoint: str,
|
|
198
|
+
bearer_token: str,
|
|
199
|
+
body: Optional[Dict[str, Any]] = None,
|
|
200
|
+
) -> Any:
|
|
201
|
+
"""
|
|
202
|
+
Synchronous POST request.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
endpoint: API endpoint path
|
|
206
|
+
bearer_token: Bearer token for authentication
|
|
207
|
+
body: Request body
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Response data
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
ChipiApiError: If request fails
|
|
214
|
+
"""
|
|
215
|
+
try:
|
|
216
|
+
url = f"{self.base_url}{endpoint}"
|
|
217
|
+
|
|
218
|
+
# Add version tracking to URL
|
|
219
|
+
params = self._add_version_params({})
|
|
220
|
+
|
|
221
|
+
response = self._sync_client.post(
|
|
222
|
+
url,
|
|
223
|
+
params=params,
|
|
224
|
+
json=body,
|
|
225
|
+
headers=self._get_headers(bearer_token),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
data = response.json()
|
|
229
|
+
|
|
230
|
+
if not response.is_success:
|
|
231
|
+
error_data = validate_error_response(data)
|
|
232
|
+
raise ChipiApiError(
|
|
233
|
+
error_data["message"],
|
|
234
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
235
|
+
response.status_code,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return data
|
|
239
|
+
except ChipiApiError:
|
|
240
|
+
raise
|
|
241
|
+
except Exception as error:
|
|
242
|
+
raise handle_api_error(error)
|
|
243
|
+
|
|
244
|
+
async def apost(
|
|
245
|
+
self,
|
|
246
|
+
endpoint: str,
|
|
247
|
+
bearer_token: str,
|
|
248
|
+
body: Optional[Dict[str, Any]] = None,
|
|
249
|
+
) -> Any:
|
|
250
|
+
"""
|
|
251
|
+
Async POST request.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
endpoint: API endpoint path
|
|
255
|
+
bearer_token: Bearer token for authentication
|
|
256
|
+
body: Request body
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Response data
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
ChipiApiError: If request fails
|
|
263
|
+
"""
|
|
264
|
+
try:
|
|
265
|
+
url = f"{self.base_url}{endpoint}"
|
|
266
|
+
|
|
267
|
+
# Add version tracking to URL
|
|
268
|
+
params = self._add_version_params({})
|
|
269
|
+
|
|
270
|
+
response = await self._async_client.post(
|
|
271
|
+
url,
|
|
272
|
+
params=params,
|
|
273
|
+
json=body,
|
|
274
|
+
headers=self._get_headers(bearer_token),
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
data = response.json()
|
|
278
|
+
|
|
279
|
+
if not response.is_success:
|
|
280
|
+
error_data = validate_error_response(data)
|
|
281
|
+
raise ChipiApiError(
|
|
282
|
+
error_data["message"],
|
|
283
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
284
|
+
response.status_code,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return data
|
|
288
|
+
except ChipiApiError:
|
|
289
|
+
raise
|
|
290
|
+
except Exception as error:
|
|
291
|
+
raise handle_api_error(error)
|
|
292
|
+
|
|
293
|
+
def put(
|
|
294
|
+
self,
|
|
295
|
+
endpoint: str,
|
|
296
|
+
bearer_token: str,
|
|
297
|
+
body: Optional[Dict[str, Any]] = None,
|
|
298
|
+
) -> Any:
|
|
299
|
+
"""
|
|
300
|
+
Synchronous PUT request.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
endpoint: API endpoint path
|
|
304
|
+
bearer_token: Bearer token for authentication
|
|
305
|
+
body: Request body
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Response data
|
|
309
|
+
|
|
310
|
+
Raises:
|
|
311
|
+
ChipiApiError: If request fails
|
|
312
|
+
"""
|
|
313
|
+
try:
|
|
314
|
+
url = f"{self.base_url}{endpoint}"
|
|
315
|
+
|
|
316
|
+
# Add version tracking to URL
|
|
317
|
+
params = self._add_version_params({})
|
|
318
|
+
|
|
319
|
+
response = self._sync_client.put(
|
|
320
|
+
url,
|
|
321
|
+
params=params,
|
|
322
|
+
json=body,
|
|
323
|
+
headers=self._get_headers(bearer_token),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
data = response.json()
|
|
327
|
+
|
|
328
|
+
if not response.is_success:
|
|
329
|
+
error_data = validate_error_response(data)
|
|
330
|
+
raise ChipiApiError(
|
|
331
|
+
error_data["message"],
|
|
332
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
333
|
+
response.status_code,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
return data
|
|
337
|
+
except ChipiApiError:
|
|
338
|
+
raise
|
|
339
|
+
except Exception as error:
|
|
340
|
+
raise handle_api_error(error)
|
|
341
|
+
|
|
342
|
+
async def aput(
|
|
343
|
+
self,
|
|
344
|
+
endpoint: str,
|
|
345
|
+
bearer_token: str,
|
|
346
|
+
body: Optional[Dict[str, Any]] = None,
|
|
347
|
+
) -> Any:
|
|
348
|
+
"""
|
|
349
|
+
Async PUT request.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
endpoint: API endpoint path
|
|
353
|
+
bearer_token: Bearer token for authentication
|
|
354
|
+
body: Request body
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
Response data
|
|
358
|
+
|
|
359
|
+
Raises:
|
|
360
|
+
ChipiApiError: If request fails
|
|
361
|
+
"""
|
|
362
|
+
try:
|
|
363
|
+
url = f"{self.base_url}{endpoint}"
|
|
364
|
+
|
|
365
|
+
# Add version tracking to URL
|
|
366
|
+
params = self._add_version_params({})
|
|
367
|
+
|
|
368
|
+
response = await self._async_client.put(
|
|
369
|
+
url,
|
|
370
|
+
params=params,
|
|
371
|
+
json=body,
|
|
372
|
+
headers=self._get_headers(bearer_token),
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
data = response.json()
|
|
376
|
+
|
|
377
|
+
if not response.is_success:
|
|
378
|
+
error_data = validate_error_response(data)
|
|
379
|
+
raise ChipiApiError(
|
|
380
|
+
error_data["message"],
|
|
381
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
382
|
+
response.status_code,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
return data
|
|
386
|
+
except ChipiApiError:
|
|
387
|
+
raise
|
|
388
|
+
except Exception as error:
|
|
389
|
+
raise handle_api_error(error)
|
|
390
|
+
|
|
391
|
+
def delete(
|
|
392
|
+
self,
|
|
393
|
+
endpoint: str,
|
|
394
|
+
bearer_token: str,
|
|
395
|
+
) -> Any:
|
|
396
|
+
"""
|
|
397
|
+
Synchronous DELETE request.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
endpoint: API endpoint path
|
|
401
|
+
bearer_token: Bearer token for authentication
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Response data
|
|
405
|
+
|
|
406
|
+
Raises:
|
|
407
|
+
ChipiApiError: If request fails
|
|
408
|
+
"""
|
|
409
|
+
try:
|
|
410
|
+
url = f"{self.base_url}{endpoint}"
|
|
411
|
+
|
|
412
|
+
# Add version tracking to URL
|
|
413
|
+
params = self._add_version_params({})
|
|
414
|
+
|
|
415
|
+
response = self._sync_client.delete(
|
|
416
|
+
url,
|
|
417
|
+
params=params,
|
|
418
|
+
headers=self._get_headers(bearer_token),
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
data = response.json()
|
|
422
|
+
|
|
423
|
+
if not response.is_success:
|
|
424
|
+
error_data = validate_error_response(data)
|
|
425
|
+
raise ChipiApiError(
|
|
426
|
+
error_data["message"],
|
|
427
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
428
|
+
response.status_code,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
return data
|
|
432
|
+
except ChipiApiError:
|
|
433
|
+
raise
|
|
434
|
+
except Exception as error:
|
|
435
|
+
raise handle_api_error(error)
|
|
436
|
+
|
|
437
|
+
async def adelete(
|
|
438
|
+
self,
|
|
439
|
+
endpoint: str,
|
|
440
|
+
bearer_token: str,
|
|
441
|
+
) -> Any:
|
|
442
|
+
"""
|
|
443
|
+
Async DELETE request.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
endpoint: API endpoint path
|
|
447
|
+
bearer_token: Bearer token for authentication
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
Response data
|
|
451
|
+
|
|
452
|
+
Raises:
|
|
453
|
+
ChipiApiError: If request fails
|
|
454
|
+
"""
|
|
455
|
+
try:
|
|
456
|
+
url = f"{self.base_url}{endpoint}"
|
|
457
|
+
|
|
458
|
+
# Add version tracking to URL
|
|
459
|
+
params = self._add_version_params({})
|
|
460
|
+
|
|
461
|
+
response = await self._async_client.delete(
|
|
462
|
+
url,
|
|
463
|
+
params=params,
|
|
464
|
+
headers=self._get_headers(bearer_token),
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
data = response.json()
|
|
468
|
+
|
|
469
|
+
if not response.is_success:
|
|
470
|
+
error_data = validate_error_response(data)
|
|
471
|
+
raise ChipiApiError(
|
|
472
|
+
error_data["message"],
|
|
473
|
+
error_data.get("code", f"HTTP_{response.status_code}"),
|
|
474
|
+
response.status_code,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
return data
|
|
478
|
+
except ChipiApiError:
|
|
479
|
+
raise
|
|
480
|
+
except Exception as error:
|
|
481
|
+
raise handle_api_error(error)
|
|
482
|
+
|
|
483
|
+
def close(self):
|
|
484
|
+
"""Close the sync HTTP client."""
|
|
485
|
+
self._sync_client.close()
|
|
486
|
+
|
|
487
|
+
async def aclose(self):
|
|
488
|
+
"""Close the async HTTP client."""
|
|
489
|
+
await self._async_client.aclose()
|
|
490
|
+
|
|
491
|
+
def __enter__(self):
|
|
492
|
+
"""Context manager entry."""
|
|
493
|
+
return self
|
|
494
|
+
|
|
495
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
496
|
+
"""Context manager exit."""
|
|
497
|
+
self.close()
|
|
498
|
+
|
|
499
|
+
async def __aenter__(self):
|
|
500
|
+
"""Async context manager entry."""
|
|
501
|
+
return self
|
|
502
|
+
|
|
503
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
504
|
+
"""Async context manager exit."""
|
|
505
|
+
await self.aclose()
|
chipi_sdk/constants.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Constants used across Chipi SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from .models.wallet import WalletType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# API Versioning
|
|
8
|
+
API_VERSION = "1"
|
|
9
|
+
API_VERSION_DATE = "2025-12-30"
|
|
10
|
+
|
|
11
|
+
# Starknet Networks
|
|
12
|
+
STARKNET_NETWORKS = {
|
|
13
|
+
"MAINNET": "https://starknet-mainnet.public.blastapi.io/rpc/v0_7",
|
|
14
|
+
"SEPOLIA": "https://starknet-sepolia.public.blastapi.io/rpc/v0_7",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Contract Addresses
|
|
18
|
+
CONTRACT_ADDRESSES = {
|
|
19
|
+
"USDC_MAINNET": "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb",
|
|
20
|
+
"VESU_USDC_MAINNET": "0x017f19582c61479f2fe0b6606300e975c0a8f439102f43eeecc1d0e9b3d84350",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Token Decimals
|
|
24
|
+
TOKEN_DECIMALS = {
|
|
25
|
+
"USDC": 6,
|
|
26
|
+
"USDT": 6,
|
|
27
|
+
"ETH": 18,
|
|
28
|
+
"STRK": 18,
|
|
29
|
+
"DAI": 18,
|
|
30
|
+
"WBTC": 8,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# API Endpoints
|
|
34
|
+
API_ENDPOINTS = {
|
|
35
|
+
"CHIPI_WALLETS": "/chipi-wallets",
|
|
36
|
+
"TRANSACTIONS": "/transactions",
|
|
37
|
+
"SKUS": "/skus",
|
|
38
|
+
"SKU_TRANSACTIONS": "/sku-transactions",
|
|
39
|
+
"EXCHANGES": "/exchanges",
|
|
40
|
+
"USERS": "/users",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Default Pagination
|
|
44
|
+
DEFAULT_PAGINATION = {
|
|
45
|
+
"PAGE": 1,
|
|
46
|
+
"LIMIT": 10,
|
|
47
|
+
"MAX_LIMIT": 100,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Error Codes
|
|
51
|
+
ERRORS = {
|
|
52
|
+
"INVALID_API_KEY": "INVALID_API_KEY",
|
|
53
|
+
"WALLET_NOT_FOUND": "WALLET_NOT_FOUND",
|
|
54
|
+
"INSUFFICIENT_BALANCE": "INSUFFICIENT_BALANCE",
|
|
55
|
+
"TRANSACTION_FAILED": "TRANSACTION_FAILED",
|
|
56
|
+
"INVALID_SIGNATURE": "INVALID_SIGNATURE",
|
|
57
|
+
"SKU_NOT_FOUND": "SKU_NOT_FOUND",
|
|
58
|
+
"SKU_UNAVAILABLE": "SKU_UNAVAILABLE",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# SKU Contracts
|
|
62
|
+
SKU_CONTRACTS = {
|
|
63
|
+
"RECHARGER_WITH_STRK_MAINNET": "0x02d65bb726d2c29e3c97669cf297c5145eac19284fb6f935c05c0bfc68dae2b7",
|
|
64
|
+
"CHIPI_BILL_SERVICE": "0x4e8150110d580069de26adec9b179023289d55859ea07487aaade5458d7aa8b",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Service Types
|
|
68
|
+
SERVICE_TYPES = {
|
|
69
|
+
"BUY_SERVICE": "BUY_SERVICE",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Carrier IDs
|
|
73
|
+
CARRIER_IDS = {
|
|
74
|
+
"CHIPI_PAY": "chipi_pay",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Chain Types
|
|
78
|
+
CHAIN_TYPES = {
|
|
79
|
+
"STARKNET": "STARKNET",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Chain Token Types
|
|
83
|
+
CHAIN_TOKEN_TYPES = {
|
|
84
|
+
"USDC": "USDC",
|
|
85
|
+
"USDT": "USDT",
|
|
86
|
+
"ETH": "ETH",
|
|
87
|
+
"STRK": "STRK",
|
|
88
|
+
"DAI": "DAI",
|
|
89
|
+
"WBTC": "WBTC",
|
|
90
|
+
"OTHER": "OTHER",
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Wallet Class Hashes
|
|
94
|
+
WALLET_CLASS_HASHES: dict[WalletType, str] = {
|
|
95
|
+
WalletType.CHIPI: "0x0484bbd2404b3c7264bea271f7267d6d4004821ac7787a9eed7f472e79ef40d1", # v33 — latest, spending policy + audit fixes
|
|
96
|
+
WalletType.READY: "0x036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f",
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Previous CHIPI wallet class hashes still deployed on-chain (newest → oldest)
|
|
100
|
+
LEGACY_CHIPI_CLASS_HASHES: list[str] = [
|
|
101
|
+
"0x035a2251aca25daba18a5d8950deffa8372a7d84774554e75283cb85552eebc9", # v32 — audit 3
|
|
102
|
+
"0x0254f6dd0427319ec614c29e4e3929500d1ba95d0da87ff81d67051ce572667", # v31
|
|
103
|
+
"0x072b77b033a874fa1b8f7ff52e18be8fb5ce01a00c59cd184ae15f5b29bc0e57", # v30
|
|
104
|
+
"0x053f4f8791ed5bed0fddaa553d180c664e32cfaf8316bb232ae77bb08f459f2a", # v29 — previous default
|
|
105
|
+
"0x02de1565226d5215a38b68c4d9a4913989b54edff64c68c45e453c417b44cd83", # v28
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_wallet_type_from_class_hash(class_hash: str) -> Optional[str]:
|
|
110
|
+
"""
|
|
111
|
+
Determine wallet type from an on-chain class hash.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
class_hash: Class hash to look up
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Wallet type string ("CHIPI" or "READY"), or None if unknown
|
|
118
|
+
"""
|
|
119
|
+
def normalize(h: str) -> str:
|
|
120
|
+
import re
|
|
121
|
+
return re.sub(r"^0x0+", "0x", h.lower())
|
|
122
|
+
|
|
123
|
+
normalized = normalize(class_hash)
|
|
124
|
+
|
|
125
|
+
if normalize(WALLET_CLASS_HASHES[WalletType.READY]) == normalized:
|
|
126
|
+
return "READY"
|
|
127
|
+
if normalize(WALLET_CLASS_HASHES[WalletType.CHIPI]) == normalized:
|
|
128
|
+
return "CHIPI"
|
|
129
|
+
if any(normalize(h) == normalized for h in LEGACY_CHIPI_CLASS_HASHES):
|
|
130
|
+
return "CHIPI"
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
# RPC Endpoints per Wallet Type
|
|
134
|
+
WALLET_RPC_ENDPOINTS: dict[WalletType, str] = {
|
|
135
|
+
WalletType.CHIPI: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7",
|
|
136
|
+
WalletType.READY: "https://cloud.argent-api.com/v1/starknet/mainnet/rpc/v0.7",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# Paymaster Configuration
|
|
140
|
+
PAYMASTER_CONFIG = {
|
|
141
|
+
"URL": "https://paymaster.chipipay.com",
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Session Key Configuration (CHIPI wallets only - SNIP-9 compatible)
|
|
145
|
+
SESSION_DEFAULTS = {
|
|
146
|
+
"DURATION_SECONDS": 21600, # Default session duration: 6 hours
|
|
147
|
+
"MAX_CALLS": 1000, # Default max calls per session
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Session-specific Error Codes
|
|
151
|
+
SESSION_ERRORS = {
|
|
152
|
+
"INVALID_WALLET_TYPE_FOR_SESSION": "INVALID_WALLET_TYPE_FOR_SESSION",
|
|
153
|
+
"SESSION_EXPIRED": "SESSION_EXPIRED",
|
|
154
|
+
"SESSION_NOT_REGISTERED": "SESSION_NOT_REGISTERED",
|
|
155
|
+
"SESSION_REVOKED": "SESSION_REVOKED",
|
|
156
|
+
"SESSION_MAX_CALLS_EXCEEDED": "SESSION_MAX_CALLS_EXCEEDED",
|
|
157
|
+
"SESSION_ENTRYPOINT_NOT_ALLOWED": "SESSION_ENTRYPOINT_NOT_ALLOWED",
|
|
158
|
+
"SESSION_DECRYPTION_FAILED": "SESSION_DECRYPTION_FAILED",
|
|
159
|
+
"SESSION_CREATION_FAILED": "SESSION_CREATION_FAILED",
|
|
160
|
+
"INVALID_SPENDING_POLICY": "INVALID_SPENDING_POLICY",
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Session Contract Entrypoint Names
|
|
164
|
+
SESSION_ENTRYPOINTS = {
|
|
165
|
+
"ADD_OR_UPDATE": "add_or_update_session_key",
|
|
166
|
+
"REVOKE": "revoke_session_key",
|
|
167
|
+
"GET_DATA": "get_session_data",
|
|
168
|
+
"SET_SPENDING_POLICY": "set_spending_policy",
|
|
169
|
+
"GET_SPENDING_POLICY": "get_spending_policy",
|
|
170
|
+
"REMOVE_SPENDING_POLICY": "remove_spending_policy",
|
|
171
|
+
}
|