Skip to main content

Overview

The ZKScore JavaScript SDK provides comprehensive achievement management capabilities, including listing achievements, tracking progress, claiming rewards, and real-time updates. The SDK handles both ZKS IDs and wallet addresses seamlessly.
All achievement operations support both ZKS IDs and wallet addresses. The SDK automatically resolves ZKS IDs to their corresponding wallet addresses when needed.

Basic Achievement Operations

1. List All Achievements

// Get all available achievements
async function listAchievements(options = {}) {
  try {
    const achievements = await sdk.achievements.listAchievements({
      category: options.category,
      rarity: options.rarity,
      status: options.status,
      limit: options.limit || 50,
      offset: options.offset || 0
    });
    
    console.log(`Found ${achievements.total} achievements`);
    console.log('Achievements:', achievements.achievements);
    
    return achievements;
  } catch (error) {
    console.error('Error listing achievements:', error.message);
    throw error;
  }
}

// Usage with different filters
const allAchievements = await listAchievements();
const defiAchievements = await listAchievements({ category: 'defi' });
const rareAchievements = await listAchievements({ rarity: 'legendary' });
const activeAchievements = await listAchievements({ status: 'active' });

2. Get User Achievements

// Get achievements for a specific user
async function getUserAchievements(identity) {
  try {
    const userAchievements = await sdk.achievements.getUserAchievements(identity);
    
    console.log(`Achievements for ${identity}:`);
    console.log('- Earned:', userAchievements.earned.length);
    console.log('- Claimable:', userAchievements.claimable.length);
    console.log('- In Progress:', userAchievements.inProgress.length);
    
    return userAchievements;
  } catch (error) {
    console.error('Error fetching user achievements:', error.message);
    throw error;
  }
}

// Usage
const achievements = await getUserAchievements('alice.zks');

3. Get Achievement Progress

// Get progress for a specific achievement
async function getAchievementProgress(identity, achievementId) {
  try {
    const progress = await sdk.achievements.getProgress(identity, achievementId);
    
    console.log(`Progress for achievement ${achievementId}:`);
    console.log('- Current progress:', progress.current);
    console.log('- Required:', progress.required);
    console.log('- Percentage:', progress.percentage);
    console.log('- Is completed:', progress.isCompleted);
    console.log('- Is claimable:', progress.isClaimable);
    
    return progress;
  } catch (error) {
    console.error('Error fetching achievement progress:', error.message);
    throw error;
  }
}

// Usage
const progress = await getAchievementProgress('alice.zks', 'defi-pioneer');

4. Claim Achievement

// Claim an achievement
async function claimAchievement(identity, achievementId, proof) {
  try {
    const result = await sdk.achievements.claimAchievement(identity, achievementId, {
      proof: proof,
      signature: await wallet.signMessage(`Claim achievement: ${achievementId}`)
    });
    
    console.log('✅ Achievement claimed successfully');
    console.log('- Achievement:', result.achievement.name);
    console.log('- Points earned:', result.points);
    console.log('- Total points:', result.totalPoints);
    console.log('- Claimed at:', result.claimedAt);
    
    return result;
  } catch (error) {
    if (error.code === 'ACHIEVEMENT_NOT_CLAIMABLE') {
      console.error('❌ Achievement is not claimable yet');
    } else if (error.code === 'ALREADY_CLAIMED') {
      console.error('❌ Achievement already claimed');
    } else if (error.code === 'INVALID_PROOF') {
      console.error('❌ Invalid proof provided');
    } else {
      console.error('❌ Error claiming achievement:', error.message);
    }
    throw error;
  }
}

// Usage
const claimResult = await claimAchievement('alice.zks', 'defi-pioneer', '0x...');

Advanced Achievement Operations

1. Achievement Discovery

// Discover new achievements based on user activity
async function discoverAchievements(identity) {
  try {
    const discoveries = await sdk.achievements.discover(identity);
    
    console.log(`Found ${discoveries.length} potential achievements`);
    
    discoveries.forEach(discovery => {
      console.log(`- ${discovery.name}: ${discovery.description}`);
      console.log(`  Progress: ${discovery.progress.current}/${discovery.progress.required}`);
      console.log(`  Rarity: ${discovery.rarity}`);
    });
    
    return discoveries;
  } catch (error) {
    console.error('Error discovering achievements:', error.message);
    throw error;
  }
}

// Usage
const discoveries = await discoverAchievements('alice.zks');

2. Achievement Analytics

