@neus/sdk 1.0.1 → 1.0.3

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/cjs/client.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -128,6 +129,50 @@ var ConfigurationError = class extends SDKError {
128
129
  };
129
130
 
130
131
  // utils.js
132
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
133
+ function encodeBase58Bytes(input) {
134
+ let source;
135
+ if (input instanceof Uint8Array) {
136
+ source = input;
137
+ } else if (input instanceof ArrayBuffer) {
138
+ source = new Uint8Array(input);
139
+ } else if (ArrayBuffer.isView(input)) {
140
+ source = new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
141
+ } else if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(input)) {
142
+ source = new Uint8Array(input);
143
+ } else {
144
+ throw new SDKError("Unsupported non-EVM signature byte format", "INVALID_SIGNATURE_FORMAT");
145
+ }
146
+ if (source.length === 0)
147
+ return "";
148
+ let zeroes = 0;
149
+ while (zeroes < source.length && source[zeroes] === 0) {
150
+ zeroes++;
151
+ }
152
+ const iFactor = Math.log(256) / Math.log(58);
153
+ const size = (source.length - zeroes) * iFactor + 1 >>> 0;
154
+ const b58 = new Uint8Array(size);
155
+ let length = 0;
156
+ for (let i = zeroes; i < source.length; i++) {
157
+ let carry = source[i];
158
+ let j = 0;
159
+ for (let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++) {
160
+ carry += 256 * b58[k];
161
+ b58[k] = carry % 58;
162
+ carry = carry / 58 | 0;
163
+ }
164
+ length = j;
165
+ }
166
+ let it = size - length;
167
+ while (it < size && b58[it] === 0) {
168
+ it++;
169
+ }
170
+ let out = BASE58_ALPHABET[0].repeat(zeroes);
171
+ for (; it < size; it++) {
172
+ out += BASE58_ALPHABET[b58[it]];
173
+ }
174
+ return out;
175
+ }
131
176
  function deterministicStringify(obj) {
132
177
  if (obj === null || obj === void 0) {
133
178
  return JSON.stringify(obj);
@@ -159,7 +204,7 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
159
204
  }
160
205
  const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
161
206
  if (!chainContext) {
162
- throw new SDKError("chainId is required (or provide chain for preview mode)", "INVALID_CHAIN_CONTEXT");
207
+ throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
163
208
  }
164
209
  if (chainContext === chainId && typeof chainId !== "number") {
165
210
  throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
@@ -186,6 +231,146 @@ function validateWalletAddress(address) {
186
231
  }
187
232
  return /^0x[a-fA-F0-9]{40}$/.test(address);
188
233
  }
234
+ function validateUniversalAddress(address, chain) {
235
+ if (!address || typeof address !== "string")
236
+ return false;
237
+ const value = address.trim();
238
+ if (!value)
239
+ return false;
240
+ const chainRef = typeof chain === "string" ? chain.trim().toLowerCase() : "";
241
+ const namespace = chainRef.includes(":") ? chainRef.split(":")[0] : "";
242
+ if (validateWalletAddress(value))
243
+ return true;
244
+ if (namespace === "eip155")
245
+ return false;
246
+ if (namespace === "solana") {
247
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
248
+ }
249
+ if (namespace === "bip122") {
250
+ return /^(bc1|tb1|bcrt1)[a-z0-9]{11,87}$/.test(value.toLowerCase()) || /^[13mn2][a-km-zA-HJ-NP-Z1-9]{25,62}$/.test(value);
251
+ }
252
+ if (namespace === "near") {
253
+ return /^[a-z0-9._-]{2,64}$/.test(value);
254
+ }
255
+ return /^[A-Za-z0-9][A-Za-z0-9._:-]{1,127}$/.test(value);
256
+ }
257
+ function toHexUtf8(value) {
258
+ const input = typeof value === "string" ? value : String(value || "");
259
+ const bytes = new TextEncoder().encode(input);
260
+ return `0x${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
261
+ }
262
+ async function signMessage({ provider, message, walletAddress, chain } = {}) {
263
+ const msg = typeof message === "string" ? message : String(message || "");
264
+ if (!msg) {
265
+ throw new SDKError("signMessage: message is required", "INVALID_ARGUMENT");
266
+ }
267
+ const resolvedProvider = provider || (typeof window !== "undefined" && window?.ethereum ? window.ethereum : null);
268
+ if (!resolvedProvider) {
269
+ throw new SDKError("signMessage: provider is required", "SIGNER_UNAVAILABLE");
270
+ }
271
+ const chainStr = typeof chain === "string" && chain.trim().length > 0 ? chain.trim() : "eip155";
272
+ const namespace = chainStr.includes(":") ? chainStr.split(":")[0] || "eip155" : "eip155";
273
+ const resolveAddress = async () => {
274
+ if (typeof walletAddress === "string" && walletAddress.trim().length > 0)
275
+ return walletAddress;
276
+ if (namespace === "solana") {
277
+ if (resolvedProvider?.publicKey && typeof resolvedProvider.publicKey.toBase58 === "function") {
278
+ const pk = resolvedProvider.publicKey.toBase58();
279
+ if (typeof pk === "string" && pk)
280
+ return pk;
281
+ }
282
+ if (typeof resolvedProvider.getAddress === "function") {
283
+ const addr = await resolvedProvider.getAddress().catch(() => null);
284
+ if (typeof addr === "string" && addr)
285
+ return addr;
286
+ }
287
+ if (typeof resolvedProvider.address === "string" && resolvedProvider.address)
288
+ return resolvedProvider.address;
289
+ return null;
290
+ }
291
+ if (typeof resolvedProvider.address === "string" && resolvedProvider.address)
292
+ return resolvedProvider.address;
293
+ if (typeof resolvedProvider.getAddress === "function")
294
+ return await resolvedProvider.getAddress();
295
+ if (typeof resolvedProvider.request === "function") {
296
+ let accounts = await resolvedProvider.request({ method: "eth_accounts" }).catch(() => []);
297
+ if (!Array.isArray(accounts) || accounts.length === 0) {
298
+ accounts = await resolvedProvider.request({ method: "eth_requestAccounts" }).catch(() => []);
299
+ }
300
+ if (Array.isArray(accounts) && accounts[0])
301
+ return accounts[0];
302
+ }
303
+ return null;
304
+ };
305
+ if (namespace !== "eip155") {
306
+ if (typeof resolvedProvider.signMessage === "function") {
307
+ const encoded = typeof msg === "string" ? new TextEncoder().encode(msg) : msg;
308
+ const result = await resolvedProvider.signMessage(encoded);
309
+ if (typeof result === "string" && result)
310
+ return result;
311
+ if (result instanceof Uint8Array)
312
+ return encodeBase58Bytes(result);
313
+ if (result instanceof ArrayBuffer)
314
+ return encodeBase58Bytes(new Uint8Array(result));
315
+ if (ArrayBuffer.isView(result))
316
+ return encodeBase58Bytes(result);
317
+ if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(result))
318
+ return encodeBase58Bytes(result);
319
+ }
320
+ throw new SDKError("Non-EVM signing requires provider.signMessage", "SIGNER_UNAVAILABLE");
321
+ }
322
+ const address = await resolveAddress();
323
+ if (typeof resolvedProvider.request === "function" && address) {
324
+ let firstPersonalSignError = null;
325
+ try {
326
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [msg, address] });
327
+ if (typeof sig === "string" && sig)
328
+ return sig;
329
+ } catch (error) {
330
+ firstPersonalSignError = error;
331
+ }
332
+ let secondPersonalSignError = null;
333
+ try {
334
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [address, msg] });
335
+ if (typeof sig === "string" && sig)
336
+ return sig;
337
+ } catch (error) {
338
+ secondPersonalSignError = error;
339
+ const signatureErrorMessage = String(
340
+ error?.message || error?.reason || firstPersonalSignError?.message || firstPersonalSignError?.reason || ""
341
+ ).toLowerCase();
342
+ const needsHex = /byte|bytes|invalid byte sequence|encoding|non-hex/i.test(signatureErrorMessage);
343
+ if (needsHex) {
344
+ try {
345
+ const hexMsg = toHexUtf8(msg);
346
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [hexMsg, address] });
347
+ if (typeof sig === "string" && sig)
348
+ return sig;
349
+ } catch {
350
+ }
351
+ }
352
+ }
353
+ try {
354
+ const sig = await resolvedProvider.request({ method: "eth_sign", params: [address, msg] });
355
+ if (typeof sig === "string" && sig)
356
+ return sig;
357
+ } catch {
358
+ }
359
+ if (secondPersonalSignError || firstPersonalSignError) {
360
+ const lastError = secondPersonalSignError || firstPersonalSignError;
361
+ const isUserRejection = [4001, "ACTION_REJECTED"].includes(lastError?.code);
362
+ if (isUserRejection) {
363
+ throw lastError;
364
+ }
365
+ }
366
+ }
367
+ if (typeof resolvedProvider.signMessage === "function") {
368
+ const result = await resolvedProvider.signMessage(msg);
369
+ if (typeof result === "string" && result)
370
+ return result;
371
+ }
372
+ throw new SDKError("Unable to sign message with provided wallet/provider", "SIGNER_UNAVAILABLE");
373
+ }
189
374
  var NEUS_CONSTANTS = {
190
375
  // Hub chain (where all verifications occur)
191
376
  HUB_CHAIN_ID: 84532,
@@ -217,18 +402,97 @@ var NEUS_CONSTANTS = {
217
402
  };
218
403
 
219
404
  // client.js
405
+ var FALLBACK_PUBLIC_VERIFIERS = [
406
+ "ownership-basic",
407
+ "ownership-pseudonym",
408
+ "ownership-dns-txt",
409
+ "ownership-social",
410
+ "ownership-org-oauth",
411
+ "contract-ownership",
412
+ "nft-ownership",
413
+ "token-holding",
414
+ "wallet-link",
415
+ "wallet-risk",
416
+ "proof-of-human",
417
+ "agent-identity",
418
+ "agent-delegation",
419
+ "ai-content-moderation"
420
+ ];
421
+ var INTERACTIVE_VERIFIERS = /* @__PURE__ */ new Set([
422
+ "ownership-social",
423
+ "ownership-org-oauth",
424
+ "proof-of-human"
425
+ ]);
426
+ var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
220
427
  var validateVerifierData = (verifierId, data) => {
221
428
  if (!data || typeof data !== "object") {
222
429
  return { valid: false, error: "Data object is required" };
223
430
  }
224
- const ownerField = verifierId === "nft-ownership" || verifierId === "token-holding" ? "ownerAddress" : "owner";
225
- if (data[ownerField] && !validateWalletAddress(data[ownerField])) {
226
- return { valid: false, error: `Invalid ${ownerField} address` };
227
- }
228
431
  switch (verifierId) {
229
432
  case "ownership-basic":
230
- if (!data.owner || !validateWalletAddress(data.owner)) {
231
- return { valid: false, error: "owner (wallet address) is required" };
433
+ if (!data.owner || !validateUniversalAddress(data.owner, typeof data.chain === "string" ? data.chain : void 0)) {
434
+ return { valid: false, error: "owner (universal wallet address) is required" };
435
+ }
436
+ if (data.content !== void 0 && data.content !== null) {
437
+ if (typeof data.content !== "string") {
438
+ return { valid: false, error: "content must be a string when provided" };
439
+ }
440
+ if (data.content.length > 5e4) {
441
+ return { valid: false, error: "content exceeds 50KB inline limit" };
442
+ }
443
+ }
444
+ if (data.contentHash !== void 0 && data.contentHash !== null) {
445
+ if (typeof data.contentHash !== "string" || !/^0x[a-fA-F0-9]{64}$/.test(data.contentHash)) {
446
+ return { valid: false, error: "contentHash must be a 32-byte hex string (0x + 64 hex chars) when provided" };
447
+ }
448
+ }
449
+ if (data.contentType !== void 0 && data.contentType !== null) {
450
+ if (typeof data.contentType !== "string" || data.contentType.length > 100) {
451
+ return { valid: false, error: "contentType must be a string (max 100 chars) when provided" };
452
+ }
453
+ const base = String(data.contentType).split(";")[0].trim().toLowerCase();
454
+ if (!base || base.includes(" ") || !base.includes("/")) {
455
+ return { valid: false, error: "contentType must be a valid MIME type when provided" };
456
+ }
457
+ }
458
+ if (data.provenance !== void 0 && data.provenance !== null) {
459
+ if (!data.provenance || typeof data.provenance !== "object" || Array.isArray(data.provenance)) {
460
+ return { valid: false, error: "provenance must be an object when provided" };
461
+ }
462
+ const dk = data.provenance.declaredKind;
463
+ if (dk !== void 0 && dk !== null) {
464
+ const allowed = ["human", "ai", "mixed", "unknown"];
465
+ if (typeof dk !== "string" || !allowed.includes(dk)) {
466
+ return { valid: false, error: `provenance.declaredKind must be one of: ${allowed.join(", ")}` };
467
+ }
468
+ }
469
+ const ai = data.provenance.aiContext;
470
+ if (ai !== void 0 && ai !== null) {
471
+ if (typeof ai !== "object" || Array.isArray(ai)) {
472
+ return { valid: false, error: "provenance.aiContext must be an object when provided" };
473
+ }
474
+ if (ai.generatorType !== void 0 && ai.generatorType !== null) {
475
+ const allowed = ["local", "saas", "agent"];
476
+ if (typeof ai.generatorType !== "string" || !allowed.includes(ai.generatorType)) {
477
+ return { valid: false, error: `provenance.aiContext.generatorType must be one of: ${allowed.join(", ")}` };
478
+ }
479
+ }
480
+ if (ai.provider !== void 0 && ai.provider !== null) {
481
+ if (typeof ai.provider !== "string" || ai.provider.length > 64) {
482
+ return { valid: false, error: "provenance.aiContext.provider must be a string (max 64 chars) when provided" };
483
+ }
484
+ }
485
+ if (ai.model !== void 0 && ai.model !== null) {
486
+ if (typeof ai.model !== "string" || ai.model.length > 128) {
487
+ return { valid: false, error: "provenance.aiContext.model must be a string (max 128 chars) when provided" };
488
+ }
489
+ }
490
+ if (ai.runId !== void 0 && ai.runId !== null) {
491
+ if (typeof ai.runId !== "string" || ai.runId.length > 128) {
492
+ return { valid: false, error: "provenance.aiContext.runId must be a string (max 128 chars) when provided" };
493
+ }
494
+ }
495
+ }
232
496
  }
233
497
  if (data.reference !== void 0) {
234
498
  if (!data.reference || typeof data.reference !== "object") {
@@ -293,17 +557,20 @@ var validateVerifierData = (verifierId, data) => {
293
557
  }
294
558
  break;
295
559
  case "wallet-link":
296
- if (!data.primaryWalletAddress || !validateWalletAddress(data.primaryWalletAddress)) {
560
+ if (!data.primaryWalletAddress || !validateUniversalAddress(data.primaryWalletAddress, data.chain)) {
297
561
  return { valid: false, error: "primaryWalletAddress is required" };
298
562
  }
299
- if (!data.secondaryWalletAddress || !validateWalletAddress(data.secondaryWalletAddress)) {
563
+ if (!data.secondaryWalletAddress || !validateUniversalAddress(data.secondaryWalletAddress, data.chain)) {
300
564
  return { valid: false, error: "secondaryWalletAddress is required" };
301
565
  }
302
566
  if (!data.signature || typeof data.signature !== "string") {
303
567
  return { valid: false, error: "signature is required (signed by secondary wallet)" };
304
568
  }
305
- if (typeof data.chainId !== "number") {
306
- return { valid: false, error: "chainId is required" };
569
+ if (typeof data.chain !== "string" || !/^[a-z0-9]+:[^\s]+$/.test(data.chain)) {
570
+ return { valid: false, error: "chain is required (namespace:reference)" };
571
+ }
572
+ if (typeof data.signatureMethod !== "string" || !data.signatureMethod.trim()) {
573
+ return { valid: false, error: "signatureMethod is required" };
307
574
  }
308
575
  if (typeof data.signedTimestamp !== "number") {
309
576
  return { valid: false, error: "signedTimestamp is required" };
@@ -368,6 +635,17 @@ var validateVerifierData = (verifierId, data) => {
368
635
  if (!validTypes.includes(contentType)) {
369
636
  return { valid: false, error: `contentType must be one of: ${validTypes.join(", ")}` };
370
637
  }
638
+ const isTextual = contentType.startsWith("text/") || contentType.includes("markdown");
639
+ if (isTextual) {
640
+ try {
641
+ const maxBytes = 50 * 1024;
642
+ const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(data.content).length : String(data.content).length;
643
+ if (bytes > maxBytes) {
644
+ return { valid: false, error: `content exceeds ${maxBytes} bytes limit for ai-content-moderation verifier (text)` };
645
+ }
646
+ } catch {
647
+ }
648
+ }
371
649
  }
372
650
  if (data.content.length > 13653334) {
373
651
  return { valid: false, error: "content exceeds 10MB limit" };
@@ -387,7 +665,7 @@ var validateVerifierData = (verifierId, data) => {
387
665
  }
388
666
  break;
389
667
  case "wallet-risk":
390
- if (data.walletAddress && !validateWalletAddress(data.walletAddress)) {
668
+ if (data.walletAddress && !validateUniversalAddress(data.walletAddress, data.chain)) {
391
669
  return { valid: false, error: "Invalid walletAddress" };
392
670
  }
393
671
  break;
@@ -419,6 +697,26 @@ var NeusClient = class {
419
697
  if (typeof this.config.apiKey === "string" && this.config.apiKey.trim().length > 0) {
420
698
  this.defaultHeaders["Authorization"] = `Bearer ${this.config.apiKey.trim()}`;
421
699
  }
700
+ if (typeof this.config.appId === "string" && this.config.appId.trim().length > 0) {
701
+ this.defaultHeaders["X-Neus-App"] = this.config.appId.trim();
702
+ }
703
+ if (typeof this.config.sponsorGrant === "string" && this.config.sponsorGrant.trim().length > 0) {
704
+ this.defaultHeaders["X-Sponsor-Grant"] = this.config.sponsorGrant.trim();
705
+ }
706
+ if (typeof this.config.paymentSignature === "string" && this.config.paymentSignature.trim().length > 0) {
707
+ this.defaultHeaders["PAYMENT-SIGNATURE"] = this.config.paymentSignature.trim();
708
+ }
709
+ if (this.config.extraHeaders && typeof this.config.extraHeaders === "object") {
710
+ for (const [k, v] of Object.entries(this.config.extraHeaders)) {
711
+ if (!k || v === void 0 || v === null)
712
+ continue;
713
+ const key = String(k).trim();
714
+ const value = String(v).trim();
715
+ if (!key || !value)
716
+ continue;
717
+ this.defaultHeaders[key] = value;
718
+ }
719
+ }
422
720
  try {
423
721
  if (typeof window !== "undefined" && window.location && window.location.origin) {
424
722
  this.defaultHeaders["X-Client-Origin"] = window.location.origin;
@@ -426,6 +724,122 @@ var NeusClient = class {
426
724
  } catch {
427
725
  }
428
726
  }
727
+ _getHubChainId() {
728
+ const configured = Number(this.config?.hubChainId);
729
+ if (Number.isFinite(configured) && configured > 0)
730
+ return Math.floor(configured);
731
+ return NEUS_CONSTANTS.HUB_CHAIN_ID;
732
+ }
733
+ _normalizeIdentity(value) {
734
+ let raw = String(value || "").trim();
735
+ if (!raw)
736
+ return "";
737
+ const didMatch = raw.match(/^did:pkh:([^:]+):([^:]+):(.+)$/i);
738
+ if (didMatch && didMatch[3]) {
739
+ raw = String(didMatch[3]).trim();
740
+ }
741
+ return EVM_ADDRESS_RE.test(raw) ? raw.toLowerCase() : raw;
742
+ }
743
+ _inferChainForAddress(address, explicitChain) {
744
+ if (typeof explicitChain === "string" && explicitChain.includes(":"))
745
+ return explicitChain.trim();
746
+ const raw = String(address || "").trim();
747
+ const didMatch = raw.match(/^did:pkh:([^:]+):([^:]+):(.+)$/i);
748
+ if (didMatch && didMatch[1] && didMatch[2]) {
749
+ return `${didMatch[1]}:${didMatch[2]}`;
750
+ }
751
+ if (EVM_ADDRESS_RE.test(raw)) {
752
+ return `eip155:${this._getHubChainId()}`;
753
+ }
754
+ return "solana:mainnet";
755
+ }
756
+ async _resolveWalletSigner(wallet) {
757
+ if (!wallet) {
758
+ throw new ConfigurationError("No wallet provider available");
759
+ }
760
+ if (wallet.address) {
761
+ return { signerWalletAddress: wallet.address, provider: wallet };
762
+ }
763
+ if (wallet.publicKey && typeof wallet.publicKey.toBase58 === "function") {
764
+ return { signerWalletAddress: wallet.publicKey.toBase58(), provider: wallet };
765
+ }
766
+ if (typeof wallet.getAddress === "function") {
767
+ const signerWalletAddress = await wallet.getAddress().catch(() => null);
768
+ return { signerWalletAddress, provider: wallet };
769
+ }
770
+ if (wallet.selectedAddress || wallet.request) {
771
+ const provider = wallet;
772
+ if (wallet.selectedAddress) {
773
+ return { signerWalletAddress: wallet.selectedAddress, provider };
774
+ }
775
+ const accounts = await provider.request({ method: "eth_accounts" });
776
+ if (!accounts || accounts.length === 0) {
777
+ throw new ConfigurationError("No wallet accounts available");
778
+ }
779
+ return { signerWalletAddress: accounts[0], provider };
780
+ }
781
+ throw new ConfigurationError("Invalid wallet provider");
782
+ }
783
+ _getDefaultBrowserWallet() {
784
+ if (typeof window === "undefined")
785
+ return null;
786
+ return window.ethereum || window.solana || window.phantom && window.phantom.solana || null;
787
+ }
788
+ async _buildPrivateGateAuth({ address, wallet, chain, signatureMethod } = {}) {
789
+ const providerWallet = wallet || this._getDefaultBrowserWallet();
790
+ const { signerWalletAddress, provider } = await this._resolveWalletSigner(providerWallet);
791
+ if (!signerWalletAddress || typeof signerWalletAddress !== "string") {
792
+ throw new ConfigurationError("No wallet accounts available");
793
+ }
794
+ const normalizedSigner = this._normalizeIdentity(signerWalletAddress);
795
+ const normalizedAddress = this._normalizeIdentity(address);
796
+ if (!normalizedSigner || normalizedSigner !== normalizedAddress) {
797
+ throw new ValidationError("wallet must match address when includePrivate=true");
798
+ }
799
+ const signerIsEvm = EVM_ADDRESS_RE.test(normalizedSigner);
800
+ const resolvedChain = this._inferChainForAddress(normalizedSigner, chain);
801
+ const resolvedSignatureMethod = typeof signatureMethod === "string" && signatureMethod.trim() ? signatureMethod.trim() : signerIsEvm ? "eip191" : "ed25519";
802
+ const signedTimestamp = Date.now();
803
+ const message = constructVerificationMessage({
804
+ walletAddress: signerWalletAddress,
805
+ signedTimestamp,
806
+ data: { action: "gate_check_private_proofs", walletAddress: normalizedAddress },
807
+ verifierIds: ["ownership-basic"],
808
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain: resolvedChain }
809
+ });
810
+ let signature;
811
+ try {
812
+ signature = await signMessage({
813
+ provider,
814
+ message,
815
+ walletAddress: signerWalletAddress,
816
+ ...signerIsEvm ? {} : { chain: resolvedChain }
817
+ });
818
+ } catch (error) {
819
+ if (error.code === 4001) {
820
+ throw new ValidationError("User rejected signature request");
821
+ }
822
+ throw new ValidationError(`Failed to sign message: ${error.message}`);
823
+ }
824
+ return {
825
+ walletAddress: signerWalletAddress,
826
+ signature,
827
+ signedTimestamp,
828
+ ...signerIsEvm ? {} : { chain: resolvedChain, signatureMethod: resolvedSignatureMethod }
829
+ };
830
+ }
831
+ async createGatePrivateAuth(params = {}) {
832
+ const address = (params.address || "").toString();
833
+ if (!validateUniversalAddress(address, params.chain)) {
834
+ throw new ValidationError("Valid address is required");
835
+ }
836
+ return this._buildPrivateGateAuth({
837
+ address,
838
+ wallet: params.wallet,
839
+ chain: params.chain,
840
+ signatureMethod: params.signatureMethod
841
+ });
842
+ }
429
843
  // ============================================================================
430
844
  // CORE VERIFICATION METHODS
431
845
  // ============================================================================
@@ -447,7 +861,7 @@ var NeusClient = class {
447
861
  * @param {string} [params.verifier] - Verifier ID (auto path)
448
862
  * @param {string} [params.content] - Content/description (auto path)
449
863
  * @param {Object} [params.wallet] - Optional injected wallet/provider (auto path)
450
- * @returns {Promise<Object>} Verification result with qHash
864
+ * @returns {Promise<Object>} Verification result with proofId (qHash is a deprecated alias)
451
865
  *
452
866
  * @example
453
867
  * const proof = await client.verify({
@@ -455,7 +869,7 @@ var NeusClient = class {
455
869
  * data: {
456
870
  * content: "My content",
457
871
  * owner: walletAddress, // or ownerAddress for nft-ownership/token-holding
458
- * reference: { type: 'content', id: 'my-unique-identifier' }
872
+ * reference: { type: 'other', id: 'my-unique-identifier' }
459
873
  * },
460
874
  * walletAddress: '0x...',
461
875
  * signature: '0x...',
@@ -472,7 +886,7 @@ var NeusClient = class {
472
886
  * @param {Object} [params.data] - Structured verification data
473
887
  * @param {Object} [params.wallet] - Wallet provider
474
888
  * @param {Object} [params.options] - Additional options
475
- * @returns {Promise<Object>} Verification result with qHash
889
+ * @returns {Promise<Object>} Verification result with proofId (qHash is a deprecated alias)
476
890
  *
477
891
  * @example
478
892
  * // Simple ownership proof
@@ -488,25 +902,23 @@ var NeusClient = class {
488
902
  if (verifier === "ownership-basic" && !data2 && (!content || typeof content !== "string")) {
489
903
  throw new ValidationError("content is required and must be a string (or use data param with owner + reference)");
490
904
  }
491
- const validVerifiers = [
492
- "ownership-basic",
493
- "ownership-pseudonym",
494
- // Pseudonymous identity (public)
495
- "nft-ownership",
496
- "token-holding",
497
- "ownership-dns-txt",
498
- "wallet-link",
499
- "contract-ownership",
500
- "wallet-risk",
501
- // Wallet risk assessment (public)
502
- // AI & Agent verifiers (ERC-8004 aligned)
503
- "agent-identity",
504
- "agent-delegation",
505
- "ai-content-moderation"
506
- ];
905
+ let validVerifiers = FALLBACK_PUBLIC_VERIFIERS;
906
+ try {
907
+ const discovered = await this.getVerifiers();
908
+ if (Array.isArray(discovered) && discovered.length > 0) {
909
+ validVerifiers = discovered;
910
+ }
911
+ } catch {
912
+ }
507
913
  if (!validVerifiers.includes(verifier)) {
508
914
  throw new ValidationError(`Invalid verifier '${verifier}'. Must be one of: ${validVerifiers.join(", ")}.`);
509
915
  }
916
+ if (INTERACTIVE_VERIFIERS.has(verifier)) {
917
+ const hostedCheckoutUrl = options2?.hostedCheckoutUrl || "https://neus.network/verify";
918
+ throw new ValidationError(
919
+ `${verifier} requires hosted interactive checkout. Use VerifyGate or redirect to ${hostedCheckoutUrl}.`
920
+ );
921
+ }
510
922
  const requiresDataParam = [
511
923
  "ownership-dns-txt",
512
924
  "wallet-link",
@@ -524,8 +936,12 @@ var NeusClient = class {
524
936
  }
525
937
  let walletAddress2, provider;
526
938
  if (wallet) {
527
- walletAddress2 = wallet.address || wallet.selectedAddress;
939
+ walletAddress2 = wallet.address || wallet.selectedAddress || wallet.walletAddress || (typeof wallet.getAddress === "function" ? await wallet.getAddress() : null);
528
940
  provider = wallet.provider || wallet;
941
+ if (!walletAddress2 && provider && typeof provider.request === "function") {
942
+ const accounts = await provider.request({ method: "eth_accounts" });
943
+ walletAddress2 = Array.isArray(accounts) ? accounts[0] : null;
944
+ }
529
945
  } else {
530
946
  if (typeof window === "undefined" || !window.ethereum) {
531
947
  throw new ConfigurationError("No Web3 wallet detected. Please install MetaMask or provide wallet parameter.");
@@ -543,6 +959,7 @@ var NeusClient = class {
543
959
  reference: data2.reference,
544
960
  ...data2.content && { content: data2.content },
545
961
  ...data2.contentHash && { contentHash: data2.contentHash },
962
+ ...data2.contentType && { contentType: data2.contentType },
546
963
  ...data2.provenance && { provenance: data2.provenance }
547
964
  };
548
965
  } else {
@@ -600,14 +1017,18 @@ var NeusClient = class {
600
1017
  if (!data2?.signature) {
601
1018
  throw new ValidationError("wallet-link requires signature in data parameter (signed by secondary wallet)");
602
1019
  }
603
- if (typeof data2?.chainId !== "number") {
604
- throw new ValidationError("wallet-link requires chainId (number) in data parameter");
1020
+ if (typeof data2?.chain !== "string" || !/^[a-z0-9]+:[^\s]+$/.test(data2.chain)) {
1021
+ throw new ValidationError("wallet-link requires chain (namespace:reference) in data parameter");
1022
+ }
1023
+ if (typeof data2?.signatureMethod !== "string" || !data2.signatureMethod.trim()) {
1024
+ throw new ValidationError("wallet-link requires signatureMethod in data parameter");
605
1025
  }
606
1026
  verificationData = {
607
1027
  primaryWalletAddress: walletAddress2,
608
1028
  secondaryWalletAddress: data2.secondaryWalletAddress,
609
1029
  signature: data2.signature,
610
- chainId: data2.chainId,
1030
+ chain: data2.chain,
1031
+ signatureMethod: data2.signatureMethod,
611
1032
  signedTimestamp: data2?.signedTimestamp || Date.now()
612
1033
  };
613
1034
  } else if (verifier === "contract-ownership") {
@@ -696,12 +1117,12 @@ var NeusClient = class {
696
1117
  signedTimestamp: signedTimestamp2,
697
1118
  data: verificationData,
698
1119
  verifierIds: verifierIds2,
699
- chainId: NEUS_CONSTANTS.HUB_CHAIN_ID
1120
+ chainId: this._getHubChainId()
700
1121
  // Protocol-managed chain
701
1122
  });
702
1123
  let signature2;
703
1124
  try {
704
- const toHexUtf8 = (s) => {
1125
+ const toHexUtf82 = (s) => {
705
1126
  try {
706
1127
  const enc = new TextEncoder();
707
1128
  const bytes = enc.encode(s);
@@ -737,7 +1158,7 @@ var NeusClient = class {
737
1158
  })();
738
1159
  if (isFarcasterWallet) {
739
1160
  try {
740
- const hexMsg = toHexUtf8(message);
1161
+ const hexMsg = toHexUtf82(message);
741
1162
  signature2 = await provider.request({ method: "personal_sign", params: [hexMsg, walletAddress2] });
742
1163
  } catch (e) {
743
1164
  }
@@ -786,7 +1207,7 @@ ${bytes.length}`;
786
1207
  }
787
1208
  } else if (needsHex) {
788
1209
  this._log("Retrying personal_sign with hex-encoded message");
789
- const hexMsg = toHexUtf8(message);
1210
+ const hexMsg = toHexUtf82(message);
790
1211
  signature2 = await provider.request({ method: "personal_sign", params: [hexMsg, walletAddress2] });
791
1212
  } else {
792
1213
  throw e;
@@ -884,18 +1305,18 @@ ${bytes.length}`;
884
1305
  /**
885
1306
  * Get verification status
886
1307
  *
887
- * @param {string} qHash - Verification ID (qHash or proofId)
1308
+ * @param {string} proofId - Proof ID (standard). `qHash` is a deprecated alias (same value).
888
1309
  * @returns {Promise<Object>} Verification status and data
889
1310
  *
890
1311
  * @example
891
1312
  * const result = await client.getStatus('0x...');
892
1313
  * console.log('Status:', result.status);
893
1314
  */
894
- async getStatus(qHash) {
895
- if (!qHash || typeof qHash !== "string") {
896
- throw new ValidationError("qHash is required");
1315
+ async getStatus(proofId) {
1316
+ if (!proofId || typeof proofId !== "string") {
1317
+ throw new ValidationError("proofId is required");
897
1318
  }
898
- const response = await this._makeRequest("GET", `/api/v1/verification/status/${qHash}`);
1319
+ const response = await this._makeRequest("GET", `/api/v1/verification/status/${proofId}`);
899
1320
  if (!response.success) {
900
1321
  throw new ApiError(`Failed to get status: ${response.error?.message || "Unknown error"}`, response.error);
901
1322
  }
@@ -904,70 +1325,72 @@ ${bytes.length}`;
904
1325
  /**
905
1326
  * Get private proof status with wallet signature
906
1327
  *
907
- * @param {string} qHash - Verification ID
1328
+ * @param {string} proofId - Proof ID (standard). `qHash` is a deprecated alias (same value).
908
1329
  * @param {Object} wallet - Wallet provider (window.ethereum or ethers Wallet)
909
1330
  * @returns {Promise<Object>} Private verification status and data
910
1331
  *
911
1332
  * @example
912
1333
  * // Access private proof
913
- * const privateData = await client.getPrivateStatus(qHash, window.ethereum);
1334
+ * const privateData = await client.getPrivateStatus(proofId, window.ethereum);
914
1335
  */
915
- async getPrivateStatus(qHash, wallet = null) {
916
- if (!qHash || typeof qHash !== "string") {
917
- throw new ValidationError("qHash is required");
1336
+ async getPrivateStatus(proofId, wallet = null) {
1337
+ if (!proofId || typeof proofId !== "string") {
1338
+ throw new ValidationError("proofId is required");
918
1339
  }
919
- if (!wallet) {
920
- if (typeof window === "undefined" || !window.ethereum) {
921
- throw new ConfigurationError("No wallet provider available");
1340
+ const isPreSignedAuth = wallet && typeof wallet === "object" && typeof wallet.walletAddress === "string" && typeof wallet.signature === "string" && typeof wallet.signedTimestamp === "number";
1341
+ if (isPreSignedAuth) {
1342
+ const auth = wallet;
1343
+ const headers = {
1344
+ "x-wallet-address": String(auth.walletAddress),
1345
+ "x-signature": String(auth.signature),
1346
+ "x-signed-timestamp": String(auth.signedTimestamp),
1347
+ ...typeof auth.chain === "string" && auth.chain.trim() ? { "x-chain": auth.chain.trim() } : {},
1348
+ ...typeof auth.signatureMethod === "string" && auth.signatureMethod.trim() ? { "x-signature-method": auth.signatureMethod.trim() } : {}
1349
+ };
1350
+ const response2 = await this._makeRequest("GET", `/api/v1/verification/status/${proofId}`, null, headers);
1351
+ if (!response2.success) {
1352
+ throw new ApiError(
1353
+ `Failed to access private proof: ${response2.error?.message || "Unauthorized"}`,
1354
+ response2.error
1355
+ );
922
1356
  }
923
- wallet = window.ethereum;
1357
+ return this._formatResponse(response2);
924
1358
  }
925
- let walletAddress, provider;
926
- if (wallet.address) {
927
- walletAddress = wallet.address;
928
- provider = wallet;
929
- } else if (wallet.selectedAddress || wallet.request) {
930
- provider = wallet;
931
- if (wallet.selectedAddress) {
932
- walletAddress = wallet.selectedAddress;
933
- } else {
934
- const accounts = await provider.request({ method: "eth_accounts" });
935
- if (!accounts || accounts.length === 0) {
936
- throw new ConfigurationError("No wallet accounts available");
937
- }
938
- walletAddress = accounts[0];
939
- }
940
- } else {
941
- throw new ConfigurationError("Invalid wallet provider");
1359
+ const providerWallet = wallet || this._getDefaultBrowserWallet();
1360
+ const { signerWalletAddress: walletAddress, provider } = await this._resolveWalletSigner(providerWallet);
1361
+ if (!walletAddress || typeof walletAddress !== "string") {
1362
+ throw new ConfigurationError("No wallet accounts available");
942
1363
  }
1364
+ const signerIsEvm = EVM_ADDRESS_RE.test(this._normalizeIdentity(walletAddress));
1365
+ const chain = this._inferChainForAddress(walletAddress);
1366
+ const signatureMethod = signerIsEvm ? "eip191" : "ed25519";
943
1367
  const signedTimestamp = Date.now();
944
1368
  const message = constructVerificationMessage({
945
1369
  walletAddress,
946
1370
  signedTimestamp,
947
- data: { action: "access_private_proof", qHash },
1371
+ data: { action: "access_private_proof", qHash: proofId },
948
1372
  verifierIds: ["ownership-basic"],
949
- chainId: NEUS_CONSTANTS.HUB_CHAIN_ID
1373
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
950
1374
  });
951
1375
  let signature;
952
1376
  try {
953
- if (provider.signMessage) {
954
- signature = await provider.signMessage(message);
955
- } else {
956
- signature = await provider.request({
957
- method: "personal_sign",
958
- params: [message, walletAddress]
959
- });
960
- }
1377
+ signature = await signMessage({
1378
+ provider,
1379
+ message,
1380
+ walletAddress,
1381
+ ...signerIsEvm ? {} : { chain }
1382
+ });
961
1383
  } catch (error) {
962
1384
  if (error.code === 4001) {
963
1385
  throw new ValidationError("User rejected signature request");
964
1386
  }
965
1387
  throw new ValidationError(`Failed to sign message: ${error.message}`);
966
1388
  }
967
- const response = await this._makeRequest("GET", `/api/v1/verification/status/${qHash}`, null, {
1389
+ const response = await this._makeRequest("GET", `/api/v1/verification/status/${proofId}`, null, {
968
1390
  "x-wallet-address": walletAddress,
969
1391
  "x-signature": signature,
970
- "x-signed-timestamp": signedTimestamp.toString()
1392
+ "x-signed-timestamp": signedTimestamp.toString(),
1393
+ ...signerIsEvm ? {} : { "x-chain": chain, "x-signature-method": signatureMethod }
971
1394
  });
972
1395
  if (!response.success) {
973
1396
  throw new ApiError(
@@ -1008,7 +1431,7 @@ ${bytes.length}`;
1008
1431
  * Polls the verification status until it reaches a terminal state (completed or failed).
1009
1432
  * Useful for providing real-time feedback to users during verification.
1010
1433
  *
1011
- * @param {string} qHash - Verification ID to poll
1434
+ * @param {string} proofId - Proof ID to poll (standard). `qHash` is a deprecated alias (same value).
1012
1435
  * @param {Object} [options] - Polling options
1013
1436
  * @param {number} [options.interval=5000] - Polling interval in ms
1014
1437
  * @param {number} [options.timeout=120000] - Total timeout in ms
@@ -1016,7 +1439,7 @@ ${bytes.length}`;
1016
1439
  * @returns {Promise<Object>} Final verification status
1017
1440
  *
1018
1441
  * @example
1019
- * const finalStatus = await client.pollProofStatus(qHash, {
1442
+ * const finalStatus = await client.pollProofStatus(proofId, {
1020
1443
  * interval: 3000,
1021
1444
  * timeout: 60000,
1022
1445
  * onProgress: (status) => {
@@ -1027,19 +1450,21 @@ ${bytes.length}`;
1027
1450
  * }
1028
1451
  * });
1029
1452
  */
1030
- async pollProofStatus(qHash, options = {}) {
1453
+ async pollProofStatus(proofId, options = {}) {
1031
1454
  const {
1032
1455
  interval = 5e3,
1033
1456
  timeout = 12e4,
1034
1457
  onProgress
1035
1458
  } = options;
1036
- if (!qHash || typeof qHash !== "string") {
1037
- throw new ValidationError("qHash is required");
1459
+ if (!proofId || typeof proofId !== "string") {
1460
+ throw new ValidationError("proofId is required");
1038
1461
  }
1039
1462
  const startTime = Date.now();
1463
+ let consecutiveRateLimits = 0;
1040
1464
  while (Date.now() - startTime < timeout) {
1041
1465
  try {
1042
- const status = await this.getStatus(qHash);
1466
+ const status = await this.getStatus(proofId);
1467
+ consecutiveRateLimits = 0;
1043
1468
  if (onProgress && typeof onProgress === "function") {
1044
1469
  onProgress(status.data || status);
1045
1470
  }
@@ -1054,7 +1479,17 @@ ${bytes.length}`;
1054
1479
  if (error instanceof ValidationError) {
1055
1480
  throw error;
1056
1481
  }
1057
- await new Promise((resolve) => setTimeout(resolve, interval));
1482
+ let nextDelay = interval;
1483
+ if (error instanceof ApiError && Number(error.statusCode) === 429) {
1484
+ consecutiveRateLimits += 1;
1485
+ const exp = Math.min(6, consecutiveRateLimits);
1486
+ const base = Math.max(500, Number(interval) || 0);
1487
+ const max = 3e4;
1488
+ const backoff = Math.min(max, base * Math.pow(2, exp));
1489
+ const jitter = Math.floor(backoff * (0.5 + Math.random() * 0.5));
1490
+ nextDelay = jitter;
1491
+ }
1492
+ await new Promise((resolve) => setTimeout(resolve, nextDelay));
1058
1493
  }
1059
1494
  }
1060
1495
  throw new NetworkError(`Polling timeout after ${timeout}ms`, "POLLING_TIMEOUT");
@@ -1076,70 +1511,51 @@ ${bytes.length}`;
1076
1511
  }
1077
1512
  }
1078
1513
  /** Revoke your own proof (owner-signed) */
1079
- async revokeOwnProof(qHash, wallet) {
1080
- if (!qHash || typeof qHash !== "string") {
1081
- throw new ValidationError("qHash is required");
1514
+ async revokeOwnProof(proofId, wallet) {
1515
+ if (!proofId || typeof proofId !== "string") {
1516
+ throw new ValidationError("proofId is required");
1082
1517
  }
1083
- const address = wallet?.address || await this._getWalletAddress();
1518
+ const providerWallet = wallet || this._getDefaultBrowserWallet();
1519
+ const { signerWalletAddress: address, provider } = await this._resolveWalletSigner(providerWallet);
1520
+ if (!address || typeof address !== "string") {
1521
+ throw new ConfigurationError("No wallet accounts available");
1522
+ }
1523
+ const signerIsEvm = EVM_ADDRESS_RE.test(this._normalizeIdentity(address));
1524
+ const chain = this._inferChainForAddress(address);
1525
+ const signatureMethod = signerIsEvm ? "eip191" : "ed25519";
1084
1526
  const signedTimestamp = Date.now();
1085
- const hubChainId = NEUS_CONSTANTS.HUB_CHAIN_ID;
1086
1527
  const message = constructVerificationMessage({
1087
1528
  walletAddress: address,
1088
1529
  signedTimestamp,
1089
- data: { action: "revoke_proof", qHash },
1530
+ // Keep wire payload key `qHash` for backwards compatibility.
1531
+ data: { action: "revoke_proof", qHash: proofId },
1090
1532
  verifierIds: ["ownership-basic"],
1091
- chainId: hubChainId
1533
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
1092
1534
  });
1093
1535
  let signature;
1094
1536
  try {
1095
- const toHexUtf8 = (s) => {
1096
- const enc = new TextEncoder();
1097
- const bytes = enc.encode(s);
1098
- let hex = "0x";
1099
- for (let i = 0; i < bytes.length; i++)
1100
- hex += bytes[i].toString(16).padStart(2, "0");
1101
- return hex;
1102
- };
1103
- const isFarcasterWallet = (() => {
1104
- if (typeof window === "undefined")
1105
- return false;
1106
- try {
1107
- const w = window;
1108
- const fc = w.farcaster;
1109
- if (!fc || !fc.context)
1110
- return false;
1111
- const fcProvider = fc.provider || fc.walletProvider || fc.context && fc.context.walletProvider;
1112
- if (fcProvider === w.ethereum)
1113
- return true;
1114
- if (w.mini && w.mini.wallet === w.ethereum && fc && fc.context)
1115
- return true;
1116
- if (w.ethereum && fc && fc.context)
1117
- return true;
1118
- } catch {
1119
- }
1120
- return false;
1121
- })();
1122
- if (isFarcasterWallet) {
1123
- try {
1124
- const hexMsg = toHexUtf8(message);
1125
- signature = await window.ethereum.request({ method: "personal_sign", params: [hexMsg, address] });
1126
- } catch {
1127
- }
1128
- }
1129
- if (!signature) {
1130
- signature = await window.ethereum.request({ method: "personal_sign", params: [message, address] });
1131
- }
1537
+ signature = await signMessage({
1538
+ provider,
1539
+ message,
1540
+ walletAddress: address,
1541
+ ...signerIsEvm ? {} : { chain }
1542
+ });
1132
1543
  } catch (error) {
1133
1544
  if (error.code === 4001) {
1134
1545
  throw new ValidationError("User rejected revocation signature");
1135
1546
  }
1136
1547
  throw new ValidationError(`Failed to sign revocation: ${error.message}`);
1137
1548
  }
1138
- const res = await fetch(`${this.config.apiUrl}/api/v1/proofs/${qHash}/revoke-self`, {
1549
+ const res = await fetch(`${this.config.apiUrl}/api/v1/proofs/${proofId}/revoke-self`, {
1139
1550
  method: "POST",
1140
1551
  // SECURITY: Do not put proof signatures into Authorization headers.
1141
1552
  headers: { "Content-Type": "application/json" },
1142
- body: JSON.stringify({ walletAddress: address, signature, signedTimestamp })
1553
+ body: JSON.stringify({
1554
+ walletAddress: address,
1555
+ signature,
1556
+ signedTimestamp,
1557
+ ...signerIsEvm ? {} : { chain, signatureMethod }
1558
+ })
1143
1559
  });
1144
1560
  const json = await res.json();
1145
1561
  if (!json.success) {
@@ -1148,12 +1564,12 @@ ${bytes.length}`;
1148
1564
  return true;
1149
1565
  }
1150
1566
  // ============================================================================
1151
- // GATE & LOOKUP METHODS
1567
+ // PROOFS & GATING METHODS
1152
1568
  // ============================================================================
1153
1569
  /**
1154
1570
  * GET PROOFS BY WALLET - Fetch proofs for a wallet address
1155
1571
  *
1156
- * @param {string} walletAddress - Wallet address (0x...) or DID (did:pkh:...)
1572
+ * @param {string} walletAddress - Wallet identity (EVM/Solana/DID)
1157
1573
  * @param {Object} [options] - Filter options
1158
1574
  * @param {number} [options.limit] - Max results (default: 50; higher limits require owner access)
1159
1575
  * @param {number} [options.offset] - Pagination offset (default: 0)
@@ -1198,7 +1614,7 @@ ${bytes.length}`;
1198
1614
  *
1199
1615
  * Signs an owner-access intent and requests private proofs via signature headers.
1200
1616
  *
1201
- * @param {string} walletAddress - Wallet address (0x...) or DID (did:pkh:...)
1617
+ * @param {string} walletAddress - Wallet identity (EVM/Solana/DID)
1202
1618
  * @param {Object} [options]
1203
1619
  * @param {number} [options.limit] - Max results (server enforces caps)
1204
1620
  * @param {number} [options.offset] - Pagination offset
@@ -1210,48 +1626,41 @@ ${bytes.length}`;
1210
1626
  }
1211
1627
  const id = walletAddress.trim();
1212
1628
  const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
1629
+ const requestedIdentity = this._normalizeIdentity(id);
1213
1630
  if (!wallet) {
1214
- if (typeof window === "undefined" || !window.ethereum) {
1631
+ const defaultWallet = this._getDefaultBrowserWallet();
1632
+ if (!defaultWallet) {
1215
1633
  throw new ConfigurationError("No wallet provider available");
1216
1634
  }
1217
- wallet = window.ethereum;
1635
+ wallet = defaultWallet;
1218
1636
  }
1219
- let signerWalletAddress, provider;
1220
- if (wallet.address) {
1221
- signerWalletAddress = wallet.address;
1222
- provider = wallet;
1223
- } else if (wallet.selectedAddress || wallet.request) {
1224
- provider = wallet;
1225
- if (wallet.selectedAddress) {
1226
- signerWalletAddress = wallet.selectedAddress;
1227
- } else {
1228
- const accounts = await provider.request({ method: "eth_accounts" });
1229
- if (!accounts || accounts.length === 0) {
1230
- throw new ConfigurationError("No wallet accounts available");
1231
- }
1232
- signerWalletAddress = accounts[0];
1233
- }
1234
- } else {
1235
- throw new ConfigurationError("Invalid wallet provider");
1637
+ const { signerWalletAddress, provider } = await this._resolveWalletSigner(wallet);
1638
+ if (!signerWalletAddress || typeof signerWalletAddress !== "string") {
1639
+ throw new ConfigurationError("No wallet accounts available");
1640
+ }
1641
+ const normalizedSigner = this._normalizeIdentity(signerWalletAddress);
1642
+ if (!normalizedSigner || normalizedSigner !== requestedIdentity) {
1643
+ throw new ValidationError("wallet must match walletAddress for private proof access");
1236
1644
  }
1645
+ const signerIsEvm = EVM_ADDRESS_RE.test(normalizedSigner);
1646
+ const chain = this._inferChainForAddress(normalizedSigner, options?.chain);
1647
+ const signatureMethod = typeof options?.signatureMethod === "string" && options.signatureMethod.trim() ? options.signatureMethod.trim() : signerIsEvm ? "eip191" : "ed25519";
1237
1648
  const signedTimestamp = Date.now();
1238
1649
  const message = constructVerificationMessage({
1239
1650
  walletAddress: signerWalletAddress,
1240
1651
  signedTimestamp,
1241
- data: { action: "access_private_proofs_by_wallet", walletAddress: signerWalletAddress.toLowerCase() },
1652
+ data: { action: "access_private_proofs_by_wallet", walletAddress: normalizedSigner },
1242
1653
  verifierIds: ["ownership-basic"],
1243
- chainId: NEUS_CONSTANTS.HUB_CHAIN_ID
1654
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
1244
1655
  });
1245
1656
  let signature;
1246
1657
  try {
1247
- if (provider.signMessage) {
1248
- signature = await provider.signMessage(message);
1249
- } else {
1250
- signature = await provider.request({
1251
- method: "personal_sign",
1252
- params: [message, signerWalletAddress]
1253
- });
1254
- }
1658
+ signature = await signMessage({
1659
+ provider,
1660
+ message,
1661
+ walletAddress: signerWalletAddress,
1662
+ ...signerIsEvm ? {} : { chain }
1663
+ });
1255
1664
  } catch (error) {
1256
1665
  if (error.code === 4001) {
1257
1666
  throw new ValidationError("User rejected signature request");
@@ -1267,7 +1676,8 @@ ${bytes.length}`;
1267
1676
  const response = await this._makeRequest("GET", `/api/v1/proofs/byWallet/${encodeURIComponent(pathId)}${query}`, null, {
1268
1677
  "x-wallet-address": signerWalletAddress,
1269
1678
  "x-signature": signature,
1270
- "x-signed-timestamp": signedTimestamp.toString()
1679
+ "x-signed-timestamp": signedTimestamp.toString(),
1680
+ ...signerIsEvm ? {} : { "x-chain": chain, "x-signature-method": signatureMethod }
1271
1681
  });
1272
1682
  if (!response.success) {
1273
1683
  throw new ApiError(`Failed to get proofs: ${response.error?.message || "Unauthorized"}`, response.error);
@@ -1281,68 +1691,34 @@ ${bytes.length}`;
1281
1691
  nextOffset: response.data?.nextOffset ?? null
1282
1692
  };
1283
1693
  }
1284
- /**
1285
- * LOOKUP MODE (API) - Non-persistent server-to-server checks
1286
- *
1287
- * Runs `external_lookup` verifiers without minting/storing a proof.
1288
- * Requires an enterprise API key (server-side only).
1289
- *
1290
- * @param {Object} params
1291
- * @param {string} params.apiKey - Enterprise API key (sk_live_... or sk_test_...)
1292
- * @param {Array<string>} params.verifierIds - Verifiers to run (external_lookup only)
1293
- * @param {string} params.targetWalletAddress - Wallet to evaluate
1294
- * @param {Object} [params.data] - Verifier input data (e.g., contractAddress/tokenId/chainId)
1295
- * @returns {Promise<Object>} API response ({ success, data })
1296
- */
1297
- async lookup(params = {}) {
1298
- const apiKey = (params.apiKey || "").toString().trim();
1299
- if (!apiKey || !(apiKey.startsWith("sk_live_") || apiKey.startsWith("sk_test_"))) {
1300
- throw new ValidationError("lookup requires apiKey (sk_live_* or sk_test_*)");
1301
- }
1302
- const verifierIds = Array.isArray(params.verifierIds) ? params.verifierIds.map((v) => String(v).trim()).filter(Boolean) : [];
1303
- if (verifierIds.length === 0) {
1304
- throw new ValidationError("lookup requires verifierIds (non-empty array)");
1305
- }
1306
- const targetWalletAddress = (params.targetWalletAddress || "").toString().trim();
1307
- if (!targetWalletAddress || !/^0x[a-fA-F0-9]{40}$/i.test(targetWalletAddress)) {
1308
- throw new ValidationError("lookup requires a valid targetWalletAddress (0x...)");
1309
- }
1310
- const body = {
1311
- verifierIds,
1312
- targetWalletAddress,
1313
- data: params.data && typeof params.data === "object" ? params.data : {}
1314
- };
1315
- const response = await this._makeRequest("POST", "/api/v1/verification/lookup", body, {
1316
- Authorization: `Bearer ${apiKey}`
1317
- });
1318
- if (!response.success) {
1319
- throw new ApiError(`Lookup failed: ${response.error?.message || "Unknown error"}`, response.error);
1320
- }
1321
- return response;
1322
- }
1323
1694
  /**
1324
1695
  * GATE CHECK (API) - Minimal eligibility check
1325
1696
  *
1326
- * Calls the public gate endpoint and returns a **minimal** yes/no response
1327
- * against **public + discoverable** proofs only.
1697
+ * Calls the gate endpoint and returns a **minimal** yes/no response.
1698
+ * By default this checks **public + discoverable** proofs only.
1699
+ *
1700
+ * When `includePrivate=true`, this can perform owner-signed private checks
1701
+ * (no full proof payloads returned) by providing a wallet/provider.
1328
1702
  *
1329
- * Prefer this over `checkGate()` for server-side integrations that want the
1703
+ * Prefer this over `checkGate()` for integrations that want the
1330
1704
  * smallest, most stable surface area (and do NOT need full proof payloads).
1331
1705
  *
1332
1706
  * @param {Object} params - Gate check query params
1333
- * @param {string} params.address - Wallet address to check (0x...)
1707
+ * @param {string} params.address - Wallet identity to check (EVM/Solana/DID)
1334
1708
  * @param {Array<string>|string} [params.verifierIds] - Verifier IDs to match (array or comma-separated)
1335
1709
  * @param {boolean} [params.requireAll] - Require all verifierIds on a single proof
1336
1710
  * @param {number} [params.minCount] - Minimum number of matching proofs
1337
1711
  * @param {number} [params.sinceDays] - Optional time window in days
1338
1712
  * @param {number} [params.since] - Optional unix timestamp in ms (lower bound)
1339
1713
  * @param {number} [params.limit] - Max rows to scan (server may clamp)
1340
- * @param {string} [params.select] - Comma-separated projections (handle,provider,profileUrl,traits.<key>)
1714
+ * @param {boolean} [params.includePrivate] - Include private proofs for owner-authenticated requests
1715
+ * @param {boolean} [params.includeQHashes] - Include matched qHashes in response (minimal IDs only)
1716
+ * @param {Object} [params.wallet] - Optional wallet/provider used to sign includePrivate owner checks
1341
1717
  * @returns {Promise<Object>} API response ({ success, data })
1342
1718
  */
1343
1719
  async gateCheck(params = {}) {
1344
1720
  const address = (params.address || "").toString();
1345
- if (!address || !/^0x[a-fA-F0-9]{40}$/i.test(address)) {
1721
+ if (!validateUniversalAddress(address, params.chain)) {
1346
1722
  throw new ValidationError("Valid address is required");
1347
1723
  }
1348
1724
  const qs = new URLSearchParams();
@@ -1377,7 +1753,8 @@ ${bytes.length}`;
1377
1753
  setIfPresent("sinceDays", params.sinceDays);
1378
1754
  setIfPresent("since", params.since);
1379
1755
  setIfPresent("limit", params.limit);
1380
- setCsvIfPresent("select", params.select);
1756
+ setBoolIfPresent("includePrivate", params.includePrivate);
1757
+ setBoolIfPresent("includeQHashes", params.includeQHashes);
1381
1758
  setIfPresent("referenceType", params.referenceType);
1382
1759
  setIfPresent("referenceId", params.referenceId);
1383
1760
  setIfPresent("tag", params.tag);
@@ -1391,7 +1768,6 @@ ${bytes.length}`;
1391
1768
  setIfPresent("domain", params.domain);
1392
1769
  setIfPresent("minBalance", params.minBalance);
1393
1770
  setIfPresent("provider", params.provider);
1394
- setIfPresent("handle", params.handle);
1395
1771
  setIfPresent("ownerAddress", params.ownerAddress);
1396
1772
  setIfPresent("riskLevel", params.riskLevel);
1397
1773
  setBoolIfPresent("sanctioned", params.sanctioned);
@@ -1399,9 +1775,40 @@ ${bytes.length}`;
1399
1775
  setIfPresent("primaryWalletAddress", params.primaryWalletAddress);
1400
1776
  setIfPresent("secondaryWalletAddress", params.secondaryWalletAddress);
1401
1777
  setIfPresent("verificationMethod", params.verificationMethod);
1402
- setIfPresent("traitPath", params.traitPath);
1403
- setIfPresent("traitGte", params.traitGte);
1404
- const response = await this._makeRequest("GET", `/api/v1/proofs/gate/check?${qs.toString()}`);
1778
+ let headersOverride = null;
1779
+ if (params.includePrivate === true) {
1780
+ const provided = params.privateAuth && typeof params.privateAuth === "object" ? params.privateAuth : null;
1781
+ let auth = provided;
1782
+ if (!auth) {
1783
+ const walletCandidate = params.wallet || this._getDefaultBrowserWallet();
1784
+ if (walletCandidate) {
1785
+ auth = await this._buildPrivateGateAuth({
1786
+ address,
1787
+ wallet: walletCandidate,
1788
+ chain: params.chain,
1789
+ signatureMethod: params.signatureMethod
1790
+ });
1791
+ }
1792
+ }
1793
+ if (!auth) {
1794
+ } else {
1795
+ const normalizedAuthWallet = this._normalizeIdentity(auth.walletAddress);
1796
+ const normalizedAddress = this._normalizeIdentity(address);
1797
+ if (!normalizedAuthWallet || normalizedAuthWallet !== normalizedAddress) {
1798
+ throw new ValidationError("privateAuth.walletAddress must match address when includePrivate=true");
1799
+ }
1800
+ const authChain = typeof auth.chain === "string" && auth.chain.includes(":") ? auth.chain.trim() : null;
1801
+ const authSignatureMethod = typeof auth.signatureMethod === "string" && auth.signatureMethod.trim() ? auth.signatureMethod.trim() : null;
1802
+ headersOverride = {
1803
+ "x-wallet-address": String(auth.walletAddress),
1804
+ "x-signature": String(auth.signature),
1805
+ "x-signed-timestamp": String(auth.signedTimestamp),
1806
+ ...authChain ? { "x-chain": authChain } : {},
1807
+ ...authSignatureMethod ? { "x-signature-method": authSignatureMethod } : {}
1808
+ };
1809
+ }
1810
+ }
1811
+ const response = await this._makeRequest("GET", `/api/v1/proofs/check?${qs.toString()}`, null, headersOverride);
1405
1812
  if (!response.success) {
1406
1813
  throw new ApiError(`Gate check failed: ${response.error?.message || "Unknown error"}`, response.error);
1407
1814
  }
@@ -1425,7 +1832,7 @@ ${bytes.length}`;
1425
1832
  * Supports verifier-specific:
1426
1833
  * - NFT/Token: 'contractAddress', 'tokenId', 'chainId', 'ownerAddress', 'minBalance'
1427
1834
  * - DNS: 'domain', 'walletAddress'
1428
- * - Wallet-link: 'primaryWalletAddress', 'secondaryWalletAddress', 'chainId'
1835
+ * - Wallet-link: 'primaryWalletAddress', 'secondaryWalletAddress', 'chain', 'signatureMethod'
1429
1836
  * - Contract-ownership: 'contractAddress', 'chainId', 'owner', 'verificationMethod'
1430
1837
  * Note: contentHash matching uses approximation in SDK; for exact SHA-256 matching, use backend API
1431
1838
  * @param {Array} [params.proofs] - Pre-fetched proofs (skip API call)
@@ -1442,7 +1849,7 @@ ${bytes.length}`;
1442
1849
  */
1443
1850
  async checkGate(params) {
1444
1851
  const { walletAddress, requirements, proofs: preloadedProofs } = params;
1445
- if (!walletAddress || !/^0x[a-fA-F0-9]{40}$/i.test(walletAddress)) {
1852
+ if (!validateUniversalAddress(walletAddress)) {
1446
1853
  throw new ValidationError("Valid walletAddress is required");
1447
1854
  }
1448
1855
  if (!Array.isArray(requirements) || requirements.length === 0) {
@@ -1636,16 +2043,20 @@ ${bytes.length}`;
1636
2043
  * @private
1637
2044
  */
1638
2045
  _formatResponse(response) {
1639
- const qHash = response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || response?.data?.id;
2046
+ const proofId = response?.data?.proofId || response?.proofId || response?.data?.resource?.proofId || response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || response?.data?.id;
2047
+ const qHash = response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || proofId || response?.data?.id;
2048
+ const finalProofId = proofId || qHash || null;
2049
+ const finalQHash = qHash || proofId || finalProofId;
1640
2050
  const status = response?.data?.status || response?.status || response?.data?.resource?.status || (response?.success ? "completed" : "unknown");
1641
2051
  return {
1642
2052
  success: response.success,
1643
- qHash,
2053
+ proofId: finalProofId,
2054
+ qHash: finalQHash,
1644
2055
  status,
1645
2056
  data: response.data,
1646
2057
  message: response.message,
1647
2058
  timestamp: Date.now(),
1648
- statusUrl: qHash ? `${this.baseUrl}/api/v1/verification/status/${qHash}` : null
2059
+ statusUrl: finalProofId ? `${this.baseUrl}/api/v1/verification/status/${finalProofId}` : null
1649
2060
  };
1650
2061
  }
1651
2062
  /**