Skip to main content

Overview

The ZKScore Achievement Registry contract emits events for all significant actions including achievement creation, claiming, progress updates, and badge minting. These events are essential for tracking achievement lifecycle and building real-time applications.
Events are indexed on the blockchain and can be efficiently queried. They provide a complete audit trail for all achievement-related activities.

Achievement Events

AchievementCreated

Emitted when a new achievement is created.
event AchievementCreated(
    uint256 indexed achievementId,
    string name,
    uint8 category,
    Rarity rarity
);
Parameters:
  • achievementId (uint256 indexed): The unique achievement ID
  • name (string): Achievement name
  • category (uint8): Category index (0-7)
  • rarity (Rarity): Rarity level (0-4)
When Emitted:
  • When createAchievement() is successfully called
  • During batch achievement creation
Example Usage:
// Listen for achievement creation events
contract.on('AchievementCreated', (achievementId, name, category, rarity, event) => {
  const categories = ['DeFi', 'NFT', 'Social', 'Trading', 'Governance', 'Gaming', 'Identity', 'Trust'];
  const rarities = ['Common', 'Uncommon', 'Rare', 'Epic', 'Legendary'];
  
  console.log(`New Achievement Created:`);
  console.log(`  ID: ${achievementId.toString()}`);
  console.log(`  Name: ${name}`);
  console.log(`  Category: ${categories[category]}`);
  console.log(`  Rarity: ${rarities[rarity]}`);
  console.log(`  Block: ${event.blockNumber}`);
});

// Get all created achievements
async function getAllCreatedAchievements() {
  const filter = contract.filters.AchievementCreated();
  const events = await contract.queryFilter(filter);
  
  return events.map(event => ({
    achievementId: event.args.achievementId.toString(),
    name: event.args.name,
    category: event.args.category,
    rarity: event.args.rarity,
    blockNumber: event.blockNumber,
    transactionHash: event.transactionHash
  }));
}

AchievementClaimed

Emitted when a user claims an achievement.
event AchievementClaimed(
    address indexed user,
    uint256 indexed achievementId,
    uint256 timestamp
);
Parameters:
  • user (address indexed): User who claimed the achievement
  • achievementId (uint256 indexed): The claimed achievement ID
  • timestamp (uint256): Claim timestamp
When Emitted:
  • When claimAchievement() is successfully executed
  • During batch achievement claims
Example Usage:
// Listen for achievement claims
contract.on('AchievementClaimed', (user, achievementId, timestamp, event) => {
  console.log(`Achievement Claimed:`);
  console.log(`  User: ${user}`);
  console.log(`  Achievement ID: ${achievementId.toString()}`);
  console.log(`  Timestamp: ${new Date(timestamp.toNumber() * 1000).toISOString()}`);
  console.log(`  Transaction: ${event.transactionHash}`);
});

// Track specific user's claims
const userFilter = contract.filters.AchievementClaimed('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb');
contract.on(userFilter, (user, achievementId, timestamp, event) => {
  console.log(`Alice claimed achievement ${achievementId.toString()}`);
});

// Get user's claim history
async function getUserClaimHistory(userAddress) {
  const filter = contract.filters.AchievementClaimed(userAddress);
  const events = await contract.queryFilter(filter);
  
  return events.map(event => ({
    achievementId: event.args.achievementId.toString(),
    timestamp: event.args.timestamp.toNumber(),
    blockNumber: event.blockNumber,
    transactionHash: event.transactionHash
  }));
}

AchievementUpdated

Emitted when an achievement’s configuration is updated.
event AchievementUpdated(
    uint256 indexed achievementId,
    bytes updateData
);
Parameters:
  • achievementId (uint256 indexed): The updated achievement ID
  • updateData (bytes): Encoded update data
When Emitted:
  • When updateAchievement() is called
  • When achievement parameters are modified

AchievementDeactivated

Emitted when an achievement is deactivated.
event AchievementDeactivated(
    uint256 indexed achievementId,
    uint256 timestamp
);
Parameters:
  • achievementId (uint256 indexed): The deactivated achievement ID
  • timestamp (uint256): Deactivation timestamp
