Skip to main content

Overview

This guide provides comprehensive examples for integrating with the ZKScore Score Calculator contract. It covers Web3 integration, real-time monitoring, error handling, and best practices for production deployments.
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 = '0x2345678901234567890123456789012345678901';
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);

Integration Class

class ScoreCalculatorIntegration {
  constructor(provider, contractAddress, abi) {
    this.provider = provider;
    this.contract = new ethers.Contract(contractAddress, abi, provider);
    this.contractWithSigner = null;
    this.categories = ['DeFi', 'NFT', 'Social', 'Trading', 'Governance', 'Gaming', 'Identity', 'Trust'];
  }
  
  // Set signer for transactions
  setSigner(signer) {
    this.contractWithSigner = this.contract.connect(signer);
  }
  
  // Get user score
  async getUserScore(userAddress) {
    try {
      const score = await this.contract.getScore(userAddress);
      return {
        success: true,
        score: score.toString(),
        address: userAddress
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Get detailed score breakdown
  async getScoreBreakdown(userAddress) {
    try {
      const breakdown = await this.contract.getScoreBreakdown(userAddress);
      
      const formattedBreakdown = breakdown.map((category, index) => ({
        name: this.categories[index],
        score: category.total.toString(),
        weight: category.weight.toNumber() / 100, // Convert basis points to percentage
        lastUpdated: new Date(category.lastUpdated.toNumber() * 1000).toISOString(),
        isActive: category.isActive
      }));
      
      return {
        success: true,
        address: userAddress,
        breakdown: formattedBreakdown
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Calculate and refresh score
  async refreshScore(userAddress) {
    if (!this.contractWithSigner) {
      throw new Error('Signer not set');
    }
    
    try {
      const tx = await this.contractWithSigner.calculateScore(userAddress);
      const receipt = await tx.wait();
      
      // Get score from event
      const event = receipt.events.find(e => e.event === 'ScoreCalculated');
      const newScore = event.args.totalScore;
      
      return {
        success: true,
        score: newScore.toString(),
        transactionHash: receipt.transactionHash,
        blockNumber: receipt.blockNumber
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Get category score
  async getCategoryScore(userAddress, categoryIndex) {
    try {
      const score = await this.contract.getCategoryScore(userAddress, categoryIndex);
      
      return {
        success: true,
        category: this.categories[categoryIndex],
        score: score.toString(),
        address: userAddress
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Batch get scores
  async batchGetScores(userAddresses) {
    try {
      const scores = await this.contract.batchGetScores(userAddresses);
      
      return {
        success: true,
        scores: userAddresses.map((address, index) => ({
          address,
          score: scores[index].toString()
        }))
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

// Usage
const integration = new ScoreCalculatorIntegration(
  provider,
  '0x2345678901234567890123456789012345678901',
  SCORE_CALCULATOR_ABI
);

integration.setSigner(signer);

// Get score
const scoreResult = await integration.getUserScore('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
console.log('User score:', scoreResult);

// Get breakdown
const breakdownResult = await integration.getScoreBreakdown('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
console.log('Score breakdown:', breakdownResult);

Real-time Monitoring

Score Update Tracker

class ScoreUpdateTracker {
  constructor(contract) {
    this.contract = contract;
    this.subscribers = new Map();
    this.scoreCache = new Map();
    this.startMonitoring();
  }
  
  startMonitoring() {
    // Monitor score calculations
    this.contract.on('ScoreCalculated', (user, totalScore, timestamp, event) => {
      this.handleScoreCalculated(user, totalScore, timestamp, event);
    });
    
    // Monitor score updates
    this.contract.on('ScoreUpdated', (user, oldScore, newScore, event) => {
      this.handleScoreUpdated(user, oldScore, newScore, event);
    });
    
    // Monitor category updates
    this.contract.on('CategoryScoreUpdated', (user, category, score, event) => {
      this.handleCategoryUpdated(user, category, score, event);
    });
  }
  
  handleScoreCalculated(user, totalScore, timestamp, event) {
    const scoreData = {
      score: totalScore.toString(),
      timestamp: timestamp.toNumber(),
      blockNumber: event.blockNumber,
      transactionHash: event.transactionHash
    };
    
    // Update cache
    this.scoreCache.set(user, scoreData);
    
    // Notify subscribers
    this.notifySubscribers(user, 'scoreCalculated', scoreData);
  }
  
  handleScoreUpdated(user, oldScore, newScore, event) {
    const updateData = {
      oldScore: oldScore.toString(),
      newScore: newScore.toString(),
      change: newScore.sub(oldScore).toString(),
      blockNumber: event.blockNumber
    };
    
    this.notifySubscribers(user, 'scoreUpdated', updateData);
  }
  
  handleCategoryUpdated(user, category, score, event) {
    const categories = ['DeFi', 'NFT', 'Social', 'Trading', 'Governance', 'Gaming', 'Identity', 'Trust'];
    
    const categoryData = {
      category: categories[category],
      categoryIndex: category,
      score: score.toString(),
      blockNumber: event.blockNumber
    };
    
    this.notifySubscribers(user, 'categoryUpdated', categoryData);
  }
  
  subscribe(user, eventType, callback) {
    const key = `${user}-${eventType}`;
    
    if (!this.subscribers.has(key)) {
      this.subscribers.set(key, []);
    }
    
    this.subscribers.get(key).push(callback);
  }
  
  notifySubscribers(user, eventType, data) {
    const key = `${user}-${eventType}`;
    
    if (this.subscribers.has(key)) {
      this.subscribers.get(key).forEach(callback => {
        try {
          callback(data);
        } catch (error) {
          console.error(`Error in subscriber callback: ${error.message}`);
        }
      });
    }
  }
  
  getCachedScore(user) {
    return this.scoreCache.get(user);
  }
  
  stopMonitoring() {
    this.contract.removeAllListeners();
  }
}

// Usage
const tracker = new ScoreUpdateTracker(contract);

// Subscribe to user score updates
tracker.subscribe('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 'scoreCalculated', (data) => {
  console.log('Score calculated:', data);
});

tracker.subscribe('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 'scoreUpdated', (data) => {
  console.log('Score updated:', data);
  console.log(`Change: ${data.change}`);
});

tracker.subscribe('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 'categoryUpdated', (data) => {
  console.log(`${data.category} score updated: ${data.score}`);
});

Complete Dashboard Integration

Score Dashboard

class ScoreDashboard {
  constructor(integration, tracker) {
    this.integration = integration;
    this.tracker = tracker;
    this.leaderboard = [];
    this.userScores = new Map();
  }
  
  // Initialize dashboard for a user
  async initializeUser(userAddress) {
    try {
      // Get current score
      const scoreResult = await this.integration.getUserScore(userAddress);
      if (!scoreResult.success) {
        throw new Error(scoreResult.error);
      }
      
      // Get detailed breakdown
      const breakdownResult = await this.integration.getScoreBreakdown(userAddress);
      if (!breakdownResult.success) {
        throw new Error(breakdownResult.error);
      }
      
      // Store user data
      this.userScores.set(userAddress, {
        score: scoreResult.score,
        breakdown: breakdownResult.breakdown,
        lastUpdated: new Date()
      });
      
      // Subscribe to updates
      this.tracker.subscribe(userAddress, 'scoreUpdated', (data) => {
        this.handleUserScoreUpdate(userAddress, data);
      });
      
      return {
        success: true,
        score: scoreResult.score,
        breakdown: breakdownResult.breakdown
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Handle real-time score updates
  handleUserScoreUpdate(userAddress, data) {
    const userData = this.userScores.get(userAddress);
    
    if (userData) {
      userData.score = data.newScore;
      userData.lastUpdated = new Date();
      
      console.log(`Score updated for ${userAddress}:`);
      console.log(`  Old: ${data.oldScore}`);
      console.log(`  New: ${data.newScore}`);
      console.log(`  Change: ${data.change}`);
      
      // Trigger UI update
      this.updateUI(userAddress, userData);
    }
  }
  
  // Get leaderboard
  async updateLeaderboard(userAddresses) {
    try {
      const result = await this.integration.batchGetScores(userAddresses);
      
      if (result.success) {
        this.leaderboard = result.scores
          .sort((a, b) => parseInt(b.score) - parseInt(a.score))
          .map((entry, index) => ({
            rank: index + 1,
            ...entry
          }));
        
        return {
          success: true,
          leaderboard: this.leaderboard
        };
      }
      
      throw new Error(result.error);
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Get user ranking
  getUserRanking(userAddress) {
    const entry = this.leaderboard.find(e => e.address === userAddress);
    return entry ? entry.rank : null;
  }
  
  // Refresh user score
  async refreshUserScore(userAddress) {
    try {
      const result = await this.integration.refreshScore(userAddress);
      
      if (result.success) {
        // Update local cache
        const userData = this.userScores.get(userAddress);
        if (userData) {
          userData.score = result.score;
          userData.lastUpdated = new Date();
        }
        
        return result;
      }
      
      throw new Error(result.error);
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  // Update UI (placeholder - implement with your UI framework)
  updateUI(userAddress, userData) {
    console.log(`UI Update for ${userAddress}:`, userData);
    // Implement with your UI framework (React, Vue, etc.)
  }
}

// Usage
const dashboard = new ScoreDashboard(integration, tracker);

// Initialize dashboard for a user
const initResult = await dashboard.initializeUser('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
console.log('Dashboard initialized:', initResult);

// Update leaderboard
const leaderboardResult = await dashboard.updateLeaderboard([
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  '0x1234567890123456789012345678901234567890',
  '0x9876543210987654321098765432109876543210'
]);
console.log('Leaderboard:', leaderboardResult);

// Get user ranking
const ranking = dashboard.getUserRanking('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
console.log(`User ranking: #${ranking}`);

Error Handling

Comprehensive Error Handler

class ScoreCalculatorErrorHandler {
  static handleError(error) {
    const errorMessage = error.message || error.toString();
    
    // Common error patterns
    if (errorMessage.includes('Invalid user address')) {
      return {
        type: 'INVALID_ADDRESS',
        message: 'Please provide a valid Ethereum address',
        code: 'E001'
      };
    }
    
    if (errorMessage.includes('Invalid category')) {
      return {
        type: 'INVALID_CATEGORY',
        message: 'Category index must be between 0 and 7',
        code: 'E002'
      };
    }
    
    if (errorMessage.includes('Score out of range')) {
      return {
        type: 'INVALID_SCORE',
        message: 'Score must be between 0 and 1000',
        code: 'E003'
      };
    }
    
    if (errorMessage.includes('Update too soon')) {
      return {
        type: 'UPDATE_COOLDOWN',
        message: 'Please wait before refreshing score again',
        code: 'E004'
      };
    }
    
    if (errorMessage.includes('AccessControl')) {
      return {
        type: 'ACCESS_DENIED',
        message: 'Insufficient permissions for this operation',
        code: 'E005'
      };
    }
    
    // Default error
    return {
      type: 'UNKNOWN_ERROR',
      message: errorMessage,
      code: 'E999'
    };
  }
  
  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 (exponential backoff)
        await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)));
      }
    }
  }
}

// Usage
try {
  const result = await ScoreCalculatorErrorHandler.safeExecute(async () => {
    return await integration.refreshScore('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
  });
  console.log('Success:', result);
} catch (error) {
  console.error('Error:', error.message);
  console.error('Code:', error.code);
  console.error('Type:', error.type);
}

Best Practices

Performance Optimization

  1. Batch Operations: Use batch functions for multiple queries
  2. Caching: Cache frequently accessed scores
  3. Event Listening: Use events for real-time updates
  4. Rate Limiting: Implement rate limiting for API calls
  5. Connection Pooling: Reuse connections when possible

Security Considerations

  1. Input Validation: Validate all user inputs
  2. Private Keys: Secure private key storage
  3. Gas Estimation: Estimate gas before transactions
  4. Error Handling: Implement comprehensive error handling
  5. Event Monitoring: Monitor for unusual activity

Testing

  1. Unit Tests: Test individual functions
  2. Integration Tests: Test complete workflows
  3. Performance Tests: Test under load
  4. Security Tests: Test for vulnerabilities
  5. End-to-End Tests: Test complete user flows