@treza/react 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +347 -0
- package/dist/index.esm.js +1041 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +1059 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,1041 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { TrezaComplianceSDK } from '@treza/sdk';
|
|
3
|
+
import { ethers } from 'ethers';
|
|
4
|
+
|
|
5
|
+
// Context for compliance SDK
|
|
6
|
+
const ComplianceContext = React.createContext({
|
|
7
|
+
sdk: null,
|
|
8
|
+
isLoading: true,
|
|
9
|
+
error: null
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Provider component that initializes the compliance SDK
|
|
13
|
+
*/
|
|
14
|
+
const ComplianceProvider = ({ provider, signer, children }) => {
|
|
15
|
+
const [sdk, setSdk] = useState(null);
|
|
16
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
17
|
+
const [error, setError] = useState(null);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
try {
|
|
20
|
+
const config = {
|
|
21
|
+
zkPassportDomain: "trezalabs.com",
|
|
22
|
+
zkVerifyEndpoint: "https://api.zkverify.io",
|
|
23
|
+
trezaTokenAddress: process.env.REACT_APP_TREZA_TOKEN_ADDRESS || "0x8278d4FbfaB7dac14eC0295421D0a2733b4504E5",
|
|
24
|
+
complianceVerifierAddress: process.env.REACT_APP_COMPLIANCE_VERIFIER_ADDRESS || "0x8c0C6e0Eaf6bc693745A1A3a722e2c9028BBe874",
|
|
25
|
+
complianceIntegrationAddress: process.env.REACT_APP_COMPLIANCE_INTEGRATION_ADDRESS || "0xf3ecfC409761D715F137Bfe7078Acec6d7F55428",
|
|
26
|
+
provider,
|
|
27
|
+
signer
|
|
28
|
+
};
|
|
29
|
+
const complianceSDK = new TrezaComplianceSDK(config);
|
|
30
|
+
setSdk(complianceSDK);
|
|
31
|
+
setError(null);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
setError(err instanceof Error ? err.message : 'Failed to initialize compliance SDK');
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
setIsLoading(false);
|
|
38
|
+
}
|
|
39
|
+
}, [provider, signer]);
|
|
40
|
+
return (React.createElement(ComplianceContext.Provider, { value: { sdk, isLoading, error } }, children));
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Hook to use compliance context (for use within ComplianceProvider)
|
|
44
|
+
*/
|
|
45
|
+
const useComplianceContext = () => {
|
|
46
|
+
const context = React.useContext(ComplianceContext);
|
|
47
|
+
if (!context) {
|
|
48
|
+
throw new Error('useComplianceContext must be used within a ComplianceProvider');
|
|
49
|
+
}
|
|
50
|
+
return context;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Main compliance verification component
|
|
54
|
+
*/
|
|
55
|
+
const ComplianceVerification = ({ userAddress, requirements, onVerificationComplete, onError, className = "" }) => {
|
|
56
|
+
const { sdk } = useComplianceContext();
|
|
57
|
+
const [verificationUrl, setVerificationUrl] = useState("");
|
|
58
|
+
const [qrCode, setQrCode] = useState("");
|
|
59
|
+
const [isGenerating, setIsGenerating] = useState(false);
|
|
60
|
+
const [status, setStatus] = useState('idle');
|
|
61
|
+
const [error, setError] = useState("");
|
|
62
|
+
const initiateVerification = useCallback(async () => {
|
|
63
|
+
if (!sdk)
|
|
64
|
+
return;
|
|
65
|
+
setIsGenerating(true);
|
|
66
|
+
setStatus('generating');
|
|
67
|
+
setError("");
|
|
68
|
+
try {
|
|
69
|
+
const url = await sdk.initiateVerification(requirements);
|
|
70
|
+
setVerificationUrl(url);
|
|
71
|
+
const qr = await sdk.generateQRCode(url);
|
|
72
|
+
setQrCode(qr);
|
|
73
|
+
setStatus('waiting');
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to initiate verification';
|
|
77
|
+
setError(errorMessage);
|
|
78
|
+
setStatus('error');
|
|
79
|
+
onError?.(errorMessage);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
setIsGenerating(false);
|
|
83
|
+
}
|
|
84
|
+
}, [sdk, requirements, onError]);
|
|
85
|
+
useCallback(async (zkPassportResult) => {
|
|
86
|
+
if (!sdk)
|
|
87
|
+
return;
|
|
88
|
+
setStatus('processing');
|
|
89
|
+
try {
|
|
90
|
+
const result = await sdk.processVerificationResult(zkPassportResult, userAddress);
|
|
91
|
+
setStatus('complete');
|
|
92
|
+
onVerificationComplete?.(result);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to process verification';
|
|
96
|
+
setError(errorMessage);
|
|
97
|
+
setStatus('error');
|
|
98
|
+
onError?.(errorMessage);
|
|
99
|
+
}
|
|
100
|
+
}, [sdk, userAddress, onVerificationComplete, onError]);
|
|
101
|
+
return (React.createElement("div", { className: `treza-compliance-verification ${className}` },
|
|
102
|
+
React.createElement("div", { className: "verification-header" },
|
|
103
|
+
React.createElement("h3", null, "\uD83D\uDEE1\uFE0F Identity Verification"),
|
|
104
|
+
React.createElement("p", null, "Verify your identity to access TREZA features while keeping your personal data private.")),
|
|
105
|
+
status === 'idle' && (React.createElement("div", { className: "verification-start" },
|
|
106
|
+
React.createElement("button", { onClick: initiateVerification, disabled: isGenerating, className: "btn-primary" }, isGenerating ? 'Generating...' : 'Start Verification'))),
|
|
107
|
+
status === 'generating' && (React.createElement("div", { className: "verification-generating" },
|
|
108
|
+
React.createElement("div", { className: "spinner" }),
|
|
109
|
+
React.createElement("p", null, "Generating verification request..."))),
|
|
110
|
+
status === 'waiting' && (React.createElement("div", { className: "verification-waiting" },
|
|
111
|
+
React.createElement("div", { className: "qr-code-section" },
|
|
112
|
+
React.createElement("h4", null, "\uD83D\uDCF1 Scan with ZKPassport App"),
|
|
113
|
+
qrCode && (React.createElement("img", { src: qrCode, alt: "Verification QR Code", className: "qr-code" })),
|
|
114
|
+
React.createElement("p", null, "Scan this QR code with the ZKPassport mobile app to verify your identity."),
|
|
115
|
+
React.createElement("a", { href: verificationUrl, target: "_blank", rel: "noopener noreferrer", className: "verification-link" }, "Or open verification link directly")),
|
|
116
|
+
React.createElement("div", { className: "verification-instructions" },
|
|
117
|
+
React.createElement("h5", null, "Instructions:"),
|
|
118
|
+
React.createElement("ol", null,
|
|
119
|
+
React.createElement("li", null, "Download the ZKPassport app if you haven't already"),
|
|
120
|
+
React.createElement("li", null, "Scan the QR code above"),
|
|
121
|
+
React.createElement("li", null, "Follow the app instructions to verify your government ID"),
|
|
122
|
+
React.createElement("li", null, "Your verification will be processed automatically"))))),
|
|
123
|
+
status === 'processing' && (React.createElement("div", { className: "verification-processing" },
|
|
124
|
+
React.createElement("div", { className: "spinner" }),
|
|
125
|
+
React.createElement("p", null, "Processing your verification..."))),
|
|
126
|
+
status === 'complete' && (React.createElement("div", { className: "verification-complete" },
|
|
127
|
+
React.createElement("div", { className: "success-icon" }, "\u2705"),
|
|
128
|
+
React.createElement("h4", null, "Verification Complete!"),
|
|
129
|
+
React.createElement("p", null, "Your identity has been verified successfully. You now have access to TREZA compliance features."))),
|
|
130
|
+
status === 'error' && (React.createElement("div", { className: "verification-error" },
|
|
131
|
+
React.createElement("div", { className: "error-icon" }, "\u274C"),
|
|
132
|
+
React.createElement("h4", null, "Verification Error"),
|
|
133
|
+
React.createElement("p", null, error),
|
|
134
|
+
React.createElement("button", { onClick: () => setStatus('idle'), className: "btn-secondary" }, "Try Again")))));
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Component to display current compliance status
|
|
138
|
+
*/
|
|
139
|
+
const ComplianceStatusDisplay = ({ userAddress, showDetails = false, className = "" }) => {
|
|
140
|
+
const { sdk } = useComplianceContext();
|
|
141
|
+
const [status, setStatus] = useState(null);
|
|
142
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
143
|
+
const [error, setError] = useState("");
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const checkStatus = async () => {
|
|
146
|
+
if (!sdk || !userAddress)
|
|
147
|
+
return;
|
|
148
|
+
setIsLoading(true);
|
|
149
|
+
try {
|
|
150
|
+
const complianceStatus = await sdk.checkComplianceStatus(userAddress);
|
|
151
|
+
setStatus(complianceStatus);
|
|
152
|
+
setError("");
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
setError(err instanceof Error ? err.message : 'Failed to check compliance status');
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
setIsLoading(false);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
checkStatus();
|
|
162
|
+
}, [sdk, userAddress]);
|
|
163
|
+
if (isLoading) {
|
|
164
|
+
return (React.createElement("div", { className: `compliance-status loading ${className}` },
|
|
165
|
+
React.createElement("div", { className: "spinner" }),
|
|
166
|
+
React.createElement("span", null, "Checking compliance status...")));
|
|
167
|
+
}
|
|
168
|
+
if (error) {
|
|
169
|
+
return (React.createElement("div", { className: `compliance-status error ${className}` },
|
|
170
|
+
React.createElement("span", { className: "status-icon" }, "\u26A0\uFE0F"),
|
|
171
|
+
React.createElement("span", null,
|
|
172
|
+
"Error: ",
|
|
173
|
+
error)));
|
|
174
|
+
}
|
|
175
|
+
if (!status) {
|
|
176
|
+
return (React.createElement("div", { className: `compliance-status unknown ${className}` },
|
|
177
|
+
React.createElement("span", { className: "status-icon" }, "\u2753"),
|
|
178
|
+
React.createElement("span", null, "Status unknown")));
|
|
179
|
+
}
|
|
180
|
+
return (React.createElement("div", { className: `compliance-status ${status.isCompliant ? 'compliant' : 'non-compliant'} ${className}` },
|
|
181
|
+
React.createElement("div", { className: "status-header" },
|
|
182
|
+
React.createElement("span", { className: "status-icon" }, status.isCompliant ? '✅' : '❌'),
|
|
183
|
+
React.createElement("span", { className: "status-text" }, status.isCompliant ? 'Verified' : 'Not Verified')),
|
|
184
|
+
showDetails && status.isCompliant && (React.createElement("div", { className: "status-details" },
|
|
185
|
+
React.createElement("div", { className: "detail-item" },
|
|
186
|
+
React.createElement("span", { className: "label" }, "Level:"),
|
|
187
|
+
React.createElement("span", { className: "value" }, status.verificationLevel)),
|
|
188
|
+
React.createElement("div", { className: "detail-item" },
|
|
189
|
+
React.createElement("span", { className: "label" }, "Expires:"),
|
|
190
|
+
React.createElement("span", { className: "value" }, new Date(status.expirationTime).toLocaleDateString())),
|
|
191
|
+
React.createElement("div", { className: "detail-item" },
|
|
192
|
+
React.createElement("span", { className: "label" }, "Attributes:"),
|
|
193
|
+
React.createElement("div", { className: "attributes" },
|
|
194
|
+
status.attributes.ageVerified && React.createElement("span", { className: "attribute" }, "Age \u2713"),
|
|
195
|
+
status.attributes.nationalityVerified && React.createElement("span", { className: "attribute" }, "Nationality \u2713"),
|
|
196
|
+
status.attributes.uniquenessVerified && React.createElement("span", { className: "attribute" }, "Uniqueness \u2713")))))));
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Component to display governance eligibility
|
|
200
|
+
*/
|
|
201
|
+
const GovernanceEligibility = ({ userAddress, proposalId, className = "" }) => {
|
|
202
|
+
const { sdk } = useComplianceContext();
|
|
203
|
+
const [eligibility, setEligibility] = useState(null);
|
|
204
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
const checkEligibility = async () => {
|
|
207
|
+
if (!sdk || !userAddress)
|
|
208
|
+
return;
|
|
209
|
+
setIsLoading(true);
|
|
210
|
+
try {
|
|
211
|
+
const result = await sdk.checkGovernanceEligibility(userAddress, proposalId);
|
|
212
|
+
setEligibility(result);
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
console.error('Error checking governance eligibility:', err);
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
setIsLoading(false);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
checkEligibility();
|
|
222
|
+
}, [sdk, userAddress, proposalId]);
|
|
223
|
+
if (isLoading) {
|
|
224
|
+
return (React.createElement("div", { className: `governance-eligibility loading ${className}` },
|
|
225
|
+
React.createElement("div", { className: "spinner" }),
|
|
226
|
+
React.createElement("span", null, "Checking eligibility...")));
|
|
227
|
+
}
|
|
228
|
+
if (!eligibility) {
|
|
229
|
+
return (React.createElement("div", { className: `governance-eligibility error ${className}` },
|
|
230
|
+
React.createElement("span", null, "Unable to check eligibility")));
|
|
231
|
+
}
|
|
232
|
+
return (React.createElement("div", { className: `governance-eligibility ${eligibility.canParticipate ? 'eligible' : 'ineligible'} ${className}` },
|
|
233
|
+
React.createElement("div", { className: "eligibility-status" },
|
|
234
|
+
React.createElement("span", { className: "status-icon" }, eligibility.canParticipate ? '🗳️' : '🚫'),
|
|
235
|
+
React.createElement("span", { className: "status-text" }, eligibility.canParticipate ? 'Eligible to Vote' : 'Not Eligible')),
|
|
236
|
+
eligibility.canParticipate && (React.createElement("div", { className: "voting-details" },
|
|
237
|
+
React.createElement("div", { className: "voting-weight" },
|
|
238
|
+
React.createElement("span", { className: "label" }, "Voting Weight:"),
|
|
239
|
+
React.createElement("span", { className: "value" },
|
|
240
|
+
eligibility.votingWeight,
|
|
241
|
+
"x")),
|
|
242
|
+
React.createElement("div", { className: "compliance-level" },
|
|
243
|
+
React.createElement("span", { className: "label" }, "Compliance Level:"),
|
|
244
|
+
React.createElement("span", { className: "value" }, eligibility.complianceLevel))))));
|
|
245
|
+
};
|
|
246
|
+
// CSS styles (you would typically put this in a separate CSS file)
|
|
247
|
+
const complianceStyles = `
|
|
248
|
+
.treza-compliance-verification {
|
|
249
|
+
max-width: 500px;
|
|
250
|
+
margin: 0 auto;
|
|
251
|
+
padding: 20px;
|
|
252
|
+
border: 1px solid #e0e0e0;
|
|
253
|
+
border-radius: 8px;
|
|
254
|
+
background: #fff;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.verification-header h3 {
|
|
258
|
+
margin: 0 0 10px 0;
|
|
259
|
+
color: #333;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.verification-header p {
|
|
263
|
+
margin: 0 0 20px 0;
|
|
264
|
+
color: #666;
|
|
265
|
+
font-size: 14px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.btn-primary, .btn-secondary {
|
|
269
|
+
padding: 12px 24px;
|
|
270
|
+
border: none;
|
|
271
|
+
border-radius: 6px;
|
|
272
|
+
font-size: 16px;
|
|
273
|
+
cursor: pointer;
|
|
274
|
+
transition: background-color 0.2s;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.btn-primary {
|
|
278
|
+
background: #007bff;
|
|
279
|
+
color: white;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.btn-primary:hover {
|
|
283
|
+
background: #0056b3;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.btn-primary:disabled {
|
|
287
|
+
background: #ccc;
|
|
288
|
+
cursor: not-allowed;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.btn-secondary {
|
|
292
|
+
background: #6c757d;
|
|
293
|
+
color: white;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.qr-code {
|
|
297
|
+
max-width: 200px;
|
|
298
|
+
height: auto;
|
|
299
|
+
margin: 10px 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.spinner {
|
|
303
|
+
width: 20px;
|
|
304
|
+
height: 20px;
|
|
305
|
+
border: 2px solid #f3f3f3;
|
|
306
|
+
border-top: 2px solid #007bff;
|
|
307
|
+
border-radius: 50%;
|
|
308
|
+
animation: spin 1s linear infinite;
|
|
309
|
+
display: inline-block;
|
|
310
|
+
margin-right: 10px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@keyframes spin {
|
|
314
|
+
0% { transform: rotate(0deg); }
|
|
315
|
+
100% { transform: rotate(360deg); }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.compliance-status {
|
|
319
|
+
display: flex;
|
|
320
|
+
align-items: center;
|
|
321
|
+
padding: 10px;
|
|
322
|
+
border-radius: 6px;
|
|
323
|
+
margin: 10px 0;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.compliance-status.compliant {
|
|
327
|
+
background: #d4edda;
|
|
328
|
+
color: #155724;
|
|
329
|
+
border: 1px solid #c3e6cb;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.compliance-status.non-compliant {
|
|
333
|
+
background: #f8d7da;
|
|
334
|
+
color: #721c24;
|
|
335
|
+
border: 1px solid #f5c6cb;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.status-icon {
|
|
339
|
+
margin-right: 8px;
|
|
340
|
+
font-size: 18px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.status-details {
|
|
344
|
+
margin-top: 10px;
|
|
345
|
+
font-size: 14px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.detail-item {
|
|
349
|
+
margin: 5px 0;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.label {
|
|
353
|
+
font-weight: bold;
|
|
354
|
+
margin-right: 8px;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.attributes {
|
|
358
|
+
display: flex;
|
|
359
|
+
gap: 8px;
|
|
360
|
+
flex-wrap: wrap;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.attribute {
|
|
364
|
+
background: #e9ecef;
|
|
365
|
+
padding: 2px 6px;
|
|
366
|
+
border-radius: 4px;
|
|
367
|
+
font-size: 12px;
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
370
|
+
|
|
371
|
+
class ComplianceService {
|
|
372
|
+
constructor(config = {}) {
|
|
373
|
+
this.sdk = null;
|
|
374
|
+
this.config = {
|
|
375
|
+
zkPassportDomain: "trezalabs.com",
|
|
376
|
+
zkVerifyEndpoint: "https://api.zkverify.io",
|
|
377
|
+
trezaTokenAddress: process.env.REACT_APP_TREZA_TOKEN_ADDRESS || "0x8278d4FbfaB7dac14eC0295421D0a2733b4504E5",
|
|
378
|
+
complianceVerifierAddress: process.env.REACT_APP_COMPLIANCE_VERIFIER_ADDRESS || "0x8c0C6e0Eaf6bc693745A1A3a722e2c9028BBe874",
|
|
379
|
+
complianceIntegrationAddress: process.env.REACT_APP_COMPLIANCE_INTEGRATION_ADDRESS || "0xf3ecfC409761D715F137Bfe7078Acec6d7F55428",
|
|
380
|
+
...config
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Initialize the compliance SDK with provider and signer
|
|
385
|
+
*/
|
|
386
|
+
async initialize(provider, signer) {
|
|
387
|
+
try {
|
|
388
|
+
this.sdk = new TrezaComplianceSDK({
|
|
389
|
+
zkPassportDomain: this.config.zkPassportDomain,
|
|
390
|
+
zkVerifyEndpoint: this.config.zkVerifyEndpoint,
|
|
391
|
+
zkVerifyApiKey: this.config.zkVerifyApiKey,
|
|
392
|
+
trezaTokenAddress: this.config.trezaTokenAddress,
|
|
393
|
+
complianceVerifierAddress: this.config.complianceVerifierAddress,
|
|
394
|
+
complianceIntegrationAddress: this.config.complianceIntegrationAddress,
|
|
395
|
+
provider,
|
|
396
|
+
signer
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
console.error('Failed to initialize compliance service:', error);
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get the initialized SDK instance
|
|
406
|
+
*/
|
|
407
|
+
getSDK() {
|
|
408
|
+
if (!this.sdk) {
|
|
409
|
+
throw new Error('Compliance service not initialized. Call initialize() first.');
|
|
410
|
+
}
|
|
411
|
+
return this.sdk;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Check if the service is initialized
|
|
415
|
+
*/
|
|
416
|
+
isInitialized() {
|
|
417
|
+
return this.sdk !== null;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Initiate verification with React-friendly error handling
|
|
421
|
+
*/
|
|
422
|
+
async initiateVerification(requirements) {
|
|
423
|
+
try {
|
|
424
|
+
if (!this.sdk) {
|
|
425
|
+
throw new Error('Service not initialized');
|
|
426
|
+
}
|
|
427
|
+
const url = await this.sdk.initiateVerification(requirements);
|
|
428
|
+
return { success: true, url };
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
432
|
+
return { success: false, error: errorMessage };
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Process verification result with React-friendly error handling
|
|
437
|
+
*/
|
|
438
|
+
async processVerificationResult(zkPassportResult, userAddress) {
|
|
439
|
+
try {
|
|
440
|
+
if (!this.sdk) {
|
|
441
|
+
throw new Error('Service not initialized');
|
|
442
|
+
}
|
|
443
|
+
const result = await this.sdk.processVerificationResult(zkPassportResult, userAddress);
|
|
444
|
+
return { success: true, result };
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
448
|
+
return { success: false, error: errorMessage };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Check compliance status with React-friendly error handling
|
|
453
|
+
*/
|
|
454
|
+
async checkComplianceStatus(userAddress) {
|
|
455
|
+
try {
|
|
456
|
+
if (!this.sdk) {
|
|
457
|
+
throw new Error('Service not initialized');
|
|
458
|
+
}
|
|
459
|
+
const status = await this.sdk.checkComplianceStatus(userAddress);
|
|
460
|
+
return { success: true, status };
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
464
|
+
return { success: false, error: errorMessage };
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Generate QR code for verification URL
|
|
469
|
+
*/
|
|
470
|
+
async generateQRCode(url) {
|
|
471
|
+
try {
|
|
472
|
+
if (!this.sdk) {
|
|
473
|
+
throw new Error('Service not initialized');
|
|
474
|
+
}
|
|
475
|
+
const qrCode = await this.sdk.generateQRCode(url);
|
|
476
|
+
return { success: true, qrCode };
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
480
|
+
return { success: false, error: errorMessage };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Batch check compliance for multiple users
|
|
485
|
+
*/
|
|
486
|
+
async batchCheckCompliance(userAddresses) {
|
|
487
|
+
try {
|
|
488
|
+
if (!this.sdk) {
|
|
489
|
+
throw new Error('Service not initialized');
|
|
490
|
+
}
|
|
491
|
+
const results = await this.sdk.batchCheckCompliance(userAddresses);
|
|
492
|
+
return { success: true, results };
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
496
|
+
return { success: false, error: errorMessage };
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Check governance eligibility
|
|
501
|
+
*/
|
|
502
|
+
async checkGovernanceEligibility(userAddress, proposalId) {
|
|
503
|
+
try {
|
|
504
|
+
if (!this.sdk) {
|
|
505
|
+
throw new Error('Service not initialized');
|
|
506
|
+
}
|
|
507
|
+
if (!proposalId) {
|
|
508
|
+
throw new Error('Proposal ID is required');
|
|
509
|
+
}
|
|
510
|
+
const proposalIdNum = parseInt(proposalId, 10);
|
|
511
|
+
if (isNaN(proposalIdNum)) {
|
|
512
|
+
throw new Error('Invalid proposal ID: must be a number');
|
|
513
|
+
}
|
|
514
|
+
const eligibility = await this.sdk.checkGovernanceEligibility(userAddress, proposalIdNum);
|
|
515
|
+
return { success: true, eligibility };
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
519
|
+
return { success: false, error: errorMessage };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Cleanup resources
|
|
524
|
+
*/
|
|
525
|
+
destroy() {
|
|
526
|
+
this.sdk = null;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// Singleton instance for React apps
|
|
530
|
+
let complianceServiceInstance = null;
|
|
531
|
+
/**
|
|
532
|
+
* Get or create the global compliance service instance
|
|
533
|
+
*/
|
|
534
|
+
function getComplianceService(config) {
|
|
535
|
+
if (!complianceServiceInstance) {
|
|
536
|
+
complianceServiceInstance = new ComplianceService(config);
|
|
537
|
+
}
|
|
538
|
+
return complianceServiceInstance;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Reset the global compliance service instance
|
|
542
|
+
*/
|
|
543
|
+
function resetComplianceService() {
|
|
544
|
+
if (complianceServiceInstance) {
|
|
545
|
+
complianceServiceInstance.destroy();
|
|
546
|
+
complianceServiceInstance = null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
class WalletService {
|
|
551
|
+
constructor() {
|
|
552
|
+
this.provider = null;
|
|
553
|
+
this.signer = null;
|
|
554
|
+
this.currentAddress = null;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Connect to MetaMask or other Web3 wallet
|
|
558
|
+
*/
|
|
559
|
+
async connectWallet() {
|
|
560
|
+
try {
|
|
561
|
+
// Check if Web3 is available
|
|
562
|
+
if (typeof window === 'undefined' || !window.ethereum) {
|
|
563
|
+
return {
|
|
564
|
+
success: false,
|
|
565
|
+
error: 'Web3 wallet not found. Please install MetaMask or another Web3 wallet.'
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
// Request account access
|
|
569
|
+
await window.ethereum.request({ method: 'eth_requestAccounts' });
|
|
570
|
+
// Create provider and signer
|
|
571
|
+
const provider = new ethers.BrowserProvider(window.ethereum);
|
|
572
|
+
const signer = await provider.getSigner();
|
|
573
|
+
const address = await signer.getAddress();
|
|
574
|
+
const network = await provider.getNetwork();
|
|
575
|
+
// Store references
|
|
576
|
+
this.provider = provider;
|
|
577
|
+
this.signer = signer;
|
|
578
|
+
this.currentAddress = address;
|
|
579
|
+
return {
|
|
580
|
+
success: true,
|
|
581
|
+
provider,
|
|
582
|
+
signer,
|
|
583
|
+
address,
|
|
584
|
+
chainId: Number(network.chainId)
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to connect wallet';
|
|
589
|
+
return {
|
|
590
|
+
success: false,
|
|
591
|
+
error: errorMessage
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Disconnect wallet
|
|
597
|
+
*/
|
|
598
|
+
disconnect() {
|
|
599
|
+
this.provider = null;
|
|
600
|
+
this.signer = null;
|
|
601
|
+
this.currentAddress = null;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Get current wallet info
|
|
605
|
+
*/
|
|
606
|
+
async getWalletInfo() {
|
|
607
|
+
if (!this.provider || !this.currentAddress) {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
const balance = await this.provider.getBalance(this.currentAddress);
|
|
612
|
+
const network = await this.provider.getNetwork();
|
|
613
|
+
return {
|
|
614
|
+
address: this.currentAddress,
|
|
615
|
+
chainId: Number(network.chainId),
|
|
616
|
+
balance: ethers.formatEther(balance),
|
|
617
|
+
isConnected: true
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
console.error('Failed to get wallet info:', error);
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Switch to a specific network
|
|
627
|
+
*/
|
|
628
|
+
async switchNetwork(chainId) {
|
|
629
|
+
try {
|
|
630
|
+
if (!window.ethereum) {
|
|
631
|
+
return { success: false, error: 'Web3 wallet not found' };
|
|
632
|
+
}
|
|
633
|
+
await window.ethereum.request({
|
|
634
|
+
method: 'wallet_switchEthereumChain',
|
|
635
|
+
params: [{ chainId: `0x${chainId.toString(16)}` }],
|
|
636
|
+
});
|
|
637
|
+
return { success: true };
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
// If the chain hasn't been added to MetaMask
|
|
641
|
+
if (error.code === 4902) {
|
|
642
|
+
return { success: false, error: 'Network not added to wallet' };
|
|
643
|
+
}
|
|
644
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to switch network';
|
|
645
|
+
return { success: false, error: errorMessage };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Add a custom network to the wallet
|
|
650
|
+
*/
|
|
651
|
+
async addNetwork(networkConfig) {
|
|
652
|
+
try {
|
|
653
|
+
if (!window.ethereum) {
|
|
654
|
+
return { success: false, error: 'Web3 wallet not found' };
|
|
655
|
+
}
|
|
656
|
+
await window.ethereum.request({
|
|
657
|
+
method: 'wallet_addEthereumChain',
|
|
658
|
+
params: [{
|
|
659
|
+
chainId: `0x${networkConfig.chainId.toString(16)}`,
|
|
660
|
+
chainName: networkConfig.chainName,
|
|
661
|
+
rpcUrls: networkConfig.rpcUrls,
|
|
662
|
+
nativeCurrency: networkConfig.nativeCurrency,
|
|
663
|
+
blockExplorerUrls: networkConfig.blockExplorerUrls
|
|
664
|
+
}],
|
|
665
|
+
});
|
|
666
|
+
return { success: true };
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to add network';
|
|
670
|
+
return { success: false, error: errorMessage };
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Sign a message
|
|
675
|
+
*/
|
|
676
|
+
async signMessage(message) {
|
|
677
|
+
try {
|
|
678
|
+
if (!this.signer) {
|
|
679
|
+
return { success: false, error: 'Wallet not connected' };
|
|
680
|
+
}
|
|
681
|
+
const signature = await this.signer.signMessage(message);
|
|
682
|
+
return { success: true, signature };
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to sign message';
|
|
686
|
+
return { success: false, error: errorMessage };
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Get current provider
|
|
691
|
+
*/
|
|
692
|
+
getProvider() {
|
|
693
|
+
return this.provider;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Get current signer
|
|
697
|
+
*/
|
|
698
|
+
getSigner() {
|
|
699
|
+
return this.signer;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Get current address
|
|
703
|
+
*/
|
|
704
|
+
getCurrentAddress() {
|
|
705
|
+
return this.currentAddress;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Check if wallet is connected
|
|
709
|
+
*/
|
|
710
|
+
isConnected() {
|
|
711
|
+
return this.provider !== null && this.signer !== null && this.currentAddress !== null;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Listen for account changes
|
|
715
|
+
*/
|
|
716
|
+
onAccountsChanged(callback) {
|
|
717
|
+
if (window.ethereum) {
|
|
718
|
+
window.ethereum.on('accountsChanged', callback);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Listen for chain changes
|
|
723
|
+
*/
|
|
724
|
+
onChainChanged(callback) {
|
|
725
|
+
if (window.ethereum) {
|
|
726
|
+
window.ethereum.on('chainChanged', callback);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Remove event listeners
|
|
731
|
+
*/
|
|
732
|
+
removeAllListeners() {
|
|
733
|
+
if (window.ethereum) {
|
|
734
|
+
window.ethereum.removeAllListeners('accountsChanged');
|
|
735
|
+
window.ethereum.removeAllListeners('chainChanged');
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
// Singleton instance for React apps
|
|
740
|
+
let walletServiceInstance = null;
|
|
741
|
+
/**
|
|
742
|
+
* Get or create the global wallet service instance
|
|
743
|
+
*/
|
|
744
|
+
function getWalletService() {
|
|
745
|
+
if (!walletServiceInstance) {
|
|
746
|
+
walletServiceInstance = new WalletService();
|
|
747
|
+
}
|
|
748
|
+
return walletServiceInstance;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Reset the global wallet service instance
|
|
752
|
+
*/
|
|
753
|
+
function resetWalletService() {
|
|
754
|
+
if (walletServiceInstance) {
|
|
755
|
+
walletServiceInstance.removeAllListeners();
|
|
756
|
+
walletServiceInstance.disconnect();
|
|
757
|
+
walletServiceInstance = null;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Hook for wallet connection and management
|
|
763
|
+
*/
|
|
764
|
+
function useWallet() {
|
|
765
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
766
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
767
|
+
const [walletInfo, setWalletInfo] = useState(null);
|
|
768
|
+
const [provider, setProvider] = useState(null);
|
|
769
|
+
const [signer, setSigner] = useState(null);
|
|
770
|
+
const [address, setAddress] = useState(null);
|
|
771
|
+
const [error, setError] = useState(null);
|
|
772
|
+
const walletService = useRef(getWalletService());
|
|
773
|
+
const connect = useCallback(async () => {
|
|
774
|
+
setIsConnecting(true);
|
|
775
|
+
setError(null);
|
|
776
|
+
try {
|
|
777
|
+
const result = await walletService.current.connectWallet();
|
|
778
|
+
if (result.success) {
|
|
779
|
+
setIsConnected(true);
|
|
780
|
+
setProvider(result.provider || null);
|
|
781
|
+
setSigner(result.signer || null);
|
|
782
|
+
setAddress(result.address || null);
|
|
783
|
+
// Get wallet info
|
|
784
|
+
const info = await walletService.current.getWalletInfo();
|
|
785
|
+
setWalletInfo(info);
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
setError(result.error || 'Failed to connect wallet');
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
catch (err) {
|
|
792
|
+
setError(err instanceof Error ? err.message : 'Failed to connect wallet');
|
|
793
|
+
}
|
|
794
|
+
finally {
|
|
795
|
+
setIsConnecting(false);
|
|
796
|
+
}
|
|
797
|
+
}, []);
|
|
798
|
+
const disconnect = useCallback(() => {
|
|
799
|
+
walletService.current.disconnect();
|
|
800
|
+
setIsConnected(false);
|
|
801
|
+
setProvider(null);
|
|
802
|
+
setSigner(null);
|
|
803
|
+
setAddress(null);
|
|
804
|
+
setWalletInfo(null);
|
|
805
|
+
setError(null);
|
|
806
|
+
}, []);
|
|
807
|
+
const switchNetwork = useCallback(async (chainId) => {
|
|
808
|
+
setError(null);
|
|
809
|
+
try {
|
|
810
|
+
const result = await walletService.current.switchNetwork(chainId);
|
|
811
|
+
if (!result.success) {
|
|
812
|
+
setError(result.error || 'Failed to switch network');
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
catch (err) {
|
|
816
|
+
setError(err instanceof Error ? err.message : 'Failed to switch network');
|
|
817
|
+
}
|
|
818
|
+
}, []);
|
|
819
|
+
// Set up event listeners
|
|
820
|
+
useEffect(() => {
|
|
821
|
+
const handleAccountsChanged = (accounts) => {
|
|
822
|
+
if (accounts.length === 0) {
|
|
823
|
+
disconnect();
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
setAddress(accounts[0]);
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
const handleChainChanged = () => {
|
|
830
|
+
// Reload wallet info when chain changes
|
|
831
|
+
if (isConnected) {
|
|
832
|
+
walletService.current.getWalletInfo().then(setWalletInfo);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
walletService.current.onAccountsChanged(handleAccountsChanged);
|
|
836
|
+
walletService.current.onChainChanged(handleChainChanged);
|
|
837
|
+
return () => {
|
|
838
|
+
walletService.current.removeAllListeners();
|
|
839
|
+
};
|
|
840
|
+
}, [isConnected, disconnect]);
|
|
841
|
+
return {
|
|
842
|
+
isConnected,
|
|
843
|
+
isConnecting,
|
|
844
|
+
walletInfo,
|
|
845
|
+
provider,
|
|
846
|
+
signer,
|
|
847
|
+
address,
|
|
848
|
+
error,
|
|
849
|
+
connect,
|
|
850
|
+
disconnect,
|
|
851
|
+
switchNetwork
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Hook for compliance operations
|
|
856
|
+
*/
|
|
857
|
+
function useCompliance(provider, signer) {
|
|
858
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
859
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
860
|
+
const [error, setError] = useState(null);
|
|
861
|
+
const complianceService = useRef(getComplianceService());
|
|
862
|
+
// Initialize service when provider/signer changes
|
|
863
|
+
useEffect(() => {
|
|
864
|
+
if (provider) {
|
|
865
|
+
setIsLoading(true);
|
|
866
|
+
complianceService.current.initialize(provider, signer)
|
|
867
|
+
.then(() => {
|
|
868
|
+
setIsInitialized(true);
|
|
869
|
+
setError(null);
|
|
870
|
+
})
|
|
871
|
+
.catch((err) => {
|
|
872
|
+
setError(err instanceof Error ? err.message : 'Failed to initialize compliance');
|
|
873
|
+
setIsInitialized(false);
|
|
874
|
+
})
|
|
875
|
+
.finally(() => {
|
|
876
|
+
setIsLoading(false);
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
setIsInitialized(false);
|
|
881
|
+
}
|
|
882
|
+
}, [provider, signer]);
|
|
883
|
+
const initiateVerification = useCallback(async (requirements) => {
|
|
884
|
+
setError(null);
|
|
885
|
+
const result = await complianceService.current.initiateVerification(requirements);
|
|
886
|
+
if (result.success) {
|
|
887
|
+
return result.url || null;
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
setError(result.error || 'Failed to initiate verification');
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
}, []);
|
|
894
|
+
const processVerification = useCallback(async (zkPassportResult, userAddress) => {
|
|
895
|
+
setError(null);
|
|
896
|
+
const result = await complianceService.current.processVerificationResult(zkPassportResult, userAddress);
|
|
897
|
+
if (result.success) {
|
|
898
|
+
return result.result || null;
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
setError(result.error || 'Failed to process verification');
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
}, []);
|
|
905
|
+
const checkCompliance = useCallback(async (userAddress) => {
|
|
906
|
+
setError(null);
|
|
907
|
+
const result = await complianceService.current.checkComplianceStatus(userAddress);
|
|
908
|
+
if (result.success) {
|
|
909
|
+
return result.status || null;
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
setError(result.error || 'Failed to check compliance');
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
}, []);
|
|
916
|
+
const generateQRCode = useCallback(async (url) => {
|
|
917
|
+
setError(null);
|
|
918
|
+
const result = await complianceService.current.generateQRCode(url);
|
|
919
|
+
if (result.success) {
|
|
920
|
+
return result.qrCode || null;
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
setError(result.error || 'Failed to generate QR code');
|
|
924
|
+
return null;
|
|
925
|
+
}
|
|
926
|
+
}, []);
|
|
927
|
+
return {
|
|
928
|
+
isInitialized,
|
|
929
|
+
isLoading,
|
|
930
|
+
error,
|
|
931
|
+
initiateVerification,
|
|
932
|
+
processVerification,
|
|
933
|
+
checkCompliance,
|
|
934
|
+
generateQRCode
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Combined hook for wallet and compliance functionality
|
|
939
|
+
*/
|
|
940
|
+
function useTreza() {
|
|
941
|
+
const wallet = useWallet();
|
|
942
|
+
const compliance = useCompliance(wallet.provider || undefined, wallet.signer || undefined);
|
|
943
|
+
const isReady = wallet.isConnected && compliance.isInitialized;
|
|
944
|
+
return {
|
|
945
|
+
wallet,
|
|
946
|
+
compliance,
|
|
947
|
+
isReady
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Hook for managing the complete verification flow
|
|
952
|
+
*/
|
|
953
|
+
function useVerificationFlow(provider, signer) {
|
|
954
|
+
const [status, setStatus] = useState('idle');
|
|
955
|
+
const [verificationUrl, setVerificationUrl] = useState(null);
|
|
956
|
+
const [qrCode, setQrCode] = useState(null);
|
|
957
|
+
const [result, setResult] = useState(null);
|
|
958
|
+
const [error, setError] = useState(null);
|
|
959
|
+
const [currentUserAddress, setCurrentUserAddress] = useState(null);
|
|
960
|
+
const compliance = useCompliance(provider, signer);
|
|
961
|
+
const startVerification = useCallback(async (userAddress, requirements) => {
|
|
962
|
+
setStatus('generating');
|
|
963
|
+
setError(null);
|
|
964
|
+
setResult(null);
|
|
965
|
+
setCurrentUserAddress(userAddress);
|
|
966
|
+
try {
|
|
967
|
+
const url = await compliance.initiateVerification(requirements);
|
|
968
|
+
if (url) {
|
|
969
|
+
setVerificationUrl(url);
|
|
970
|
+
const qr = await compliance.generateQRCode(url);
|
|
971
|
+
if (qr) {
|
|
972
|
+
setQrCode(qr);
|
|
973
|
+
setStatus('waiting');
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
throw new Error('Failed to generate QR code');
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
else {
|
|
980
|
+
throw new Error('Failed to initiate verification');
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
catch (err) {
|
|
984
|
+
setError(err instanceof Error ? err.message : 'Verification failed');
|
|
985
|
+
setStatus('error');
|
|
986
|
+
}
|
|
987
|
+
}, [compliance]);
|
|
988
|
+
const processResult = useCallback(async (zkPassportResult) => {
|
|
989
|
+
if (!currentUserAddress) {
|
|
990
|
+
setError('No user address set');
|
|
991
|
+
setStatus('error');
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
setStatus('processing');
|
|
995
|
+
setError(null);
|
|
996
|
+
try {
|
|
997
|
+
const verificationResult = await compliance.processVerification(zkPassportResult, currentUserAddress);
|
|
998
|
+
if (verificationResult) {
|
|
999
|
+
setResult(verificationResult);
|
|
1000
|
+
setStatus('completed');
|
|
1001
|
+
}
|
|
1002
|
+
else {
|
|
1003
|
+
throw new Error('Failed to process verification result');
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
catch (err) {
|
|
1007
|
+
setError(err instanceof Error ? err.message : 'Processing failed');
|
|
1008
|
+
setStatus('error');
|
|
1009
|
+
}
|
|
1010
|
+
}, [compliance, currentUserAddress]);
|
|
1011
|
+
const reset = useCallback(() => {
|
|
1012
|
+
setStatus('idle');
|
|
1013
|
+
setVerificationUrl(null);
|
|
1014
|
+
setQrCode(null);
|
|
1015
|
+
setResult(null);
|
|
1016
|
+
setError(null);
|
|
1017
|
+
setCurrentUserAddress(null);
|
|
1018
|
+
}, []);
|
|
1019
|
+
return {
|
|
1020
|
+
status,
|
|
1021
|
+
verificationUrl,
|
|
1022
|
+
qrCode,
|
|
1023
|
+
result,
|
|
1024
|
+
error,
|
|
1025
|
+
startVerification,
|
|
1026
|
+
processResult,
|
|
1027
|
+
reset
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* @treza/react - React components for TREZA SDK
|
|
1033
|
+
*
|
|
1034
|
+
* Privacy-first DeFi UI components with zero-knowledge compliance
|
|
1035
|
+
*/
|
|
1036
|
+
// Component exports
|
|
1037
|
+
// Version
|
|
1038
|
+
const VERSION = '1.0.0';
|
|
1039
|
+
|
|
1040
|
+
export { ComplianceProvider, ComplianceService, ComplianceStatusDisplay, ComplianceVerification, GovernanceEligibility, VERSION, WalletService, complianceStyles, getComplianceService, getWalletService, resetComplianceService, resetWalletService, useCompliance, useComplianceContext, useTreza, useVerificationFlow, useWallet };
|
|
1041
|
+
//# sourceMappingURL=index.esm.js.map
|