// Analyze achievement patterns and statistics
async function analyzeAchievements(identity) {
  try {
    const userAchievements = await sdk.achievements.getUserAchievements(identity);
    const allAchievements = await sdk.achievements.listAchievements();
    
    const analysis = {
      totalEarned: userAchievements.earned.length,
      totalAvailable: allAchievements.total,
      completionRate: (userAchievements.earned.length / allAchievements.total) * 100,
      categories: {},
      rarities: {},
      points: {
        total: userAchievements.earned.reduce((sum, a) => sum + a.points, 0),
        average: 0
      }
    };
    
    // Analyze by category
    userAchievements.earned.forEach(achievement => {
      const category = achievement.category;
      analysis.categories[category] = (analysis.categories[category] || 0) + 1;
    });
    
    // Analyze by rarity
    userAchievements.earned.forEach(achievement => {
      const rarity = achievement.rarity;
      analysis.rarities[rarity] = (analysis.rarities[rarity] || 0) + 1;
    });
    
    analysis.points.average = analysis.points.total / userAchievements.earned.length;
    
    console.log('Achievement Analysis:');
    console.log('- Total earned:', analysis.totalEarned);
    console.log('- Completion rate:', analysis.completionRate.toFixed(2) + '%');
    console.log('- Total points:', analysis.points.total);
    console.log('- Average points per achievement:', analysis.points.average.toFixed(2));
    console.log('- Categories:', analysis.categories);
    console.log('- Rarities:', analysis.rarities);
    
    return analysis;
  } catch (error) {
    console.error('Error analyzing achievements:', error.message);
    throw error;
  }
}

// Usage
const analysis = await analyzeAchievements('alice.zks');

3. Achievement Leaderboard

// Get achievement leaderboard
async function getAchievementLeaderboard(options = {}) {
  try {
    const leaderboard = await sdk.achievements.getLeaderboard({
      metric: options.metric || 'points', // points, count, rarity
      category: options.category,
      timeframe: options.timeframe || 'all',
      limit: options.limit || 10,
      offset: options.offset || 0
    });
    
    console.log('Achievement Leaderboard:');
    leaderboard.rankings.forEach((entry, index) => {
      console.log(`${index + 1}. ${entry.identity}: ${entry.value} ${options.metric || 'points'}`);
    });
    
    return leaderboard;
  } catch (error) {
    console.error('Error fetching achievement leaderboard:', error.message);
    throw error;
  }
}

// Usage
const pointsLeaderboard = await getAchievementLeaderboard({ metric: 'points' });
const countLeaderboard = await getAchievementLeaderboard({ metric: 'count' });
const defiLeaderboard = await getAchievementLeaderboard({ category: 'defi' });

Real-time Achievement Updates

1. Subscribe to Achievement Updates

// Subscribe to real-time achievement updates
async function subscribeToAchievementUpdates(identity) {
  try {
    const unsubscribe = sdk.achievements.subscribeToUpdates(identity, (update) => {
      console.log('Achievement update:', update);
      
      switch (update.type) {
        case 'earned':
          console.log('🎉 New achievement earned:', update.achievement.name);
          break;
        case 'claimed':
          console.log('✅ Achievement claimed:', update.achievement.name);
          break;
        case 'progress':
          console.log('📈 Progress updated:', update.achievement.name, update.progress);
          break;
      }
    });
    
    console.log('✅ Subscribed to achievement updates');
    return unsubscribe;
  } catch (error) {
    console.error('❌ Error subscribing to updates:', error.message);
    throw error;
  }
}

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

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

2. Achievement Notifications

// Set up achievement notifications
class AchievementNotificationService {
  constructor(sdk) {
    this.sdk = sdk;
    this.subscriptions = new Map();
    this.notifications = [];
  }
  
  async subscribe(identity, options = {}) {
    const {
      notifyOnEarn = true,
      notifyOnClaim = true,
      notifyOnProgress = false,
      callback = null
    } = options;
    
    const unsubscribe = await this.sdk.achievements.subscribeToUpdates(identity, (update) => {
      this.handleAchievementUpdate(identity, update, { notifyOnEarn, notifyOnClaim, notifyOnProgress, callback });
    });
    
    this.subscriptions.set(identity, unsubscribe);
    return unsubscribe;
  }
  
  handleAchievementUpdate(identity, update, options) {
    const notification = {
      identity,
      type: update.type,
      achievement: update.achievement,
      timestamp: new Date().toISOString()
    };
    
    let shouldNotify = false;
    
    switch (update.type) {
      case 'earned':
        shouldNotify = options.notifyOnEarn;
        notification.message = `🎉 New achievement earned: ${update.achievement.name}`;
        break;
      case 'claimed':
        shouldNotify = options.notifyOnClaim;
        notification.message = `✅ Achievement claimed: ${update.achievement.name}`;
        break;
      case 'progress':
        shouldNotify = options.notifyOnProgress;
        notification.message = `📈 Progress updated: ${update.achievement.name} (${update.progress}%)`;
        break;
    }
    
    if (shouldNotify) {
      this.notifications.push(notification);
      
      if (options.callback) {
        options.callback(notification);
      } else {
        console.log(notification.message);
      }
    }
  }
  