When Emitted:
  • When deactivateAchievement() is called
  • When achievement is permanently disabled

Progress Events

ProgressUpdated

Emitted when a user’s progress toward an achievement is updated.
event ProgressUpdated(
    address indexed user,
    uint256 indexed achievementId,
    uint256 progress
);
Parameters:
  • user (address indexed): User whose progress was updated
  • achievementId (uint256 indexed): The achievement ID
  • progress (uint256): New progress value
When Emitted:
  • When updateProgress() is called
  • When automatic progress tracking updates
  • During batch progress updates
Example Usage:
// Monitor progress updates
contract.on('ProgressUpdated', (user, achievementId, progress, event) => {
  console.log(`Progress Update:`);
  console.log(`  User: ${user}`);
  console.log(`  Achievement: ${achievementId.toString()}`);
  console.log(`  Progress: ${progress.toString()}`);
});

// Track specific achievement progress across all users
const achievementFilter = contract.filters.ProgressUpdated(null, 123);
contract.on(achievementFilter, (user, achievementId, progress, event) => {
  console.log(`User ${user} made progress on achievement 123: ${progress.toString()}`);
});

// Get progress history for a user
async function getProgressHistory(userAddress, achievementId) {
  const filter = contract.filters.ProgressUpdated(userAddress, achievementId);
  const events = await contract.queryFilter(filter);
  
  return events.map(event => ({
    progress: event.args.progress.toString(),
    blockNumber: event.blockNumber,
    timestamp: event.args.timestamp
  }));
}

Badge Events

BadgeMinted

Emitted when an NFT badge is minted for a claimed achievement.
event BadgeMinted(
    address indexed user,
    uint256 indexed achievementId,
    uint256 tokenId
);
Parameters:
  • user (address indexed): Badge recipient
  • achievementId (uint256 indexed): Associated achievement ID
  • tokenId (uint256): Minted badge token ID
When Emitted:
  • When achievement is successfully claimed
  • Immediately after AchievementClaimed event
Example Usage:
// Listen for badge minting
contract.on('BadgeMinted', (user, achievementId, tokenId, event) => {
  console.log(`Badge Minted:`);
  console.log(`  User: ${user}`);
  console.log(`  Achievement: ${achievementId.toString()}`);
  console.log(`  Token ID: ${tokenId.toString()}`);
  console.log(`  Transaction: ${event.transactionHash}`);
});

// Get all badges for a user
async function getUserBadges(userAddress) {
  const filter = contract.filters.BadgeMinted(userAddress);
  const events = await contract.queryFilter(filter);
  
  return events.map(event => ({
    achievementId: event.args.achievementId.toString(),
    tokenId: event.args.tokenId.toString(),
    blockNumber: event.blockNumber,
    transactionHash: event.transactionHash
  }));
}

Event Monitoring

Real-time Achievement Tracker

class AchievementEventMonitor {
  constructor(contract) {
    this.contract = contract;
    this.userProgress = new Map();
    this.claimHistory = [];
    this.setupListeners();
  }
  
  setupListeners() {
    // Monitor all achievement events
    this.contract.on('AchievementCreated', (id, name, category, rarity, event) => {
      this.handleAchievementCreated(id, name, category, rarity, event);
    });
    
    this.contract.on('AchievementClaimed', (user, achievementId, timestamp, event) => {
      this.handleAchievementClaimed(user, achievementId, timestamp, event);
    });
    
    this.contract.on('ProgressUpdated', (user, achievementId, progress, event) => {
      this.handleProgressUpdated(user, achievementId, progress, event);
    });
    
    this.contract.on('BadgeMinted', (user, achievementId, tokenId, event) => {
      this.handleBadgeMinted(user, achievementId, tokenId, event);
    });
  }
  
  handleAchievementCreated(id, name, category, rarity, event) {
    console.log(`New Achievement: ${name} (ID: ${id.toString()})`);
    
    // Trigger notification
    this.notifyNewAchievement(id, name, category, rarity);
  }
  
