datadid-sdk-ts
v1.0.7
Published
TypeScript SDK for the DataDID developer platform — Data API and DID API clients
Downloads
675
Readme
Getting Started with datadid-sdk-ts
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-api.memolabs.net)DIDClient— DID creation/deletion and file operations (prodidapi.memolabs.org)
Installation
npm install datadid-sdk-tsRequirements: Node.js 18+ (uses native
fetch)
Quick Start
import { DataClient } from "datadid-sdk-ts";
async function main() {
const client = DataClient.production();
// Log in — the client stores the access token automatically
const tokens = await client.loginWithEmailPassword("[email protected]", "yourpassword");
// Fetch your profile
const me = await client.getMe();
console.log(me.uid, me.role);
}
main();Servers
| Client | Purpose | Production URL | Test URL |
|---|---|---|---|
| DataClient | Auth, user info, action records | https://data-api.memolabs.net | https://testdata-api.memolabs.net |
| DIDClient | DID creation, file operations | https://prodidapi.memolabs.org | https://testdidapi.memolabs.org |
Part 1 — DataClient
Create a client
import { DataClient } from "datadid-sdk-ts";
const client = DataClient.production();
// or: const client = DataClient.testnet();
// or: const client = new DataClient({ baseURL: "https://data-api.memolabs.net" });Login with email (verification code)
// Step 1: send a code to the user's inbox
await client.sendEmailCode("[email protected]");
// Step 2: log in with the code
const tokens = await client.loginWithEmail(
"[email protected]",
"123456", // code from inbox
"Web" // source: a string identifying your app — e.g. "Web", "App", "Mobile"
);
console.log(tokens.accessToken);
console.log(tokens.refreshToken);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.sendEmailCode("[email protected]");
const tokens = await client.registerWithEmail(
"[email protected]",
"123456", // code from inbox
"mypassword", // choose a password
"Web"
);Login with email + password
const tokens = await client.loginWithEmailPassword(
"[email protected]",
"mypassword"
);Reset password
await client.sendEmailCode("[email protected]");
await client.resetPassword(
"[email protected]",
"123456", // code from inbox
"newpassword"
);Login with Telegram
const tokens = await client.loginWithTelegram(
telegramInitData, // string from Telegram WebApp.initData
"App"
);Login with Pi Browser
const tokens = await client.loginWithPi(
piAccessToken, // access token from Pi Browser SDK
"App"
);Login with EVM wallet (MetaMask, etc.)
// Step 1: request a challenge message from the server
// origin is required — use your app's own domain
const message = await client.getEvmChallenge(
"0xYourWalletAddress",
985, // chain ID (optional, defaults to 985)
"https://myapp.com" // origin — required for loginWithEvm to succeed
);
// Step 2: sign the message with the user's wallet
// (using ethers.js, wagmi, viem, or any signing library)
const signature = await signer.signMessage(message);
// Step 3: submit the signature to log in
const result = await client.loginWithEvm(message, signature, "Web");
console.log(result.accessToken);
console.log(result.did); // the user's DID string (e.g. "did:memo:...")
console.log(result.number); // the user's numeric platform IDRefresh an access token
const newAccessToken = await client.refreshToken(tokens.refreshToken);Get current user info
// Basic info (uid, email, username, role)
const me = await client.getMe();
console.log(me.uid, me.role);
// Full profile (avatar, DID, linked social accounts, etc.)
const info = await client.getUserInfo();
console.log(info.name);
console.log(info.address); // numeric platform ID (e.g. "2018955010523533312")
console.log(info.did);
console.log(info.email);
console.log(info.telegram_info); // if linked
console.log(info.twitter_info); // if linked
console.log(info.discord_info); // if linked
console.log(info.pi_info); // if linkedAction 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
const record = await client.getActionRecord(61);
if (record) {
console.log(`Completed at ${record.time}, earned ${record.points} points`);
} else {
console.log("Not completed yet");
}
// Mark action #61 as completed
await client.addActionRecord(61);
// With extra options (some actions require additional data)
await client.addActionRecord(61, { someOption: "value" });AliveCheck action IDs (AliveCheck is the platform's liveness/subscription service):
5— first-time AliveCheck subscription6— AliveCheck renewal
Error handling
import { DataDIDApiError } from "datadid-sdk-ts";
try {
await client.loginWithEmailPassword("[email protected]", "wrongpassword");
} catch (err) {
if (err instanceof DataDIDApiError) {
console.error(err.message); // human-readable message
console.error(err.statusCode); // HTTP status code (e.g. 401, 500)
console.error(err.responseBody); // raw JSON from the server
}
}Manual token management
By default, login methods store the access token on the client automatically. You can disable this:
const client = new DataClient({
baseURL: "https://data-api.memolabs.net",
disableAutoToken: true,
});
const tokens = await client.loginWithEmail("[email protected]", "123456", "Web");
// Token was NOT stored automatically — set it yourself:
client.setAccessToken(tokens.accessToken);
// Read the current token at any time:
const token = client.getAccessToken();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
import { DIDClient } from "datadid-sdk-ts";
const didClient = DIDClient.production();
// or: const didClient = DIDClient.testnet();Create a DID
const address = "0xYourWalletAddress";
// Step 1: get the message to sign
const message = await didClient.getCreateMessage(address);
// Step 2: sign it with your wallet (ethers.js example)
// The message is a hex-encoded byte string — sign the bytes, not the hex string
const signature = await wallet.signMessage(ethers.getBytes(message));
// Step 3: submit — server creates the DID on-chain
const newDID = await didClient.createDID(signature, address);
console.log(newDID); // "did:memo:abc123..."Check if a DID exists
const result = await didClient.getDIDExists("0xYourWalletAddress");
console.log(result);Get DID info
const info = await didClient.getDIDInfo("0xYourWalletAddress");
console.log(info.did); // the DID string (e.g. "did:memo:...")
console.log(info.number); // the numeric platform IDDelete a DID
const myDID = "did:memo:abc123...";
// Step 1: get the message to sign
const message = await didClient.getDeleteMessage(myDID);
// Step 2: sign it
// The message is a hex-encoded byte string — sign the bytes, not the hex string
const signature = await wallet.signMessage(ethers.getBytes(message));
// Step 3: submit
const result = await didClient.deleteDID(signature, myDID);
console.log(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 didClient.uploadFile(fileData, "0xYourWalletAddress");List files
const files = await didClient.listFiles("0xYourWalletAddress");Download a file
const file = await didClient.downloadFile("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
const message = await didClient.createMfileUpload(fileData, "0xYourWalletAddress");
// Step 2: sign it
const signature = await wallet.signMessage(message);
// Step 3: confirm the upload
const result = await didClient.confirmMfileUpload(signature, "0xYourWalletAddress");Download an mfile
const file = await didClient.downloadMfile(
"did:mfile:bafkrei...", // the mfile DID
"0xYourWalletAddress"
);API Reference
DataClient
| Method | Description |
|---|---|
| DataClient.production() | Client for production |
| DataClient.testnet() | Client for test server |
| setAccessToken(token) | Manually set the auth token |
| getAccessToken() | Read the current token |
| sendEmailCode(email) | Send verification code to email |
| loginWithEmail(email, code, source) | Login with email + code |
| registerWithEmail(email, code, password, source) | Register new account |
| loginWithEmailPassword(email, password) | Login with email + password |
| resetPassword(email, code, newPassword) | Reset password |
| loginWithTelegram(initdata, source) | Login with Telegram |
| loginWithPi(piAccessToken, source) | Login with Pi Browser |
| getEvmChallenge(address, chainId?) | Get EVM sign-in challenge |
| loginWithEvm(message, signature, source) | Login with EVM wallet signature |
| refreshToken(refreshToken) | Get a new access token |
| getMe() | Basic user info (uid, email, role) |
| getUserInfo() | Full user profile |
| getActionRecord(actionId) | Get action completion record (or null) |
| addActionRecord(actionId, 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) |
| getCreateMessage(address) | Get message to sign before creating a DID |
| createDID(sig, address) | Create a DID (submit signature) |
| createDIDAdmin(address) | Admin: create DID without signature |
| createTonDID(address) | Create a Ton-network DID |
| getDIDExists(address) | Check if a DID exists |
| getDIDInfo(address) | Get DID info and chain balances |
| getDeleteMessage(did) | Get message to sign before deleting a DID |
| deleteDID(sig, did) | Delete a DID (submit signature) |
| uploadFile(data, address) | Upload a file |
| listFiles(address) | List files for an address |
| downloadFile(address) | Download a file |
| createMfileUpload(data, address) | Start an mfile upload (returns message to sign) |
| confirmMfileUpload(sig, address) | Confirm mfile upload (submit signature) |
| downloadMfile(mdid, address) | Download a file by its mfile DID |
Error class
| Property | Type | Description |
|---|---|---|
| message | string | Human-readable error message |
| statusCode | number | HTTP status code |
| responseBody | any | Raw JSON response from server |
