Working with DIDs
Elastos DIDs are W3C-compliant Decentralized Identifiers anchored on the EID chain. They follow the format did:elastos:i<base58-encoded-id>. The JavaScript SDK implements the W3C DID Core profile used on EID: create and publish documents, issue credentials, and resolve others’ DIDs on-chain.
DID Architecture
┌──────────────────────────────────────────────────────┐
│ DID Document │
│ │
│ DID: did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN │
│ │
│ ┌─────────────────────────────┐ │
│ │ Public Keys │ │
│ │ - authentication key │ │
│ │ - assertion key │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Services │ │
│ │ - Hive vault endpoint │ │
│ │ - Carrier address │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Verifiable Credentials │ │
│ │ - Self-proclaimed │ │
│ │ - Third-party issued │ │
│ └─────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
│ IDChainRequest transaction
▼
┌──────────────────┐
│ EID Chain │
│ (Chain ID: 22) │
│ │
│ Precompiled │
│ contracts: │
│ - 0x16 (22) │ ← operationDID (create/update/deactivate)
│ - 0x17 (23) │ ← resolveDID (resolve DID documents)
│ - 0x7D7 (2007) │ ← KYC operations
└──────────────────┘
SDK Installation
npm install @elastosfoundation/did-js-sdk
Configure the resolver (DIDBackend)
Point the SDK at mainnet or testnet before any DID operation:
import { DIDBackend, DefaultDIDAdapter } from "@elastosfoundation/did-js-sdk";
// Mainnet
DIDBackend.initialize(new DefaultDIDAdapter("https://api.elastos.io/eid"));
// Testnet
DIDBackend.initialize(new DefaultDIDAdapter("https://api-testnet.elastos.io/eid"));
Call DIDBackend.initialize() once at application startup. Calling it again replaces the global resolver and can cause confusing resolution behavior.
Key Classes
| Class | Purpose |
|---|---|
DIDStore | Persistent storage for DID documents and private keys |
RootIdentity | Root identity derived from HD mnemonic |
DID | A decentralized identifier reference |
DIDDocument | The full document associated with a DID |
DIDURL | URL format for referencing DID resources |
VerifiableCredential | A signed credential (claim about a subject) |
VerifiablePresentation | A package of credentials presented to a verifier |
Issuer | Entity that creates and signs credentials |
DIDStore operations
| Operation | Method | Description |
|---|---|---|
| Open / create store | DIDStore.open(path) | Opens an existing store or creates one at the given path |
| Load a DID | store.loadDid(did) | Loads a DID document from local storage |
| List DIDs | store.listDids() | Lists DIDs in the store |
| Delete a DID | store.deleteDid(did) | Removes a DID and its keys from local storage |
| Export | store.exportDid(did, password, storepass) | Exports a DID (with keys) as encrypted JSON |
| Import | store.importDid(exportedJson, password, storepass) | Imports a previously exported DID |
| Sync | store.synchronize(identity, storepass) | Pulls the latest DID documents from the chain into the store |
Creating a DID Identity
import {
DIDStore,
RootIdentity,
DID,
DIDDocument,
Mnemonic,
DIDBackend,
DefaultDIDAdapter,
} from "@elastosfoundation/did-js-sdk";
// Initialize the DID backend resolver
const resolver = new DefaultDIDAdapter("https://api.elastos.io/eid");
DIDBackend.initialize(resolver);
// Create or open a DID store
const store = await DIDStore.open("/path/to/did-store");
// Generate a new mnemonic or use existing one
const mnemonic = Mnemonic.generate("english");
// Create root identity from mnemonic
// Derives keys at m/44'/0'/0'/0/<index> using P-256 curve
const rootIdentity = RootIdentity.createFromMnemonic(
mnemonic,
"", // passphrase (empty for default)
store,
"storepass" // store encryption password
);
// Create the first DID document (index 0)
const doc = await rootIdentity.newDid("storepass");
const did = doc.getSubject();
console.log("Created DID:", did.toString());
// Output: did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN
The mnemonic is the master secret for every DID derived from this RootIdentity. Store it offline; losing it means losing access to those DIDs permanently.
Publishing a DID Document
Publishing anchors the DID document on the EID chain via an IDChainRequest transaction:
// Publish the DID document to the EID chain
await doc.publish("storepass");
// The SDK handles:
// 1. Serializing the DID document to JSON
// 2. Creating an IDChainRequest transaction
// 3. Signing with the DID's private key
// 4. Submitting to the EID chain
// Wait for confirmation (usually 10-20 seconds on EID)
// Then resolve to verify
const resolvedDoc = await DID.resolve(did);
console.log("Resolved:", resolvedDoc !== null);
DID publication requires at least one EID block confirmation. EID block time is approximately 5-10 seconds, but network conditions can vary. See Common Pitfalls for retry strategies.
Resolving a DID with the SDK
After a DID is published, you can resolve its document from the chain using a DID instance (this uses the same DIDBackend you initialized earlier):
import { DID } from "@elastosfoundation/did-js-sdk";
const did = new DID("did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN");
const doc = await did.resolve();
if (doc) {
console.log("Public keys:", doc.getPublicKeyCount());
console.log("Services:", doc.getServiceCount());
console.log("Expires:", doc.getExpires()?.toISOString());
} else {
console.log("DID not found on chain");
}
A DID that was just published may take roughly 10–20 seconds to become resolvable while EID confirms blocks.
Issuing Verifiable Credentials
import { Issuer, VerifiableCredential, DIDURL } from "@elastosfoundation/did-js-sdk";
async function issueCredential(
issuerDoc: DIDDocument,
subjectDID: DID,
storepass: string
): Promise<VerifiableCredential> {
const issuer = new Issuer(issuerDoc);
const credentialId = DIDURL.from("#emailCredential", subjectDID);
const vc = await issuer.issueFor(subjectDID)
.id(credentialId)
.type("EmailCredential", "BasicProfileCredential")
.property("email", "[email protected]")
.property("name", "Jane Developer")
.property("timestamp", new Date().toISOString())
.expirationDate(new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)) // 1 year
.seal(storepass);
console.log("Credential ID:", vc.getId().toString());
console.log("Issuer:", vc.getIssuer().toString());
console.log("Subject:", vc.getSubject().getId().toString());
return vc;
}
Creating Verifiable Presentations
import { VerifiablePresentation } from "@elastosfoundation/did-js-sdk";
async function createPresentation(
holderDoc: DIDDocument,
credentials: VerifiableCredential[],
nonce: string,
realm: string,
storepass: string
): Promise<VerifiablePresentation> {
const vp = await VerifiablePresentation.createFor(
holderDoc.getSubject(),
credentials,
nonce,
realm,
storepass,
holderDoc.getStore()!
);
console.log("Presentation holder:", vp.getHolder().toString());
console.log("Credentials count:", vp.getCredentialCount());
return vp;
}
Verifying Credentials and Presentations
async function verifyCredential(vc: VerifiableCredential): Promise<boolean> {
// Checks:
// 1. Signature is valid (signed by issuer's key)
// 2. Credential is not expired
// 3. Issuer DID is resolvable and not deactivated
// 4. Credential has not been revoked
const isValid = await vc.isValid();
if (!isValid) {
// Check specific failure reasons
const isExpired = vc.isExpired();
const isGenuine = await vc.isGenuine();
console.log("Expired:", isExpired);
console.log("Genuine signature:", isGenuine);
}
return isValid;
}
async function verifyPresentation(vp: VerifiablePresentation): Promise<boolean> {
const isValid = await vp.isValid();
if (isValid) {
const holder = vp.getHolder();
const credentials = vp.getCredentials();
for (const cred of credentials) {
console.log(`Credential ${cred.getId()}: subject=${cred.getSubject().getId()}`);
}
}
return isValid;
}
Resolving DIDs On-Chain
DIDs are resolved by calling the precompiled contract at address 22 (0x16) on the EID chain:
import { ethers } from "ethers";
async function resolveDIDOnChain(didString: string): Promise<object | null> {
const provider = new ethers.JsonRpcProvider("https://api.elastos.io/eid");
const result = await provider.call({
to: "0x0000000000000000000000000000000017", // resolveDID (address 23)
data: ethers.hexlify(ethers.toUtf8Bytes(didString)),
});
if (result === "0x") return null;
const docJson = ethers.toUtf8String(result);
return JSON.parse(docJson);
}
// Usage
const doc = await resolveDIDOnChain("did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN");
if (doc) {
console.log("DID Document:", JSON.stringify(doc, null, 2));
}
For more details on EID precompiled contracts, see ESC Precompiled Contracts.
DID Document Structure
A resolved DID document looks like this:
{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN",
"publicKey": [
{
"id": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN#primary",
"type": "ECDSAsecp256r1",
"controller": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN",
"publicKeyBase58": "27bqfhMew5hEFLYp2kFnhCrJrij7Z6xPSQq6dBgKmyJBw"
}
],
"authentication": [
"did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN#primary"
],
"verifiableCredential": [
{
"id": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN#name",
"type": ["SelfProclaimedCredential"],
"issuer": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN",
"issuanceDate": "2024-01-15T10:00:00Z",
"credentialSubject": {
"id": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN",
"name": "Jane Developer"
}
}
],
"service": [
{
"id": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN#hivevault",
"type": "HiveVault",
"serviceEndpoint": "https://your-hive-node.example.com"
}
],
"expires": "2029-01-15T10:00:00Z",
"proof": {
"type": "ECDSAsecp256r1",
"verificationMethod": "did:elastos:icJ4z2DULrHEzYSvjKNJpKyhqFDxvYV7pN#primary",
"signature": "..."
}
}
SDKs in other languages
| Language | Repository |
|---|---|
| Java | Elastos.DID.Java.SDK |
| Swift | Elastos.DID.Swift.SDK |
| C/C++ | Elastos.DID.Native.SDK |
Future: Runtime DID Bridge
The ElastOS Runtime currently uses did:key (Ed25519, locally generated) for device identity and message signing. A bridge from the runtime's did:key to on-chain did:elastos DID is planned but not yet available. When implemented, this will allow runtime identities to anchor to the EID chain, connecting local device identity with globally verifiable on-chain identity. See the Runtime Roadmap for status.
Resources
- GitHub — DID JS SDK
- DID Login tutorial — authenticate users with DIDs
- W3C DID Core