@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
package/cjs/utils.cjs
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// utils.js
|
|
30
|
+
var utils_exports = {};
|
|
31
|
+
__export(utils_exports, {
|
|
32
|
+
NEUS_CONSTANTS: () => NEUS_CONSTANTS,
|
|
33
|
+
StatusPoller: () => StatusPoller,
|
|
34
|
+
buildVerificationRequest: () => buildVerificationRequest,
|
|
35
|
+
computeContentHash: () => computeContentHash,
|
|
36
|
+
constructVerificationMessage: () => constructVerificationMessage,
|
|
37
|
+
createVerificationData: () => createVerificationData,
|
|
38
|
+
delay: () => delay,
|
|
39
|
+
deriveDid: () => deriveDid,
|
|
40
|
+
formatTimestamp: () => formatTimestamp,
|
|
41
|
+
formatVerificationStatus: () => formatVerificationStatus,
|
|
42
|
+
isFailureStatus: () => isFailureStatus,
|
|
43
|
+
isSuccessStatus: () => isSuccessStatus,
|
|
44
|
+
isSupportedChain: () => isSupportedChain,
|
|
45
|
+
isTerminalStatus: () => isTerminalStatus,
|
|
46
|
+
normalizeAddress: () => normalizeAddress,
|
|
47
|
+
validateQHash: () => validateQHash,
|
|
48
|
+
validateSignatureComponents: () => validateSignatureComponents,
|
|
49
|
+
validateTimestamp: () => validateTimestamp,
|
|
50
|
+
validateVerifierPayload: () => validateVerifierPayload,
|
|
51
|
+
validateWalletAddress: () => validateWalletAddress,
|
|
52
|
+
withRetry: () => withRetry
|
|
53
|
+
});
|
|
54
|
+
module.exports = __toCommonJS(utils_exports);
|
|
55
|
+
|
|
56
|
+
// errors.js
|
|
57
|
+
var SDKError = class _SDKError extends Error {
|
|
58
|
+
constructor(message, code = "SDK_ERROR", details = {}) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = "SDKError";
|
|
61
|
+
this.code = code;
|
|
62
|
+
this.details = details;
|
|
63
|
+
this.timestamp = Date.now();
|
|
64
|
+
if (Error.captureStackTrace) {
|
|
65
|
+
Error.captureStackTrace(this, _SDKError);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
toJSON() {
|
|
69
|
+
return {
|
|
70
|
+
name: this.name,
|
|
71
|
+
message: this.message,
|
|
72
|
+
code: this.code,
|
|
73
|
+
details: this.details,
|
|
74
|
+
timestamp: this.timestamp
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// utils.js
|
|
80
|
+
function deterministicStringify(obj) {
|
|
81
|
+
if (obj === null || obj === void 0) {
|
|
82
|
+
return JSON.stringify(obj);
|
|
83
|
+
}
|
|
84
|
+
if (typeof obj !== "object") {
|
|
85
|
+
return JSON.stringify(obj);
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(obj)) {
|
|
88
|
+
return "[" + obj.map((item) => deterministicStringify(item)).join(",") + "]";
|
|
89
|
+
}
|
|
90
|
+
const sortedKeys = Object.keys(obj).sort();
|
|
91
|
+
const pairs = sortedKeys.map(
|
|
92
|
+
(key) => JSON.stringify(key) + ":" + deterministicStringify(obj[key])
|
|
93
|
+
);
|
|
94
|
+
return "{" + pairs.join(",") + "}";
|
|
95
|
+
}
|
|
96
|
+
function constructVerificationMessage({ walletAddress, signedTimestamp, data, verifierIds, chainId, chain }) {
|
|
97
|
+
if (!walletAddress || typeof walletAddress !== "string") {
|
|
98
|
+
throw new SDKError("walletAddress is required and must be a string", "INVALID_WALLET_ADDRESS");
|
|
99
|
+
}
|
|
100
|
+
if (!signedTimestamp || typeof signedTimestamp !== "number") {
|
|
101
|
+
throw new SDKError("signedTimestamp is required and must be a number", "INVALID_TIMESTAMP");
|
|
102
|
+
}
|
|
103
|
+
if (!data || typeof data !== "object") {
|
|
104
|
+
throw new SDKError("data is required and must be an object", "INVALID_DATA");
|
|
105
|
+
}
|
|
106
|
+
if (!Array.isArray(verifierIds) || verifierIds.length === 0) {
|
|
107
|
+
throw new SDKError("verifierIds is required and must be a non-empty array", "INVALID_VERIFIER_IDS");
|
|
108
|
+
}
|
|
109
|
+
const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
|
|
110
|
+
if (!chainContext) {
|
|
111
|
+
throw new SDKError("chainId is required (or provide chain for preview mode)", "INVALID_CHAIN_CONTEXT");
|
|
112
|
+
}
|
|
113
|
+
if (chainContext === chainId && typeof chainId !== "number") {
|
|
114
|
+
throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
|
|
115
|
+
}
|
|
116
|
+
if (chainContext === chain && (typeof chain !== "string" || !chain.includes(":"))) {
|
|
117
|
+
throw new SDKError('chain must be a "namespace:reference" string', "INVALID_CHAIN");
|
|
118
|
+
}
|
|
119
|
+
const namespace = typeof chain === "string" && chain.includes(":") ? chain.split(":")[0] : "eip155";
|
|
120
|
+
const normalizedWalletAddress = namespace === "eip155" ? walletAddress.toLowerCase() : walletAddress;
|
|
121
|
+
const dataString = deterministicStringify(data);
|
|
122
|
+
const messageComponents = [
|
|
123
|
+
"NEUS Verification Request",
|
|
124
|
+
`Wallet: ${normalizedWalletAddress}`,
|
|
125
|
+
`Chain: ${chainContext}`,
|
|
126
|
+
`Verifiers: ${verifierIds.join(",")}`,
|
|
127
|
+
`Data: ${dataString}`,
|
|
128
|
+
`Timestamp: ${signedTimestamp}`
|
|
129
|
+
];
|
|
130
|
+
return messageComponents.join("\n");
|
|
131
|
+
}
|
|
132
|
+
function validateWalletAddress(address) {
|
|
133
|
+
if (!address || typeof address !== "string") {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
137
|
+
}
|
|
138
|
+
function validateTimestamp(timestamp, maxAgeMs = 5 * 60 * 1e3) {
|
|
139
|
+
if (!timestamp || typeof timestamp !== "number") {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const age = now - timestamp;
|
|
144
|
+
return age >= 0 && age <= maxAgeMs;
|
|
145
|
+
}
|
|
146
|
+
function createVerificationData(content, owner, reference = null) {
|
|
147
|
+
const stableRefId = (value) => {
|
|
148
|
+
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
149
|
+
let hash = 0;
|
|
150
|
+
for (let i = 0; i < str.length; i++) {
|
|
151
|
+
hash = (hash << 5) - hash + str.charCodeAt(i);
|
|
152
|
+
hash |= 0;
|
|
153
|
+
}
|
|
154
|
+
const hex = Math.abs(hash).toString(16).padStart(8, "0");
|
|
155
|
+
return `ref-id:${hex}:${str.length}`;
|
|
156
|
+
};
|
|
157
|
+
return {
|
|
158
|
+
content,
|
|
159
|
+
owner: owner.toLowerCase(),
|
|
160
|
+
reference: reference || {
|
|
161
|
+
// Must be a valid backend enum value; 'content' is not supported.
|
|
162
|
+
type: "other",
|
|
163
|
+
id: stableRefId(content)
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function deriveDid(address, chainIdOrChain) {
|
|
168
|
+
if (!address || typeof address !== "string") {
|
|
169
|
+
throw new SDKError("deriveDid: address is required", "INVALID_ARGUMENT");
|
|
170
|
+
}
|
|
171
|
+
const chainContext = chainIdOrChain || NEUS_CONSTANTS.HUB_CHAIN_ID;
|
|
172
|
+
const isCAIP = typeof chainContext === "string" && chainContext.includes(":");
|
|
173
|
+
if (isCAIP) {
|
|
174
|
+
const [namespace, segment] = chainContext.split(":");
|
|
175
|
+
const normalized = namespace === "eip155" ? address.toLowerCase() : address;
|
|
176
|
+
return `did:pkh:${namespace}:${segment}:${normalized}`;
|
|
177
|
+
} else {
|
|
178
|
+
if (typeof chainContext !== "number") {
|
|
179
|
+
throw new SDKError("deriveDid: chainId (number) or chain (namespace:reference string) is required", "INVALID_ARGUMENT");
|
|
180
|
+
}
|
|
181
|
+
return `did:pkh:eip155:${chainContext}:${address.toLowerCase()}`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function isTerminalStatus(status) {
|
|
185
|
+
if (!status || typeof status !== "string")
|
|
186
|
+
return false;
|
|
187
|
+
const successStates = [
|
|
188
|
+
"verified",
|
|
189
|
+
"verified_no_verifiers",
|
|
190
|
+
"verified_crosschain_propagated",
|
|
191
|
+
"partially_verified",
|
|
192
|
+
"verified_propagation_failed"
|
|
193
|
+
];
|
|
194
|
+
const failureStates = [
|
|
195
|
+
"rejected",
|
|
196
|
+
"rejected_verifier_failure",
|
|
197
|
+
"rejected_zk_initiation_failure",
|
|
198
|
+
"error_processing_exception",
|
|
199
|
+
"error_initialization",
|
|
200
|
+
"error_storage_unavailable",
|
|
201
|
+
"error_storage_query",
|
|
202
|
+
"not_found"
|
|
203
|
+
];
|
|
204
|
+
return successStates.includes(status) || failureStates.includes(status);
|
|
205
|
+
}
|
|
206
|
+
function isSuccessStatus(status) {
|
|
207
|
+
if (!status || typeof status !== "string")
|
|
208
|
+
return false;
|
|
209
|
+
const successStates = [
|
|
210
|
+
"verified",
|
|
211
|
+
"verified_no_verifiers",
|
|
212
|
+
"verified_crosschain_propagated",
|
|
213
|
+
"partially_verified",
|
|
214
|
+
"verified_propagation_failed"
|
|
215
|
+
];
|
|
216
|
+
return successStates.includes(status);
|
|
217
|
+
}
|
|
218
|
+
function isFailureStatus(status) {
|
|
219
|
+
if (!status || typeof status !== "string")
|
|
220
|
+
return false;
|
|
221
|
+
const failureStates = [
|
|
222
|
+
"rejected",
|
|
223
|
+
"rejected_verifier_failure",
|
|
224
|
+
"rejected_zk_initiation_failure",
|
|
225
|
+
"error_processing_exception",
|
|
226
|
+
"error_initialization",
|
|
227
|
+
"error_storage_unavailable",
|
|
228
|
+
"error_storage_query",
|
|
229
|
+
"not_found"
|
|
230
|
+
];
|
|
231
|
+
return failureStates.includes(status);
|
|
232
|
+
}
|
|
233
|
+
function formatVerificationStatus(status) {
|
|
234
|
+
const statusMap = {
|
|
235
|
+
"processing_verifiers": {
|
|
236
|
+
label: "Processing",
|
|
237
|
+
description: "Verifiers are being executed",
|
|
238
|
+
category: "processing",
|
|
239
|
+
color: "blue"
|
|
240
|
+
},
|
|
241
|
+
"processing_zk_proofs": {
|
|
242
|
+
label: "Generating ZK Proofs",
|
|
243
|
+
description: "Zero-knowledge proofs are being generated",
|
|
244
|
+
category: "processing",
|
|
245
|
+
color: "blue"
|
|
246
|
+
},
|
|
247
|
+
"verified": {
|
|
248
|
+
label: "Verified",
|
|
249
|
+
description: "Verification completed successfully",
|
|
250
|
+
category: "success",
|
|
251
|
+
color: "green"
|
|
252
|
+
},
|
|
253
|
+
"verified_crosschain_initiated": {
|
|
254
|
+
label: "Cross-chain Initiated",
|
|
255
|
+
description: "Verification successful, cross-chain propagation started",
|
|
256
|
+
category: "processing",
|
|
257
|
+
color: "blue"
|
|
258
|
+
},
|
|
259
|
+
"verified_crosschain_propagating": {
|
|
260
|
+
label: "Cross-chain Propagating",
|
|
261
|
+
description: "Verification successful, transactions propagating to spoke chains",
|
|
262
|
+
category: "processing",
|
|
263
|
+
color: "blue"
|
|
264
|
+
},
|
|
265
|
+
"verified_crosschain_propagated": {
|
|
266
|
+
label: "Fully Propagated",
|
|
267
|
+
description: "Verification completed and propagated to all target chains",
|
|
268
|
+
category: "success",
|
|
269
|
+
color: "green"
|
|
270
|
+
},
|
|
271
|
+
"verified_no_verifiers": {
|
|
272
|
+
label: "Verified (No Verifiers)",
|
|
273
|
+
description: "Verification completed without specific verifiers",
|
|
274
|
+
category: "success",
|
|
275
|
+
color: "green"
|
|
276
|
+
},
|
|
277
|
+
"verified_propagation_failed": {
|
|
278
|
+
label: "Propagation Failed",
|
|
279
|
+
description: "Verification successful but cross-chain propagation failed",
|
|
280
|
+
category: "warning",
|
|
281
|
+
color: "orange"
|
|
282
|
+
},
|
|
283
|
+
"partially_verified": {
|
|
284
|
+
label: "Partially Verified",
|
|
285
|
+
description: "Some verifiers succeeded, others failed",
|
|
286
|
+
category: "warning",
|
|
287
|
+
color: "orange"
|
|
288
|
+
},
|
|
289
|
+
"rejected": {
|
|
290
|
+
label: "Rejected",
|
|
291
|
+
description: "Verification failed",
|
|
292
|
+
category: "error",
|
|
293
|
+
color: "red"
|
|
294
|
+
},
|
|
295
|
+
"rejected_verifier_failure": {
|
|
296
|
+
label: "Verifier Failed",
|
|
297
|
+
description: "One or more verifiers failed",
|
|
298
|
+
category: "error",
|
|
299
|
+
color: "red"
|
|
300
|
+
},
|
|
301
|
+
"rejected_zk_initiation_failure": {
|
|
302
|
+
label: "ZK Initiation Failed",
|
|
303
|
+
description: "Zero-knowledge proof generation failed to start",
|
|
304
|
+
category: "error",
|
|
305
|
+
color: "red"
|
|
306
|
+
},
|
|
307
|
+
"error_processing_exception": {
|
|
308
|
+
label: "Processing Error",
|
|
309
|
+
description: "An error occurred during verification processing",
|
|
310
|
+
category: "error",
|
|
311
|
+
color: "red"
|
|
312
|
+
},
|
|
313
|
+
"error_initialization": {
|
|
314
|
+
label: "Initialization Error",
|
|
315
|
+
description: "Failed to initialize verification",
|
|
316
|
+
category: "error",
|
|
317
|
+
color: "red"
|
|
318
|
+
},
|
|
319
|
+
"not_found": {
|
|
320
|
+
label: "Not Found",
|
|
321
|
+
description: "Verification record not found",
|
|
322
|
+
category: "error",
|
|
323
|
+
color: "red"
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
return statusMap[status] || {
|
|
327
|
+
label: status?.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) || "Unknown",
|
|
328
|
+
description: "Unknown status",
|
|
329
|
+
category: "unknown",
|
|
330
|
+
color: "gray"
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
async function computeContentHash(input) {
|
|
334
|
+
try {
|
|
335
|
+
const ethers = await import("ethers");
|
|
336
|
+
const toBytes = typeof input === "string" ? ethers.toUtf8Bytes(input) : input;
|
|
337
|
+
return ethers.keccak256(toBytes);
|
|
338
|
+
} catch {
|
|
339
|
+
throw new SDKError('computeContentHash requires peer dependency "ethers" >= 6.0.0', "MISSING_PEER_DEP");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function delay(ms) {
|
|
343
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
344
|
+
}
|
|
345
|
+
var StatusPoller = class {
|
|
346
|
+
constructor(client, qHash, options = {}) {
|
|
347
|
+
this.client = client;
|
|
348
|
+
this.qHash = qHash;
|
|
349
|
+
this.options = {
|
|
350
|
+
interval: 2e3,
|
|
351
|
+
// 2 seconds
|
|
352
|
+
maxAttempts: 150,
|
|
353
|
+
// 5 minutes total
|
|
354
|
+
exponentialBackoff: true,
|
|
355
|
+
maxInterval: 1e4,
|
|
356
|
+
// 10 seconds max
|
|
357
|
+
...options
|
|
358
|
+
};
|
|
359
|
+
this.attempt = 0;
|
|
360
|
+
this.currentInterval = this.options.interval;
|
|
361
|
+
}
|
|
362
|
+
async poll() {
|
|
363
|
+
return new Promise((resolve, reject) => {
|
|
364
|
+
const pollAttempt = async () => {
|
|
365
|
+
try {
|
|
366
|
+
this.attempt++;
|
|
367
|
+
const response = await this.client.getStatus(this.qHash);
|
|
368
|
+
if (isTerminalStatus(response.status)) {
|
|
369
|
+
resolve(response);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (this.attempt >= this.options.maxAttempts) {
|
|
373
|
+
reject(new SDKError(
|
|
374
|
+
"Verification polling timeout",
|
|
375
|
+
"POLLING_TIMEOUT"
|
|
376
|
+
));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (this.options.exponentialBackoff) {
|
|
380
|
+
this.currentInterval = Math.min(
|
|
381
|
+
this.currentInterval * 1.5,
|
|
382
|
+
this.options.maxInterval
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
setTimeout(pollAttempt, this.currentInterval);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
reject(new SDKError(
|
|
388
|
+
`Polling failed: ${error.message}`,
|
|
389
|
+
"POLLING_ERROR"
|
|
390
|
+
));
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
pollAttempt();
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
var NEUS_CONSTANTS = {
|
|
398
|
+
// Hub chain (where all verifications occur)
|
|
399
|
+
HUB_CHAIN_ID: 84532,
|
|
400
|
+
// Supported target chains for cross-chain propagation
|
|
401
|
+
TESTNET_CHAINS: [
|
|
402
|
+
11155111,
|
|
403
|
+
// Ethereum Sepolia
|
|
404
|
+
11155420,
|
|
405
|
+
// Optimism Sepolia
|
|
406
|
+
421614,
|
|
407
|
+
// Arbitrum Sepolia
|
|
408
|
+
80002
|
|
409
|
+
// Polygon Amoy
|
|
410
|
+
],
|
|
411
|
+
// API endpoints
|
|
412
|
+
API_BASE_URL: "https://api.neus.network",
|
|
413
|
+
API_VERSION: "v1",
|
|
414
|
+
// Timeouts and limits
|
|
415
|
+
SIGNATURE_MAX_AGE_MS: 5 * 60 * 1e3,
|
|
416
|
+
// 5 minutes
|
|
417
|
+
REQUEST_TIMEOUT_MS: 30 * 1e3,
|
|
418
|
+
// 30 seconds
|
|
419
|
+
// Default verifier set for quick starts
|
|
420
|
+
DEFAULT_VERIFIERS: [
|
|
421
|
+
"ownership-basic",
|
|
422
|
+
"nft-ownership",
|
|
423
|
+
"token-holding"
|
|
424
|
+
]
|
|
425
|
+
};
|
|
426
|
+
function validateQHash(qHash) {
|
|
427
|
+
return typeof qHash === "string" && /^0x[a-fA-F0-9]{64}$/.test(qHash);
|
|
428
|
+
}
|
|
429
|
+
function formatTimestamp(timestamp) {
|
|
430
|
+
return new Date(timestamp).toLocaleString();
|
|
431
|
+
}
|
|
432
|
+
function isSupportedChain(chainId) {
|
|
433
|
+
return NEUS_CONSTANTS.TESTNET_CHAINS.includes(chainId) || chainId === NEUS_CONSTANTS.HUB_CHAIN_ID;
|
|
434
|
+
}
|
|
435
|
+
function normalizeAddress(address) {
|
|
436
|
+
if (!validateWalletAddress(address)) {
|
|
437
|
+
throw new SDKError("Invalid wallet address format", "INVALID_ADDRESS");
|
|
438
|
+
}
|
|
439
|
+
return address.toLowerCase();
|
|
440
|
+
}
|
|
441
|
+
function validateVerifierPayload(verifierId, data) {
|
|
442
|
+
const result = { valid: true, missing: [], warnings: [] };
|
|
443
|
+
if (!verifierId || typeof verifierId !== "string") {
|
|
444
|
+
return { valid: false, error: "verifierId is required and must be a string" };
|
|
445
|
+
}
|
|
446
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
447
|
+
return { valid: false, error: "data must be a non-null object" };
|
|
448
|
+
}
|
|
449
|
+
const id = verifierId.replace(/@\d+$/, "");
|
|
450
|
+
if (id === "nft-ownership") {
|
|
451
|
+
["contractAddress", "tokenId", "chainId"].forEach((key) => {
|
|
452
|
+
if (!(key in data))
|
|
453
|
+
result.missing.push(key);
|
|
454
|
+
});
|
|
455
|
+
if (!("ownerAddress" in data)) {
|
|
456
|
+
result.warnings.push("ownerAddress omitted (most deployments default to the signed walletAddress)");
|
|
457
|
+
}
|
|
458
|
+
} else if (id === "token-holding") {
|
|
459
|
+
["contractAddress", "minBalance", "chainId"].forEach((key) => {
|
|
460
|
+
if (!(key in data))
|
|
461
|
+
result.missing.push(key);
|
|
462
|
+
});
|
|
463
|
+
if (!("ownerAddress" in data)) {
|
|
464
|
+
result.warnings.push("ownerAddress omitted (most deployments default to the signed walletAddress)");
|
|
465
|
+
}
|
|
466
|
+
} else if (id === "ownership-basic") {
|
|
467
|
+
["content"].forEach((key) => {
|
|
468
|
+
if (!(key in data))
|
|
469
|
+
result.missing.push(key);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (result.missing.length > 0) {
|
|
473
|
+
result.valid = false;
|
|
474
|
+
result.error = `Missing required fields: ${result.missing.join(", ")}`;
|
|
475
|
+
}
|
|
476
|
+
return result;
|
|
477
|
+
}
|
|
478
|
+
function buildVerificationRequest({
|
|
479
|
+
verifierIds,
|
|
480
|
+
data,
|
|
481
|
+
walletAddress,
|
|
482
|
+
chainId = NEUS_CONSTANTS.HUB_CHAIN_ID,
|
|
483
|
+
options = void 0,
|
|
484
|
+
signedTimestamp = Date.now()
|
|
485
|
+
}) {
|
|
486
|
+
if (!Array.isArray(verifierIds) || verifierIds.length === 0) {
|
|
487
|
+
throw new SDKError("verifierIds must be a non-empty array", "INVALID_ARGUMENT");
|
|
488
|
+
}
|
|
489
|
+
if (!validateWalletAddress(walletAddress)) {
|
|
490
|
+
throw new SDKError("walletAddress must be a valid 0x address", "INVALID_ARGUMENT");
|
|
491
|
+
}
|
|
492
|
+
if (!data || typeof data !== "object") {
|
|
493
|
+
throw new SDKError("data must be a non-null object", "INVALID_ARGUMENT");
|
|
494
|
+
}
|
|
495
|
+
if (typeof chainId !== "number") {
|
|
496
|
+
throw new SDKError("chainId must be a number", "INVALID_ARGUMENT");
|
|
497
|
+
}
|
|
498
|
+
const message = constructVerificationMessage({
|
|
499
|
+
walletAddress,
|
|
500
|
+
signedTimestamp,
|
|
501
|
+
data,
|
|
502
|
+
verifierIds,
|
|
503
|
+
chainId
|
|
504
|
+
});
|
|
505
|
+
const request = {
|
|
506
|
+
verifierIds,
|
|
507
|
+
data,
|
|
508
|
+
walletAddress,
|
|
509
|
+
signedTimestamp,
|
|
510
|
+
chainId,
|
|
511
|
+
...options ? { options } : {}
|
|
512
|
+
};
|
|
513
|
+
return { message, request };
|
|
514
|
+
}
|
|
515
|
+
async function withRetry(fn, options = {}) {
|
|
516
|
+
const {
|
|
517
|
+
maxAttempts = 3,
|
|
518
|
+
baseDelay = 1e3,
|
|
519
|
+
maxDelay = 1e4,
|
|
520
|
+
backoffFactor = 2
|
|
521
|
+
} = options;
|
|
522
|
+
let lastError;
|
|
523
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
524
|
+
try {
|
|
525
|
+
return await fn();
|
|
526
|
+
} catch (error) {
|
|
527
|
+
lastError = error;
|
|
528
|
+
if (attempt === maxAttempts)
|
|
529
|
+
break;
|
|
530
|
+
const delayMs = Math.min(
|
|
531
|
+
baseDelay * Math.pow(backoffFactor, attempt - 1),
|
|
532
|
+
maxDelay
|
|
533
|
+
);
|
|
534
|
+
await delay(delayMs);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
throw lastError;
|
|
538
|
+
}
|
|
539
|
+
function validateSignatureComponents({ walletAddress, signature, signedTimestamp, data, verifierIds, chainId }) {
|
|
540
|
+
const result = {
|
|
541
|
+
valid: true,
|
|
542
|
+
errors: [],
|
|
543
|
+
warnings: [],
|
|
544
|
+
debugInfo: {}
|
|
545
|
+
};
|
|
546
|
+
if (!validateWalletAddress(walletAddress)) {
|
|
547
|
+
result.valid = false;
|
|
548
|
+
result.errors.push("Invalid wallet address format - must be 0x + 40 hex characters");
|
|
549
|
+
} else {
|
|
550
|
+
result.debugInfo.normalizedAddress = walletAddress.toLowerCase();
|
|
551
|
+
if (walletAddress !== walletAddress.toLowerCase()) {
|
|
552
|
+
result.warnings.push("Wallet address should be lowercase for consistency");
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (!signature || typeof signature !== "string") {
|
|
556
|
+
result.valid = false;
|
|
557
|
+
result.errors.push("Signature is required and must be a string");
|
|
558
|
+
} else if (!/^0x[a-fA-F0-9]{130}$/.test(signature)) {
|
|
559
|
+
result.valid = false;
|
|
560
|
+
result.errors.push("Invalid signature format - must be 0x + 130 hex characters (65 bytes)");
|
|
561
|
+
}
|
|
562
|
+
if (!validateTimestamp(signedTimestamp)) {
|
|
563
|
+
result.valid = false;
|
|
564
|
+
result.errors.push("Invalid or expired timestamp - must be within 5 minutes");
|
|
565
|
+
} else {
|
|
566
|
+
result.debugInfo.timestampAge = Date.now() - signedTimestamp;
|
|
567
|
+
}
|
|
568
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
569
|
+
result.valid = false;
|
|
570
|
+
result.errors.push("Data must be a non-null object");
|
|
571
|
+
} else {
|
|
572
|
+
result.debugInfo.dataString = deterministicStringify(data);
|
|
573
|
+
}
|
|
574
|
+
if (!Array.isArray(verifierIds) || verifierIds.length === 0) {
|
|
575
|
+
result.valid = false;
|
|
576
|
+
result.errors.push("VerifierIds must be a non-empty array");
|
|
577
|
+
}
|
|
578
|
+
if (typeof chainId !== "number") {
|
|
579
|
+
result.valid = false;
|
|
580
|
+
result.errors.push("ChainId must be a number");
|
|
581
|
+
}
|
|
582
|
+
if (result.valid || result.errors.length < 3) {
|
|
583
|
+
try {
|
|
584
|
+
result.debugInfo.messageToSign = constructVerificationMessage({
|
|
585
|
+
walletAddress: walletAddress?.toLowerCase() || walletAddress,
|
|
586
|
+
signedTimestamp,
|
|
587
|
+
data,
|
|
588
|
+
verifierIds,
|
|
589
|
+
chainId
|
|
590
|
+
});
|
|
591
|
+
} catch (error) {
|
|
592
|
+
result.errors.push(`Failed to construct message: ${error.message}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
597
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
598
|
+
0 && (module.exports = {
|
|
599
|
+
NEUS_CONSTANTS,
|
|
600
|
+
StatusPoller,
|
|
601
|
+
buildVerificationRequest,
|
|
602
|
+
computeContentHash,
|
|
603
|
+
constructVerificationMessage,
|
|
604
|
+
createVerificationData,
|
|
605
|
+
delay,
|
|
606
|
+
deriveDid,
|
|
607
|
+
formatTimestamp,
|
|
608
|
+
formatVerificationStatus,
|
|
609
|
+
isFailureStatus,
|
|
610
|
+
isSuccessStatus,
|
|
611
|
+
isSupportedChain,
|
|
612
|
+
isTerminalStatus,
|
|
613
|
+
normalizeAddress,
|
|
614
|
+
validateQHash,
|
|
615
|
+
validateSignatureComponents,
|
|
616
|
+
validateTimestamp,
|
|
617
|
+
validateVerifierPayload,
|
|
618
|
+
validateWalletAddress,
|
|
619
|
+
withRetry
|
|
620
|
+
});
|