Skip to main content

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:

  1. Ask the server for a message to sign
  2. Sign that message with your wallet (free, off-chain)
  3. 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.

  1. Copy .env.example to .env
  2. 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

datadid_sdk_python-1.0.3.tar.gz (11.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

datadid_sdk_python-1.0.3-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

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

Hashes for datadid_sdk_python-1.0.3.tar.gz
Algorithm Hash digest
SHA256 e0bb683f20b50e718b2895324df2e866aba8cac03c4c39384255ff5bd6b9a86a
MD5 9c38d7979cb2bcbcdcd23af85ae93507
BLAKE2b-256 cf742a0a8ce3c5e52a3f236f85048e035992998eb31a7dafcc4dbb9c66bd0e4d

See more details on using hashes here.

File details

Details for the file datadid_sdk_python-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for datadid_sdk_python-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 9e808659fba8da63048d42c225984ae4487708ca86e582379128a5109cd4b0e2
MD5 54e54d3c79ec8c4c1409a5fdc309cbd6
BLAKE2b-256 0e3c94b80c637aee079e22693315d5d100cf24b157486175e9013ab0b94f9ce5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page