DID (Decentralized Identity)

@bsv/simple supports did:bsv: Decentralized Identifiers — W3C-compatible DIDs anchored on the BSV blockchain using UTXO chain linking.

How it Works

A did:bsv: DID is identified by the txid of its issuance transaction:

did:bsv:d803b04af611b67074af7864e5ee520060265547f8b929f2494b3e05af190636

The DID lifecycle uses a chain of UTXO spends:

  1. TX0 (Issuance) — Creates a chain UTXO (output 0) + OP_RETURN with BSVDID marker and payload "1". The txid becomes the DID identifier.

  2. TX1 (Document) — Spends TX0 output 0, creates a new chain UTXO (output 0) + OP_RETURN with the full DID Document as JSON.

  3. TX2+ (Updates) — Same pattern: spend previous output 0, create new chain UTXO + updated document.

  4. Revocation — Spend the chain UTXO with OP_RETURN payload "3". Chain terminates.

Any resolver can follow this output-0-spend chain to discover the latest DID Document.

Creating a DID

import { createWallet } from '@bsv/simple/browser'

const wallet = await createWallet()

const result = await wallet.createDID()
console.log(result.did)          // 'did:bsv:<txid>'
console.log(result.document)     // Full W3C DID Document
console.log(result.identityCode) // Internal identity code

With services:

DID Document Structure (V2)

Resolving a DID

Same Wallet (Fast Path)

Resolving your own DID checks the local basket first — no network calls needed:

Cross-Wallet Resolution

Resolving a DID created by another wallet requires on-chain lookups. In a browser environment, this needs a server-side proxy because:

  • The nChain Universal Resolver (bsvdid-universal-resolver.nchain.systems) is currently unreliable (returns HTTP 500)

  • Direct WhatsOnChain API calls from browsers are blocked by CORS and rate-limited (HTTP 429)

The proxy makes all external API calls server-side where there are no CORS restrictions or browser rate limits.

Resolution Flow

Setting Up the Resolution Proxy

1. Create the API Route

Create app/api/resolve-did/route.ts in your Next.js app using the handler factory:

That's it. The handler automatically:

  • Tries the nChain Universal Resolver first (10s timeout)

  • Falls back to WoC chain-following on failure (parses OP_RETURN, follows output-0 spend chain)

  • Handles deactivated DIDs (returns deactivated: true)

  • Limits chain hops to 100

API: GET ?did=did:bsv:<txid>DIDResolutionResult

Custom config (optional):

2. Configure the Wallet

Pass didProxyUrl in your wallet defaults so resolveDID() uses the proxy:

Or if your proxy is hosted elsewhere:

3. Resolve Cross-Wallet

Updating a DID

Deactivating a DID

Listing Your DIDs

The DID Utility Class

DID is a standalone class — no wallet needed:

Legacy DIDs

The library still supports legacy identity-key-based DIDs (did:bsv:<66-char-pubkey>):

Server-Side Usage (Without Proxy)

When using @bsv/simple on the server (e.g., in a Node.js script), no proxy is needed. The SDK calls resolvers directly:

The resolution order without a proxy is:

  1. Local basket

  2. nChain Universal Resolver (direct)

  3. WhatsOnChain chain-following (direct)

Troubleshooting

Problem
Cause
Solution

Cross-wallet resolve fails with CORS error

Browser calling WoC directly

Set up the /api/resolve-did proxy route

nChain returns HTTP 500

Their infrastructure is down

The proxy automatically falls back to WoC

WoC returns HTTP 429

Browser rate-limited

Use the server-side proxy (no browser rate limits)

resolveDID returns notYetAvailable

Document TX hasn't propagated

Wait a few seconds and retry

Own DID resolves but others don't

No didProxyUrl configured

Pass didProxyUrl: '/api/resolve-did' to createWallet()

Last updated