# DIDRegistry

### Overview

The **DIDRegistry** contract implements a decentralized identity (DID) registry following the W3C DID specification. It enables users to register, update, and manage DID Documents on-chain with support for verification methods, services, and delegated control.

### Contract Details

| Property         | Value                          |
| ---------------- | ------------------------------ |
| Solidity Version | ^0.8.28                        |
| License          | MIT                            |
| Inheritance      | Context, EIP712 (OpenZeppelin) |
| DID Method       | `did:pkh` (Public Key Hash)    |

***

### Purpose

The DIDRegistry contract provides a complete on-chain DID management system:

* **Self-Sovereign Identity**: Users control their own DID documents
* **Delegated Control**: Authorized controllers can manage DIDs on behalf of subjects
* **Verification Methods**: Support for multiple cryptographic keys with relationship types
* **Services**: Link external services to DID documents
* **Meta-Transactions**: EIP-712 signatures enable gasless updates

***

### Data Structures

#### VerificationMethod

Represents a cryptographic key or verification method.

```solidity
struct VerificationMethod {
    string id;              // Unique identifier (e.g., "did:pkh:0x...#key-1")
    string methodType;      // Type (e.g., "EcdsaSecp256k1VerificationKey2019")
    string metadata;        // JSON string with additional properties
    address controller;     // Address that controls this method
    uint8 relationships;    // Packed bitflags for relationship types
}
```

| Field           | Type    | Description                                    |
| --------------- | ------- | ---------------------------------------------- |
| `id`            | string  | Full DID URL identifier for the method         |
| `methodType`    | string  | Cryptographic suite type                       |
| `metadata`      | string  | JSON-encoded additional properties             |
| `controller`    | address | Ethereum address of the controller             |
| `relationships` | uint8   | Bitflags indicating verification relationships |

#### Service

Represents an external service endpoint.

```solidity
struct Service {
    string id;              // Service identifier
    string serviceType;     // Type of service (e.g., "LinkedDomains")
    string serviceEndpoint; // URL or endpoint reference
}
```

| Field             | Type   | Description                     |
| ----------------- | ------ | ------------------------------- |
| `id`              | string | Unique service identifier       |
| `serviceType`     | string | Type classification             |
| `serviceEndpoint` | string | URL, URI, or JSON endpoint data |

#### DIDDocument

The complete DID Document structure.

```solidity
struct DIDDocument {
    string[] context;                       // JSON-LD @context values
    address[] controllers;                  // Addresses authorized to update
    string[] alsoKnownAs;                   // Alternative identifiers
    VerificationMethod[] verificationMethods; // Cryptographic methods
    Service[] services;                     // Service endpoints
    uint256 nonce;                          // Replay protection counter
}
```

| Field                 | Type                  | Description                           |
| --------------------- | --------------------- | ------------------------------------- |
| `context`             | string\[]             | JSON-LD context URIs                  |
| `controllers`         | address\[]            | Delegated controller addresses        |
| `alsoKnownAs`         | string\[]             | Alias identifiers                     |
| `verificationMethods` | VerificationMethod\[] | Array of verification methods         |
| `services`            | Service\[]            | Array of service endpoints            |
| `nonce`               | uint256               | Auto-incrementing counter for updates |

***

### Verification Relationships

Verification relationships are stored as packed bitflags in a `uint8`:

| Constant                | Value | Hex  | Description                                    |
| ----------------------- | ----- | ---- | ---------------------------------------------- |
| `AUTHENTICATION`        | 1     | 0x01 | Key can authenticate as the DID subject        |
| `ASSERTION`             | 2     | 0x02 | Key can make assertions/claims                 |
| `KEY_AGREEMENT`         | 4     | 0x04 | Key can be used for key agreement (encryption) |
| `CAPABILITY_INVOCATION` | 8     | 0x08 | Key can invoke capabilities                    |
| `CAPABILITY_DELEGATION` | 16    | 0x10 | Key can delegate capabilities                  |

