# V1 to V2 Migration Guide

This guide helps you migrate from Self Protocol V1 to V2. V2 introduces multi-document support, dynamic configuration, and improved data structures.

## Overview of Changes

### What's New in V2

* **Multi-document support**: E-Passports, EU ID Cards, Aadhaar, and KYC
* **Dynamic configuration**: Switch configurations without redeployment
* **Enhanced data formats**: Pre-extracted, human-readable outputs
* **User context data**: Pass custom data through verification flow
* **Improved error handling**: Detailed configuration mismatch reporting

### Breaking Changes

* Backend SDK constructor requires new parameters
* Configuration methods replaced with interface
* Verification method signature changed
* Frontend requires disclosures object
* Smart contract interfaces updated

## Backend Migration

### 1. Update Dependencies

```bash
npm install @selfxyz/core@latest
```

### 2. Update Constructor

**V1 (Old):**

```javascript
const verifier = new SelfBackendVerifier(
  "my-app-scope",
  "https://api.example.com/verify",
  false  // mock mode
);
```

**V2 (New):**

```javascript
import { SelfBackendVerifier, AttestationId, UserIdType, IConfigStorage, AllIds } from '@selfxyz/core';

// Option 1: Use AllIds for all document types (recommended for most cases)
const allowedIds = AllIds;

// Option 2: Define specific allowed document types
// const allowedIds = new Map();
// allowedIds.set(AttestationId.E_PASSPORT, true);  // Accept passports
// allowedIds.set(AttestationId.EU_ID_CARD, true);  // Accept EU ID cards
// allowedIds.set(AttestationId.AADHAAR, true);  // Accept Aadhaar
// allowedIds.set(AttestationId.KYC, true);  // Accept KYC

// Implement configuration storage
class ConfigStorage implements IConfigStorage {
  async getConfig(configId: string) {
    // Return your verification requirements
    return {
      minimumAge: 18,
      excludedCountries: ['IRN', 'PRK'],
      ofac: true
    };
  }
  
  async getActionId(userIdentifier: string, userDefinedData?: string) {
    // Return config ID based on your logic
    return 'default_config';
  }
}

const verifier = new SelfBackendVerifier(
  "my-app-scope",
  "https://api.example.com/verify",
  false,                        // mock mode
  allowedIds,                   // NEW: allowed document types
  new ConfigStorage(),          // NEW: config storage
  UserIdType.UUID               // NEW: user ID type
);
```

### 3. Update Configuration

**V1 (Old):**

```javascript
// Direct method calls
verifier.setMinimumAge(18);
verifier.excludeCountries('Iran', 'North Korea');
verifier.enablePassportNoOfacCheck();
verifier.enableNameOfacCheck();
verifier.enableDobOfacCheck();
```

**V2 (New):**

```javascript
// Configuration via IConfigStorage implementation
class ConfigStorage implements IConfigStorage {
  async getConfig(configId: string) {
    // All configuration in one place
    return {
      minimumAge: 18,
      excludedCountries: ['IRN', 'PRK'],  // Use ISO 3-letter codes
      ofac: true  // Single boolean for all OFAC checks
    };
  }
}
```

### 4. Update Verification Method

**V1 (Old):**

```javascript
app.post('/api/verify', async (req, res) => {
  const { proof, publicSignals } = req.body;
  
  try {
    const isValid = await verifier.verify(proof, publicSignals);
    res.json({ valid: isValid });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});
```

**V2 (New):**

```javascript
app.post('/api/verify', async (req, res) => {
  const { attestationId, proof, pubSignals, userContextData } = req.body;
  
  try {
    const result = await verifier.verify(
      attestationId,      // NEW: 1 passport, 2 EU ID, 3 Aadhaar, 4 KYC
      proof,
      pubSignals,
      userContextData     // NEW: hex-encoded context data
    );
    
    if (result.isValidDetails.isValid) {
      res.json({
        status: 'success',
        result: true,
        credentialSubject: result.discloseOutput,
        documentType:
          attestationId === 1 ? 'passport' :
          attestationId === 2 ? 'eu_id_card' :
          attestationId === 3 ? 'aadhaar' :
          'kyc'
      });
    } else {
      res.status(400).json({
        status: 'error',
        result: false,
        details: result.isValidDetails
      });
    }
  } catch (error) {
    if (error.name === 'ConfigMismatchError') {
      res.status(400).json({
        status: 'error',
        message: 'Configuration mismatch',
        issues: error.issues
      });
    } else {
      res.status(500).json({ error: error.message });
    }
  }
});
```

### 5. Handle New Response Format

**V1 Response:**

```javascript
// Simple boolean
true/false
```

**V2 Response:**