  getNotifications(identity) {
    return this.notifications.filter(n => n.identity === identity);
  }
  
  clearNotifications(identity) {
    this.notifications = this.notifications.filter(n => n.identity !== identity);
  }
  
  unsubscribe(identity) {
    const unsubscribe = this.subscriptions.get(identity);
    if (unsubscribe) {
      unsubscribe();
      this.subscriptions.delete(identity);
    }
  }
}

// Usage
const notificationService = new AchievementNotificationService(sdk);

await notificationService.subscribe('alice.zks', {
  notifyOnEarn: true,
  notifyOnClaim: true,
  notifyOnProgress: false,
  callback: (notification) => {
    console.log('Achievement notification:', notification);
  }
});

Achievement Caching and Optimization

1. Achievement Caching

// Implement achievement caching
class AchievementCache {
  constructor(ttl = 300000) { // 5 minutes
    this.cache = new Map();
    this.ttl = ttl;
  }
  
  async getUserAchievements(identity) {
    const key = `user:${identity.toLowerCase()}`;
    const cached = this.cache.get(key);
    
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }
    
    const achievements = await sdk.achievements.getUserAchievements(identity);
    this.cache.set(key, {
      data: achievements,
      timestamp: Date.now()
    });
    
    return achievements;
  }
  
  async getAchievementProgress(identity, achievementId) {
    const key = `progress:${identity.toLowerCase()}:${achievementId}`;
    const cached = this.cache.get(key);
    
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }
    
    const progress = await sdk.achievements.getProgress(identity, achievementId);
    this.cache.set(key, {
      data: progress,
      timestamp: Date.now()
    });
    
    return progress;
  }
  
  invalidateUser(identity) {
    const keys = Array.from(this.cache.keys()).filter(key => 
      key.startsWith(`user:${identity.toLowerCase()}`) || 
      key.startsWith(`progress:${identity.toLowerCase()}`)
    );
    
    keys.forEach(key => this.cache.delete(key));
  }
  
  clear() {
    this.cache.clear();
  }
}

// Usage
const achievementCache = new AchievementCache();
const achievements = await achievementCache.getUserAchievements('alice.zks');

2. Batch Achievement Operations

// Batch multiple achievement operations
async function batchAchievementOperations(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.achievements.getUserAchievements('alice.zks'),
  () => sdk.achievements.getUserAchievements('bob.zks'),
  () => sdk.achievements.getUserAchievements('charlie.zks')
];

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

Achievement Validation

1. Achievement Requirements Validation

// Validate achievement requirements
class AchievementValidator {
  static validateRequirements(achievement, userData) {
    const validation = {
      isValid: true,
      errors: [],
      warnings: []
    };
    
    // Check if achievement is active
    if (achievement.status !== 'active') {
      validation.isValid = false;
      validation.errors.push('Achievement is not active');
    }
    
    // Check if user meets requirements
    if (achievement.requirements) {
      Object.entries(achievement.requirements).forEach(([key, value]) => {
        if (userData[key] < value) {
          validation.warnings.push(`Requirement not met: ${key} (${userData[key]}/${value})`);
        }
      });
    }
    
    // Check if achievement is already earned
    if (userData.earnedAchievements?.includes(achievement.id)) {
      validation.isValid = false;
      validation.errors.push('Achievement already earned');
    }
    
    return validation;
  }
}

// Usage
const achievement = await sdk.achievements.getAchievement('defi-pioneer');
const userData = await sdk.scores.getScore('alice.zks');
const validation = AchievementValidator.validateRequirements(achievement, userData);

2. Achievement Progress Tracking

// Track achievement progress over time
class AchievementProgressTracker {
  constructor(sdk) {
    this.sdk = sdk;
    this.progressHistory = new Map();
  }
  
  async trackProgress(identity, achievementId) {
    const progress = await this.sdk.achievements.getProgress(identity, achievementId);
    const key = `${identity}:${achievementId}`;
    
    if (!this.progressHistory.has(key)) {
      this.progressHistory.set(key, []);
    }
    
    const history = this.progressHistory.get(key);
    history.push({
      progress: progress.percentage,
      timestamp: new Date().toISOString()
    });
    
    // Keep only last 10 progress points
    if (history.length > 10) {
      history.shift();
    }
    
    return {
      current: progress,
      history: history,
      trend: this.calculateTrend(history)
    };
  }
  
  calculateTrend(history) {
    if (history.length < 2) return 'stable';
    
    const first = history[0].progress;
    const last = history[history.length - 1].progress;
    const change = last - first;
    
    if (change > 5) return 'increasing';
    if (change < -5) return 'decreasing';
    return 'stable';
  }
}

