Overview
Complete guide for integrating ZKScore smart contracts using the ethers.js library.Installation
Copy
npm install ethers
# or
yarn add ethers
Basic Setup
Provider Configuration
Copy
import { ethers } from 'ethers';
// Provider setup
const provider = new ethers.providers.JsonRpcProvider(
'https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY'
);
// Wallet setup
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
// Contract addresses
const CONTRACTS = {
identitySBT: '0x1234567890123456789012345678901234567890',
scoreCalculator: '0x2345678901234567890123456789012345678901',
achievementRegistry: '0x3456789012345678901234567890123456789012',
trustRegistry: '0x4567890123456789012345678901234567890123'
};
Contract Initialization
Copy
// Import ABIs
import IdentitySBTABI from '@zkscore/contracts/abis/IdentitySBT.json';
import ScoreCalculatorABI from '@zkscore/contracts/abis/ScoreCalculator.json';
// Initialize contracts
const identityContract = new ethers.Contract(
CONTRACTS.identitySBT,
IdentitySBTABI,
provider
);
const scoreContract = new ethers.Contract(
CONTRACTS.scoreCalculator,
ScoreCalculatorABI,
provider
);
Identity Operations
Check Identity Status
Copy
async function getIdentityInfo(userAddress) {
try {
const identity = await identityContract.getIdentity(userAddress);
const isActivated = await identityContract.isActivated(userAddress);
return {
exists: identity.exists,
zksId: identity.zksId,
displayName: identity.displayName,
avatarUrl: identity.avatarUrl,
isActivated: isActivated,
tokenId: identity.tokenId.toString()
};
} catch (error) {
console.error('Error getting identity:', error);
return null;
}
}
Mint Identity
Copy
async function mintIdentity(zksId, displayName, avatarUrl) {
try {
// Get minting fee
const mintingFee = await identityContract.mintingFee();
console.log('Minting fee:', ethers.utils.formatEther(mintingFee), 'ETH');
// Estimate gas
const gasEstimate = await identityContract.estimateGas.mintIdentity(
zksId,
displayName,
avatarUrl,
{ value: mintingFee }
);
// Execute transaction
const tx = await identityContract.mintIdentity(
zksId,
displayName,
avatarUrl,
{
value: mintingFee,
gasLimit: gasEstimate.mul(120).div(100) // 20% buffer
}
);
console.log('Transaction sent:', tx.hash);
const receipt = await tx.wait();
console.log('Transaction confirmed:', receipt.transactionHash);
return receipt;
} catch (error) {
console.error('Minting failed:', error);
throw error;
}
}
Score Operations
Get User Score
Copy
async function getUserScore(userAddress) {
try {
const score = await scoreContract.getScore(userAddress);
const breakdown = await scoreContract.getScoreBreakdown(userAddress);
return {
total: score.total.toString(),
breakdown: {
defi: breakdown.defi.toString(),
nft: breakdown.nft.toString(),
social: breakdown.social.toString(),
trading: breakdown.trading.toString(),
governance: breakdown.governance.toString(),
gaming: breakdown.gaming.toString(),
identity: breakdown.identity.toString(),
trust: breakdown.trust.toString()
},
lastUpdated: new Date(score.lastUpdated.toNumber() * 1000)
};
} catch (error) {
console.error('Error getting score:', error);
return null;
}
}
Listen to Score Updates
Copy
// Set up event listeners
function setupScoreListeners() {
scoreContract.on('ScoreUpdated', (user, oldScore, newScore, category, event) => {
console.log(`Score updated for ${user}:`);
console.log(`Old: ${oldScore.toString()}, New: ${newScore.toString()}`);
console.log(`Category: ${category}`);
console.log(`Block: ${event.blockNumber}`);
});
scoreContract.on('ScoreCalculated', (user, totalScore, breakdown, event) => {
console.log(`Score calculated for ${user}: ${totalScore.toString()}`);
});
}
// Remove listeners when done
function removeScoreListeners() {
scoreContract.removeAllListeners('ScoreUpdated');
scoreContract.removeAllListeners('ScoreCalculated');
}
Achievement Operations
Get User Achievements
Copy
async function getUserAchievements(userAddress) {
try {
const achievementCount = await achievementContract.getUserAchievementCount(userAddress);
const achievements = [];
for (let i = 0; i < achievementCount.toNumber(); i++) {
const achievement = await achievementContract.getUserAchievement(userAddress, i);
achievements.push({
id: achievement.id.toString(),
name: achievement.name,
description: achievement.description,
category: achievement.category,
rarity: achievement.rarity,
points: achievement.points.toString(),
claimedAt: new Date(achievement.claimedAt.toNumber() * 1000)
});
}
return achievements;
} catch (error) {
console.error('Error getting achievements:', error);
return [];
}
}
Claim Achievement
Copy
async function claimAchievement(achievementId) {
try {
// Check if user can claim
const canClaim = await achievementContract.canClaimAchievement(
wallet.address,
achievementId
);
if (!canClaim) {
throw new Error('Cannot claim this achievement');
}
// Estimate gas
const gasEstimate = await achievementContract.estimateGas.claimAchievement(
achievementId
);
// Execute transaction
const tx = await achievementContract.claimAchievement(achievementId, {
gasLimit: gasEstimate.mul(120).div(100)
});
console.log('Claim transaction sent:', tx.hash);
const receipt = await tx.wait();
console.log('Achievement claimed!', receipt.transactionHash);
return receipt;
} catch (error) {
console.error('Claim failed:', error);
throw error;
}
}
Trust Registry Operations
Create Attestation
Copy
async function createAttestation(recipient, schemaUID, data, expiration, revocable) {
try {
// Encode data
const encodedData = ethers.utils.defaultAbiCoder.encode(
['string', 'uint256', 'address'],
[data.skill, data.level, data.verifiedBy]
);
// Estimate gas
const gasEstimate = await trustContract.estimateGas.createAttestation(
recipient,
schemaUID,
encodedData,
expiration,
revocable
);
// Execute transaction
const tx = await trustContract.createAttestation(
recipient,
schemaUID,
encodedData,
expiration,
revocable,
{ gasLimit: gasEstimate.mul(120).div(100) }
);
console.log('Attestation transaction sent:', tx.hash);
const receipt = await tx.wait();
console.log('Attestation created!', receipt.transactionHash);
return receipt;
} catch (error) {
console.error('Attestation creation failed:', error);
throw error;
}
}
Get Attestations
Copy
async function getUserAttestations(userAddress) {
try {
const attestationCount = await trustContract.getUserAttestationCount(userAddress);
const attestations = [];
for (let i = 0; i < attestationCount.toNumber(); i++) {
const attestation = await trustContract.getUserAttestation(userAddress, i);
attestations.push({
uid: attestation.uid,
attester: attestation.attester,
recipient: attestation.recipient,
schema: attestation.schema,
data: attestation.data,
expiration: attestation.expiration.toNumber(),
revocable: attestation.revocable,
revoked: attestation.revoked
});
}
return attestations;
} catch (error) {
console.error('Error getting attestations:', error);
return [];
}
}
Error Handling
Comprehensive Error Handling
Copy
class ZKScoreError extends Error {
constructor(message, code, details) {
super(message);
this.name = 'ZKScoreError';
this.code = code;
this.details = details;
}
}
async function safeContractCall(contractMethod, ...args) {
try {
const result = await contractMethod(...args);
return { success: true, data: result };
} catch (error) {
let errorMessage = 'Unknown error';
let errorCode = 'UNKNOWN_ERROR';
if (error.code === 'INSUFFICIENT_FUNDS') {
errorMessage = 'Insufficient funds for transaction';
errorCode = 'INSUFFICIENT_FUNDS';
} else if (error.code === 'USER_REJECTED') {
errorMessage = 'User rejected transaction';
errorCode = 'USER_REJECTED';
} else if (error.message.includes('Identity already exists')) {
errorMessage = 'User already has an identity';
errorCode = 'IDENTITY_EXISTS';
} else if (error.message.includes('ZKS ID already taken')) {
errorMessage = 'ZKS ID is already taken';
errorCode = 'ZKS_ID_TAKEN';
}
return {
success: false,
error: new ZKScoreError(errorMessage, errorCode, error)
};
}
}
Best Practices
- Always Estimate Gas: Use gas estimation before transactions
- Handle Errors Gracefully: Implement comprehensive error handling
- Use Event Listeners: Listen to contract events for real-time updates
- Batch Operations: Combine multiple calls when possible
- Monitor Gas Prices: Use appropriate gas prices for network conditions