#### Examples

| Relationships              | Value | Binary |
| -------------------------- | ----- | ------ |
| Authentication only        | 1     | 00001  |
| Authentication + Assertion | 3     | 00011  |
| Key Agreement only         | 4     | 00100  |
| All relationships          | 31    | 11111  |

***

### State Variables

| Variable                | Type                            | Visibility      | Description                        |
| ----------------------- | ------------------------------- | --------------- | ---------------------------------- |
| `DID_TYPE`              | string                          | public constant | DID method type (`"did:pkh"`)      |
| `AUTHENTICATION`        | uint8                           | public constant | Authentication bitflag (1)         |
| `ASSERTION`             | uint8                           | public constant | Assertion bitflag (2)              |
| `KEY_AGREEMENT`         | uint8                           | public constant | Key Agreement bitflag (4)          |
| `CAPABILITY_INVOCATION` | uint8                           | public constant | Capability Invocation bitflag (8)  |
| `CAPABILITY_DELEGATION` | uint8                           | public constant | Capability Delegation bitflag (16) |
| `didDocuments`          | mapping(address => DIDDocument) | internal        | DID document storage               |

***

### Functions

#### Constructor

```solidity
constructor(string memory name, string memory version)
```

Initializes the contract with EIP-712 domain parameters.

**Parameters:**

| Name      | Type   | Description                       |
| --------- | ------ | --------------------------------- |
| `name`    | string | Domain name (e.g., "DIDRegistry") |
| `version` | string | Domain version (e.g., "1")        |

***

#### Registration Functions

**registerDID**

```solidity
function registerDID(DIDDocument calldata _didDocument) external
```

Registers a new DID document for `msg.sender`.

**Parameters:**

| Name           | Type        | Description                  |
| -------------- | ----------- | ---------------------------- |
| `_didDocument` | DIDDocument | The DID document to register |

**Requirements:**

* Subject must not already have a registered DID (nonce == 0)
* Context array must not be empty

**Emits:** `DIDRegistered`

***

**registerDIDOf**

```solidity
function registerDIDOf(
    DIDDocument calldata _didDocument,
    address _didSubject,
    bytes calldata _subjectSignature
) external
```

Registers a DID on behalf of another address using their signature.

**Parameters:**

| Name                | Type        | Description                         |
| ------------------- | ----------- | ----------------------------------- |
| `_didDocument`      | DIDDocument | The DID document to register        |
| `_didSubject`       | address     | The address to register the DID for |
| `_subjectSignature` | bytes       | EIP-712 signature from the subject  |

**Requirements:**

* Signature must be from the `_didSubject`
* Subject must not already have a registered DID

**Emits:** `DIDRegistered`

***

#### Update Functions

**updateDID**

```solidity
function updateDID(
    DIDDocument calldata _didDocument,
    address _didSubject
) external
```

Updates an existing DID document.

**Parameters:**

| Name           | Type        | Description                     |
| -------------- | ----------- | ------------------------------- |
| `_didDocument` | DIDDocument | The updated DID document        |
| `_didSubject`  | address     | The address whose DID to update |

**Requirements:**

* Caller must be the subject OR a listed controller
* Provided nonce must match current stored nonce
* Context array must not be empty

**Emits:** `DIDUpdated`

***

**updateDIDOf**

```solidity
function updateDIDOf(
    DIDDocument calldata _didDocument,
    address _didSubject,
    bytes calldata _signature
) external
```

Updates a DID document using a signature from an authorized party.

**Parameters:**

| Name           | Type        | Description                                  |
| -------------- | ----------- | -------------------------------------------- |
| `_didDocument` | DIDDocument | The updated DID document                     |
| `_didSubject`  | address     | The address whose DID to update              |
| `_signature`   | bytes       | EIP-712 signature from subject or controller |

**Requirements:**

* Signer must be the subject OR a listed controller
* Provided nonce must match current stored nonce

