Overview
Complete guide for integrating ZKScore smart contracts using wagmi hooks in React applications.Installation
Copy
npm install wagmi viem @tanstack/react-query
# or
yarn add wagmi viem @tanstack/react-query
Basic Setup
Provider Configuration
Copy
import { WagmiProvider, createConfig, http } from 'wagmi';
import { mainnet, polygon, arbitrum, base } from 'wagmi/chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { injected, metaMask, walletConnect } from 'wagmi/connectors';
// Create wagmi config
const config = createConfig({
chains: [mainnet, polygon, arbitrum, base],
connectors: [
injected(),
metaMask(),
walletConnect({ projectId: 'YOUR_PROJECT_ID' })
],
transports: {
[mainnet.id]: http(),
[polygon.id]: http(),
[arbitrum.id]: http(),
[base.id]: http()
}
});
// Query client
const queryClient = new QueryClient();
// App component
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
</WagmiProvider>
);
}
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
Get Identity Hook
Copy
import { useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
function useIdentity(userAddress) {
const { data: identity, isLoading, error } = useReadContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'getIdentity',
args: [userAddress]
});
const { data: isActivated } = useReadContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'isActivated',
args: [userAddress]
});
return {
identity,
isActivated,
isLoading,
error
};
}
Mint Identity Hook
Copy
function useMintIdentity() {
const { writeContract, data: hash, isPending } = useWriteContract();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
hash
});
const mintIdentity = async (zksId, displayName, avatarUrl) => {
try {
// Get minting fee
const mintingFee = await publicClient.readContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'mintingFee'
});
await writeContract({
address: CONTRACTS.identitySBT,
abi: IdentitySBTABI,
functionName: 'mintIdentity',
args: [zksId, displayName, avatarUrl],
value: mintingFee
});
} catch (error) {
console.error('Minting failed:', error);
}
};
return {
mintIdentity,
hash,
isPending,
isConfirming,
isSuccess
};
}
Score Operations
Get Score Hook
Copy
function useScore(userAddress) {
const { data: score, isLoading, error } = useReadContract({
address: CONTRACTS.scoreCalculator,
abi: ScoreCalculatorABI,
functionName: 'getScore',
args: [userAddress]
});
const { data: breakdown } = useReadContract({
address: CONTRACTS.scoreCalculator,
abi: ScoreCalculatorABI,
functionName: 'getScoreBreakdown',
args: [userAddress]
});
return {
score,
breakdown,
isLoading,
error
};
}
Score Events Hook
Copy
import { useWatchContractEvent } from 'wagmi';
function useScoreEvents(userAddress) {
const [scoreUpdates, setScoreUpdates] = useState([]);
useWatchContractEvent({
address: CONTRACTS.scoreCalculator,
abi: ScoreCalculatorABI,
eventName: 'ScoreUpdated',
args: {
user: userAddress
},
onLogs: (logs) => {
setScoreUpdates(prev => [...prev, ...logs]);
}
});
return scoreUpdates;
}
Achievement Operations
Get Achievements Hook
Copy
function useAchievements(userAddress) {
const { data: achievementCount } = useReadContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'getUserAchievementCount',
args: [userAddress]
});
const achievements = useMemo(() => {
if (!achievementCount) return [];
const achievementPromises = [];
for (let i = 0; i < Number(achievementCount); i++) {
achievementPromises.push(
publicClient.readContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'getUserAchievement',
args: [userAddress, BigInt(i)]
})
);
}
return Promise.all(achievementPromises);
}, [achievementCount, userAddress]);
return {
achievements,
count: achievementCount
};
}
Claim Achievement Hook
Copy
function useClaimAchievement() {
const { writeContract, data: hash, isPending } = useWriteContract();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
hash
});
const claimAchievement = async (achievementId) => {
try {
await writeContract({
address: CONTRACTS.achievementRegistry,
abi: AchievementRegistryABI,
functionName: 'claimAchievement',
args: [achievementId]
});
} catch (error) {
console.error('Claim failed:', error);
}
};
return {
claimAchievement,
hash,
isPending,
isConfirming,
isSuccess
};
}
React Components
Identity Component
Copy
function IdentityCard({ userAddress }) {
const { identity, isActivated, isLoading } = useIdentity(userAddress);
const { mintIdentity, isPending } = useMintIdentity();
if (isLoading) return <div>Loading...</div>;
if (!identity?.exists) {
return (
<div>
<h3>No Identity Found</h3>
<button
onClick={() => mintIdentity('alice.zks', 'Alice Smith', 'https://example.com/avatar.jpg')}
disabled={isPending}
>
{isPending ? 'Minting...' : 'Mint Identity'}
</button>
</div>
);
}
return (
<div>
<h3>{identity.displayName}</h3>
<p>ZKS ID: {identity.zksId}</p>
<p>Status: {isActivated ? 'Activated' : 'Not Activated'}</p>
</div>
);
}
Score Component
Copy
function ScoreDisplay({ userAddress }) {
const { score, breakdown, isLoading } = useScore(userAddress);
const scoreUpdates = useScoreEvents(userAddress);
if (isLoading) return <div>Loading score...</div>;
return (
<div>
<h3>ZKScore: {score?.total.toString() || '0'}</h3>
<div>
<h4>Breakdown:</h4>
<ul>
<li>DeFi: {breakdown?.defi.toString() || '0'}</li>
<li>NFT: {breakdown?.nft.toString() || '0'}</li>
<li>Social: {breakdown?.social.toString() || '0'}</li>
<li>Trading: {breakdown?.trading.toString() || '0'}</li>
</ul>
</div>
{scoreUpdates.length > 0 && (
<div>
<h4>Recent Updates:</h4>
{scoreUpdates.map((update, index) => (
<div key={index}>
Score: {update.args.oldScore.toString()} → {update.args.newScore.toString()}
</div>
))}
</div>
)}
</div>
);
}
Best Practices
- Use Hooks: Leverage wagmi’s built-in hooks for state management
- Handle Loading States: Always handle loading and error states
- Optimize Re-renders: Use useMemo and useCallback appropriately
- Error Boundaries: Implement error boundaries for better UX
- Type Safety: Use TypeScript for better development experience