```javascript
{
  attestationId: 1,              // Document type
  isValidDetails: {
    isValid: boolean,            // Overall result
    isOlderThanValid: boolean,   // Age check result
    isOfacValid: boolean         // OFAC check result
  },
  forbiddenCountriesList: [],    // Excluded countries
  discloseOutput: {              // Pre-extracted data
    nationality: "USA",
    minimumAge: "21",
    name: ["JOHN", "DOE"],
    dateOfBirth: "01-01-1990",
    issuingState: "USA",
    idNumber: "123456789",
    gender: "M",
    expiryDate: "01-01-2030",
    ofac: [true, true, true]
  },
  userData: {
    userIdentifier: "uuid-here",
    userDefinedData: "custom-data"
  }
}
```

## Frontend Migration

### 1. Update Dependencies

```bash
npm install @selfxyz/qrcode@latest
```

### 2. Update QR Code Configuration

**V1 (Old):**

```javascript
import { SelfAppBuilder } from '@selfxyz/qrcode';

const selfApp = new SelfAppBuilder({
  appName: "My App",
  scope: "my-app-scope",
  endpoint: "https://api.example.com/verify",
  userId: userId,
  logoBase64: logo
}).build();
```

**V2 (New):**

```javascript
import { SelfAppBuilder } from '@selfxyz/qrcode';

const selfApp = new SelfAppBuilder({
  appName: "My App",
  scope: "my-app-scope",
  endpoint: "https://api.example.com/verify",
  userId: userId,
  logoBase64: logo,
  version: 2,                          // NEW: Specify V2
  userDefinedData: "custom-data",      // NEW: Optional custom data
  disclosures: {                       // NEW: Must match backend config
    // Verification rules
    minimumAge: 18,
    excludedCountries: ['IRN', 'PRK'],
    ofac: true,
    // Data fields to reveal
    name: true,
    nationality: true,
    dateOfBirth: true
  }
}).build();
```

### 3. Important: Disclosures Object

The `disclosures` object in V2 contains **both** verification rules and data fields:

```javascript
disclosures: {
  // Verification rules (must match backend exactly)
  minimumAge: 18,                      // Age requirement
  excludedCountries: ['IRN', 'PRK'],   // ISO 3-letter codes
  ofac: true,                          // OFAC checking
  
  // Data fields to reveal
  name: true,                          // Full name
  nationality: true,                   // Nationality
  dateOfBirth: true,                   // Date of birth
  issuingState: true,                  // Issuing country
  idNumber: true,                      // Document number
  gender: true,                        // Gender
  expiryDate: true                     // Expiration date
}
```

## Smart Contract Migration

### 1. Update Contract Inheritance

**V1 (Old):**

```solidity
import { IIdentityVerificationHub } from "@selfxyz/contracts/interfaces/IIdentityVerificationHub.sol";

contract MyContract {
    IIdentityVerificationHub public hub;
    
    function verify(/* params */) external {
        // Direct hub integration
    }
}
```

**V2 (New):**

```solidity
import { SelfVerificationRoot } from "@selfxyz/contracts/abstract/SelfVerificationRoot.sol";

contract MyContract is SelfVerificationRoot {
    constructor(
        address _hub,
        bytes32 _scope
    ) SelfVerificationRoot(_hub, _scope) {}
    
    // Override to handle verification results
    function customVerificationHook(
        GenericDiscloseOutputV2 memory output,
        bytes memory userData
    ) internal override {
        // Your business logic here
    }
    
    // Override to provide configuration ID
    function getConfigId(
        bytes32 destinationChainId,
        bytes32 userIdentifier,
        bytes memory userDefinedData
    ) public view override returns (bytes32) {
        // Generate your config ID at https://tools.self.xyz/
        // Default config ID: 0x7b6436b0c98f62380866d9432c2af0ee08ce16a171bda6951aecd95ee1307d61
        return 0x7b6436b0c98f62380866d9432c2af0ee08ce16a171bda6951aecd95ee1307d61;
    }
}
```

### 2. Update Hub Addresses

**V2 Hub Addresses:**

```solidity
// Celo Mainnet
address constant HUB_V2 = 0xe57F4773bd9c9d8b6Cd70431117d353298B9f5BF;

// Celo Sepolia Testnet
address constant HUB_V2_STAGING = 0x16ECBA51e18a4a7e61fdC417f0d47AFEeDfbed74;
```

### 3. Handle New Data Structure

**V1 Structure:**

```solidity
struct VcAndDiscloseVerificationResult {
    uint256 attestationId;
    uint256 scope;
    uint256 userIdentifier;
    uint256 nullifier;
    uint256[3] revealedDataPacked;   // Packed data
}
```

**V2 Structure:**

```solidity
struct GenericDiscloseOutputV2 {
    bytes32 attestationId;           // Now bytes32
    uint256 userIdentifier;
    uint256 nullifier;
    string issuingState;             // Pre-extracted
    string[] name;                   // Pre-extracted array
    string idNumber;                 // Renamed from passportNumber
    string nationality;              // Pre-extracted
    string dateOfBirth;              // Pre-extracted format
    string gender;                   // Pre-extracted
    string expiryDate;               // Pre-extracted
    uint256 minimumAge;
    bool[3] ofac;                    // Bool array
    uint256[4] forbiddenCountriesListPacked;
}
```

