Skip to main content

Overview

The ZKScore JavaScript SDK provides comprehensive identity management capabilities, including minting, activating, and resolving ZKS identities. The SDK handles both ZKS IDs (like alice.zks) and wallet addresses seamlessly.
All identity operations support both ZKS IDs and wallet addresses. The SDK automatically resolves ZKS IDs to their corresponding wallet addresses when needed.

Identity Operations

1. Check Name Availability

// Check if a ZKS ID is available
async function checkAvailability() {
  try {
    const result = await sdk.identity.checkAvailability('alice.zks');
    
    if (result.available) {
      console.log('✅ Name is available');
    } else {
      console.log('❌ Name is taken');
      console.log('Suggestions:', result.suggestions);
    }
    
    return result;
  } catch (error) {
    console.error('Error checking availability:', error.message);
    throw error;
  }
}

// Usage
const availability = await checkAvailability();

2. Mint New Identity

// Mint a new ZKS identity
async function mintIdentity() {
  try {
    const identity = await sdk.identity.mint({
      name: 'alice.zks',
      walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      metadata: {
        bio: 'Web3 developer and DeFi enthusiast',
        avatar: 'https://example.com/avatar.png',
        social: {
          twitter: '@alice',
          github: 'alice-dev'
        }
      }
    });
    
    console.log('✅ Identity minted successfully');
    console.log('Token ID:', identity.tokenId);
    console.log('Name:', identity.name);
    console.log('Owner:', identity.ownerAddress);
    
    return identity;
  } catch (error) {
    if (error.code === 'NAME_TAKEN') {
      console.error('❌ Name is already taken');
    } else if (error.code === 'INVALID_NAME') {
      console.error('❌ Invalid name format');
    } else {
      console.error('❌ Error minting identity:', error.message);
    }
    throw error;
  }
}

// Usage
const newIdentity = await mintIdentity();

3. Activate Identity

// Activate an identity (make it soulbound)
async function activateIdentity(tokenId, signature) {
  try {
    const result = await sdk.identity.activate(tokenId, {
      signature: signature,
      metadata: {
        activationReason: 'Making identity soulbound',
        timestamp: new Date().toISOString()
      }
    });
    
    console.log('✅ Identity activated successfully');
    console.log('Activated at:', result.activatedAt);
    console.log('Is soulbound:', result.isSoulbound);
    
    return result;
  } catch (error) {
    if (error.code === 'ALREADY_ACTIVATED') {
      console.error('❌ Identity is already activated');
    } else if (error.code === 'INVALID_SIGNATURE') {
      console.error('❌ Invalid signature');
    } else {
      console.error('❌ Error activating identity:', error.message);
    }
    throw error;
  }
}

// Usage with wallet signature
const signature = await wallet.signMessage('Activate identity: alice.zks');
const activation = await activateIdentity(newIdentity.tokenId, signature);

4. Get Identity Information

// Get identity by ZKS ID or wallet address
async function getIdentity(identity) {
  try {
    const identityData = await sdk.identity.getIdentity(identity);
    
    console.log('Identity found:');
    console.log('- Name:', identityData.name);
    console.log('- Address:', identityData.address);
    console.log('- Token ID:', identityData.tokenId);
    console.log('- Is activated:', identityData.isActivated);
    console.log('- Created at:', identityData.createdAt);
    console.log('- Metadata:', identityData.metadata);
    
    return identityData;
  } catch (error) {
    if (error.code === 'IDENTITY_NOT_FOUND') {
      console.error('❌ Identity not found');
    } else {
      console.error('❌ Error fetching identity:', error.message);
    }
    throw error;
  }
}

// Usage with ZKS ID
const identityByZksId = await getIdentity('alice.zks');

// Usage with wallet address
const identityByAddress = await getIdentity('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');

5. Resolve Identity

// Resolve ZKS ID to wallet address or vice versa
async function resolveIdentity(input) {
  try {
    const resolved = await sdk.identity.resolve(input);
    
    console.log('Resolved identity:');
    console.log('- Input:', input);
    console.log('- ZKS ID:', resolved.zksId);
    console.log('- Address:', resolved.address);
    console.log('- Is primary:', resolved.isPrimary);
    
    return resolved;
  } catch (error) {
    console.error('❌ Error resolving identity:', error.message);
    throw error;
  }
}

// Usage
const resolved = await resolveIdentity('alice.zks');
// Output: { zksId: 'alice.zks', address: '0x742d35Cc...', isPrimary: true }

Advanced Identity Operations

1. Batch Identity Operations

