Overview
Complete guide for integrating ZKScore smart contracts using the viem library.Installation
Copy
npm install viem
# or
yarn add viem
Basic Setup
Client Configuration
Copy
import { createPublicClient, createWalletClient, http } from 'viem';
import { mainnet, polygon, arbitrum, base } from 'viem/chains';
// Public client for read operations
const publicClient = createPublicClient({
chain: mainnet,
transport: http('https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY')
});
// Wallet client for write operations
const walletClient = createWalletClient({
chain: mainnet,
transport: http('https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY'),
account: privateKeyToAccount(PRIVATE_KEY)
});
Contract Configuration
Copy
// Contract addresses
const CONTRACTS = {
identitySBT: '0x1234567890123456789012345678901234567890',
scoreCalculator: '0x2345678901234567890123456789012345678901',
achievementRegistry: '0x3456789012345678901234567890123456789012',
trustRegistry: '0x4567890123456789012345678901234567890123'
};
// Contract ABIs
import IdentitySBTABI from '@zkscore/contracts/abis/IdentitySBT.json';
import ScoreCalculatorABI from '@zkscore/contracts/abis/ScoreCalculator.json';
Identity Operations
Check Identity Status
Copy
async function getIdentityInfo(userAddress) {
try {
const identity = await publicClient.readContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'getIdentity',
args: [userAddress]
});
const isActivated = await publicClient.readContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'isActivated',
args: [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 publicClient.readContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'mintingFee'
});
console.log('Minting fee:', formatEther(mintingFee), 'ETH');
// Execute transaction
const hash = await walletClient.writeContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'mintIdentity',
args: [zksId, displayName, avatarUrl],
value: mintingFee
});
console.log('Transaction sent:', hash);
// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash });
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 publicClient.readContract({
address: CONTRACTS.scoreCalculator,
abi: ScoreCalculatorABI,
functionName: 'getScore',
args: [userAddress]
});
const breakdown = await publicClient.readContract({
address: CONTRACTS.scoreCalculator,
abi: ScoreCalculatorABI,
functionName: 'getScoreBreakdown',
args: [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(Number(score.lastUpdated) * 1000)
};
} catch (error) {
console.error('Error getting score:', error);
return null;
}
}
Listen to Score Updates
Copy
// Set up event listeners
function setupScoreListeners() {
publicClient.watchContractEvent({
address: CONTRACTS.scoreCalculator,
abi: ScoreCalculatorABI,
eventName: 'ScoreUpdated',
onLogs: (logs) => {
logs.forEach((log) => {
console.log(`Score updated for ${log.args.user}:`);
console.log(`Old: ${log.args.oldScore.toString()}, New: ${log.args.newScore.toString()}`);
console.log(`Category: ${log.args.category}`);
console.log(`Block: ${log.blockNumber}`);
});
}
});
}
Achievement Operations
Get User Achievements
Copy
async function getUserAchievements(userAddress) {
try {
const achievementCount = await publicClient.readContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'getUserAchievementCount',
args: [userAddress]
});
const achievements = [];
for (let i = 0; i < Number(achievementCount); i++) {
const achievement = await publicClient.readContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'getUserAchievement',
args: [userAddress, BigInt(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(Number(achievement.claimedAt) * 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 publicClient.readContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'canClaimAchievement',
args: [walletClient.account.address, achievementId]
});
if (!canClaim) {
throw new Error('Cannot claim this achievement');
}
// Execute transaction
const hash = await walletClient.writeContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'claimAchievement',
args: [achievementId]
});
console.log('Claim transaction sent:', hash);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
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 = encodeAbiParameters(
parseAbiParameters('string, uint256, address'),
[data.skill, BigInt(data.level), data.verifiedBy]
);
// Execute transaction
const hash = await walletClient.writeContract({
address: CONTRACTS.trustRegistry,
abi: TrustRegistryABI,
functionName: 'createAttestation',
args: [recipient, schemaUID, encodedData, BigInt(expiration), revocable]
});
console.log('Attestation transaction sent:', hash);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Attestation created!', receipt.transactionHash);
return receipt;
} catch (error) {
console.error('Attestation creation failed:', error);
throw error;
}
}
Multi-chain Support
Chain Configuration
Copy
// Multi-chain setup
const chains = {
ethereum: mainnet,
polygon: polygon,
arbitrum: arbitrum,
base: base
};
function createClient(chainName, rpcUrl) {
return createPublicClient({
chain: chains[chainName],
transport: http(rpcUrl)
});
}
// Usage
const ethereumClient = createClient('ethereum', ETHEREUM_RPC_URL);
const polygonClient = createClient('polygon', POLYGON_RPC_URL);
Best Practices
- Use Type Safety: Leverage viem’s TypeScript support
- Handle Errors Gracefully: Implement comprehensive error handling
- Use Event Watchers: Watch contract events for real-time updates
- Batch Operations: Combine multiple calls when possible
- Monitor Gas Prices: Use appropriate gas prices for network conditions