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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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
- Batch Operations: Use batch functions for multiple queries
- Caching: Cache frequently accessed scores
- Event Listening: Use events for real-time updates
- Rate Limiting: Implement rate limiting for API calls
- Connection Pooling: Reuse connections when possible
Security Considerations
- Input Validation: Validate all user inputs
- Private Keys: Secure private key storage
- Gas Estimation: Estimate gas before transactions
- Error Handling: Implement comprehensive error handling
- Event Monitoring: Monitor for unusual activity
Testing
- Unit Tests: Test individual functions
- Integration Tests: Test complete workflows
- Performance Tests: Test under load
- Security Tests: Test for vulnerabilities
- End-to-End Tests: Test complete user flows
Related Documentation
- Contract Overview - Contract architecture and features
- Functions Reference - Complete function documentation
- Events Reference - Event documentation
- Identity SBT - Identity contract documentation