// Check multiple names at once
async function checkMultipleNames(names) {
  try {
    const results = await Promise.all(
      names.map(name => sdk.identity.checkAvailability(name))
    );
    
    const available = results
      .filter(result => result.available)
      .map(result => result.name);
    
    const taken = results
      .filter(result => !result.available)
      .map(result => result.name);
    
    console.log('Available names:', available);
    console.log('Taken names:', taken);
    
    return { available, taken };
  } catch (error) {
    console.error('❌ Error checking multiple names:', error.message);
    throw error;
  }
}

// Usage
const names = ['alice.zks', 'bob.zks', 'charlie.zks'];
const batchResults = await checkMultipleNames(names);
// Search for identities by criteria
async function searchIdentities(criteria) {
  try {
    const results = await sdk.identity.search({
      query: criteria.query,
      limit: criteria.limit || 10,
      offset: criteria.offset || 0,
      filters: {
        isActivated: criteria.isActivated,
        createdAfter: criteria.createdAfter,
        createdBefore: criteria.createdBefore
      }
    });
    
    console.log(`Found ${results.total} identities`);
    console.log('Results:', results.identities);
    
    return results;
  } catch (error) {
    console.error('❌ Error searching identities:', error.message);
    throw error;
  }
}

// Usage
const searchResults = await searchIdentities({
  query: 'alice',
  limit: 5,
  isActivated: true
});

3. Identity Metadata Management

// Update identity metadata
async function updateMetadata(identity, metadata) {
  try {
    const result = await sdk.identity.updateMetadata(identity, {
      metadata: metadata,
      signature: await wallet.signMessage('Update metadata')
    });
    
    console.log('✅ Metadata updated successfully');
    console.log('New metadata:', result.metadata);
    
    return result;
  } catch (error) {
    console.error('❌ Error updating metadata:', error.message);
    throw error;
  }
}

// Usage
const updatedIdentity = await updateMetadata('alice.zks', {
  bio: 'Updated bio',
  avatar: 'https://new-avatar.com/image.png',
  social: {
    twitter: '@new_alice',
    github: 'new-alice-dev'
  }
});

Identity Validation

1. Name Validation

// Validate ZKS ID format
function validateZksId(name) {
  const zksIdRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?\.zks$/;
  
  if (!zksIdRegex.test(name)) {
    throw new Error('Invalid ZKS ID format. Must be lowercase letters, numbers, and hyphens, ending with .zks');
  }
  
  if (name.length < 3 || name.length > 63) {
    throw new Error('ZKS ID must be between 3 and 63 characters');
  }
  
  return true;
}

// Usage
try {
  validateZksId('alice.zks'); // ✅ Valid
  validateZksId('alice-123.zks'); // ✅ Valid
  validateZksId('Alice.zks'); // ❌ Invalid (uppercase)
  validateZksId('alice'); // ❌ Invalid (no .zks)
} catch (error) {
  console.error('Validation error:', error.message);
}

2. Address Validation

// Validate Ethereum address
function validateAddress(address) {
  const addressRegex = /^0x[a-fA-F0-9]{40}$/;
  
  if (!addressRegex.test(address)) {
    throw new Error('Invalid Ethereum address format');
  }
  
  return true;
}

// Usage
try {
  validateAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'); // ✅ Valid
  validateAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'); // ✅ Valid
  validateAddress('742d35Cc6634C0532925a3b844Bc9e7595f0bEb'); // ❌ Invalid (no 0x)
} catch (error) {
  console.error('Validation error:', error.message);
}

Error Handling

1. Common Error Codes

// Handle identity-specific errors
function handleIdentityError(error) {
  switch (error.code) {
    case 'IDENTITY_NOT_FOUND':
      return 'Identity not found. Please check the ZKS ID or wallet address.';
    
    case 'NAME_TAKEN':
      return 'This name is already taken. Please try a different name.';
    
    case 'INVALID_NAME':
      return 'Invalid name format. Names must be 3-63 characters, lowercase letters, numbers, and hyphens only.';
    
    case 'ALREADY_ACTIVATED':
      return 'This identity is already activated.';
    
    case 'INVALID_SIGNATURE':
      return 'Invalid signature. Please ensure you are signing with the correct wallet.';
    
    case 'INSUFFICIENT_FUNDS':
      return 'Insufficient funds to complete the transaction.';
    
    case 'NETWORK_ERROR':
      return 'Network error. Please check your connection and try again.';
    
    default:
      return `Unexpected error: ${error.message}`;
  }
}

// Usage
try {
  const identity = await sdk.identity.getIdentity('alice.zks');
} catch (error) {
  const message = handleIdentityError(error);
  console.error(message);
}

2. Retry Logic

