Documentation Index
Fetch the complete documentation index at: https://core.anylayer.org/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide provides comprehensive examples for integrating with the ZKScore Identity SBT contract. It covers Web3 integration, error handling, best practices, and real-world use cases.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
import { ethers } from 'ethers';
// Contract configuration
const CONTRACT_ADDRESS = '0x1234567890123456789012345678901234567890';
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);
Contract Interaction Class
class IdentitySBTIntegration {
constructor(provider, contractAddress, abi) {
this.provider = provider;
this.contract = new ethers.Contract(contractAddress, abi, provider);
this.contractWithSigner = null;
}
// Set signer for transactions
setSigner(signer) {
this.contractWithSigner = this.contract.connect(signer);
}
// Mint new identity
async mintIdentity(to, name, metadataURI) {
if (!this.contractWithSigner) {
throw new Error('Signer not set');
}
try {
const tx = await this.contractWithSigner.mint(to, name, metadataURI);
const receipt = await tx.wait();
// Get token ID from event
const event = receipt.events.find(e => e.event === 'IdentityMinted');
const tokenId = event.args.tokenId;
return {
success: true,
tokenId: tokenId.toString(),
transactionHash: receipt.transactionHash,
blockNumber: receipt.blockNumber
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// Activate identity
async activateIdentity(tokenId) {
if (!this.contractWithSigner) {
throw new Error('Signer not set');
}
try {
const tx = await this.contractWithSigner.activate(tokenId);
const receipt = await tx.wait();
return {
success: true,
transactionHash: receipt.transactionHash,
blockNumber: receipt.blockNumber
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// Get identity information
async getIdentityInfo(tokenId) {
try {
const [owner, isActivated, tokenURI] = await Promise.all([
this.contract.ownerOf(tokenId),
this.contract.isActivated(tokenId),
this.contract.tokenURI(tokenId)
]);
return {
tokenId: tokenId.toString(),
owner,
isActivated,
tokenURI
};
} catch (error) {
return {
error: error.message
};
}
}
// Get user's identities
async getUserIdentities(address) {
try {
const balance = await this.contract.balanceOf(address);
const identities = [];
for (let i = 0; i < balance.toNumber(); i++) {
const tokenId = await this.contract.tokenOfOwnerByIndex(address, i);
const info = await this.getIdentityInfo(tokenId);
identities.push(info);
}
return identities;
} catch (error) {
return {
error: error.message
};
}
}
}
// Usage
const integration = new IdentitySBTIntegration(
provider,
'0x1234567890123456789012345678901234567890',
IDENTITY_SBT_ABI
);
integration.setSigner(signer);
Error Handling
Comprehensive Error Handling
class IdentitySBTErrorHandler {
static handleError(error) {
const errorMessage = error.message || error.toString();
// Common error patterns
if (errorMessage.includes('AccessControl')) {
return {
type: 'PERMISSION_ERROR',
message: 'Insufficient permissions to perform this action',
code: 'ACCESS_DENIED'
};
}
if (errorMessage.includes('Token is soulbound')) {
return {
type: 'SOULBOUND_ERROR',
message: 'Token is soulbound and cannot be transferred',
code: 'TOKEN_SOULBOUND'
};
}
if (errorMessage.includes('Token does not exist')) {
return {
type: 'NOT_FOUND_ERROR',
message: 'Token does not exist',
code: 'TOKEN_NOT_FOUND'
};
}
if (errorMessage.includes('Already activated')) {
return {
type: 'ALREADY_ACTIVATED_ERROR',
message: 'Token is already activated',
code: 'ALREADY_ACTIVATED'
};
}
if (errorMessage.includes('Not token owner')) {
return {
type: 'OWNERSHIP_ERROR',
message: 'Caller is not the owner of the token',
code: 'NOT_OWNER'
};
}
if (errorMessage.includes('name already exists')) {
return {
type: 'DUPLICATE_ERROR',
message: 'Name is already taken',
code: 'NAME_EXISTS'
};
}
if (errorMessage.includes('insufficient funds')) {
return {
type: 'INSUFFICIENT_FUNDS_ERROR',
message: 'Insufficient funds for transaction',
code: 'INSUFFICIENT_FUNDS'
};
}
if (errorMessage.includes('gas limit exceeded')) {
return {
type: 'GAS_ERROR',
message: 'Transaction gas limit exceeded',
code: 'GAS_LIMIT_EXCEEDED'
};
}
// Default error
return {
type: 'UNKNOWN_ERROR',
message: errorMessage,
code: 'UNKNOWN'
};
}
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
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
}
// Usage
try {
const result = await IdentitySBTErrorHandler.safeExecute(async () => {
return await contract.mint(to, name, metadataURI);
});
console.log('Success:', result);
} catch (error) {
console.error('Error:', error.message);
console.error('Code:', error.code);
}
Event Integration
Event Listener Setup
class IdentityEventManager {
constructor(contract) {
this.contract = contract;
this.listeners = new Map();
this.isListening = false;
}
startListening() {
if (this.isListening) return;
this.contract.on('IdentityMinted', (to, tokenId, name, event) => {
this.handleEvent('IdentityMinted', {
to,
tokenId: tokenId.toString(),
name,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash
});
});
this.contract.on('IdentityActivated', (tokenId, owner, event) => {
this.handleEvent('IdentityActivated', {
tokenId: tokenId.toString(),
owner,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash
});
});
this.contract.on('Transfer', (from, to, tokenId, event) => {
this.handleEvent('Transfer', {
from,
to,
tokenId: tokenId.toString(),
blockNumber: event.blockNumber,
transactionHash: event.transactionHash
});
});
this.isListening = true;
}
stopListening() {
this.contract.removeAllListeners();
this.isListening = false;
}
handleEvent(eventType, data) {
console.log(`Event: ${eventType}`, data);
if (this.listeners.has(eventType)) {
this.listeners.get(eventType).forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`Error in event listener for ${eventType}:`, error);
}
});
}
}
on(eventType, callback) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, []);
}
this.listeners.get(eventType).push(callback);
}
off(eventType, callback) {
if (this.listeners.has(eventType)) {
const callbacks = this.listeners.get(eventType);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
}
// Usage
const eventManager = new IdentityEventManager(contract);
eventManager.startListening();
eventManager.on('IdentityMinted', (data) => {
console.log('New identity minted:', data);
// Update UI, send notifications, etc.
});
eventManager.on('IdentityActivated', (data) => {
console.log('Identity activated:', data);
// Update UI, send notifications, etc.
});
Real-world Integration Examples
Complete Identity Management System
class IdentityManagementSystem {
constructor(provider, contractAddress, abi) {
this.provider = provider;
this.contract = new ethers.Contract(contractAddress, abi, provider);
this.contractWithSigner = null;
this.eventManager = new IdentityEventManager(this.contract);
}
setSigner(signer) {
this.contractWithSigner = this.contract.connect(signer);
}
// Complete identity creation flow
async createIdentity(name, metadataURI, autoActivate = false) {
try {
// Step 1: Mint identity
const mintResult = await this.mintIdentity(name, metadataURI);
if (!mintResult.success) {
return mintResult;
}
const tokenId = mintResult.tokenId;
// Step 2: Auto-activate if requested
if (autoActivate) {
const activateResult = await this.activateIdentity(tokenId);
if (!activateResult.success) {
return {
success: false,
error: `Identity minted but activation failed: ${activateResult.error}`,
tokenId
};
}
}
return {
success: true,
tokenId,
isActivated: autoActivate,
transactionHash: mintResult.transactionHash
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// Get comprehensive identity data
async getIdentityData(tokenId) {
try {
const [owner, isActivated, tokenURI, balance] = await Promise.all([
this.contract.ownerOf(tokenId),
this.contract.isActivated(tokenId),
this.contract.tokenURI(tokenId),
this.contract.balanceOf(await this.contract.ownerOf(tokenId))
]);
return {
tokenId: tokenId.toString(),
owner,
isActivated,
tokenURI,
ownerBalance: balance.toString(),
isSoulbound: isActivated
};
} catch (error) {
return {
error: error.message
};
}
}
// Batch operations
async batchMintIdentities(identities) {
const results = [];
for (const identity of identities) {
try {
const result = await this.mintIdentity(identity.name, identity.metadataURI);
results.push({
name: identity.name,
success: result.success,
tokenId: result.tokenId,
error: result.error
});
} catch (error) {
results.push({
name: identity.name,
success: false,
error: error.message
});
}
}
return results;
}
// Start event monitoring
startEventMonitoring() {
this.eventManager.startListening();
}
// Stop event monitoring
stopEventMonitoring() {
this.eventManager.stopListening();
}
}
// Usage
const identitySystem = new IdentityManagementSystem(
provider,
'0x1234567890123456789012345678901234567890',
IDENTITY_SBT_ABI
);
identitySystem.setSigner(signer);
identitySystem.startEventMonitoring();
// Create identity
const result = await identitySystem.createIdentity(
'alice.zks',
'https://api.onzks.com/metadata/alice.zks',
true // Auto-activate
);
if (result.success) {
console.log('Identity created:', result.tokenId);
} else {
console.error('Failed to create identity:', result.error);
}
Best Practices
Security Considerations
- Private Key Management: Never hardcode private keys in your application
- Input Validation: Always validate inputs before sending transactions
- Gas Estimation: Estimate gas before sending transactions
- Error Handling: Implement comprehensive error handling
- Event Monitoring: Monitor events for state changes
Performance Optimization
- Batch Operations: Group multiple operations when possible
- Caching: Cache frequently accessed data
- Event Filtering: Use event filters to reduce data processing
- Gas Optimization: Use appropriate gas limits
- Connection Pooling: Reuse connections when possible
Testing
- Unit Tests: Test individual functions
- Integration Tests: Test complete workflows
- Error Testing: Test error conditions
- Performance Testing: Test under load
- Security Testing: Test for vulnerabilities
Related Documentation
- Contract Overview - Contract architecture and features
- Functions Reference - Complete function documentation
- Events Reference - Event documentation
- Security Guide - Security considerations
- Deployment Guide - Deployment instructions