datadid-sdk-python 1.0.3__tar.gz → 1.0.5__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.
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/PKG-INFO +31 -36
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/README.md +30 -35
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/__init__.py +0 -2
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/data/client.py +8 -2
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/did/client.py +41 -16
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/did/types.py +2 -12
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/datadid_sdk_python.egg-info/PKG-INFO +31 -36
- datadid_sdk_python-1.0.5/datadid_sdk_python.egg-info/SOURCES.txt +23 -0
- datadid_sdk_python-1.0.5/datadid_sdk_python.egg-info/top_level.txt +1 -0
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/pyproject.toml +2 -2
- datadid_sdk_python-1.0.3/datadid_sdk_python.egg-info/SOURCES.txt +0 -23
- datadid_sdk_python-1.0.3/datadid_sdk_python.egg-info/top_level.txt +0 -1
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/data/__init__.py +0 -0
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/data/types.py +0 -0
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/did/__init__.py +0 -0
- {datadid_sdk_python-1.0.3/src → datadid_sdk_python-1.0.5/datadid}/errors.py +0 -0
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/datadid_sdk_python.egg-info/dependency_links.txt +0 -0
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/datadid_sdk_python.egg-info/requires.txt +0 -0
- {datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datadid-sdk-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: Python SDK for the DataDID developer platform — Data API and DID API clients
|
|
5
5
|
License-Expression: ISC
|
|
6
6
|
Keywords: datadid,did,memo
|
|
@@ -31,7 +31,7 @@ pip install datadid-sdk-python
|
|
|
31
31
|
|
|
32
32
|
```python
|
|
33
33
|
import asyncio
|
|
34
|
-
from
|
|
34
|
+
from datadid import DataClient
|
|
35
35
|
|
|
36
36
|
async def main():
|
|
37
37
|
client = DataClient.production()
|
|
@@ -62,8 +62,8 @@ asyncio.run(main())
|
|
|
62
62
|
### Create a client
|
|
63
63
|
|
|
64
64
|
```python
|
|
65
|
-
from
|
|
66
|
-
from
|
|
65
|
+
from datadid import DataClient
|
|
66
|
+
from datadid.data.types import DataClientOptions
|
|
67
67
|
|
|
68
68
|
client = DataClient.production()
|
|
69
69
|
# or: client = DataClient.testnet()
|
|
@@ -159,9 +159,11 @@ tokens = await client.login_with_pi(
|
|
|
159
159
|
|
|
160
160
|
```python
|
|
161
161
|
# Step 1: request a challenge message from the server
|
|
162
|
+
# origin is required — use your app's own domain
|
|
162
163
|
message = await client.get_evm_challenge(
|
|
163
164
|
"0xYourWalletAddress",
|
|
164
|
-
chain_id=985,
|
|
165
|
+
chain_id=985, # optional, defaults to 985
|
|
166
|
+
origin="https://myapp.com", # required for login_with_evm to succeed
|
|
165
167
|
)
|
|
166
168
|
|
|
167
169
|
# Step 2: sign the message with the user's wallet
|
|
@@ -229,12 +231,16 @@ await client.add_action_record(61)
|
|
|
229
231
|
await client.add_action_record(61, {"some_option": "value"})
|
|
230
232
|
```
|
|
231
233
|
|
|
234
|
+
**AliveCheck action IDs** (AliveCheck is the platform's liveness/subscription service):
|
|
235
|
+
- `5` — first-time AliveCheck subscription
|
|
236
|
+
- `6` — AliveCheck renewal
|
|
237
|
+
|
|
232
238
|
---
|
|
233
239
|
|
|
234
240
|
### Error handling
|
|
235
241
|
|
|
236
242
|
```python
|
|
237
|
-
from
|
|
243
|
+
from datadid import DataDIDApiError
|
|
238
244
|
|
|
239
245
|
try:
|
|
240
246
|
await client.login_with_email_password("alice@example.com", "wrongpassword")
|
|
@@ -251,8 +257,8 @@ except DataDIDApiError as err:
|
|
|
251
257
|
By default, login methods store the access token on the client automatically. You can disable this:
|
|
252
258
|
|
|
253
259
|
```python
|
|
254
|
-
from
|
|
255
|
-
from
|
|
260
|
+
from datadid import DataClient
|
|
261
|
+
from datadid.data.types import DataClientOptions
|
|
256
262
|
|
|
257
263
|
client = DataClient(DataClientOptions(
|
|
258
264
|
base_url="https://data-be.metamemo.one",
|
|
@@ -282,7 +288,7 @@ DID operations use a **sign-then-submit** pattern. You never send your private k
|
|
|
282
288
|
### Create a client
|
|
283
289
|
|
|
284
290
|
```python
|
|
285
|
-
from
|
|
291
|
+
from datadid import DIDClient
|
|
286
292
|
|
|
287
293
|
did_client = DIDClient.production()
|
|
288
294
|
# or: did_client = DIDClient.testnet()
|
|
@@ -298,10 +304,10 @@ address = "0xYourWalletAddress"
|
|
|
298
304
|
# Step 1: get the message to sign
|
|
299
305
|
message = await did_client.get_create_message(address)
|
|
300
306
|
|
|
301
|
-
# Step 2: sign it
|
|
307
|
+
# Step 2: sign it — the message is a hex-encoded byte string, sign the bytes
|
|
302
308
|
from eth_account.messages import encode_defunct
|
|
303
309
|
from eth_account import Account
|
|
304
|
-
signed = Account.sign_message(encode_defunct(
|
|
310
|
+
signed = Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), private_key=private_key)
|
|
305
311
|
signature = signed.signature.hex()
|
|
306
312
|
|
|
307
313
|
# Step 3: submit — server creates the DID on-chain
|
|
@@ -325,8 +331,8 @@ print(result)
|
|
|
325
331
|
|
|
326
332
|
```python
|
|
327
333
|
info = await did_client.get_did_info("0xYourWalletAddress")
|
|
328
|
-
print(info.did)
|
|
329
|
-
print(info.
|
|
334
|
+
print(info.did) # the DID string (e.g. "did:memo:...")
|
|
335
|
+
print(info.number) # the numeric platform ID
|
|
330
336
|
```
|
|
331
337
|
|
|
332
338
|
---
|
|
@@ -339,8 +345,9 @@ my_did = "did:memo:abc123..."
|
|
|
339
345
|
# Step 1: get the message to sign
|
|
340
346
|
message = await did_client.get_delete_message(my_did)
|
|
341
347
|
|
|
342
|
-
# Step 2: sign it
|
|
343
|
-
|
|
348
|
+
# Step 2: sign it — the message is a hex-encoded byte string, sign the bytes
|
|
349
|
+
signed = Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), private_key=private_key)
|
|
350
|
+
signature = signed.signature.hex()
|
|
344
351
|
|
|
345
352
|
# Step 3: submit
|
|
346
353
|
result = await did_client.delete_did(signature, my_did)
|
|
@@ -384,7 +391,7 @@ An mfile is a file that gets its own DID minted on-chain, making it permanently
|
|
|
384
391
|
message = await did_client.create_mfile_upload(file_data, "0xYourWalletAddress")
|
|
385
392
|
|
|
386
393
|
# Step 2: sign it
|
|
387
|
-
signature = ...
|
|
394
|
+
signature = ... # sign message with your wallet
|
|
388
395
|
|
|
389
396
|
# Step 3: confirm the upload
|
|
390
397
|
result = await did_client.confirm_mfile_upload(signature, "0xYourWalletAddress")
|
|
@@ -409,19 +416,7 @@ file = await did_client.download_mfile(
|
|
|
409
416
|
python tests/run.py
|
|
410
417
|
```
|
|
411
418
|
|
|
412
|
-
Tests hit the real production and testnet servers.
|
|
413
|
-
|
|
414
|
-
1. Copy `.env.example` to `.env`
|
|
415
|
-
2. Paste your token as `DATADID_TOKEN=eyJ...`
|
|
416
|
-
|
|
417
|
-
To get a token: log into the DataDID app, open browser devtools (F12 → Network tab), find the login request, and copy the `access_token` from the response JSON. Tokens expire after 24 hours.
|
|
418
|
-
|
|
419
|
-
You can also pass the token inline without creating a `.env` file:
|
|
420
|
-
|
|
421
|
-
```bash
|
|
422
|
-
DATADID_TOKEN=eyJ... python tests/run.py # macOS / Linux
|
|
423
|
-
set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
424
|
-
```
|
|
419
|
+
Tests hit the real production and testnet servers. The `ACCESS_TOKEN` constant in `tests/data_client_test.py` needs to be a valid token; replace it if tests fail with a 401 error.
|
|
425
420
|
|
|
426
421
|
---
|
|
427
422
|
|
|
@@ -436,14 +431,14 @@ set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
|
436
431
|
| `set_access_token(token)` | Manually set the auth token |
|
|
437
432
|
| `get_access_token()` | Read the current token |
|
|
438
433
|
| `send_email_code(email)` | Send verification code to email |
|
|
439
|
-
| `login_with_email(email, code, source
|
|
440
|
-
| `register_with_email(email, code, password, source
|
|
434
|
+
| `login_with_email(email, code, source)` | Login with email + code |
|
|
435
|
+
| `register_with_email(email, code, password, source)` | Register new account |
|
|
441
436
|
| `login_with_email_password(email, password)` | Login with email + password |
|
|
442
437
|
| `reset_password(email, code, new_password)` | Reset password |
|
|
443
|
-
| `login_with_telegram(initdata, source
|
|
444
|
-
| `login_with_pi(pi_access_token, source
|
|
445
|
-
| `get_evm_challenge(address, chain_id
|
|
446
|
-
| `login_with_evm(message, signature, source
|
|
438
|
+
| `login_with_telegram(initdata, source)` | Login with Telegram |
|
|
439
|
+
| `login_with_pi(pi_access_token, source)` | Login with Pi Browser |
|
|
440
|
+
| `get_evm_challenge(address, chain_id?)` | Get EVM sign-in challenge |
|
|
441
|
+
| `login_with_evm(message, signature, source)` | Login with EVM wallet signature |
|
|
447
442
|
| `refresh_token(refresh_token)` | Get a new access token |
|
|
448
443
|
| `get_me()` | Basic user info (uid, email, role) |
|
|
449
444
|
| `get_user_info()` | Full user profile |
|
|
@@ -483,5 +478,5 @@ set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
|
483
478
|
|
|
484
479
|
## Links
|
|
485
480
|
|
|
486
|
-
- [Data API reference](https://
|
|
481
|
+
- [Data API reference](https://memolabs.gitbook.io/datadid-developer-platform-v2/en/api)
|
|
487
482
|
- [DID API reference (Swagger)](https://prodidapi.memolabs.org/swagger/index.html)
|
|
@@ -21,7 +21,7 @@ pip install datadid-sdk-python
|
|
|
21
21
|
|
|
22
22
|
```python
|
|
23
23
|
import asyncio
|
|
24
|
-
from
|
|
24
|
+
from datadid import DataClient
|
|
25
25
|
|
|
26
26
|
async def main():
|
|
27
27
|
client = DataClient.production()
|
|
@@ -52,8 +52,8 @@ asyncio.run(main())
|
|
|
52
52
|
### Create a client
|
|
53
53
|
|
|
54
54
|
```python
|
|
55
|
-
from
|
|
56
|
-
from
|
|
55
|
+
from datadid import DataClient
|
|
56
|
+
from datadid.data.types import DataClientOptions
|
|
57
57
|
|
|
58
58
|
client = DataClient.production()
|
|
59
59
|
# or: client = DataClient.testnet()
|
|
@@ -149,9 +149,11 @@ tokens = await client.login_with_pi(
|
|
|
149
149
|
|
|
150
150
|
```python
|
|
151
151
|
# Step 1: request a challenge message from the server
|
|
152
|
+
# origin is required — use your app's own domain
|
|
152
153
|
message = await client.get_evm_challenge(
|
|
153
154
|
"0xYourWalletAddress",
|
|
154
|
-
chain_id=985,
|
|
155
|
+
chain_id=985, # optional, defaults to 985
|
|
156
|
+
origin="https://myapp.com", # required for login_with_evm to succeed
|
|
155
157
|
)
|
|
156
158
|
|
|
157
159
|
# Step 2: sign the message with the user's wallet
|
|
@@ -219,12 +221,16 @@ await client.add_action_record(61)
|
|
|
219
221
|
await client.add_action_record(61, {"some_option": "value"})
|
|
220
222
|
```
|
|
221
223
|
|
|
224
|
+
**AliveCheck action IDs** (AliveCheck is the platform's liveness/subscription service):
|
|
225
|
+
- `5` — first-time AliveCheck subscription
|
|
226
|
+
- `6` — AliveCheck renewal
|
|
227
|
+
|
|
222
228
|
---
|
|
223
229
|
|
|
224
230
|
### Error handling
|
|
225
231
|
|
|
226
232
|
```python
|
|
227
|
-
from
|
|
233
|
+
from datadid import DataDIDApiError
|
|
228
234
|
|
|
229
235
|
try:
|
|
230
236
|
await client.login_with_email_password("alice@example.com", "wrongpassword")
|
|
@@ -241,8 +247,8 @@ except DataDIDApiError as err:
|
|
|
241
247
|
By default, login methods store the access token on the client automatically. You can disable this:
|
|
242
248
|
|
|
243
249
|
```python
|
|
244
|
-
from
|
|
245
|
-
from
|
|
250
|
+
from datadid import DataClient
|
|
251
|
+
from datadid.data.types import DataClientOptions
|
|
246
252
|
|
|
247
253
|
client = DataClient(DataClientOptions(
|
|
248
254
|
base_url="https://data-be.metamemo.one",
|
|
@@ -272,7 +278,7 @@ DID operations use a **sign-then-submit** pattern. You never send your private k
|
|
|
272
278
|
### Create a client
|
|
273
279
|
|
|
274
280
|
```python
|
|
275
|
-
from
|
|
281
|
+
from datadid import DIDClient
|
|
276
282
|
|
|
277
283
|
did_client = DIDClient.production()
|
|
278
284
|
# or: did_client = DIDClient.testnet()
|
|
@@ -288,10 +294,10 @@ address = "0xYourWalletAddress"
|
|
|
288
294
|
# Step 1: get the message to sign
|
|
289
295
|
message = await did_client.get_create_message(address)
|
|
290
296
|
|
|
291
|
-
# Step 2: sign it
|
|
297
|
+
# Step 2: sign it — the message is a hex-encoded byte string, sign the bytes
|
|
292
298
|
from eth_account.messages import encode_defunct
|
|
293
299
|
from eth_account import Account
|
|
294
|
-
signed = Account.sign_message(encode_defunct(
|
|
300
|
+
signed = Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), private_key=private_key)
|
|
295
301
|
signature = signed.signature.hex()
|
|
296
302
|
|
|
297
303
|
# Step 3: submit — server creates the DID on-chain
|
|
@@ -315,8 +321,8 @@ print(result)
|
|
|
315
321
|
|
|
316
322
|
```python
|
|
317
323
|
info = await did_client.get_did_info("0xYourWalletAddress")
|
|
318
|
-
print(info.did)
|
|
319
|
-
print(info.
|
|
324
|
+
print(info.did) # the DID string (e.g. "did:memo:...")
|
|
325
|
+
print(info.number) # the numeric platform ID
|
|
320
326
|
```
|
|
321
327
|
|
|
322
328
|
---
|
|
@@ -329,8 +335,9 @@ my_did = "did:memo:abc123..."
|
|
|
329
335
|
# Step 1: get the message to sign
|
|
330
336
|
message = await did_client.get_delete_message(my_did)
|
|
331
337
|
|
|
332
|
-
# Step 2: sign it
|
|
333
|
-
|
|
338
|
+
# Step 2: sign it — the message is a hex-encoded byte string, sign the bytes
|
|
339
|
+
signed = Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), private_key=private_key)
|
|
340
|
+
signature = signed.signature.hex()
|
|
334
341
|
|
|
335
342
|
# Step 3: submit
|
|
336
343
|
result = await did_client.delete_did(signature, my_did)
|
|
@@ -374,7 +381,7 @@ An mfile is a file that gets its own DID minted on-chain, making it permanently
|
|
|
374
381
|
message = await did_client.create_mfile_upload(file_data, "0xYourWalletAddress")
|
|
375
382
|
|
|
376
383
|
# Step 2: sign it
|
|
377
|
-
signature = ...
|
|
384
|
+
signature = ... # sign message with your wallet
|
|
378
385
|
|
|
379
386
|
# Step 3: confirm the upload
|
|
380
387
|
result = await did_client.confirm_mfile_upload(signature, "0xYourWalletAddress")
|
|
@@ -399,19 +406,7 @@ file = await did_client.download_mfile(
|
|
|
399
406
|
python tests/run.py
|
|
400
407
|
```
|
|
401
408
|
|
|
402
|
-
Tests hit the real production and testnet servers.
|
|
403
|
-
|
|
404
|
-
1. Copy `.env.example` to `.env`
|
|
405
|
-
2. Paste your token as `DATADID_TOKEN=eyJ...`
|
|
406
|
-
|
|
407
|
-
To get a token: log into the DataDID app, open browser devtools (F12 → Network tab), find the login request, and copy the `access_token` from the response JSON. Tokens expire after 24 hours.
|
|
408
|
-
|
|
409
|
-
You can also pass the token inline without creating a `.env` file:
|
|
410
|
-
|
|
411
|
-
```bash
|
|
412
|
-
DATADID_TOKEN=eyJ... python tests/run.py # macOS / Linux
|
|
413
|
-
set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
414
|
-
```
|
|
409
|
+
Tests hit the real production and testnet servers. The `ACCESS_TOKEN` constant in `tests/data_client_test.py` needs to be a valid token; replace it if tests fail with a 401 error.
|
|
415
410
|
|
|
416
411
|
---
|
|
417
412
|
|
|
@@ -426,14 +421,14 @@ set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
|
426
421
|
| `set_access_token(token)` | Manually set the auth token |
|
|
427
422
|
| `get_access_token()` | Read the current token |
|
|
428
423
|
| `send_email_code(email)` | Send verification code to email |
|
|
429
|
-
| `login_with_email(email, code, source
|
|
430
|
-
| `register_with_email(email, code, password, source
|
|
424
|
+
| `login_with_email(email, code, source)` | Login with email + code |
|
|
425
|
+
| `register_with_email(email, code, password, source)` | Register new account |
|
|
431
426
|
| `login_with_email_password(email, password)` | Login with email + password |
|
|
432
427
|
| `reset_password(email, code, new_password)` | Reset password |
|
|
433
|
-
| `login_with_telegram(initdata, source
|
|
434
|
-
| `login_with_pi(pi_access_token, source
|
|
435
|
-
| `get_evm_challenge(address, chain_id
|
|
436
|
-
| `login_with_evm(message, signature, source
|
|
428
|
+
| `login_with_telegram(initdata, source)` | Login with Telegram |
|
|
429
|
+
| `login_with_pi(pi_access_token, source)` | Login with Pi Browser |
|
|
430
|
+
| `get_evm_challenge(address, chain_id?)` | Get EVM sign-in challenge |
|
|
431
|
+
| `login_with_evm(message, signature, source)` | Login with EVM wallet signature |
|
|
437
432
|
| `refresh_token(refresh_token)` | Get a new access token |
|
|
438
433
|
| `get_me()` | Basic user info (uid, email, role) |
|
|
439
434
|
| `get_user_info()` | Full user profile |
|
|
@@ -473,5 +468,5 @@ set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
|
473
468
|
|
|
474
469
|
## Links
|
|
475
470
|
|
|
476
|
-
- [Data API reference](https://
|
|
471
|
+
- [Data API reference](https://memolabs.gitbook.io/datadid-developer-platform-v2/en/api)
|
|
477
472
|
- [DID API reference (Swagger)](https://prodidapi.memolabs.org/swagger/index.html)
|
|
@@ -24,7 +24,6 @@ from .did.types import (
|
|
|
24
24
|
CreateDIDResponse,
|
|
25
25
|
DeleteDIDResponse,
|
|
26
26
|
DIDInfoResponse,
|
|
27
|
-
DIDChainInfo,
|
|
28
27
|
)
|
|
29
28
|
|
|
30
29
|
__all__ = [
|
|
@@ -46,5 +45,4 @@ __all__ = [
|
|
|
46
45
|
"CreateDIDResponse",
|
|
47
46
|
"DeleteDIDResponse",
|
|
48
47
|
"DIDInfoResponse",
|
|
49
|
-
"DIDChainInfo",
|
|
50
48
|
]
|
|
@@ -188,6 +188,12 @@ class DataClient:
|
|
|
188
188
|
Get the sign-in challenge message for EVM wallet login.
|
|
189
189
|
|
|
190
190
|
GET /v2/login/evm/challenge
|
|
191
|
+
|
|
192
|
+
The `origin` parameter is required for login_with_evm() to succeed.
|
|
193
|
+
It must be a URL string identifying your app (e.g. "https://myapp.com").
|
|
194
|
+
The server embeds it in the SIWE challenge message, and verifies it
|
|
195
|
+
when the signature is submitted. Omitting it produces a malformed
|
|
196
|
+
challenge that login_with_evm() will reject.
|
|
191
197
|
"""
|
|
192
198
|
params: dict[str, str] = {"address": address}
|
|
193
199
|
if chain_id is not None:
|
|
@@ -251,7 +257,7 @@ class DataClient:
|
|
|
251
257
|
data = await self._request(
|
|
252
258
|
"POST",
|
|
253
259
|
"/v2/login/refresh",
|
|
254
|
-
extra_headers={"Authorization": refresh_token},
|
|
260
|
+
extra_headers={"Authorization": f"Bearer {refresh_token}"},
|
|
255
261
|
)
|
|
256
262
|
new_token = (
|
|
257
263
|
data.get("access_token")
|
|
@@ -453,7 +459,7 @@ class DataClient:
|
|
|
453
459
|
result_val = json_body.get("result")
|
|
454
460
|
if result_val is not None and result_val != 1:
|
|
455
461
|
raise DataDIDApiError(
|
|
456
|
-
json_body.get("message") or f"API returned result={result_val}",
|
|
462
|
+
json_body.get("message") or json_body.get("message_en") or f"API returned result={result_val}",
|
|
457
463
|
response.status_code,
|
|
458
464
|
json_body,
|
|
459
465
|
)
|
|
@@ -6,12 +6,9 @@ import httpx
|
|
|
6
6
|
|
|
7
7
|
from ..errors import DataDIDApiError
|
|
8
8
|
from .types import (
|
|
9
|
-
CreateDIDResponse,
|
|
10
9
|
DeleteDIDResponse,
|
|
11
|
-
DIDChainInfo,
|
|
12
10
|
DIDClientOptions,
|
|
13
11
|
DIDInfoResponse,
|
|
14
|
-
SigMsgResponse,
|
|
15
12
|
)
|
|
16
13
|
|
|
17
14
|
_PRODUCTION_URL = "https://prodidapi.memolabs.org"
|
|
@@ -49,6 +46,12 @@ class DIDClient:
|
|
|
49
46
|
Get the message you need to sign before creating a DID.
|
|
50
47
|
Sign the returned msg with your wallet, then pass the signature to create_did().
|
|
51
48
|
|
|
49
|
+
IMPORTANT: the returned message is a hex-encoded byte string. Sign the decoded
|
|
50
|
+
bytes, not the hex string itself:
|
|
51
|
+
eth_account: Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), key)
|
|
52
|
+
web3.py: w3.eth.account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), key)
|
|
53
|
+
Signing the raw hex string will produce an invalid signature.
|
|
54
|
+
|
|
52
55
|
GET /did/createsigmsg
|
|
53
56
|
"""
|
|
54
57
|
data = await self._request("GET", f"/did/createsigmsg?address={address}")
|
|
@@ -81,36 +84,50 @@ class DIDClient:
|
|
|
81
84
|
data = await self._request("POST", "/did/createton", {"address": address})
|
|
82
85
|
return data.get("did", "") if isinstance(data, dict) else str(data)
|
|
83
86
|
|
|
84
|
-
async def get_did_exists(self, address: str) ->
|
|
87
|
+
async def get_did_exists(self, address: str) -> bool:
|
|
85
88
|
"""
|
|
86
89
|
Check whether a DID exists for the given address.
|
|
87
90
|
|
|
88
91
|
GET /did/exist
|
|
92
|
+
|
|
93
|
+
NOTE: There is a short propagation delay before a newly
|
|
94
|
+
created or deleted DID is reflected here.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
address: EVM wallet address (0x...)
|
|
89
98
|
"""
|
|
90
|
-
|
|
99
|
+
data = await self._request("GET", f"/did/exist?address={address}")
|
|
100
|
+
return data.get("exist") == 1
|
|
91
101
|
|
|
92
102
|
async def get_did_info(self, address: str) -> DIDInfoResponse:
|
|
93
103
|
"""
|
|
94
|
-
Get DID info
|
|
104
|
+
Get DID info for an EVM wallet address.
|
|
95
105
|
|
|
96
106
|
GET /did/info
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
address: EVM wallet address (0x...)
|
|
110
|
+
Returns:
|
|
111
|
+
DIDInfoResponse with did (string) and number (string).
|
|
97
112
|
"""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
for item in (data.get("info") or [])
|
|
106
|
-
]
|
|
107
|
-
return DIDInfoResponse(did=data.get("did", ""), info=chain_list)
|
|
113
|
+
json_data = await self._request("GET", f"/did/info?address={address}")
|
|
114
|
+
inner = json_data.get("data") or json_data
|
|
115
|
+
return DIDInfoResponse(
|
|
116
|
+
did=inner.get("did", ""),
|
|
117
|
+
number=inner.get("number", ""),
|
|
118
|
+
)
|
|
108
119
|
|
|
109
120
|
async def get_delete_message(self, did: str) -> str:
|
|
110
121
|
"""
|
|
111
122
|
Get the message you need to sign before deleting a DID.
|
|
112
123
|
Sign the returned msg with your wallet, then pass the signature to delete_did().
|
|
113
124
|
|
|
125
|
+
IMPORTANT: the returned message is a hex-encoded byte string. Sign the decoded
|
|
126
|
+
bytes, not the hex string itself:
|
|
127
|
+
eth_account: Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), key)
|
|
128
|
+
web3.py: w3.eth.account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), key)
|
|
129
|
+
Signing the raw hex string will produce an invalid signature.
|
|
130
|
+
|
|
114
131
|
GET /did/deletesigmsg
|
|
115
132
|
"""
|
|
116
133
|
data = await self._request("GET", f"/did/deletesigmsg?did={did}")
|
|
@@ -222,4 +239,12 @@ class DIDClient:
|
|
|
222
239
|
json_body,
|
|
223
240
|
)
|
|
224
241
|
|
|
242
|
+
result_val = json_body.get("result")
|
|
243
|
+
if result_val is not None and result_val != 1:
|
|
244
|
+
raise DataDIDApiError(
|
|
245
|
+
json_body.get("error") or json_body.get("message") or f"API returned result={result_val}",
|
|
246
|
+
response.status_code,
|
|
247
|
+
json_body,
|
|
248
|
+
)
|
|
249
|
+
|
|
225
250
|
return json_body
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import List
|
|
1
|
+
from dataclasses import dataclass
|
|
3
2
|
|
|
4
3
|
|
|
5
4
|
@dataclass
|
|
@@ -32,18 +31,9 @@ class DeleteDIDResponse:
|
|
|
32
31
|
status: str
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
@dataclass
|
|
36
|
-
class DIDChainInfo:
|
|
37
|
-
"""Chain-specific info within a DID info response."""
|
|
38
|
-
|
|
39
|
-
address: str
|
|
40
|
-
balance: str
|
|
41
|
-
chain: str
|
|
42
|
-
|
|
43
|
-
|
|
44
34
|
@dataclass
|
|
45
35
|
class DIDInfoResponse:
|
|
46
36
|
"""Response from GET /did/info."""
|
|
47
37
|
|
|
48
38
|
did: str
|
|
49
|
-
|
|
39
|
+
number: str
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datadid-sdk-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: Python SDK for the DataDID developer platform — Data API and DID API clients
|
|
5
5
|
License-Expression: ISC
|
|
6
6
|
Keywords: datadid,did,memo
|
|
@@ -31,7 +31,7 @@ pip install datadid-sdk-python
|
|
|
31
31
|
|
|
32
32
|
```python
|
|
33
33
|
import asyncio
|
|
34
|
-
from
|
|
34
|
+
from datadid import DataClient
|
|
35
35
|
|
|
36
36
|
async def main():
|
|
37
37
|
client = DataClient.production()
|
|
@@ -62,8 +62,8 @@ asyncio.run(main())
|
|
|
62
62
|
### Create a client
|
|
63
63
|
|
|
64
64
|
```python
|
|
65
|
-
from
|
|
66
|
-
from
|
|
65
|
+
from datadid import DataClient
|
|
66
|
+
from datadid.data.types import DataClientOptions
|
|
67
67
|
|
|
68
68
|
client = DataClient.production()
|
|
69
69
|
# or: client = DataClient.testnet()
|
|
@@ -159,9 +159,11 @@ tokens = await client.login_with_pi(
|
|
|
159
159
|
|
|
160
160
|
```python
|
|
161
161
|
# Step 1: request a challenge message from the server
|
|
162
|
+
# origin is required — use your app's own domain
|
|
162
163
|
message = await client.get_evm_challenge(
|
|
163
164
|
"0xYourWalletAddress",
|
|
164
|
-
chain_id=985,
|
|
165
|
+
chain_id=985, # optional, defaults to 985
|
|
166
|
+
origin="https://myapp.com", # required for login_with_evm to succeed
|
|
165
167
|
)
|
|
166
168
|
|
|
167
169
|
# Step 2: sign the message with the user's wallet
|
|
@@ -229,12 +231,16 @@ await client.add_action_record(61)
|
|
|
229
231
|
await client.add_action_record(61, {"some_option": "value"})
|
|
230
232
|
```
|
|
231
233
|
|
|
234
|
+
**AliveCheck action IDs** (AliveCheck is the platform's liveness/subscription service):
|
|
235
|
+
- `5` — first-time AliveCheck subscription
|
|
236
|
+
- `6` — AliveCheck renewal
|
|
237
|
+
|
|
232
238
|
---
|
|
233
239
|
|
|
234
240
|
### Error handling
|
|
235
241
|
|
|
236
242
|
```python
|
|
237
|
-
from
|
|
243
|
+
from datadid import DataDIDApiError
|
|
238
244
|
|
|
239
245
|
try:
|
|
240
246
|
await client.login_with_email_password("alice@example.com", "wrongpassword")
|
|
@@ -251,8 +257,8 @@ except DataDIDApiError as err:
|
|
|
251
257
|
By default, login methods store the access token on the client automatically. You can disable this:
|
|
252
258
|
|
|
253
259
|
```python
|
|
254
|
-
from
|
|
255
|
-
from
|
|
260
|
+
from datadid import DataClient
|
|
261
|
+
from datadid.data.types import DataClientOptions
|
|
256
262
|
|
|
257
263
|
client = DataClient(DataClientOptions(
|
|
258
264
|
base_url="https://data-be.metamemo.one",
|
|
@@ -282,7 +288,7 @@ DID operations use a **sign-then-submit** pattern. You never send your private k
|
|
|
282
288
|
### Create a client
|
|
283
289
|
|
|
284
290
|
```python
|
|
285
|
-
from
|
|
291
|
+
from datadid import DIDClient
|
|
286
292
|
|
|
287
293
|
did_client = DIDClient.production()
|
|
288
294
|
# or: did_client = DIDClient.testnet()
|
|
@@ -298,10 +304,10 @@ address = "0xYourWalletAddress"
|
|
|
298
304
|
# Step 1: get the message to sign
|
|
299
305
|
message = await did_client.get_create_message(address)
|
|
300
306
|
|
|
301
|
-
# Step 2: sign it
|
|
307
|
+
# Step 2: sign it — the message is a hex-encoded byte string, sign the bytes
|
|
302
308
|
from eth_account.messages import encode_defunct
|
|
303
309
|
from eth_account import Account
|
|
304
|
-
signed = Account.sign_message(encode_defunct(
|
|
310
|
+
signed = Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), private_key=private_key)
|
|
305
311
|
signature = signed.signature.hex()
|
|
306
312
|
|
|
307
313
|
# Step 3: submit — server creates the DID on-chain
|
|
@@ -325,8 +331,8 @@ print(result)
|
|
|
325
331
|
|
|
326
332
|
```python
|
|
327
333
|
info = await did_client.get_did_info("0xYourWalletAddress")
|
|
328
|
-
print(info.did)
|
|
329
|
-
print(info.
|
|
334
|
+
print(info.did) # the DID string (e.g. "did:memo:...")
|
|
335
|
+
print(info.number) # the numeric platform ID
|
|
330
336
|
```
|
|
331
337
|
|
|
332
338
|
---
|
|
@@ -339,8 +345,9 @@ my_did = "did:memo:abc123..."
|
|
|
339
345
|
# Step 1: get the message to sign
|
|
340
346
|
message = await did_client.get_delete_message(my_did)
|
|
341
347
|
|
|
342
|
-
# Step 2: sign it
|
|
343
|
-
|
|
348
|
+
# Step 2: sign it — the message is a hex-encoded byte string, sign the bytes
|
|
349
|
+
signed = Account.sign_message(encode_defunct(primitive=bytes.fromhex(message[2:])), private_key=private_key)
|
|
350
|
+
signature = signed.signature.hex()
|
|
344
351
|
|
|
345
352
|
# Step 3: submit
|
|
346
353
|
result = await did_client.delete_did(signature, my_did)
|
|
@@ -384,7 +391,7 @@ An mfile is a file that gets its own DID minted on-chain, making it permanently
|
|
|
384
391
|
message = await did_client.create_mfile_upload(file_data, "0xYourWalletAddress")
|
|
385
392
|
|
|
386
393
|
# Step 2: sign it
|
|
387
|
-
signature = ...
|
|
394
|
+
signature = ... # sign message with your wallet
|
|
388
395
|
|
|
389
396
|
# Step 3: confirm the upload
|
|
390
397
|
result = await did_client.confirm_mfile_upload(signature, "0xYourWalletAddress")
|
|
@@ -409,19 +416,7 @@ file = await did_client.download_mfile(
|
|
|
409
416
|
python tests/run.py
|
|
410
417
|
```
|
|
411
418
|
|
|
412
|
-
Tests hit the real production and testnet servers.
|
|
413
|
-
|
|
414
|
-
1. Copy `.env.example` to `.env`
|
|
415
|
-
2. Paste your token as `DATADID_TOKEN=eyJ...`
|
|
416
|
-
|
|
417
|
-
To get a token: log into the DataDID app, open browser devtools (F12 → Network tab), find the login request, and copy the `access_token` from the response JSON. Tokens expire after 24 hours.
|
|
418
|
-
|
|
419
|
-
You can also pass the token inline without creating a `.env` file:
|
|
420
|
-
|
|
421
|
-
```bash
|
|
422
|
-
DATADID_TOKEN=eyJ... python tests/run.py # macOS / Linux
|
|
423
|
-
set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
424
|
-
```
|
|
419
|
+
Tests hit the real production and testnet servers. The `ACCESS_TOKEN` constant in `tests/data_client_test.py` needs to be a valid token; replace it if tests fail with a 401 error.
|
|
425
420
|
|
|
426
421
|
---
|
|
427
422
|
|
|
@@ -436,14 +431,14 @@ set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
|
436
431
|
| `set_access_token(token)` | Manually set the auth token |
|
|
437
432
|
| `get_access_token()` | Read the current token |
|
|
438
433
|
| `send_email_code(email)` | Send verification code to email |
|
|
439
|
-
| `login_with_email(email, code, source
|
|
440
|
-
| `register_with_email(email, code, password, source
|
|
434
|
+
| `login_with_email(email, code, source)` | Login with email + code |
|
|
435
|
+
| `register_with_email(email, code, password, source)` | Register new account |
|
|
441
436
|
| `login_with_email_password(email, password)` | Login with email + password |
|
|
442
437
|
| `reset_password(email, code, new_password)` | Reset password |
|
|
443
|
-
| `login_with_telegram(initdata, source
|
|
444
|
-
| `login_with_pi(pi_access_token, source
|
|
445
|
-
| `get_evm_challenge(address, chain_id
|
|
446
|
-
| `login_with_evm(message, signature, source
|
|
438
|
+
| `login_with_telegram(initdata, source)` | Login with Telegram |
|
|
439
|
+
| `login_with_pi(pi_access_token, source)` | Login with Pi Browser |
|
|
440
|
+
| `get_evm_challenge(address, chain_id?)` | Get EVM sign-in challenge |
|
|
441
|
+
| `login_with_evm(message, signature, source)` | Login with EVM wallet signature |
|
|
447
442
|
| `refresh_token(refresh_token)` | Get a new access token |
|
|
448
443
|
| `get_me()` | Basic user info (uid, email, role) |
|
|
449
444
|
| `get_user_info()` | Full user profile |
|
|
@@ -483,5 +478,5 @@ set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
|
|
|
483
478
|
|
|
484
479
|
## Links
|
|
485
480
|
|
|
486
|
-
- [Data API reference](https://
|
|
481
|
+
- [Data API reference](https://memolabs.gitbook.io/datadid-developer-platform-v2/en/api)
|
|
487
482
|
- [DID API reference (Swagger)](https://prodidapi.memolabs.org/swagger/index.html)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
./datadid/__init__.py
|
|
4
|
+
./datadid/errors.py
|
|
5
|
+
./datadid/data/__init__.py
|
|
6
|
+
./datadid/data/client.py
|
|
7
|
+
./datadid/data/types.py
|
|
8
|
+
./datadid/did/__init__.py
|
|
9
|
+
./datadid/did/client.py
|
|
10
|
+
./datadid/did/types.py
|
|
11
|
+
datadid/__init__.py
|
|
12
|
+
datadid/errors.py
|
|
13
|
+
datadid/data/__init__.py
|
|
14
|
+
datadid/data/client.py
|
|
15
|
+
datadid/data/types.py
|
|
16
|
+
datadid/did/__init__.py
|
|
17
|
+
datadid/did/client.py
|
|
18
|
+
datadid/did/types.py
|
|
19
|
+
datadid_sdk_python.egg-info/PKG-INFO
|
|
20
|
+
datadid_sdk_python.egg-info/SOURCES.txt
|
|
21
|
+
datadid_sdk_python.egg-info/dependency_links.txt
|
|
22
|
+
datadid_sdk_python.egg-info/requires.txt
|
|
23
|
+
datadid_sdk_python.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
datadid
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "datadid-sdk-python"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.5"
|
|
8
8
|
description = "Python SDK for the DataDID developer platform — Data API and DID API clients"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
|
|
17
17
|
[tool.setuptools.packages.find]
|
|
18
18
|
where = ["."]
|
|
19
|
-
include = ["
|
|
19
|
+
include = ["datadid*"]
|
|
20
20
|
|
|
21
21
|
[tool.setuptools.package-dir]
|
|
22
22
|
"" = "."
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
README.md
|
|
2
|
-
pyproject.toml
|
|
3
|
-
./src/__init__.py
|
|
4
|
-
./src/errors.py
|
|
5
|
-
./src/data/__init__.py
|
|
6
|
-
./src/data/client.py
|
|
7
|
-
./src/data/types.py
|
|
8
|
-
./src/did/__init__.py
|
|
9
|
-
./src/did/client.py
|
|
10
|
-
./src/did/types.py
|
|
11
|
-
datadid_sdk_python.egg-info/PKG-INFO
|
|
12
|
-
datadid_sdk_python.egg-info/SOURCES.txt
|
|
13
|
-
datadid_sdk_python.egg-info/dependency_links.txt
|
|
14
|
-
datadid_sdk_python.egg-info/requires.txt
|
|
15
|
-
datadid_sdk_python.egg-info/top_level.txt
|
|
16
|
-
src/__init__.py
|
|
17
|
-
src/errors.py
|
|
18
|
-
src/data/__init__.py
|
|
19
|
-
src/data/client.py
|
|
20
|
-
src/data/types.py
|
|
21
|
-
src/did/__init__.py
|
|
22
|
-
src/did/client.py
|
|
23
|
-
src/did/types.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
src
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datadid_sdk_python-1.0.3 → datadid_sdk_python-1.0.5}/datadid_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|