ZK Aadhar Vite Setup is designed to build privacy-first identity verification platforms, focusing on security, user privacy, and developer experience. Built with React.js, Vite, and the Anon Aadhar SDK, this open-source template helps developers implement zero-knowledge proof-based Aadhar verification while maintaining complete user anonymity. The template enables secure and private identity verification without exposing any personal data.
1import {
2 AnonAadhaarProof,
3 useAnonAadhaar,
4 useProver,
5} from "@anon-aadhaar/react";
6import { useEffect, useState, useCallback } from "react";
7import { BlockchainService } from "./services/BlockchainService";
8import { ProofStatus } from "./components/ProofStatus";
9import { ethers } from "ethers";
10import ProofGenerationLoader from "./components/ProofGenerationLoader";
11import AnonAadhaarWrapper from "./components/AnonAadhaarWrapper";
12import ProofVerifier from "./components/ProofVerifier";
13import LandingPage from "./components/LandingPage";
14
15const CONTRACT_ADDRESS= "0xA2DD8Fe6933120bF47B2130A46010519C1ef5460";
16export default function App() {
17 const [anonAadhaar] = useAnonAadhaar();
18 const [, latestProof] = useProver();
19 const [isLoading, setIsLoading] = useState(false);
20 const [error, setError] = useState<string | null>(null);
21 const [proofHistory, setProofHistory] = useState<any[]>([]);
22 const [blockchainService, setBlockchainService] = useState<BlockchainService | null>(null);
23 const [walletAddress, setWalletAddress] = useState<string | null>(null);
24 const [currentStep, setCurrentStep] = useState<number>(1);
25 const [transactionHash, setTransactionHash] = useState<string | null>(null);
26 const [isMetaMaskInstalled, setIsMetaMaskInstalled] = useState(false);
27 const [networkName, setNetworkName] = useState<string>("");
28 const [isGeneratingProof, setIsGeneratingProof] = useState(false);
29 const [mode, setMode] = useState<'generate' | 'verify' | null>(null);
30 const [showApp, setShowApp] = useState(false);
31// const CONTRACT_ADDRESS = import.meta.env.VITE_CONTRACT_ADDRESS;
32 useEffect(() => {
33 const initializeBlockchainService = async () => {
34 try {
35 if (window.ethereum) {
36 setIsMetaMaskInstalled(true);
37 const service = new BlockchainService(CONTRACT_ADDRESS);
38 setBlockchainService(service);
39 setCurrentStep(2); // Set to connect wallet step if MetaMask is installed
40 } else {
41 setIsMetaMaskInstalled(false);
42 console.error('MetaMask is not installed');
43 }
44 } catch (error) {
45 console.error('Error initializing blockchain service:', error);
46 }
47 };
48
49 initializeBlockchainService();
50 }, []);
51
52 // Update network name
53 const updateNetworkName = useCallback(async () => {
54 if (window.ethereum) {
55 const provider = new ethers.providers.Web3Provider(window.ethereum);
56 const network = await provider.getNetwork();
57 setNetworkName(network.name);
58 }
59 }, []);
60
61 // Check initial connection and set up listeners
62 useEffect(() => {
63 if (!blockchainService) return;
64
65 const checkConnection = async () => {
66 try {
67 if (window.ethereum) {
68 const accounts = await window.ethereum.request({ method: 'eth_accounts' });
69 if (accounts && accounts.length > 0) {
70 const address = accounts[0];
71 setWalletAddress(address);
72 setCurrentStep(3);
73 await updateNetworkName();
74 }
75 }
76 } catch (err) {
77 console.error('Connection check error:', err);
78 }
79 };
80
81 const handleAccountsChanged = async (accounts: string[]) => {
82 if (accounts.length > 0) {
83 setWalletAddress(accounts[0]);
84 setCurrentStep(3);
85 } else {
86 setWalletAddress(null);
87 setCurrentStep(2);
88 }
89 };
90
91 const handleNetworkChanged = async () => {
92 await updateNetworkName();
93 };
94
95 checkConnection();
96
97 if (window.ethereum) {
98 window.ethereum.on('accountsChanged', handleAccountsChanged);
99 window.ethereum.on('chainChanged', handleNetworkChanged);
100 }
101
102 return () => {
103 if (window.ethereum) {
104 window.ethereum.removeListener('accountsChanged', handleAccountsChanged);
105 window.ethereum.removeListener('chainChanged', handleNetworkChanged);
106 }
107 };
108 }, [blockchainService, updateNetworkName]);
109
110 const handleConnectWallet = async () => {
111 try {
112 setError(null);
113 if (!blockchainService) {
114 throw new Error('Blockchain service not initialized');
115 }
116
117 const address = await blockchainService.connectWallet();
118 setWalletAddress(address);
119 setCurrentStep(3);
120 await updateNetworkName();
121 } catch (err: any) {
122 console.error('Wallet connection error:', err);
123 setError(err.message || 'Failed to connect wallet');
124 }
125 };
126
127 useEffect(() => {
128 const handleProofGeneration = async () => {
129 if (!blockchainService) return;
130
131 if (anonAadhaar.status === "logging-in") {
132 setIsGeneratingProof(true);
133 setError(null);
134 } else if (anonAadhaar.status === "logged-in" && latestProof) {
135 setIsGeneratingProof(false);
136 setCurrentStep(4);
137
138 try {
139 setCurrentStep(5);
140 setIsLoading(true);
141 const result = await blockchainService.submitProof(latestProof);
142 setTransactionHash(result.transactionHash);
143 setProofHistory(prev => [...prev, latestProof]);
144 setIsLoading(false);
145
146 // Show message if proof already exists (when transactionHash is empty)
147 if (!result.transactionHash) {
148 setError("Proof already exists on the blockchain. No need to store again.");
149 }
150 } catch (err: any) {
151 setError("Failed to store proof on blockchain. Please try again.");
152 setIsLoading(false);
153 }
154 } else if (anonAadhaar.status === "logged-out") {
155 // Initial state - user hasn't started the verification process yet
156 setIsGeneratingProof(false);
157 }
158 };
159
160 handleProofGeneration();
161 }, [anonAadhaar.status, latestProof, blockchainService]);
162
163 const renderStep = (stepNumber: number, title: string, isComplete: boolean) => {
164 let status = "";
165 if (stepNumber === 4 && currentStep === 4) {
166 status = "Proof Generated";
167 } else if (stepNumber === 5 && currentStep === 5 && !transactionHash) {
168 status = "Storing on Blockchain...";
169 } else if (stepNumber === 5 && transactionHash) {
170 status = "Stored Successfully";
171 }
172
173 return (
174 <div className="flex items-center gap-4 relative">
175 <div className={`
176 w-8 h-8 rounded-full flex items-center justify-center relative z-10
177 ${currentStep === stepNumber ? 'bg-cyan-500 text-white ring-2 ring-cyan-500/20 ring-offset-2 ring-offset-[#0a1929]' :
178 isComplete ? 'bg-green-500 text-white' : 'bg-[#0c1f35] text-gray-400 border border-blue-500/20'}
179 transition-all duration-300
180 `}>
181 {isComplete ? '✓' : stepNumber}
182 </div>
183 <div className="flex flex-col">
184 <span className={`${currentStep === stepNumber ? 'text-cyan-400 font-semibold' : isComplete ? 'text-gray-200' : 'text-gray-400'} transition-colors`}>
185 {title}
186 </span>
187 {status && (
188 <span className={`text-sm ${
189 status.includes('Successfully') ? 'text-green-400' :
190 status.includes('Generated') ? 'text-cyan-400' :
191 'text-gray-400'
192 }`}>
193 {status}
194 </span>
195 )}
196 </div>
197 </div>
198 );
199 };
200
201 const handleLogout = () => {
202 setProofHistory([]);
203 setTransactionHash(null);
204 setCurrentStep(isMetaMaskInstalled ? 2 : 1);
205 };
206
207 const handleDownloadProof = () => {
208 if (!latestProof || !transactionHash) return;
209
210 const proofData = {
211 proof: latestProof,
212 transactionHash,
213 timestamp: Date.now(),
214 network: networkName,
215 contract: CONTRACT_ADDRESS
216 };
217
218 const blob = new Blob([JSON.stringify(proofData, null, 2)], { type: 'application/json' });
219 const url = URL.createObjectURL(blob);
220 const a = document.createElement('a');
221 a.href = url;
222 a.download = `anon-aadhaar-proof-${Date.now()}.json`;
223 document.body.appendChild(a);
224 a.click();
225 document.body.removeChild(a);
226 URL.revokeObjectURL(url);
227 };
228
229 return (
230 <div>
231 {!showApp ? (
232 <LandingPage onLaunchApp={() => setShowApp(true)} />
233 ) : (
234 <div className="min-h-screen bg-[#0a1929] text-gray-200">
235 {/* Header */}
236 <header className="bg-[#0a1929]/80 backdrop-blur-md border-b border-blue-500/10 sticky top-0 z-50 transition-all duration-300">
237 <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
238 <div className="flex justify-between items-center h-20">
239 <div className="flex items-center gap-4 group cursor-pointer">
240 <div className="relative">
241 <div className="absolute -inset-2 bg-gradient-to-r from-blue-500 to-blue-600 rounded-full blur-lg opacity-20 group-hover:opacity-40 transition-opacity duration-300"></div>
242 <img src="/logo.svg" alt="Logo" className="h-10 w-10 relative" />
243 </div>
244 <span className="font-bold text-2xl text-white group-hover:text-blue-400 transition-all duration-300">
245 ZK Aadhar
246 </span>
247 </div>
248 <button
249 onClick={() => setShowApp(false)}
250 className="inline-flex items-center px-6 py-2.5 text-sm font-medium rounded-full text-white bg-blue-600 hover:bg-blue-700 transition-all duration-300 hover:scale-105"
251 >
252 ← Back to Home
253 </button>
254 </div>
255 </div>
256 </header>
257
258 {/* Main Content */}
259 <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
260 {!mode ? (
261 <div className="space-y-12">
262 <div className="text-center">
263 <h2 className="text-3xl font-bold mb-4">Choose an Option</h2>
264 </div>
265
266 <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
267 {/* Option 1 */}
268 <div
269 onClick={() => setMode('generate')}
270 className="group relative cursor-pointer"
271 >
272 <div className="absolute -inset-0.5 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-lg blur opacity-25 group-hover:opacity-75 transition duration-300"></div>
273 <div className="relative p-8 bg-[#0c1f35] rounded-lg border border-blue-500/10 hover:border-blue-500/30 transition-all duration-300">
274 <h3 className="text-xl font-semibold mb-4 text-gray-200 group-hover:text-cyan-400 transition-colors">Option 1: New User</h3>
275 <p className="text-gray-400 group-hover:text-gray-300 transition-colors">
276 Generate a new anonymous proof using your Aadhar card and store it on-chain.
277 </p>
278 </div>
279 </div>
280
281 {/* Option 2 */}
282 <div
283 onClick={() => setMode('verify')}
284 className="group relative cursor-pointer"
285 >
286 <div className="absolute -inset-0.5 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-lg blur opacity-25 group-hover:opacity-75 transition duration-300"></div>
287 <div className="relative p-8 bg-[#0c1f35] rounded-lg border border-blue-500/10 hover:border-blue-500/30 transition-all duration-300">
288 <h3 className="text-xl font-semibold mb-4 text-gray-200 group-hover:text-cyan-400 transition-colors">Option 2: Verify Existing Proof</h3>
289 <p className="text-gray-400 group-hover:text-gray-300 transition-colors">
290 Verify an existing proof by uploading the JSON file and checking its validity on-chain.
291 </p>
292 </div>
293 </div>
294 </div>
295 </div>
296 ) : mode === 'generate' ? (
297 <div className="space-y-8">
298 <div>
299 <button
300 onClick={() => setMode(null)}
301 className="text-gray-400 hover:text-gray-200 transition-colors flex items-center gap-2 mb-8"
302 >
303 <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
304 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
305 </svg>
306 Back to Options
307 </button>
308
309 {/* Steps */}
310 <div className="relative">
311 <div className="absolute left-4 inset-y-0 w-0.5 bg-gradient-to-b from-blue-500/50 via-cyan-500/50 to-transparent"></div>
312 <div className="relative space-y-6 pl-12">
313 {renderStep(1, "Install MetaMask", currentStep > 1)}
314 {renderStep(2, "Connect Wallet", currentStep > 2)}
315 {renderStep(3, "Provide Aadhar QR", currentStep > 3)}
316 {renderStep(4, "Generate Proof", currentStep > 4)}
317 {renderStep(5, "Store on Blockchain", currentStep > 5)}
318 </div>
319 </div>
320
321 <div className="mt-8 space-y-4">
322 <p className="text-gray-400">
323 Prove your Identity anonymously using your Aadhar card.
324 Your proof will be securely stored on Sepolia testnet.
325 </p>
326
327 {error && (
328 <div className="p-4 bg-red-500/10 border border-red-500/30 rounded-lg">
329 <p className="text-red-400">{error}</p>
330 </div>
331 )}
332
333 {!isMetaMaskInstalled && (
334 <div className="p-4 bg-[#0c1f35] rounded-lg border border-blue-500/10">
335 <p className="text-gray-300 mb-4">Please install MetaMask to continue</p>
336 <a
337 href="https://metamask.io/download/"
338 target="_blank"
339 rel="noopener noreferrer"
340 className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
341 >
342 Install MetaMask
343 </a>
344 </div>
345 )}
346
347 {isMetaMaskInstalled && !walletAddress && (
348 <button
349 onClick={handleConnectWallet}
350 className="w-full px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center justify-center gap-2"
351 >
352 Connect Wallet
353 </button>
354 )}
355
356 {walletAddress && !isGeneratingProof && currentStep === 3 && (
357 <div className="space-y-4">
358 <div className="p-4 bg-[#0c1f35] rounded-lg border border-blue-500/10">
359 <p className="text-gray-300 mb-2">Connected Wallet:</p>
360 <p className="font-mono text-cyan-400 break-all">{walletAddress}</p>
361 <p className="text-gray-400 mt-2">Network: {networkName}</p>
362 </div>
363 <AnonAadhaarWrapper
364 onProofGenerated={() => {
365 setIsGeneratingProof(true);
366 }}
367 onVerify={() => {
368 setCurrentStep(4);
369 }}
370 onError={(error) => {
371 setError(error.message);
372 setIsGeneratingProof(false);
373 }}
374 />
375 </div>
376 )}
377
378 {isGeneratingProof && (
379 <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-[9999]">
380 <div className="bg-[#0a1929] rounded-lg p-8 mx-4 border border-blue-500/20">
381 <ProofGenerationLoader />
382 </div>
383 </div>
384 )}
385
386 {anonAadhaar.status === "logged-in" && (
387 <div className="space-y-6">
388 <div className="p-6 bg-[#0c1f35] rounded-lg border border-blue-500/10">
389 <ProofStatus
390 status="success"
391 proofHash={latestProof ? ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(latestProof))) : undefined}
392 transactionHash={transactionHash || undefined}
393 timestamp={Date.now() / 1000}
394 />
395
396 {latestProof && transactionHash && (
397 <div className="flex gap-4 mt-6">
398 <button
399 onClick={handleDownloadProof}
400 className="px-4 py-2 bg-gradient-to-r from-blue-600 to-cyan-600 text-white rounded-lg hover:from-blue-700 hover:to-cyan-700 transition-all duration-300 flex items-center gap-2 shadow-lg shadow-blue-500/20"
401 >
402 <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
403 <path fillRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clipRule="evenodd" />
404 </svg>
405 Download Proof
406 </button>
407
408 <button
409 onClick={() => {
410 handleLogout();
411 setMode(null);
412 }}
413 className="px-4 py-2 bg-red-500/10 text-red-400 border border-red-500/20 rounded-lg hover:bg-red-500/20 transition-all duration-300"
414 >
415 Start New Verification
416 </button>
417 </div>
418 )}
419 </div>
420
421 {latestProof && (
422 <div className="p-6 bg-[#0c1f35] rounded-lg border border-blue-500/10">
423 <div className="flex justify-between items-center mb-4">
424 <h3 className="font-semibold text-gray-200">Proof Details</h3>
425 {transactionHash && (
426 <a
427 href={`https://sepolia.etherscan.io/tx/${transactionHash}`}
428 target="_blank"
429 rel="noopener noreferrer"
430 className="text-cyan-400 hover:text-cyan-300 transition-colors flex items-center gap-2"
431 >
432 <span>View on Etherscan</span>
433 <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
434 <path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
435 <path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
436 </svg>
437 </a>
438 )}
439 </div>
440 <div className="bg-[#0a1929] rounded-lg p-4 border border-blue-500/10">
441 <AnonAadhaarProof code={JSON.stringify(latestProof, null, 2)} />
442 </div>
443 </div>
444 )}
445 </div>
446 )}
447
448 {isLoading && !isGeneratingProof && (
449 <div className="flex flex-col items-center gap-3 p-6 bg-[#0c1f35] rounded-lg border border-blue-500/10">
450 <div className="w-10 h-10 border-t-2 border-blue-500 rounded-full animate-spin"></div>
451 <p className="text-gray-300">Processing blockchain transaction...</p>
452 </div>
453 )}
454 </div>
455 </div>
456 </div>
457 ) : (
458 <div>
459 <button
460 onClick={() => setMode(null)}
461 className="text-gray-400 hover:text-gray-200 transition-colors flex items-center gap-2 mb-8"
462 >
463 <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
464 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
465 </svg>
466 Back to Options
467 </button>
468
469 <ProofVerifier blockchainService={blockchainService} />
470 </div>
471 )}
472 </div>
473 </div>
474 )}
475 </div>
476 );
477}
478
Last updated 2 months ago