**Emits:** `DIDUpdated`

***

#### Query Functions

**getDIDDocument**

```solidity
function getDIDDocument(address subject) public view returns (DIDDocument memory)
```

Retrieves the DID document for a given address.

**Parameters:**

| Name      | Type    | Description             |
| --------- | ------- | ----------------------- |
| `subject` | address | The DID subject address |

**Returns:** `DIDDocument` struct (empty if not registered)

***

**isController**

```solidity
function isController(address subject, address account) public view returns (bool)
```

Checks if an address is a controller for a DID.

**Parameters:**

| Name      | Type    | Description             |
| --------- | ------- | ----------------------- |
| `subject` | address | The DID subject address |
| `account` | address | The address to check    |

**Returns:** `true` if account is listed as a controller

***

#### Relationship Helper Functions

**hasRelationship**

```solidity
function hasRelationship(uint8 relationships, uint8 flag) public pure returns (bool)
```

Checks if a relationship flag is set.

**Parameters:**

| Name            | Type  | Description                                |
| --------------- | ----- | ------------------------------------------ |
| `relationships` | uint8 | The packed relationship bitflags           |
| `flag`          | uint8 | The flag to check (e.g., `AUTHENTICATION`) |

**Returns:** `true` if the flag is set

**Example:**

```solidity
// Check if key has authentication relationship
bool canAuth = registry.hasRelationship(vm.relationships, registry.AUTHENTICATION());
```

***

**addRelationship**

```solidity
function addRelationship(uint8 relationships, uint8 flag) public pure returns (uint8)
```

Adds a relationship flag.

**Parameters:**

| Name            | Type  | Description                   |
| --------------- | ----- | ----------------------------- |
| `relationships` | uint8 | Current relationship bitflags |
| `flag`          | uint8 | The flag to add               |

**Returns:** Updated relationship bitflags

***

**removeRelationship**

```solidity
function removeRelationship(uint8 relationships, uint8 flag) public pure returns (uint8)
```

Removes a relationship flag.

**Parameters:**

| Name            | Type  | Description                   |
| --------------- | ----- | ----------------------------- |
| `relationships` | uint8 | Current relationship bitflags |
| `flag`          | uint8 | The flag to remove            |

**Returns:** Updated relationship bitflags

***

#### Hashing Functions

**hashDIDDocument**

```solidity
function hashDIDDocument(DIDDocument calldata doc) public pure returns (bytes32)
```

Computes the EIP-712 struct hash for a DID document.

**getTypedDataHash**

```solidity
function getTypedDataHash(DIDDocument calldata doc) public view returns (bytes32)
```

Returns the complete EIP-712 typed data hash for signing.

***

### Events

#### DIDRegistered

```solidity
event DIDRegistered(address indexed subject);
```

Emitted when a new DID document is registered.

#### DIDUpdated

```solidity
event DIDUpdated(
    address indexed subject,
    address indexed updatedBy,
    uint256 nonce
);
```

Emitted when a DID document is updated.

| Parameter   | Description                           |
| ----------- | ------------------------------------- |
| `subject`   | The DID subject address               |
| `updatedBy` | The address that performed the update |
| `nonce`     | The new nonce value after update      |

***

### Errors

| Error                  | Description                               |
| ---------------------- | ----------------------------------------- |
| `InvalidContext()`     | Context array is empty                    |
| `InvalidControllers()` | Controllers array validation failed       |
| `InvalidSignature()`   | Signature verification failed             |
| `InvalidNonce()`       | Provided nonce doesn't match stored nonce |
| `Unauthorized()`       | Caller is not authorized to update        |
| `AlreadyRegistered()`  | DID already registered for this address   |

***

### EIP-712 Type Definitions

#### VerificationMethod

```javascript
const VerificationMethod = [
  { name: "id", type: "string" },
  { name: "methodType", type: "string" },
  { name: "controller", type: "address" },
  { name: "metadata", type: "string" },
  { name: "relationships", type: "uint8" },
];
```

