Skip to main content

Documentation Index

Fetch the complete documentation index at: https://core.anylayer.org/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide provides comprehensive examples for integrating with the ZKScore Identity SBT contract. It covers Web3 integration, error handling, best practices, and real-world use cases.
Always test your integration on testnet before deploying to mainnet. Use the testnet contract addresses provided in the Contract Overview section.

Web3 Integration

Basic Setup

import { ethers } from 'ethers';

// Contract configuration
const CONTRACT_ADDRESS = '0x1234567890123456789012345678901234567890';
const CONTRACT_ABI = [
  // ... ABI definitions
];

// Initialize provider and contract
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');
const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);

// Initialize with signer for transactions
const signer = provider.getSigner();
const contractWithSigner = contract.connect(signer);

Contract Interaction Class

class IdentitySBTIntegration {
  constructor(provider, contractAddress, abi) {
    this.provider = provider;
    this.contract = new ethers.Contract(contractAddress, abi, provider);
    this.contractWithSigner = null;
  }
  
  // Set signer for transactions
  setSigner(signer) {
    this.contractWithSigner = this.contract.connect(signer);
  }
  
  // Mint new identity
  async mintIdentity(to, name, metadataURI) {
    if (!this.contractWithSigner) {
      throw new Error('Signer not set');
    }
    
    try {
      const tx = await this.contractWithSigner.mint(to, name, metadataURI);
      const receipt = await tx.wait();
      
      // Get token ID from event
      const event = receipt.events.find(e => e.event === 'IdentityMinted');
      const tokenId = event.args.tokenId;
      
      return {
        success: true,
        tokenId: tokenId.toString(),
        transactionHash: receipt.transactionHash,
        blockNumber: receipt.blockNumber
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Activate identity
  async activateIdentity(tokenId) {
    if (!this.contractWithSigner) {
      throw new Error('Signer not set');
    }
    
    try {
      const tx = await this.contractWithSigner.activate(tokenId);
      const receipt = await tx.wait();
      
      return {
        success: true,
        transactionHash: receipt.transactionHash,
        blockNumber: receipt.blockNumber
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Get identity information
  async getIdentityInfo(tokenId) {
    try {
      const [owner, isActivated, tokenURI] = await Promise.all([
        this.contract.ownerOf(tokenId),
        this.contract.isActivated(tokenId),
        this.contract.tokenURI(tokenId)
      ]);
      
      return {
        tokenId: tokenId.toString(),
        owner,
        isActivated,
        tokenURI
      };
    } catch (error) {
      return {
        error: error.message
      };
    }
  }
  
  // Get user's identities
  async getUserIdentities(address) {
    try {
      const balance = await this.contract.balanceOf(address);
      const identities = [];
      
      for (let i = 0; i < balance.toNumber(); i++) {
        const tokenId = await this.contract.tokenOfOwnerByIndex(address, i);
        const info = await this.getIdentityInfo(tokenId);
        identities.push(info);
      }
      
      return identities;
    } catch (error) {
      return {
        error: error.message
      };
    }
  }
}

// Usage
const integration = new IdentitySBTIntegration(
  provider,
  '0x1234567890123456789012345678901234567890',
  IDENTITY_SBT_ABI
);

integration.setSigner(signer);

Error Handling

Comprehensive Error Handling

class IdentitySBTErrorHandler {
  static handleError(error) {
    const errorMessage = error.message || error.toString();
    
    // Common error patterns
    if (errorMessage.includes('AccessControl')) {
      return {
        type: 'PERMISSION_ERROR',
        message: 'Insufficient permissions to perform this action',
        code: 'ACCESS_DENIED'
      };
    }
    
    if (errorMessage.includes('Token is soulbound')) {
      return {
        type: 'SOULBOUND_ERROR',
        message: 'Token is soulbound and cannot be transferred',
        code: 'TOKEN_SOULBOUND'
      };
    }
    
    if (errorMessage.includes('Token does not exist')) {
      return {
        type: 'NOT_FOUND_ERROR',
        message: 'Token does not exist',
        code: 'TOKEN_NOT_FOUND'
      };
    }
    
    if (errorMessage.includes('Already activated')) {
      return {
        type: 'ALREADY_ACTIVATED_ERROR',
        message: 'Token is already activated',
        code: 'ALREADY_ACTIVATED'
      };
    }
    
    if (errorMessage.includes('Not token owner')) {
      return {
        type: 'OWNERSHIP_ERROR',
        message: 'Caller is not the owner of the token',
        code: 'NOT_OWNER'
      };
    }
    
    if (errorMessage.includes('name already exists')) {
      return {
        type: 'DUPLICATE_ERROR',
        message: 'Name is already taken',
        code: 'NAME_EXISTS'
      };
    }
    
    if (errorMessage.includes('insufficient funds')) {
      return {
        type: 'INSUFFICIENT_FUNDS_ERROR',
        message: 'Insufficient funds for transaction',
        code: 'INSUFFICIENT_FUNDS'
      };
    }
    
    if (errorMessage.includes('gas limit exceeded')) {
      return {
        type: 'GAS_ERROR',
        message: 'Transaction gas limit exceeded',
        code: 'GAS_LIMIT_EXCEEDED'
      };
    }
    
    // Default error
    return {
      type: 'UNKNOWN_ERROR',
      message: errorMessage,
      code: 'UNKNOWN'
    };
  }
  
  static async safeExecute(operation, retries = 3) {
    for (let attempt = 1; attempt <= retries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        const errorInfo = this.handleError(error);
        
        if (attempt === retries) {
          throw errorInfo;
        }
        
        // Wait before retry
        await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
      }
    }
  }
}

// Usage
try {
  const result = await IdentitySBTErrorHandler.safeExecute(async () => {
    return await contract.mint(to, name, metadataURI);
  });
  console.log('Success:', result);
} catch (error) {
  console.error('Error:', error.message);
  console.error('Code:', error.code);
}

Event Integration

Event Listener Setup

class IdentityEventManager {
  constructor(contract) {
    this.contract = contract;
    this.listeners = new Map();
    this.isListening = false;
  }
  
  startListening() {
    if (this.isListening) return;
    
    this.contract.on('IdentityMinted', (to, tokenId, name, event) => {
      this.handleEvent('IdentityMinted', {
        to,
        tokenId: tokenId.toString(),
        name,
        blockNumber: event.blockNumber,
        transactionHash: event.transactionHash
      });
    });
    
    this.contract.on('IdentityActivated', (tokenId, owner, event) => {
      this.handleEvent('IdentityActivated', {
        tokenId: tokenId.toString(),
        owner,
        blockNumber: event.blockNumber,
        transactionHash: event.transactionHash
      });
    });
    
    this.contract.on('Transfer', (from, to, tokenId, event) => {
      this.handleEvent('Transfer', {
        from,
        to,
        tokenId: tokenId.toString(),
        blockNumber: event.blockNumber,
        transactionHash: event.transactionHash
      });
    });
    
    this.isListening = true;
  }
  
  stopListening() {
    this.contract.removeAllListeners();
    this.isListening = false;
  }
  
  handleEvent(eventType, data) {
    console.log(`Event: ${eventType}`, data);
    
    if (this.listeners.has(eventType)) {
      this.listeners.get(eventType).forEach(callback => {
        try {
          callback(data);
        } catch (error) {
          console.error(`Error in event listener for ${eventType}:`, error);
        }
      });
    }
  }
  
  on(eventType, callback) {
    if (!this.listeners.has(eventType)) {
      this.listeners.set(eventType, []);
    }
    this.listeners.get(eventType).push(callback);
  }
  
  off(eventType, callback) {
    if (this.listeners.has(eventType)) {
      const callbacks = this.listeners.get(eventType);
      const index = callbacks.indexOf(callback);
      if (index > -1) {
        callbacks.splice(index, 1);
      }
    }
  }
}

// Usage
const eventManager = new IdentityEventManager(contract);
eventManager.startListening();

eventManager.on('IdentityMinted', (data) => {
  console.log('New identity minted:', data);
  // Update UI, send notifications, etc.
});

eventManager.on('IdentityActivated', (data) => {
  console.log('Identity activated:', data);
  // Update UI, send notifications, etc.
});

Real-world Integration Examples

Complete Identity Management System

class IdentityManagementSystem {
  constructor(provider, contractAddress, abi) {
    this.provider = provider;
    this.contract = new ethers.Contract(contractAddress, abi, provider);
    this.contractWithSigner = null;
    this.eventManager = new IdentityEventManager(this.contract);
  }
  
  setSigner(signer) {
    this.contractWithSigner = this.contract.connect(signer);
  }
  
  // Complete identity creation flow
  async createIdentity(name, metadataURI, autoActivate = false) {
    try {
      // Step 1: Mint identity
      const mintResult = await this.mintIdentity(name, metadataURI);
      if (!mintResult.success) {
        return mintResult;
      }
      
      const tokenId = mintResult.tokenId;
      
      // Step 2: Auto-activate if requested
      if (autoActivate) {
        const activateResult = await this.activateIdentity(tokenId);
        if (!activateResult.success) {
          return {
            success: false,
            error: `Identity minted but activation failed: ${activateResult.error}`,
            tokenId
          };
        }
      }
      
      return {
        success: true,
        tokenId,
        isActivated: autoActivate,
        transactionHash: mintResult.transactionHash
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Get comprehensive identity data
  async getIdentityData(tokenId) {
    try {
      const [owner, isActivated, tokenURI, balance] = await Promise.all([
        this.contract.ownerOf(tokenId),
        this.contract.isActivated(tokenId),
        this.contract.tokenURI(tokenId),
        this.contract.balanceOf(await this.contract.ownerOf(tokenId))
      ]);
      
      return {
        tokenId: tokenId.toString(),
        owner,
        isActivated,
        tokenURI,
        ownerBalance: balance.toString(),
        isSoulbound: isActivated
      };
    } catch (error) {
      return {
        error: error.message
      };
    }
  }
  
  // Batch operations
  async batchMintIdentities(identities) {
    const results = [];
    
    for (const identity of identities) {
      try {
        const result = await this.mintIdentity(identity.name, identity.metadataURI);
        results.push({
          name: identity.name,
          success: result.success,
          tokenId: result.tokenId,
          error: result.error
        });
      } catch (error) {
        results.push({
          name: identity.name,
          success: false,
          error: error.message
        });
      }
    }
    
    return results;
  }
  
  // Start event monitoring
  startEventMonitoring() {
    this.eventManager.startListening();
  }
  
  // Stop event monitoring
  stopEventMonitoring() {
    this.eventManager.stopListening();
  }
}

// Usage
const identitySystem = new IdentityManagementSystem(
  provider,
  '0x1234567890123456789012345678901234567890',
  IDENTITY_SBT_ABI
);

identitySystem.setSigner(signer);
identitySystem.startEventMonitoring();

// Create identity
const result = await identitySystem.createIdentity(
  'alice.zks',
  'https://api.onzks.com/metadata/alice.zks',
  true // Auto-activate
);

if (result.success) {
  console.log('Identity created:', result.tokenId);
} else {
  console.error('Failed to create identity:', result.error);
}

Best Practices

Security Considerations

  1. Private Key Management: Never hardcode private keys in your application
  2. Input Validation: Always validate inputs before sending transactions
  3. Gas Estimation: Estimate gas before sending transactions
  4. Error Handling: Implement comprehensive error handling
  5. Event Monitoring: Monitor events for state changes

Performance Optimization

  1. Batch Operations: Group multiple operations when possible
  2. Caching: Cache frequently accessed data
  3. Event Filtering: Use event filters to reduce data processing
  4. Gas Optimization: Use appropriate gas limits
  5. Connection Pooling: Reuse connections when possible

Testing

  1. Unit Tests: Test individual functions
  2. Integration Tests: Test complete workflows
  3. Error Testing: Test error conditions
  4. Performance Testing: Test under load
  5. Security Testing: Test for vulnerabilities