Python SDK for the DataDID developer platform — Data API and DID API clients
Project description
Getting Started with datadid-sdk-python
DataDID is a developer platform that combines decentralized identity (DID) with user authentication and action records. The SDK provides two clients:
DataClient— authentication, user info, and action records (data-be.metamemo.one)DIDClient— DID creation/deletion and file operations (prodidapi.memolabs.org)
Installation
pip install datadid-sdk-python
Requirements: Python 3.10+
Quick Start
import asyncio
from src import DataClient
async def main():
client = DataClient.production()
# Log in — the client stores the access token automatically
tokens = await client.login_with_email_password("you@example.com", "yourpassword")
# Fetch your profile
me = await client.get_me()
print(me.uid, me.role)
asyncio.run(main())
Servers
| Client | Purpose | Production URL | Test URL |
|---|---|---|---|
DataClient |
Auth, user info, action records | https://data-be.metamemo.one |
https://test-data-be-v2.memolabs.net |
DIDClient |
DID creation, file operations | https://prodidapi.memolabs.org |
https://testdidapi.memolabs.org |
Part 1 — DataClient
Create a client
from src import DataClient
from src.data.types import DataClientOptions
client = DataClient.production()
# or: client = DataClient.testnet()
# or: client = DataClient(DataClientOptions(base_url="https://data-be.metamemo.one"))
Login with email (verification code)
# Step 1: send a code to the user's inbox
await client.send_email_code("alice@example.com")
# Step 2: log in with the code
tokens = await client.login_with_email(
"alice@example.com",
"123456", # code from inbox
"Web", # source: a string identifying your app — e.g. "Web", "App", "Mobile"
)
print(tokens.access_token)
print(tokens.refresh_token)
After a successful login the client stores the access token automatically. All subsequent calls that need authentication will use it.
Register a new account
await client.send_email_code("bob@example.com")
tokens = await client.register_with_email(
"bob@example.com",
"123456", # code from inbox
"mypassword", # choose a password
"Web",
)
Login with email + password
tokens = await client.login_with_email_password(
"alice@example.com",
"mypassword",
)
Reset password
await client.send_email_code("alice@example.com")
await client.reset_password(
"alice@example.com",
"123456", # code from inbox
"newpassword",
)
Login with Telegram
tokens = await client.login_with_telegram(
telegram_init_data, # string from Telegram WebApp.initData
"App",
)
Login with Pi Browser
tokens = await client.login_with_pi(
pi_access_token, # access token from Pi Browser SDK
"App",
)
Login with EVM wallet (MetaMask, etc.)
# Step 1: request a challenge message from the server
message = await client.get_evm_challenge(
"0xYourWalletAddress",
chain_id=985, # optional
)
# Step 2: sign the message with the user's wallet
# (using eth_account or any signing library)
from eth_account.messages import encode_defunct
from eth_account import Account
signed = Account.sign_message(encode_defunct(text=message), private_key=private_key)
signature = signed.signature.hex()
# Step 3: submit the signature to log in
result = await client.login_with_evm(message, signature, "Web")
print(result.access_token)
print(result.did) # the user's DID string (e.g. "did:memo:...")
print(result.number) # the user's numeric platform ID
Refresh an access token
new_access_token = await client.refresh_token(tokens.refresh_token)
Get current user info
# Basic info (uid, email, username, role)
me = await client.get_me()
print(me.uid, me.role)
# Full profile (avatar, DID, linked social accounts, etc.)
info = await client.get_user_info()
print(info.name)
print(info.address) # numeric platform ID (e.g. "2018955010523533312")
print(info.did)
print(info.email)
print(info.telegram_info) # if linked
print(info.twitter_info) # if linked
print(info.discord_info) # if linked
print(info.pi_info) # if linked
Action records
Action records are the platform's points/achievement system. Each action has a numeric ID. When a user completes an action, a record is stored with the points earned and a Unix timestamp.
# Check if the user has completed action #61
record = await client.get_action_record(61)
if record:
print(f"Completed at {record.time}, earned {record.points} points")
else:
print("Not completed yet")
# Mark action #61 as completed
await client.add_action_record(61)
# With extra options (some actions require additional data)
await client.add_action_record(61, {"some_option": "value"})
Error handling
from src import DataDIDApiError
try:
await client.login_with_email_password("alice@example.com", "wrongpassword")
except DataDIDApiError as err:
print(str(err)) # human-readable message
print(err.status_code) # HTTP status code (e.g. 401, 500)
print(err.response_body) # raw JSON from the server
Manual token management
By default, login methods store the access token on the client automatically. You can disable this:
from src import DataClient
from src.data.types import DataClientOptions
client = DataClient(DataClientOptions(
base_url="https://data-be.metamemo.one",
disable_auto_token=True,
))
tokens = await client.login_with_email("alice@example.com", "123456", "Web")
# Token was NOT stored automatically — set it yourself:
client.set_access_token(tokens.access_token)
# Read the current token at any time:
token = client.get_access_token()
Part 2 — DIDClient
DID operations use a sign-then-submit pattern. You never send your private key to the server — you only sign a message locally, and the server pays the gas fee and submits the transaction on your behalf.
The pattern for every write operation:
- Ask the server for a message to sign
- Sign that message with your wallet (free, off-chain)
- Submit the signature — the server does the rest
Create a client
from src import DIDClient
did_client = DIDClient.production()
# or: did_client = DIDClient.testnet()
Create a DID
address = "0xYourWalletAddress"
# Step 1: get the message to sign
message = await did_client.get_create_message(address)
# Step 2: sign it with your wallet (eth_account example)
from eth_account.messages import encode_defunct
from eth_account import Account
signed = Account.sign_message(encode_defunct(text=message), private_key=private_key)
signature = signed.signature.hex()
# Step 3: submit — server creates the DID on-chain
new_did = await did_client.create_did(signature, address)
print(new_did) # "did:memo:abc123..."
Check if a DID exists
result = await did_client.get_did_exists("0xYourWalletAddress")
print(result)
Get DID info
info = await did_client.get_did_info("0xYourWalletAddress")
print(info.did) # the DID string
print(info.info) # list of DIDChainInfo(address, balance, chain)
Delete a DID
my_did = "did:memo:abc123..."
# Step 1: get the message to sign
message = await did_client.get_delete_message(my_did)
# Step 2: sign it
signature = ...
# Step 3: submit
result = await did_client.delete_did(signature, my_did)
print(result.status)
Upload a file
Files are stored directly by wallet address. For files that need their own on-chain DID, see mfile operations below.
await did_client.upload_file(file_data, "0xYourWalletAddress")
List files
files = await did_client.list_files("0xYourWalletAddress")
Download a file
file = await did_client.download_file("0xYourWalletAddress")
Upload an mfile (file with an on-chain DID)
An mfile is a file that gets its own DID minted on-chain, making it permanently addressable and verifiable on the Memo network. This uses the sign-then-submit pattern.
# Step 1: create the upload request — returns a message to sign
message = await did_client.create_mfile_upload(file_data, "0xYourWalletAddress")
# Step 2: sign it
signature = ...
# Step 3: confirm the upload
result = await did_client.confirm_mfile_upload(signature, "0xYourWalletAddress")
Download an mfile
file = await did_client.download_mfile(
"did:mfile:bafkrei...", # the mfile DID
"0xYourWalletAddress",
)
Running the tests
python tests/run.py
Tests hit the real production and testnet servers. A valid access token is required.
- Copy
.env.exampleto.env - Paste your token as
DATADID_TOKEN=eyJ...
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.
You can also pass the token inline without creating a .env file:
DATADID_TOKEN=eyJ... python tests/run.py # macOS / Linux
set DATADID_TOKEN=eyJ... && python tests/run.py # Windows cmd
API Reference
DataClient
| Method | Description |
|---|---|
DataClient.production() |
Client for production |
DataClient.testnet() |
Client for test server |
set_access_token(token) |
Manually set the auth token |
get_access_token() |
Read the current token |
send_email_code(email) |
Send verification code to email |
login_with_email(email, code, source, useragent?) |
Login with email + code |
register_with_email(email, code, password, source, useragent?) |
Register new account |
login_with_email_password(email, password) |
Login with email + password |
reset_password(email, code, new_password) |
Reset password |
login_with_telegram(initdata, source, useragent?) |
Login with Telegram |
login_with_pi(pi_access_token, source, useragent?) |
Login with Pi Browser |
get_evm_challenge(address, chain_id?, origin?) |
Get EVM sign-in challenge |
login_with_evm(message, signature, source, useragent?) |
Login with EVM wallet signature |
refresh_token(refresh_token) |
Get a new access token |
get_me() |
Basic user info (uid, email, role) |
get_user_info() |
Full user profile |
get_action_record(action_id) |
Get action completion record (or None) |
add_action_record(action_id, opts?) |
Record an action completion |
DIDClient
| Method | Description |
|---|---|
DIDClient.production() |
Client for production (prodidapi.memolabs.org) |
DIDClient.testnet() |
Client for test server (testdidapi.memolabs.org) |
get_create_message(address) |
Get message to sign before creating a DID |
create_did(sig, address) |
Create a DID (submit signature) |
create_did_admin(address) |
Admin: create DID without signature |
create_ton_did(address) |
Create a Ton-network DID |
get_did_exists(address) |
Check if a DID exists |
get_did_info(address) |
Get DID info and chain balances |
get_delete_message(did) |
Get message to sign before deleting a DID |
delete_did(sig, did) |
Delete a DID (submit signature) |
upload_file(data, address) |
Upload a file |
list_files(address) |
List files for an address |
download_file(address) |
Download a file |
create_mfile_upload(data, address) |
Start an mfile upload (returns message to sign) |
confirm_mfile_upload(sig, address) |
Confirm mfile upload (submit signature) |
download_mfile(mdid, address) |
Download a file by its mfile DID |
Error class
| Attribute | Type | Description |
|---|---|---|
str(err) |
str |
Human-readable error message |
status_code |
int |
HTTP status code |
response_body |
Any |
Raw JSON response from server |
Links
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file datadid_sdk_python-1.0.3.tar.gz.
File metadata
- Download URL: datadid_sdk_python-1.0.3.tar.gz
- Upload date:
- Size: 11.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0bb683f20b50e718b2895324df2e866aba8cac03c4c39384255ff5bd6b9a86a
|
|
| MD5 |
9c38d7979cb2bcbcdcd23af85ae93507
|
|
| BLAKE2b-256 |
cf742a0a8ce3c5e52a3f236f85048e035992998eb31a7dafcc4dbb9c66bd0e4d
|
File details
Details for the file datadid_sdk_python-1.0.3-py3-none-any.whl.
File metadata
- Download URL: datadid_sdk_python-1.0.3-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e808659fba8da63048d42c225984ae4487708ca86e582379128a5109cd4b0e2
|
|
| MD5 |
54e54d3c79ec8c4c1409a5fdc309cbd6
|
|
| BLAKE2b-256 |
0e3c94b80c637aee079e22693315d5d100cf24b157486175e9013ab0b94f9ce5
|