#### Service

```javascript
const Service = [
  { name: "id", type: "string" },
  { name: "serviceType", type: "string" },
  { name: "serviceEndpoint", type: "string" },
];
```

#### DIDDocument

```javascript
const DIDDocument = [
  { name: "context", type: "string[]" },
  { name: "controllers", type: "address[]" },
  { name: "alsoKnownAs", type: "string[]" },
  { name: "verificationMethods", type: "VerificationMethod[]" },
  { name: "services", type: "Service[]" },
  { name: "nonce", type: "uint256" },
];
```

***

### Usage Examples

#### Registering a DID Document (TypeScript/Viem)

```typescript
// Helper function to calculate relationships
function calculateRelationships(flags: {
  authentication?: boolean;
  assertion?: boolean;
  keyAgreement?: boolean;
  capabilityInvocation?: boolean;
  capabilityDelegation?: boolean;
}): number {
  let relationships = 0;
  if (flags.authentication) relationships |= 1;
  if (flags.assertion) relationships |= 2;
  if (flags.keyAgreement) relationships |= 4;
  if (flags.capabilityInvocation) relationships |= 8;
  if (flags.capabilityDelegation) relationships |= 16;
  return relationships;
}

// Prepare DID document
const userAddress = userWallet.account.address;
const did = `did:pkh:${userAddress}`;

const didDocument = {
  context: ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/secp256k1-2019/v1"],
  controllers: [], // No delegated controllers
  alsoKnownAs: [],
  verificationMethods: [
    {
      id: `${did}#key-1`,
      methodType: "EcdsaSecp256k1VerificationKey2019",
      controller: userAddress,
      metadata: JSON.stringify({ blockchainAccountId: `eip155:1:${userAddress}` }),
      relationships: calculateRelationships({
        authentication: true,
        assertion: true,
      }),
    },
  ],
  services: [
    {
      id: `${did}#profile`,
      serviceType: "LinkedDomains",
      serviceEndpoint: "https://example.com",
    },
  ],
  nonce: 0n,
};

// Register DID
await registry.write.registerDID([didDocument], { account: userWallet.account });
```

#### Registering on Behalf of Another User

```typescript
const typedData = {
  domain: {
    name: "DIDRegistry",
    version: "1",
    chainId: 1n,
    verifyingContract: registryAddress,
  },
  types: {
    VerificationMethod: [
      { name: "id", type: "string" },
      { name: "methodType", type: "string" },
      { name: "controller", type: "address" },
      { name: "metadata", type: "string" },
      { name: "relationships", type: "uint8" },
    ],
    Service: [
      { name: "id", type: "string" },
      { name: "serviceType", type: "string" },
      { name: "serviceEndpoint", type: "string" },
    ],
    DIDDocument: [
      { name: "context", type: "string[]" },
      { name: "controllers", type: "address[]" },
      { name: "alsoKnownAs", type: "string[]" },
      { name: "verificationMethods", type: "VerificationMethod[]" },
      { name: "services", type: "Service[]" },
      { name: "nonce", type: "uint256" },
    ],
  },
  primaryType: "DIDDocument",
  message: didDocument,
};

// Subject signs the document
const signature = await subjectWallet.signTypedData(typedData);

// Relayer submits the transaction
await registry.write.registerDIDOf([didDocument, subjectAddress, signature], {
  account: relayerWallet.account,
});
```

#### Updating with Delegated Controller

```typescript
// Original registration includes controller
const docWithController = {
  ...didDocument,
  controllers: [controllerAddress],
};
await registry.write.registerDID([docWithController], { account: userWallet.account });

// Controller can now update (nonce is 1 after registration)
const updatedDoc = {
  ...didDocument,
  context: ["https://www.w3.org/ns/did/v1", "https://example.com/custom-context"],
  controllers: [controllerAddress],
  nonce: 1n, // Must match current nonce
};

