NEODID STANDARDIZED

NeoDID

NeoDID is the Morpheus privacy identity layer for Neo N3. It now has four aligned surfaces: an independent N3 registry contract, Oracle-only request types for bind and ticket issuance, Web3Auth-backed identity verification inside the TEE, and a public W3C DID resolver for service discovery without disclosing private identity material.

Privacy boundary: DID resolution is intentionally public and minimal. It exposes service metadata, verifier material, contract anchors, and routing hints. It does not expose provider UIDs, raw Web3Auth claims, master nullifiers, action nullifiers, encrypted params, or ticket payloads.

Core Model

  • Master Nullifier: binds a private Web2 identity to a Neo vault account without disclosing the provider UID on-chain.
  • Action Nullifier: derives a task-specific nullifier so the same person can act through disposable accounts without global linkability.
  • Oracle-Only Execution: production binds, action tickets, and recovery tickets enter through MorpheusOracle.request(...) and come back through callbacks.
  • Public DID Layer: a resolver exposes the W3C DID document for the service namespace and subject namespaces, while keeping private claims private.

Contracts And Domains

  • MorpheusOracle: 0x017520f068fd602082fe5572596185e62a4ad991 via oracle.morpheus.neo
  • NeoDIDRegistry: 0xb81f31ea81e279793b30411b82c2e82078b63105 via neodid.morpheus.neo
  • AbstractAccount: 0x9742b4ed62a84a886f404d36149da6147528ee33 via smartwallet.neo

Network anchors are intentionally explicit:

  • Mainnet Oracle: 0x017520f068fd602082fe5572596185e62a4ad991 via oracle.morpheus.neo
  • Mainnet NeoDIDRegistry: 0xb81f31ea81e279793b30411b82c2e82078b63105 via neodid.morpheus.neo
  • Mainnet AA: 0x9742b4ed62a84a886f404d36149da6147528ee33 via smartwallet.neo
  • Mainnet AA Web3AuthVerifier: 0xb4107cb2cb4bace0ebe15bc4842890734abe133a
  • Mainnet AA RecoveryVerifier: 0x51ef9639deb29284cc8577a7fa3fdfbc92ada7c3
  • Testnet Oracle: 0x4b882e94ed766807c4fd728768f972e13008ad52
  • Testnet AA: 0xe24d2980d17d2580ff4ee8dc5dddaa20e3caec38
  • Testnet NeoDIDRegistry: unpublished in the canonical shared registry right now

Public docs use the stable runtime label UnifiedSmartWalletV3. Historical or deployment-specific manifest-name suffixes are implementation details, not the canonical AA product name.

W3C DID Method

NeoDID now exposes a W3C-aligned DID method under did:morpheus. The currently supported Neo N3 subjects are:

  • Service DID: did:morpheus:neo_n3:service:neodid
  • Vault DID: did:morpheus:neo_n3:vault:6d0656f6dd91469db1c90cc1e574380613f43738
  • AA DID: did:morpheus:neo_n3:aa:aa-social-recovery-demo

The service DID publishes the TEE verification key as a JsonWebKey2020 verification method. Vault and AA DIDs resolve to privacy-preserving service endpoints and contract anchors, not to raw user claims.

bashResolver
curl "https://oracle.meshmini.app/mainnet/neodid/resolve?did=did%3Amorpheus%3Aneo_n3%3Aservice%3Aneodid"
jsonGET /mainnet/neodid/resolve?did=did:morpheus:neo_n3:service:neodid
{
  "didResolutionMetadata": {
    "contentType": "application/ld+json;profile=\"https://w3id.org/did-resolution\""
  },
  "didDocument": {
    "@context": [
      "https://www.w3.org/ns/did/v1",
      "https://w3id.org/security/suites/jws-2020/v1"
    ],
    "id": "did:morpheus:neo_n3:service:neodid",
    "verificationMethod": [
      {
        "id": "did:morpheus:neo_n3:service:neodid#tee-verifier",
        "type": "JsonWebKey2020"
      }
    ],
    "service": [
      {
        "id": "did:morpheus:neo_n3:service:neodid#registry",
        "type": "MorpheusNeoDIDRegistry"
      },
      {
        "id": "did:morpheus:neo_n3:service:neodid#oracle-entry",
        "type": "MorpheusOracleGateway"
      }
    ]
  },
  "didDocumentMetadata": {
    "canonicalId": "did:morpheus:neo_n3:service:neodid",
    "network": "neo_n3"
  }
}

Interactive entrypoint: NeoDID Resolver lets you resolve the full DID resolution object or the raw application/did+ld+json document directly in the browser.

Independent Contract

NeoDID remains an independent contract module anchored separately from the Oracle gateway:

csharpNeoDIDRegistry
[DisplayName("NeoDIDRegistry")]
public class NeoDIDRegistry : SmartContract
{
    public static UInt160 Admin();
    public static ECPoint Verifier();
    public static void SetAdmin(UInt160 newAdmin);
    public static void SetVerifier(ECPoint publicKey);
    public static void RegisterBinding(UInt160 vaultAccount, string provider, string claimType, string claimValue, ByteString masterNullifier, ByteString metadataHash, ByteString verificationSignature);
    public static void RevokeBinding(UInt160 vaultAccount, string provider, string claimType);
    public static BindingRecord GetBinding(UInt160 vaultAccount, string provider, string claimType);
    public static bool IsMasterNullifierUsed(ByteString masterNullifier);
    public static bool IsActionNullifierUsed(ByteString actionNullifier);
    public static bool UseActionTicket(UInt160 disposableAccount, string actionId, ByteString actionNullifier, ByteString verificationSignature);
}

Worker Routes

  • GET /api/neodid/providers
  • GET /api/neodid/runtime
  • GET /api/neodid/resolve?did=...
  • POST /api/neodid/bind
  • POST /api/neodid/action-ticket
  • POST /api/neodid/recovery-ticket

Oracle Request Types

Preferred production usage is on-chain, not direct worker invocation. These request types go through MorpheusOracle.request(...) and are fulfilled asynchronously:

  • neodid_bind
  • neodid_action_ticket
  • neodid_recovery_ticket

Supported Identity Sources

  • web3auth with alias w3a
  • twitter
  • github
  • google
  • discord
  • telegram
  • binance
  • okx with alias okex
  • email

Recommended AA integration: treat web3auth as the DID root. Link Google / Apple / email / SMS / other social providers inside Web3Auth first, then pass the resulting id_token to NeoDID. The TEE verifies the JWT against the configured JWKS, derives the stable provider root internally, and emits a ticket that AA verifiers can consume without knowing the underlying login method.

Web3Auth-In-TEE Path

The TEE now verifies the Web3Auth JWT directly against the configured JWKS and audience. This means the worker derives provider_uid inside the enclave instead of trusting a user-supplied identifier.

jsonPOST /api/neodid/bind (provider = web3auth)
{
  "vault_account": "0x6d0656f6dd91469db1c90cc1e574380613f43738",
  "provider": "web3auth",
  "id_token": "<web3auth jwt>",
  "claim_type": "Web3Auth_PrimaryIdentity",
  "claim_value": "linked_social_root"
}

Large JWT Oracle Flow

Large Web3Auth JWTs should be sealed locally, stored as ciphertext, and referenced on-chain with encrypted_params_refso the Oracle notification payload stays short enough for Neo N3.

jsonPOST /api/confidential/store
{
  "ciphertext": "<sealed id_token patch>",
  "target_chain": "neo_n3",
  "metadata": {
    "source": "web3auth-live-studio"
  }
}
jsonOracle payload using encrypted_params_ref
{
  "vault_account": "0x6d0656f6dd91469db1c90cc1e574380613f43738",
  "provider": "web3auth",
  "claim_type": "Web3Auth_PrimaryIdentity",
  "claim_value": "linked_social_root_oracle_ref",
  "encrypted_params_ref": "<secret_ref>"
}

Action Ticket Example

jsonPOST /api/neodid/action-ticket
{
  "provider": "binance",
  "provider_uid": "binance_uid_12345",
  "disposable_account": "0x89b05cac00804648c666b47ecb1c57bc185821b7",
  "action_id": "Airdrop_Season_1"
}

AA Recovery Ticket Example

jsonPOST /api/neodid/recovery-ticket
{
  "provider": "web3auth",
  "network": "neo_n3",
  "aa_contract": "0x9742b4ed62a84a886f404d36149da6147528ee33",
  "verifier_contract": "0x51ef9639deb29284cc8577a7fa3fdfbc92ada7c3",
  "account_id": "aa-social-recovery-demo",
  "new_owner": "0x89b05cac00804648c666b47ecb1c57bc185821b7",
  "recovery_nonce": "7",
  "expires_at": "1735689600",
  "encrypted_params": "<sealed id_token / linked account patch>"
}

Third-Party Contract Pattern

csharpDApp Ticket Consumption
public static bool Vote(UInt160 disposableAccount, string actionId, ByteString actionNullifier, ByteString sgxSignature)
{
    ExecutionEngine.Assert(Runtime.CheckWitness(disposableAccount), "Unauthorized");

    bool accepted = (bool)Contract.Call(
        NeoDidRegistryHash,
        "useActionTicket",
        CallFlags.All,
        disposableAccount,
        actionId,
        actionNullifier,
        sgxSignature
    );

    ExecutionEngine.Assert(accepted, "Invalid NeoDID action ticket");
    return true;
}
CURRENT DESIGNUPDATED FOR DUAL-CVM ARCHITECTURE