// Usage
const tracker = new AchievementProgressTracker(sdk);
const progressData = await tracker.trackProgress('alice.zks', 'defi-pioneer');

Error Handling

1. Achievement-Specific Error Handling

// Handle achievement-specific errors
function handleAchievementError(error) {
  switch (error.code) {
    case 'ACHIEVEMENT_NOT_FOUND':
      return 'Achievement not found.';
    
    case 'ACHIEVEMENT_NOT_CLAIMABLE':
      return 'Achievement is not claimable yet.';
    
    case 'ALREADY_CLAIMED':
      return 'Achievement already claimed.';
    
    case 'INVALID_PROOF':
      return 'Invalid proof provided.';
    
    case 'INSUFFICIENT_PROGRESS':
      return 'Insufficient progress to claim achievement.';
    
    case 'ACHIEVEMENT_EXPIRED':
      return 'Achievement has expired.';
    
    default:
      return `Unexpected error: ${error.message}`;
  }
}

// Usage
try {
  const result = await sdk.achievements.claimAchievement('alice.zks', 'defi-pioneer', '0x...');
} catch (error) {
  const message = handleAchievementError(error);
  console.error(message);
}

2. Retry Logic for Achievement Operations

// Retry achievement operations with exponential backoff
async function retryAchievementOperation(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;
        console.log(`Retrying achievement operation in ${delay}ms... (attempt ${attempt}/${maxRetries})`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

// Usage
const result = await retryAchievementOperation(
  () => sdk.achievements.claimAchievement('alice.zks', 'defi-pioneer', '0x...')
);

Best Practices

1. Achievement Monitoring

// Monitor achievement progress and notify users
class AchievementMonitor {
  constructor(sdk) {
    this.sdk = sdk;
    this.monitoredAchievements = new Map();
  }
  
  async startMonitoring(identity, achievementIds) {
    for (const achievementId of achievementIds) {
      const progress = await this.sdk.achievements.getProgress(identity, achievementId);
      
      if (!progress.isCompleted) {
        this.monitoredAchievements.set(`${identity}:${achievementId}`, {
          identity,
          achievementId,
          progress: progress.percentage,
          lastChecked: new Date()
        });
      }
    }
    
    // Check progress every 5 minutes
    setInterval(() => {
      this.checkProgress();
    }, 300000);
  }
  
  async checkProgress() {
    for (const [key, monitored] of this.monitoredAchievements) {
      try {
        const progress = await this.sdk.achievements.getProgress(monitored.identity, monitored.achievementId);
        
        if (progress.percentage > monitored.progress) {
          console.log(`Progress update: ${monitored.achievementId} - ${progress.percentage}%`);
          monitored.progress = progress.percentage;
          monitored.lastChecked = new Date();
        }
        
        if (progress.isCompleted) {
          console.log(`Achievement completed: ${monitored.achievementId}`);
          this.monitoredAchievements.delete(key);
        }
      } catch (error) {
        console.error(`Error checking progress for ${key}:`, error.message);
      }
    }
  }
}

// Usage
const monitor = new AchievementMonitor(sdk);
await monitor.startMonitoring('alice.zks', ['defi-pioneer', 'nft-collector']);

2. Achievement Analytics

// Analyze achievement patterns and provide insights
class AchievementAnalytics {
  constructor(sdk) {
    this.sdk = sdk;
  }
  
  async analyzeUserAchievements(identity) {
    const achievements = await this.sdk.achievements.getUserAchievements(identity);
    
    const analysis = {
      totalEarned: achievements.earned.length,
      totalPoints: achievements.earned.reduce((sum, a) => sum + a.points, 0),
      categories: {},
      rarities: {},
      recent: achievements.earned
        .sort((a, b) => new Date(b.earnedAt) - new Date(a.earnedAt))
        .slice(0, 5),
      recommendations: []
    };
    
    // Analyze by category
    achievements.earned.forEach(achievement => {
      const category = achievement.category;
      analysis.categories[category] = (analysis.categories[category] || 0) + 1;
    });
    
    // Analyze by rarity
    achievements.earned.forEach(achievement => {
      const rarity = achievement.rarity;
      analysis.rarities[rarity] = (analysis.rarities[rarity] || 0) + 1;
    });
    
    // Generate recommendations
    if (analysis.categories.defi < 3) {
      analysis.recommendations.push('Try more DeFi activities to earn DeFi achievements');
    }
    
    if (analysis.categories.nft < 2) {
      analysis.recommendations.push('Explore NFT collections to earn NFT achievements');
    }
    
    return analysis;
  }
}

// Usage
const analytics = new AchievementAnalytics(sdk);
const analysis = await analytics.analyzeUserAchievements('alice.zks');