await registry.write.updateDID([updatedDoc, userAddress], {
  account: controllerWallet.account,
});
```

#### Reading and Decoding a DID Document

```typescript
const storedDoc = await registry.read.getDIDDocument([userAddress]);

// Check if registered
const isRegistered = storedDoc.nonce > 0n;

// Parse verification method metadata
const vm = storedDoc.verificationMethods[0];
const metadata = JSON.parse(vm.metadata);

// Check relationships
const AUTHENTICATION = 1;
const ASSERTION = 2;
const hasAuth = (vm.relationships & AUTHENTICATION) !== 0;
const hasAssertion = (vm.relationships & ASSERTION) !== 0;
```

***

### W3C DID Document Mapping

The contract's structures map to W3C DID Document format:

| W3C Property           | Contract Field          | Notes                            |
| ---------------------- | ----------------------- | -------------------------------- |
| `@context`             | `context`               | Array of context URIs            |
| `id`                   | N/A (mapping key)       | Derived from `did:pkh:{address}` |
| `controller`           | `controllers`           | Array of controller addresses    |
| `alsoKnownAs`          | `alsoKnownAs`           | Alternative identifiers          |
| `verificationMethod`   | `verificationMethods`   | Array of methods                 |
| `authentication`       | `relationships` bitflag | Derived from bitflag             |
| `assertionMethod`      | `relationships` bitflag | Derived from bitflag             |
| `keyAgreement`         | `relationships` bitflag | Derived from bitflag             |
| `capabilityInvocation` | `relationships` bitflag | Derived from bitflag             |
| `capabilityDelegation` | `relationships` bitflag | Derived from bitflag             |
| `service`              | `services`              | Array of services                |

#### Example: JSON-LD to Solidity Encoding

**W3C JSON-LD Format:**

```json
{
  "@context": ["https://www.w3.org/ns/did/v1"],
  "id": "did:pkh:0x1234...",
  "verificationMethod": [
    {
      "id": "did:pkh:0x1234...#key-1",
      "type": "EcdsaSecp256k1VerificationKey2019",
      "controller": "did:pkh:0x1234...",
      "blockchainAccountId": "eip155:1:0x1234..."
    }
  ],
  "authentication": ["did:pkh:0x1234...#key-1"],
  "assertionMethod": ["did:pkh:0x1234...#key-1"]
}
```

**Solidity Struct Encoding:**

```typescript
{
  context: ["https://www.w3.org/ns/did/v1"],
  controllers: [],
  alsoKnownAs: [],
  verificationMethods: [{
    id: "did:pkh:0x1234...#key-1",
    methodType: "EcdsaSecp256k1VerificationKey2019",
    controller: "0x1234...",
    metadata: '{"blockchainAccountId":"eip155:1:0x1234..."}',
    relationships: 3  // AUTHENTICATION (1) | ASSERTION (2)
  }],
  services: [],
  nonce: 0n
}
```

***

### Security Considerations

1. **Nonce Protection**: Each update increments the nonce, preventing replay attacks
2. **Controller Authorization**: Only subject or listed controllers can update
3. **Signature Verification**: Meta-transactions verified via EIP-712 ECDSA recovery
4. **Context Validation**: Empty context arrays are rejected
5. **Immutable Registration**: DID cannot be re-registered once created

***

### Related Contracts

* **Agreement**: Uses DIDs for party identification
* **Consent**: Links consent records to DID-identified suppliers

***

### Deployment

The contract is deployed using Hardhat Ignition:

```typescript
// ignition/modules/DIDRegistry.ts
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

const DOMAIN_NAME = "DIDRegistry";
const DOMAIN_VERSION = "1";

export default buildModule("DIDRegistryModule", (m) => {
  const didRegistry = m.contract("DIDRegistry", [DOMAIN_NAME, DOMAIN_VERSION]);

  return { didRegistry };
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.permission.ai/permission-protocol/v1-protocol/contracts/didregistry.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