// Retry identity operations with exponential backoff
async function retryIdentityOperation(operation, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
      
      if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT') {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        console.log(`Retrying in ${delay}ms... (attempt ${attempt}/${maxRetries})`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error; // Don't retry for non-retryable errors
      }
    }
  }
}

// Usage
const identity = await retryIdentityOperation(
  () => sdk.identity.getIdentity('alice.zks')
);

Real-time Updates

1. Subscribe to Identity Changes

// Subscribe to identity updates
async function subscribeToIdentityUpdates(identity) {
  try {
    const unsubscribe = sdk.identity.subscribeToUpdates(identity, (update) => {
      console.log('Identity updated:', update);
      
      switch (update.type) {
        case 'metadata_updated':
          console.log('Metadata changed:', update.metadata);
          break;
        case 'activated':
          console.log('Identity activated');
          break;
        case 'deactivated':
          console.log('Identity deactivated');
          break;
      }
    });
    
    console.log('✅ Subscribed to identity updates');
    return unsubscribe;
  } catch (error) {
    console.error('❌ Error subscribing to updates:', error.message);
    throw error;
  }
}

// Usage
const unsubscribe = await subscribeToIdentityUpdates('alice.zks');

// Clean up subscription
setTimeout(() => {
  unsubscribe();
  console.log('Unsubscribed from updates');
}, 300000); // 5 minutes

2. Identity Events

// Listen to specific identity events
class IdentityEventListener {
  constructor(sdk) {
    this.sdk = sdk;
    this.listeners = new Map();
  }
  
  on(event, callback) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event).push(callback);
  }
  
  emit(event, data) {
    const callbacks = this.listeners.get(event) || [];
    callbacks.forEach(callback => callback(data));
  }
  
  async startListening(identity) {
    const unsubscribe = await this.sdk.identity.subscribeToUpdates(identity, (update) => {
      this.emit(update.type, update);
    });
    
    return unsubscribe;
  }
}

// Usage
const eventListener = new IdentityEventListener(sdk);

eventListener.on('metadata_updated', (data) => {
  console.log('Metadata updated:', data);
});

eventListener.on('activated', (data) => {
  console.log('Identity activated:', data);
});

const unsubscribe = await eventListener.startListening('alice.zks');

Best Practices

1. Identity Caching

// Cache identity data to reduce API calls
class IdentityCache {
  constructor(ttl = 300000) { // 5 minutes
    this.cache = new Map();
    this.ttl = ttl;
  }
  
  async get(identity) {
    const key = identity.toLowerCase();
    const cached = this.cache.get(key);
    
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }
    
    const data = await sdk.identity.getIdentity(identity);
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
    
    return data;
  }
  
  clear() {
    this.cache.clear();
  }
}

// Usage
const identityCache = new IdentityCache();
const identity = await identityCache.get('alice.zks');

2. Batch Operations

// Batch multiple identity operations
async function batchIdentityOperations(operations) {
  const results = [];
  const errors = [];
  
  for (const operation of operations) {
    try {
      const result = await operation();
      results.push(result);
    } catch (error) {
      errors.push({ operation, error });
    }
  }
  
  return { results, errors };
}

// Usage
const operations = [
  () => sdk.identity.getIdentity('alice.zks'),
  () => sdk.identity.getIdentity('bob.zks'),
  () => sdk.identity.getIdentity('charlie.zks')
];

const { results, errors } = await batchIdentityOperations(operations);
console.log('Successful operations:', results.length);
console.log('Failed operations:', errors.length);

3. Identity Validation Service

// Comprehensive identity validation
class IdentityValidationService {
  static async validateIdentity(identity) {
    const validation = {
      isValid: true,
      errors: [],
      warnings: []
    };
    
    try {
      // Check if identity exists
      const identityData = await sdk.identity.getIdentity(identity);
      
      if (!identityData.isActivated) {
        validation.warnings.push('Identity is not activated');
      }
      
      if (!identityData.metadata) {
        validation.warnings.push('Identity has no metadata');
      }
      
    } catch (error) {
      if (error.code === 'IDENTITY_NOT_FOUND') {
        validation.isValid = false;
        validation.errors.push('Identity not found');
      } else {
        validation.isValid = false;
        validation.errors.push(`Validation error: ${error.message}`);
      }
    }
    
    return validation;
  }
}

// Usage
const validation = await IdentityValidationService.validateIdentity('alice.zks');
if (!validation.isValid) {
  console.error('Validation failed:', validation.errors);
} else {
  console.log('Identity is valid');
  if (validation.warnings.length > 0) {
    console.warn('Warnings:', validation.warnings);
  }
}