  handleAchievementClaimed(user, achievementId, timestamp, event) {
    const claim = {
      user,
      achievementId: achievementId.toString(),
      timestamp: timestamp.toNumber(),
      blockNumber: event.blockNumber,
      transactionHash: event.transactionHash
    };
    
    this.claimHistory.push(claim);
    console.log(`Achievement ${achievementId.toString()} claimed by ${user}`);
    
    // Trigger celebration
    this.celebrateClaim(user, achievementId);
  }
  
  handleProgressUpdated(user, achievementId, progress, event) {
    const key = `${user}-${achievementId.toString()}`;
    
    if (!this.userProgress.has(key)) {
      this.userProgress.set(key, []);
    }
    
    this.userProgress.get(key).push({
      progress: progress.toString(),
      blockNumber: event.blockNumber
    });
    
    console.log(`Progress update: ${user} - Achievement ${achievementId.toString()}: ${progress.toString()}`);
  }
  
  handleBadgeMinted(user, achievementId, tokenId, event) {
    console.log(`Badge #${tokenId.toString()} minted for ${user}`);
    
    // Update UI with new badge
    this.displayBadge(user, achievementId, tokenId);
  }
  
  getUserProgress(user, achievementId) {
    const key = `${user}-${achievementId}`;
    return this.userProgress.get(key) || [];
  }
  
  getRecentClaims(limit = 10) {
    return this.claimHistory.slice(-limit);
  }
  
  notifyNewAchievement(id, name, category, rarity) {
    // Implement notification logic
    console.log(`📢 New achievement available: ${name}`);
  }
  
  celebrateClaim(user, achievementId) {
    // Implement celebration logic
    console.log(`🎉 Achievement unlocked!`);
  }
  
  displayBadge(user, achievementId, tokenId) {
    // Implement badge display logic
    console.log(`🏆 Badge earned: #${tokenId.toString()}`);
  }
}

// Usage
const monitor = new AchievementEventMonitor(contract);

Event Analytics

// Analyze achievement statistics
async function analyzeAchievementStats() {
  const currentBlock = await provider.getBlockNumber();
  const startBlock = currentBlock - 100000;
  
  // Get all claims
  const claimFilter = contract.filters.AchievementClaimed();
  const claimEvents = await contract.queryFilter(claimFilter, startBlock, currentBlock);
  
  const stats = {
    totalClaims: claimEvents.length,
    uniqueClaimers: new Set(claimEvents.map(e => e.args.user)).size,
    mostClaimedAchievement: null,
    claimsByCategory: {},
    claimsByRarity: {},
    recentActivity: []
  };
  
  // Analyze claims by achievement
  const claimCounts = {};
  for (const event of claimEvents) {
    const id = event.args.achievementId.toString();
    claimCounts[id] = (claimCounts[id] || 0) + 1;
  }
  
  // Find most claimed
  const mostClaimed = Object.entries(claimCounts)
    .sort(([,a], [,b]) => b - a)[0];
  
  if (mostClaimed) {
    stats.mostClaimedAchievement = {
      id: mostClaimed[0],
      claims: mostClaimed[1]
    };
  }
  
  // Get recent activity (last 10 claims)
  stats.recentActivity = claimEvents
    .slice(-10)
    .map(e => ({
      user: e.args.user,
      achievementId: e.args.achievementId.toString(),
      timestamp: e.args.timestamp.toNumber(),
      blockNumber: e.blockNumber
    }));
  
  return stats;
}

Best Practices

Event Handling

  1. Use Indexed Parameters: Filter events efficiently using indexed parameters
  2. Handle Missing Events: Implement retry logic for missed events
  3. Store Event Data: Cache important event data locally
  4. Monitor Gas Costs: Be aware of gas costs for event queries
  5. Error Recovery: Implement error handling for event listeners

Performance Optimization

  1. Limit Block Ranges: Query smaller block ranges to avoid timeouts
  2. Use Filters: Apply filters to reduce data processing
  3. Batch Processing: Process multiple events in batches
  4. Cache Results: Cache frequently accessed event data
  5. Pagination: Implement pagination for large event sets