@neus/sdk 1.0.0 → 1.0.1
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/LICENSE +36 -40
- package/README.md +86 -152
- package/SECURITY.md +29 -224
- package/cjs/client.cjs +1686 -0
- package/cjs/errors.cjs +202 -0
- package/cjs/gates.cjs +140 -0
- package/cjs/index.cjs +2315 -0
- package/cjs/utils.cjs +620 -0
- package/client.js +1693 -844
- package/errors.js +223 -228
- package/gates.js +175 -0
- package/index.js +21 -26
- package/package.json +68 -18
- package/types.d.ts +519 -71
- package/utils.js +752 -722
- package/widgets/README.md +53 -0
- package/widgets/index.js +9 -0
- package/widgets/verify-gate/dist/ProofBadge.js +355 -0
- package/widgets/verify-gate/dist/VerifyGate.js +601 -0
- package/widgets/verify-gate/index.js +13 -0
- package/widgets.cjs +20 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// widgets/verify-gate/VerifyGate.jsx
|
|
4
|
+
import React, { useCallback, useMemo, useState, useEffect } from "react";
|
|
5
|
+
import { NeusClient } from "@neus/sdk/client";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
var THEME = {
|
|
8
|
+
// Primary colors - use CSS variables with fallbacks
|
|
9
|
+
primary: "var(--neus-primary, #98C0EF)",
|
|
10
|
+
primaryHover: "var(--neus-primary-hover,rgb(91, 126, 182))",
|
|
11
|
+
success: "var(--neus-success, #22c55e)",
|
|
12
|
+
error: "var(--neus-error, #ef4444)",
|
|
13
|
+
warning: "var(--neus-warning, #f59e0b)",
|
|
14
|
+
// Background colors
|
|
15
|
+
bgDark: "var(--neus-bg-dark, rgba(2, 6, 23, 0.95))",
|
|
16
|
+
bgCard: "var(--neus-bg-card, rgba(15, 23, 42, 0.8))",
|
|
17
|
+
// Text colors
|
|
18
|
+
textPrimary: "var(--neus-text-primary, #ffffff)",
|
|
19
|
+
textSecondary: "var(--neus-text-secondary, #94a3b8)",
|
|
20
|
+
textMuted: "var(--neus-text-muted, #64748b)",
|
|
21
|
+
// Border colors
|
|
22
|
+
border: "var(--neus-border, rgba(148, 163, 184, 0.2))",
|
|
23
|
+
borderHover: "var(--neus-border-hover, rgba(61, 114, 201, 0.4))"
|
|
24
|
+
};
|
|
25
|
+
var DEFAULT_MAX_AGE_MS_BY_VERIFIER = {
|
|
26
|
+
// point_in_time (recommended: 1 hour)
|
|
27
|
+
"ownership-dns-txt": 60 * 60 * 1e3,
|
|
28
|
+
"contract-ownership": 60 * 60 * 1e3,
|
|
29
|
+
"nft-ownership": 60 * 60 * 1e3,
|
|
30
|
+
"token-holding": 60 * 60 * 1e3,
|
|
31
|
+
"wallet-risk": 60 * 60 * 1e3,
|
|
32
|
+
// expiring (recommended: 7 days)
|
|
33
|
+
"agent-delegation": 7 * 24 * 60 * 60 * 1e3
|
|
34
|
+
};
|
|
35
|
+
var maxAgeMsForVerifier = (verifierId, overrideMs) => {
|
|
36
|
+
if (typeof overrideMs === "number")
|
|
37
|
+
return overrideMs;
|
|
38
|
+
return DEFAULT_MAX_AGE_MS_BY_VERIFIER[verifierId];
|
|
39
|
+
};
|
|
40
|
+
var CREATABLE_VERIFIERS = /* @__PURE__ */ new Set([
|
|
41
|
+
"ownership-basic",
|
|
42
|
+
"ownership-pseudonym",
|
|
43
|
+
"ownership-dns-txt",
|
|
44
|
+
"contract-ownership",
|
|
45
|
+
"nft-ownership",
|
|
46
|
+
"token-holding",
|
|
47
|
+
"wallet-link",
|
|
48
|
+
"wallet-risk",
|
|
49
|
+
"agent-identity",
|
|
50
|
+
"agent-delegation",
|
|
51
|
+
"ai-content-moderation"
|
|
52
|
+
]);
|
|
53
|
+
var NeusLogo = ({ size = 16 }) => /* @__PURE__ */ jsxs(
|
|
54
|
+
"svg",
|
|
55
|
+
{
|
|
56
|
+
width: size,
|
|
57
|
+
height: size,
|
|
58
|
+
viewBox: "0 0 24 24",
|
|
59
|
+
"aria-hidden": "true",
|
|
60
|
+
focusable: "false",
|
|
61
|
+
style: {
|
|
62
|
+
width: size,
|
|
63
|
+
height: size,
|
|
64
|
+
marginRight: 8,
|
|
65
|
+
verticalAlign: "middle",
|
|
66
|
+
borderRadius: 4,
|
|
67
|
+
flexShrink: 0
|
|
68
|
+
},
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "20", height: "20", rx: "5", fill: "currentColor", opacity: "0.18" }),
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
"path",
|
|
73
|
+
{
|
|
74
|
+
d: "M7 16V8h2.1l4.9 5.9V8H17v8h-2.1L10 10.1V16H7z",
|
|
75
|
+
fill: "currentColor",
|
|
76
|
+
opacity: "0.9"
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
var Spinner = ({ size = 16 }) => /* @__PURE__ */ jsxs(
|
|
83
|
+
"svg",
|
|
84
|
+
{
|
|
85
|
+
className: "animate-spin",
|
|
86
|
+
width: size,
|
|
87
|
+
height: size,
|
|
88
|
+
viewBox: "0 0 24 24",
|
|
89
|
+
fill: "none",
|
|
90
|
+
style: { marginRight: 8 },
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
"circle",
|
|
94
|
+
{
|
|
95
|
+
className: "opacity-25",
|
|
96
|
+
cx: "12",
|
|
97
|
+
cy: "12",
|
|
98
|
+
r: "10",
|
|
99
|
+
stroke: "currentColor",
|
|
100
|
+
strokeWidth: "3"
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/* @__PURE__ */ jsx(
|
|
104
|
+
"path",
|
|
105
|
+
{
|
|
106
|
+
className: "opacity-75",
|
|
107
|
+
fill: "currentColor",
|
|
108
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
function VerifyGate({
|
|
115
|
+
requiredVerifiers = ["ownership-basic"],
|
|
116
|
+
onVerified = void 0,
|
|
117
|
+
apiUrl = void 0,
|
|
118
|
+
style = void 0,
|
|
119
|
+
children = void 0,
|
|
120
|
+
// Verifier configuration
|
|
121
|
+
verifierOptions = void 0,
|
|
122
|
+
verifierData = void 0,
|
|
123
|
+
// Proof creation options (privacy, discoverability, content storage)
|
|
124
|
+
proofOptions = void 0,
|
|
125
|
+
// Display options
|
|
126
|
+
showBrand = true,
|
|
127
|
+
disabled = false,
|
|
128
|
+
buttonText = void 0,
|
|
129
|
+
// Custom button text
|
|
130
|
+
// Private proof access mode
|
|
131
|
+
mode = "create",
|
|
132
|
+
// 'create' or 'access'
|
|
133
|
+
qHash = null,
|
|
134
|
+
// Required when mode='access'
|
|
135
|
+
// Proof strategy for static vs dynamic verification
|
|
136
|
+
// - 'reuse': Always use existing proof if available (best for static facts)
|
|
137
|
+
// - 'fresh': Always create new proof (best for dynamic facts that change)
|
|
138
|
+
// - 'reuse-or-create': Use existing if valid, else create new (default)
|
|
139
|
+
strategy = "reuse-or-create",
|
|
140
|
+
// Gate-first options (used when strategy includes 'reuse')
|
|
141
|
+
checkExisting = true,
|
|
142
|
+
// Check for existing proofs before verification
|
|
143
|
+
maxProofAgeMs = void 0,
|
|
144
|
+
// Optional max age override (ms) for proof reuse
|
|
145
|
+
allowPrivateReuse = true,
|
|
146
|
+
// Allow owner-signed lookups for private proofs (interactive)
|
|
147
|
+
// Callbacks
|
|
148
|
+
onStateChange = void 0,
|
|
149
|
+
onError = void 0
|
|
150
|
+
}) {
|
|
151
|
+
const [state, setState] = useState("idle");
|
|
152
|
+
const [error, setError] = useState(null);
|
|
153
|
+
const [notice, setNotice] = useState(null);
|
|
154
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
155
|
+
const [walletAddress, setWalletAddress] = useState(null);
|
|
156
|
+
const [existingProofs, setExistingProofs] = useState(null);
|
|
157
|
+
const [operation, setOperation] = useState("verify");
|
|
158
|
+
const client = useMemo(() => new NeusClient({ apiUrl }), [apiUrl]);
|
|
159
|
+
const verifierList = useMemo(() => {
|
|
160
|
+
return Array.isArray(requiredVerifiers) && requiredVerifiers.length > 0 ? requiredVerifiers : ["ownership-basic"];
|
|
161
|
+
}, [requiredVerifiers]);
|
|
162
|
+
const primaryVerifier = verifierList[0];
|
|
163
|
+
const shouldCheckExisting = checkExisting && strategy !== "fresh";
|
|
164
|
+
const buildGateRequirements = useCallback(() => {
|
|
165
|
+
return verifierList.map((verifierId) => ({
|
|
166
|
+
verifierId,
|
|
167
|
+
...maxAgeMsForVerifier(verifierId, maxProofAgeMs) && { maxAgeMs: maxAgeMsForVerifier(verifierId, maxProofAgeMs) }
|
|
168
|
+
}));
|
|
169
|
+
}, [verifierList, maxProofAgeMs]);
|
|
170
|
+
const applySatisfiedGateResult = useCallback((gateResult, address) => {
|
|
171
|
+
if (!gateResult?.satisfied)
|
|
172
|
+
return false;
|
|
173
|
+
setNotice(null);
|
|
174
|
+
setError(null);
|
|
175
|
+
setState("verified");
|
|
176
|
+
setExistingProofs(gateResult);
|
|
177
|
+
const existingProof = gateResult.existing?.[primaryVerifier];
|
|
178
|
+
if (existingProof && onVerified) {
|
|
179
|
+
onVerified({
|
|
180
|
+
qHash: existingProof.qHash,
|
|
181
|
+
address: existingProof.walletAddress || address,
|
|
182
|
+
verifierIds: verifierList,
|
|
183
|
+
verifiedVerifiers: existingProof.verifiedVerifiers || [],
|
|
184
|
+
existing: true,
|
|
185
|
+
proofsByVerifierId: gateResult.existing || {},
|
|
186
|
+
statusUrl: existingProof.qHash ? `${apiUrl || "https://api.neus.network"}/api/v1/verification/status/${existingProof.qHash}` : null
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}, [apiUrl, onVerified, primaryVerifier, verifierList]);
|
|
191
|
+
const getOrRequestWalletAddress = useCallback(async () => {
|
|
192
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
193
|
+
throw new Error("No wallet provider available");
|
|
194
|
+
}
|
|
195
|
+
let accounts = await window.ethereum.request({ method: "eth_accounts" });
|
|
196
|
+
if (!accounts || accounts.length === 0) {
|
|
197
|
+
await window.ethereum.request({ method: "eth_requestAccounts" });
|
|
198
|
+
accounts = await window.ethereum.request({ method: "eth_accounts" });
|
|
199
|
+
}
|
|
200
|
+
if (!accounts || accounts.length === 0) {
|
|
201
|
+
throw new Error("No wallet accounts available");
|
|
202
|
+
}
|
|
203
|
+
return accounts[0];
|
|
204
|
+
}, []);
|
|
205
|
+
const tryPrivateReuse = useCallback(async (address) => {
|
|
206
|
+
setOperation("reuse");
|
|
207
|
+
setState("signing");
|
|
208
|
+
const result = await client.getPrivateProofsByWallet(
|
|
209
|
+
address,
|
|
210
|
+
{ limit: 200, offset: 0 },
|
|
211
|
+
typeof window !== "undefined" ? window.ethereum : null
|
|
212
|
+
);
|
|
213
|
+
const proofs = result?.proofs || [];
|
|
214
|
+
const gateResult = await client.checkGate({
|
|
215
|
+
walletAddress: address,
|
|
216
|
+
requirements: buildGateRequirements(),
|
|
217
|
+
proofs
|
|
218
|
+
});
|
|
219
|
+
setExistingProofs(gateResult);
|
|
220
|
+
return gateResult;
|
|
221
|
+
}, [client, buildGateRequirements]);
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
onStateChange?.(state);
|
|
224
|
+
}, [state, onStateChange]);
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (!shouldCheckExisting || mode === "access")
|
|
227
|
+
return;
|
|
228
|
+
const checkExistingProofs = async () => {
|
|
229
|
+
try {
|
|
230
|
+
if (typeof window === "undefined" || !window.ethereum)
|
|
231
|
+
return;
|
|
232
|
+
const accounts = await window.ethereum.request({ method: "eth_accounts" });
|
|
233
|
+
if (!accounts || accounts.length === 0)
|
|
234
|
+
return;
|
|
235
|
+
const address = accounts[0];
|
|
236
|
+
setWalletAddress(address);
|
|
237
|
+
const gateResult = await client.checkGate({
|
|
238
|
+
walletAddress: address,
|
|
239
|
+
requirements: buildGateRequirements()
|
|
240
|
+
});
|
|
241
|
+
setExistingProofs(gateResult);
|
|
242
|
+
applySatisfiedGateResult(gateResult, address);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
checkExistingProofs();
|
|
247
|
+
if (typeof window !== "undefined" && window.ethereum) {
|
|
248
|
+
const handleAccountsChanged = () => {
|
|
249
|
+
setWalletAddress(null);
|
|
250
|
+
setExistingProofs(null);
|
|
251
|
+
if (state === "verified")
|
|
252
|
+
setState("idle");
|
|
253
|
+
checkExistingProofs();
|
|
254
|
+
};
|
|
255
|
+
window.ethereum.on("accountsChanged", handleAccountsChanged);
|
|
256
|
+
return () => window.ethereum.removeListener("accountsChanged", handleAccountsChanged);
|
|
257
|
+
}
|
|
258
|
+
}, [shouldCheckExisting, mode, client, buildGateRequirements, applySatisfiedGateResult, state]);
|
|
259
|
+
const handleClick = useCallback(async () => {
|
|
260
|
+
if (disabled || isProcessing)
|
|
261
|
+
return;
|
|
262
|
+
if (state === "verified" && existingProofs?.satisfied) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
setError(null);
|
|
266
|
+
setNotice(null);
|
|
267
|
+
if (shouldCheckExisting && walletAddress) {
|
|
268
|
+
try {
|
|
269
|
+
const gateResult = await client.checkGate({
|
|
270
|
+
walletAddress,
|
|
271
|
+
requirements: buildGateRequirements()
|
|
272
|
+
});
|
|
273
|
+
if (applySatisfiedGateResult(gateResult, walletAddress))
|
|
274
|
+
return;
|
|
275
|
+
} catch (err) {
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
if (mode === "access") {
|
|
280
|
+
setOperation("access");
|
|
281
|
+
setIsProcessing(true);
|
|
282
|
+
setState("signing");
|
|
283
|
+
if (!qHash) {
|
|
284
|
+
throw new Error("qHash is required for access mode");
|
|
285
|
+
}
|
|
286
|
+
setState("verifying");
|
|
287
|
+
const privateData = await client.getPrivateStatus(qHash);
|
|
288
|
+
setState("verified");
|
|
289
|
+
onVerified?.({
|
|
290
|
+
qHash,
|
|
291
|
+
data: privateData.data,
|
|
292
|
+
mode: "access",
|
|
293
|
+
statusUrl: privateData.statusUrl
|
|
294
|
+
});
|
|
295
|
+
} else if (strategy === "reuse") {
|
|
296
|
+
setOperation("reuse");
|
|
297
|
+
if (!allowPrivateReuse) {
|
|
298
|
+
setNotice("No existing proof was found.");
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
setIsProcessing(true);
|
|
302
|
+
const address = walletAddress || await getOrRequestWalletAddress();
|
|
303
|
+
setWalletAddress(address);
|
|
304
|
+
const gateResult = await tryPrivateReuse(address);
|
|
305
|
+
if (applySatisfiedGateResult(gateResult, address))
|
|
306
|
+
return;
|
|
307
|
+
setState("idle");
|
|
308
|
+
setNotice("No matching proof was found. Create a proof to continue.");
|
|
309
|
+
} else {
|
|
310
|
+
setOperation("verify");
|
|
311
|
+
setIsProcessing(true);
|
|
312
|
+
setState("signing");
|
|
313
|
+
const resolvedProofOptions = {
|
|
314
|
+
privacyLevel: "private",
|
|
315
|
+
publicDisplay: false,
|
|
316
|
+
storeOriginalContent: false,
|
|
317
|
+
...proofOptions && typeof proofOptions === "object" ? proofOptions : {},
|
|
318
|
+
...verifierOptions && { verifierOptions }
|
|
319
|
+
};
|
|
320
|
+
const buildDataForVerifier = (verifierId) => {
|
|
321
|
+
if (!CREATABLE_VERIFIERS.has(verifierId)) {
|
|
322
|
+
throw new Error(`${verifierId} cannot be created via the wallet flow. It requires deployment configuration.`);
|
|
323
|
+
}
|
|
324
|
+
const explicit = verifierData && verifierData[verifierId];
|
|
325
|
+
if (explicit && typeof explicit === "object")
|
|
326
|
+
return explicit;
|
|
327
|
+
if (verifierId === "ownership-basic") {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
if (verifierId === "wallet-risk") {
|
|
331
|
+
return {};
|
|
332
|
+
}
|
|
333
|
+
throw new Error(`${verifierId} requires explicit verifierData`);
|
|
334
|
+
};
|
|
335
|
+
const verifyOne = async (verifierId) => {
|
|
336
|
+
const dataForVerifier = buildDataForVerifier(verifierId);
|
|
337
|
+
const params = verifierId === "ownership-basic" && dataForVerifier === null ? {
|
|
338
|
+
verifier: "ownership-basic",
|
|
339
|
+
content: verifierData?.["ownership-basic"]?.content || `NEUS verification (${verifierId})`,
|
|
340
|
+
options: resolvedProofOptions
|
|
341
|
+
} : {
|
|
342
|
+
verifier: verifierId,
|
|
343
|
+
data: dataForVerifier,
|
|
344
|
+
options: resolvedProofOptions
|
|
345
|
+
};
|
|
346
|
+
setState("signing");
|
|
347
|
+
const created = await client.verify(params);
|
|
348
|
+
setState("verifying");
|
|
349
|
+
const qHashToCheck = created.qHash || created?.data?.qHash;
|
|
350
|
+
const final = await client.pollProofStatus(qHashToCheck, { interval: 3e3, timeout: 6e4 });
|
|
351
|
+
const verifiedVerifiers = final?.data?.verifiedVerifiers || [];
|
|
352
|
+
const verifierResult = verifiedVerifiers.find((v) => v.verifierId === verifierId);
|
|
353
|
+
if (!verifierResult || verifierResult.verified !== true) {
|
|
354
|
+
throw new Error(`Verification failed for ${verifierId}`);
|
|
355
|
+
}
|
|
356
|
+
const hubTx = final?.data?.hubTransaction || {};
|
|
357
|
+
const crosschain = final?.data?.crosschain || {};
|
|
358
|
+
const txHash = hubTx?.txHash || crosschain?.hubTxHash || null;
|
|
359
|
+
return {
|
|
360
|
+
verifierId,
|
|
361
|
+
qHash: final.qHash,
|
|
362
|
+
address: final?.data?.walletAddress,
|
|
363
|
+
txHash,
|
|
364
|
+
verifiedVerifiers,
|
|
365
|
+
statusUrl: final?.statusUrl
|
|
366
|
+
};
|
|
367
|
+
};
|
|
368
|
+
const results = [];
|
|
369
|
+
for (const verifierId of verifierList) {
|
|
370
|
+
results.push(await verifyOne(verifierId));
|
|
371
|
+
}
|
|
372
|
+
setState("verified");
|
|
373
|
+
const last = results[results.length - 1];
|
|
374
|
+
onVerified?.({
|
|
375
|
+
qHash: last?.qHash,
|
|
376
|
+
qHashes: results.map((r) => r.qHash),
|
|
377
|
+
address: last?.address,
|
|
378
|
+
txHash: last?.txHash,
|
|
379
|
+
verifierIds: verifierList,
|
|
380
|
+
verifiedVerifiers: last?.verifiedVerifiers,
|
|
381
|
+
statusUrl: last?.statusUrl,
|
|
382
|
+
results
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
} catch (err) {
|
|
386
|
+
const errorMessage = err?.message || (mode === "access" ? "Access failed" : "Verification failed");
|
|
387
|
+
setError(errorMessage);
|
|
388
|
+
setState("error");
|
|
389
|
+
onError?.(err);
|
|
390
|
+
} finally {
|
|
391
|
+
setIsProcessing(false);
|
|
392
|
+
}
|
|
393
|
+
}, [
|
|
394
|
+
disabled,
|
|
395
|
+
isProcessing,
|
|
396
|
+
mode,
|
|
397
|
+
qHash,
|
|
398
|
+
verifierList,
|
|
399
|
+
client,
|
|
400
|
+
verifierOptions,
|
|
401
|
+
verifierData,
|
|
402
|
+
proofOptions,
|
|
403
|
+
onVerified,
|
|
404
|
+
onError,
|
|
405
|
+
shouldCheckExisting,
|
|
406
|
+
walletAddress,
|
|
407
|
+
existingProofs,
|
|
408
|
+
strategy,
|
|
409
|
+
allowPrivateReuse,
|
|
410
|
+
buildGateRequirements,
|
|
411
|
+
applySatisfiedGateResult,
|
|
412
|
+
getOrRequestWalletAddress,
|
|
413
|
+
tryPrivateReuse,
|
|
414
|
+
state
|
|
415
|
+
]);
|
|
416
|
+
const handleReuseExisting = useCallback(async () => {
|
|
417
|
+
if (disabled || isProcessing)
|
|
418
|
+
return;
|
|
419
|
+
if (mode === "access")
|
|
420
|
+
return;
|
|
421
|
+
if (!allowPrivateReuse)
|
|
422
|
+
return;
|
|
423
|
+
setError(null);
|
|
424
|
+
setNotice(null);
|
|
425
|
+
try {
|
|
426
|
+
setIsProcessing(true);
|
|
427
|
+
const address = walletAddress || await getOrRequestWalletAddress();
|
|
428
|
+
setWalletAddress(address);
|
|
429
|
+
const gateResult = await tryPrivateReuse(address);
|
|
430
|
+
if (applySatisfiedGateResult(gateResult, address))
|
|
431
|
+
return;
|
|
432
|
+
setState("idle");
|
|
433
|
+
setNotice("No matching proof was found. Verify to create a proof.");
|
|
434
|
+
} catch (err) {
|
|
435
|
+
const errorMessage = err?.message || "Unable to access private proofs";
|
|
436
|
+
setError(errorMessage);
|
|
437
|
+
setState("error");
|
|
438
|
+
onError?.(err);
|
|
439
|
+
} finally {
|
|
440
|
+
setIsProcessing(false);
|
|
441
|
+
}
|
|
442
|
+
}, [disabled, isProcessing, mode, allowPrivateReuse, walletAddress, getOrRequestWalletAddress, tryPrivateReuse, applySatisfiedGateResult, onError]);
|
|
443
|
+
const getLabel = () => {
|
|
444
|
+
if (buttonText)
|
|
445
|
+
return buttonText;
|
|
446
|
+
if (mode === "access") {
|
|
447
|
+
return {
|
|
448
|
+
idle: "Sign to view",
|
|
449
|
+
signing: "Waiting for signature...",
|
|
450
|
+
verifying: "Accessing...",
|
|
451
|
+
verified: "Access granted",
|
|
452
|
+
error: "Retry"
|
|
453
|
+
}[state];
|
|
454
|
+
}
|
|
455
|
+
if (strategy === "reuse") {
|
|
456
|
+
return {
|
|
457
|
+
idle: "Check proofs",
|
|
458
|
+
signing: "Waiting for signature...",
|
|
459
|
+
verifying: "Checking...",
|
|
460
|
+
verified: "Verified",
|
|
461
|
+
error: "Retry"
|
|
462
|
+
}[state];
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
idle: "Verify with NEUS",
|
|
466
|
+
signing: "Waiting for signature...",
|
|
467
|
+
verifying: operation === "reuse" ? "Checking..." : "Verifying...",
|
|
468
|
+
verified: "Verified",
|
|
469
|
+
error: "Retry"
|
|
470
|
+
}[state];
|
|
471
|
+
};
|
|
472
|
+
const buttonBaseStyle = {
|
|
473
|
+
display: "inline-flex",
|
|
474
|
+
alignItems: "center",
|
|
475
|
+
justifyContent: "center",
|
|
476
|
+
gap: "8px",
|
|
477
|
+
padding: "12px 24px",
|
|
478
|
+
borderRadius: "8px",
|
|
479
|
+
border: "none",
|
|
480
|
+
fontWeight: 500,
|
|
481
|
+
fontSize: "14px",
|
|
482
|
+
cursor: disabled || isProcessing ? "not-allowed" : "pointer",
|
|
483
|
+
transition: "all 0.2s ease",
|
|
484
|
+
opacity: disabled || isProcessing ? 0.6 : 1,
|
|
485
|
+
fontFamily: "inherit"
|
|
486
|
+
};
|
|
487
|
+
const getButtonStyle = () => {
|
|
488
|
+
if (state === "verified") {
|
|
489
|
+
return {
|
|
490
|
+
...buttonBaseStyle,
|
|
491
|
+
background: `rgba(34, 197, 94, 0.15)`,
|
|
492
|
+
color: THEME.success,
|
|
493
|
+
border: `1px solid rgba(34, 197, 94, 0.3)`
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
if (state === "error") {
|
|
497
|
+
return {
|
|
498
|
+
...buttonBaseStyle,
|
|
499
|
+
background: `rgba(239, 68, 68, 0.15)`,
|
|
500
|
+
color: THEME.error,
|
|
501
|
+
border: `1px solid rgba(239, 68, 68, 0.3)`
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
if (state === "signing" || state === "verifying") {
|
|
505
|
+
return {
|
|
506
|
+
...buttonBaseStyle,
|
|
507
|
+
background: `rgba(61, 114, 201, 0.15)`,
|
|
508
|
+
color: "#98c0ef",
|
|
509
|
+
border: `1px solid rgba(61, 114, 201, 0.3)`
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
...buttonBaseStyle,
|
|
514
|
+
background: THEME.primary,
|
|
515
|
+
color: THEME.textPrimary,
|
|
516
|
+
border: "none",
|
|
517
|
+
boxShadow: "0 4px 14px rgba(61, 114, 201, 0.25)"
|
|
518
|
+
};
|
|
519
|
+
};
|
|
520
|
+
if (children) {
|
|
521
|
+
if (state === "verified") {
|
|
522
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
523
|
+
}
|
|
524
|
+
return /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "20px", ...style }, children: [
|
|
525
|
+
/* @__PURE__ */ jsxs(
|
|
526
|
+
"button",
|
|
527
|
+
{
|
|
528
|
+
onClick: handleClick,
|
|
529
|
+
disabled: disabled || isProcessing,
|
|
530
|
+
style: getButtonStyle(),
|
|
531
|
+
children: [
|
|
532
|
+
(state === "signing" || state === "verifying" || state.startsWith("zkpassport")) && /* @__PURE__ */ jsx(Spinner, { size: 16 }),
|
|
533
|
+
showBrand && state === "idle" && /* @__PURE__ */ jsx(NeusLogo, { size: 16 }),
|
|
534
|
+
state === "verified" && /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
|
|
535
|
+
/* @__PURE__ */ jsx("span", { children: getLabel() })
|
|
536
|
+
]
|
|
537
|
+
}
|
|
538
|
+
),
|
|
539
|
+
notice && /* @__PURE__ */ jsx("div", { style: {
|
|
540
|
+
color: THEME.textSecondary,
|
|
541
|
+
marginTop: "10px",
|
|
542
|
+
fontSize: "13px",
|
|
543
|
+
padding: "8px 12px",
|
|
544
|
+
background: "rgba(148, 163, 184, 0.08)",
|
|
545
|
+
borderRadius: "6px",
|
|
546
|
+
border: "1px solid rgba(148, 163, 184, 0.14)"
|
|
547
|
+
}, children: notice }),
|
|
548
|
+
mode !== "access" && allowPrivateReuse && shouldCheckExisting && strategy !== "reuse" && state === "idle" && /* @__PURE__ */ jsx(
|
|
549
|
+
"button",
|
|
550
|
+
{
|
|
551
|
+
type: "button",
|
|
552
|
+
onClick: handleReuseExisting,
|
|
553
|
+
disabled: disabled || isProcessing,
|
|
554
|
+
style: {
|
|
555
|
+
marginTop: notice ? "10px" : "12px",
|
|
556
|
+
background: "transparent",
|
|
557
|
+
border: "none",
|
|
558
|
+
padding: 0,
|
|
559
|
+
color: THEME.textSecondary,
|
|
560
|
+
fontSize: "12px",
|
|
561
|
+
cursor: disabled || isProcessing ? "not-allowed" : "pointer",
|
|
562
|
+
textDecoration: "underline",
|
|
563
|
+
textUnderlineOffset: "2px",
|
|
564
|
+
opacity: disabled || isProcessing ? 0.6 : 0.9
|
|
565
|
+
},
|
|
566
|
+
children: "Already verified? Sign to reuse existing proofs."
|
|
567
|
+
}
|
|
568
|
+
),
|
|
569
|
+
error && /* @__PURE__ */ jsx("div", { style: {
|
|
570
|
+
color: THEME.error,
|
|
571
|
+
marginTop: "8px",
|
|
572
|
+
fontSize: "13px",
|
|
573
|
+
padding: "8px 12px",
|
|
574
|
+
background: "rgba(239, 68, 68, 0.1)",
|
|
575
|
+
borderRadius: "6px",
|
|
576
|
+
border: "1px solid rgba(239, 68, 68, 0.2)"
|
|
577
|
+
}, children: error })
|
|
578
|
+
] });
|
|
579
|
+
}
|
|
580
|
+
return /* @__PURE__ */ jsxs(
|
|
581
|
+
"button",
|
|
582
|
+
{
|
|
583
|
+
onClick: handleClick,
|
|
584
|
+
style: { ...getButtonStyle(), ...style },
|
|
585
|
+
disabled: disabled || isProcessing,
|
|
586
|
+
children: [
|
|
587
|
+
(state === "signing" || state === "verifying" || state.startsWith("zkpassport")) && /* @__PURE__ */ jsx(Spinner, { size: 16 }),
|
|
588
|
+
showBrand && state === "idle" && /* @__PURE__ */ jsx(NeusLogo, { size: 16 }),
|
|
589
|
+
state === "verified" && /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }),
|
|
590
|
+
/* @__PURE__ */ jsx("span", { children: getLabel() }),
|
|
591
|
+
error && /* @__PURE__ */ jsxs("span", { style: { opacity: 0.8, marginLeft: "8px" }, children: [
|
|
592
|
+
" \u2014 ",
|
|
593
|
+
error
|
|
594
|
+
] })
|
|
595
|
+
]
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
export {
|
|
600
|
+
VerifyGate
|
|
601
|
+
};
|
package/widgets.cjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @neus/sdk/widgets is an ESM + React entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* This file exists to provide a clear, deterministic error for CommonJS consumers
|
|
5
|
+
* rather than a cryptic ESM resolution failure.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
throw new Error(
|
|
10
|
+
[
|
|
11
|
+
'@neus/sdk/widgets is ESM-only.',
|
|
12
|
+
'',
|
|
13
|
+
'Use ESM imports:',
|
|
14
|
+
" import { VerifyGate, ProofBadge } from '@neus/sdk/widgets';",
|
|
15
|
+
'',
|
|
16
|
+
'Or in CommonJS, use dynamic import:',
|
|
17
|
+
" const { VerifyGate } = await import('@neus/sdk/widgets');"
|
|
18
|
+
].join('\n')
|
|
19
|
+
);
|
|
20
|
+
|