## Common Migration Issues

### 1. Configuration Mismatch

**Problem:** Frontend disclosures don't match backend configuration

```
ConfigMismatchError: Configuration mismatch
```

**Solution:** Ensure frontend and backend have identical settings:

```javascript
// Frontend
disclosures: {
  minimumAge: 18,
  excludedCountries: ['IRN', 'PRK'],
  ofac: true
}

// Backend (in getConfig)
return {
  minimumAge: 18,
  excludedCountries: ['IRN', 'PRK'],
  ofac: true
};
```

### 2. Missing Attestation ID

**Problem:** Verification fails with missing attestation ID

**Solution:** Frontend must send attestation ID:

```javascript
const requestBody = {
  attestationId: 1,  // 1 passport, 2 EU ID, 3 Aadhaar, 4 KYC
  proof: proof,
  pubSignals: pubSignals,
  userContextData: userContextData
};
```

### 3. Invalid User Context Data

**Problem:** User context data validation fails

**Solution:** Ensure proper hex encoding:

```javascript
// Create user context data (256 bytes total)
const userContextData = '0x' + '0'.repeat(512);  // 512 hex chars = 256 bytes
```

### 4. Document Type Not Allowed

**Problem:** "Attestation ID is not allowed" error

**Solution:** Add document type to allowedIds:

```javascript
// Option 1: Use AllIds for all document types
const allowedIds = AllIds;

// Option 2: Define specific allowed document types
// const allowedIds = new Map();
// allowedIds.set(AttestationId.E_PASSPORT, true);  // Add passport
// allowedIds.set(AttestationId.EU_ID_CARD, true);  // Add EU ID card
// allowedIds.set(AttestationId.AADHAAR, true);  // Add Aadhaar
// allowedIds.set(AttestationId.KYC, true);  // Add KYC
```

## Testing Your Migration

### 1. Test with Mock Passports

```javascript
// Use staging/testnet for development
const verifier = new SelfBackendVerifier(
  "test-scope",
  "https://test.ngrok.app/verify",
  true,  // Enable mock mode
  allowedIds,
  configStorage,
  UserIdType.UUID
);

// Disable OFAC for mock passports
class ConfigStorage {
  async getConfig() {
    return {
      minimumAge: 18,
      ofac: false  // Must be false for mock passports
    };
  }
}
```

### 2. Test Multiple Document Types

```javascript
// Test passport verification
const passportResult = await verifier.verify(
  AttestationId.E_PASSPORT,  // 1
  passportProof,
  passportSignals,
  userContextData
);

// Test EU ID card verification
const idCardResult = await verifier.verify(
  AttestationId.EU_ID_CARD,  // 2
  idCardProof,
  idCardSignals,
  userContextData
);

// Test KYC verification
const kycResult = await verifier.verify(
  AttestationId.KYC,  // 4
  kycProof,
  kycSignals,
  userContextData
);
```

### 3. Verify Configuration Switching

```javascript
class DynamicConfigStorage {
  async getConfig(configId: string) {
    switch(configId) {
      case 'strict':
        return { minimumAge: 21, ofac: true };
      case 'relaxed':
        return { minimumAge: 18, ofac: false };
      default:
        return { minimumAge: 18, ofac: true };
    }
  }
  
  async getActionId(userIdentifier: string, userDefinedData?: string) {
    // Select config based on user data
    return userDefinedData === 'premium' ? 'strict' : 'relaxed';
  }
}
```

## Best Practices

### 1. Configuration Management

* Store configurations in a database for easy updates
* Version your configurations for rollback capability
* Use meaningful config IDs (not just hashes)
* Document configuration requirements

### 2. Error Handling

```javascript
try {
  const result = await verifier.verify(/* params */);
} catch (error) {
  if (error.name === 'ConfigMismatchError') {
    // Log detailed issues for debugging
    console.error('Config issues:', error.issues);
    // Return user-friendly message
    return { error: 'Verification configuration error' };
  }
  // Handle other errors
}
```

### 3. Security Considerations

* Always validate attestation IDs
* Store and check nullifiers to prevent replay
* Use appropriate scopes for different use cases
* Never expose configuration details to frontend

### 4. Performance Optimization

* Cache configuration objects
* Reuse verifier instances
* Batch verification requests when possible
* Use connection pooling for RPC calls

## Resources

* [Quickstart Guide](https://docs.self.xyz/self-pass/quickstart) - Basic V2 setup
* [Basic Integration](https://docs.self.xyz/self-pass/basic-integration-1) - Contract examples
* [Workshop Example](https://github.com/selfxyz/self-integration-boilerplate) - Simple implementation

## Need Help?

If you encounter issues during migration:

1. Review example implementations
2. Report issues at [GitHub Issues](https://github.com/selfxyz/self/issues)


---

# 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.self.xyz/self-pass/migration-v1-v2.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.
