@neus/sdk 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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;
@@ -20,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
21
  var client_exports = {};
21
22
  __export(client_exports, {
22
23
  NeusClient: () => NeusClient,
24
+ PORTABLE_PROOF_SIGNER_HEADER: () => PORTABLE_PROOF_SIGNER_HEADER,
23
25
  constructVerificationMessage: () => constructVerificationMessage
24
26
  });
25
27
  module.exports = __toCommonJS(client_exports);
@@ -128,21 +130,77 @@ var ConfigurationError = class extends SDKError {
128
130
  };
129
131
 
130
132
  // utils.js
133
+ var PORTABLE_PROOF_SIGNER_HEADER = "Portable Proof Verification Request";
134
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
135
+ function encodeBase58Bytes(input) {
136
+ let source;
137
+ if (input instanceof Uint8Array) {
138
+ source = input;
139
+ } else if (input instanceof ArrayBuffer) {
140
+ source = new Uint8Array(input);
141
+ } else if (ArrayBuffer.isView(input)) {
142
+ source = new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
143
+ } else if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(input)) {
144
+ source = new Uint8Array(input);
145
+ } else {
146
+ throw new SDKError("Unsupported non-EVM signature byte format", "INVALID_SIGNATURE_FORMAT");
147
+ }
148
+ if (source.length === 0) return "";
149
+ let zeroes = 0;
150
+ while (zeroes < source.length && source[zeroes] === 0) {
151
+ zeroes++;
152
+ }
153
+ const iFactor = Math.log(256) / Math.log(58);
154
+ const size = (source.length - zeroes) * iFactor + 1 >>> 0;
155
+ const b58 = new Uint8Array(size);
156
+ let length = 0;
157
+ for (let i = zeroes; i < source.length; i++) {
158
+ let carry = source[i];
159
+ let j = 0;
160
+ for (let k = size - 1; (carry !== 0 || j < length) && k >= 0; k--, j++) {
161
+ carry += 256 * b58[k];
162
+ b58[k] = carry % 58;
163
+ carry = carry / 58 | 0;
164
+ }
165
+ length = j;
166
+ }
167
+ let it = size - length;
168
+ while (it < size && b58[it] === 0) {
169
+ it++;
170
+ }
171
+ let out = BASE58_ALPHABET[0].repeat(zeroes);
172
+ for (; it < size; it++) {
173
+ out += BASE58_ALPHABET[b58[it]];
174
+ }
175
+ return out;
176
+ }
131
177
  function deterministicStringify(obj) {
132
178
  if (obj === null || obj === void 0) {
133
179
  return JSON.stringify(obj);
134
180
  }
135
181
  if (typeof obj !== "object") {
182
+ if (typeof obj === "string") return JSON.stringify(obj.normalize("NFC"));
136
183
  return JSON.stringify(obj);
137
184
  }
138
185
  if (Array.isArray(obj)) {
139
- return "[" + obj.map((item) => deterministicStringify(item)).join(",") + "]";
186
+ return `[${obj.map((item) => item === void 0 ? "null" : deterministicStringify(item)).join(",")}]`;
140
187
  }
141
- const sortedKeys = Object.keys(obj).sort();
188
+ const sortedKeys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
142
189
  const pairs = sortedKeys.map(
143
- (key) => JSON.stringify(key) + ":" + deterministicStringify(obj[key])
190
+ (key) => `${JSON.stringify(key)}:${deterministicStringify(obj[key])}`
144
191
  );
145
- return "{" + pairs.join(",") + "}";
192
+ return `{${pairs.join(",")}}`;
193
+ }
194
+ function chainLineForPortableProofSigner(chain, chainId) {
195
+ if (typeof chain === "string" && chain.length > 0) {
196
+ const m = chain.match(/^eip155:(\d+)$/);
197
+ if (m) return Number(m[1]);
198
+ return chain;
199
+ }
200
+ if (typeof chainId === "number" && Number.isFinite(chainId) && chainId > 0) {
201
+ return chainId;
202
+ }
203
+ throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
146
204
  }
147
205
  function constructVerificationMessage({ walletAddress, signedTimestamp, data, verifierIds, chainId, chain }) {
148
206
  if (!walletAddress || typeof walletAddress !== "string") {
@@ -157,28 +215,28 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
157
215
  if (!Array.isArray(verifierIds) || verifierIds.length === 0) {
158
216
  throw new SDKError("verifierIds is required and must be a non-empty array", "INVALID_VERIFIER_IDS");
159
217
  }
160
- const chainContext = typeof chain === "string" && chain.length > 0 ? chain : chainId;
161
- if (!chainContext) {
218
+ if ((typeof chain !== "string" || !chain.length) && !(typeof chainId === "number" && chainId > 0)) {
162
219
  throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
163
220
  }
164
- if (chainContext === chainId && typeof chainId !== "number") {
165
- throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
166
- }
167
- if (chainContext === chain && (typeof chain !== "string" || !chain.includes(":"))) {
221
+ if (typeof chain === "string" && chain.length > 0 && !chain.includes(":")) {
168
222
  throw new SDKError('chain must be a "namespace:reference" string', "INVALID_CHAIN");
169
223
  }
224
+ if ((!chain || !chain.length) && typeof chainId !== "number") {
225
+ throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
226
+ }
227
+ const chainLine = chainLineForPortableProofSigner(chain, chainId);
170
228
  const namespace = typeof chain === "string" && chain.includes(":") ? chain.split(":")[0] : "eip155";
171
229
  const normalizedWalletAddress = namespace === "eip155" ? walletAddress.toLowerCase() : walletAddress;
172
230
  const dataString = deterministicStringify(data);
173
231
  const messageComponents = [
174
- "NEUS Verification Request",
232
+ PORTABLE_PROOF_SIGNER_HEADER,
175
233
  `Wallet: ${normalizedWalletAddress}`,
176
- `Chain: ${chainContext}`,
234
+ `Chain: ${chainLine}`,
177
235
  `Verifiers: ${verifierIds.join(",")}`,
178
236
  `Data: ${dataString}`,
179
237
  `Timestamp: ${signedTimestamp}`
180
238
  ];
181
- return messageComponents.join("\n");
239
+ return messageComponents.join("\n").normalize("NFC");
182
240
  }
183
241
  function validateWalletAddress(address) {
184
242
  if (!address || typeof address !== "string") {
@@ -186,8 +244,127 @@ function validateWalletAddress(address) {
186
244
  }
187
245
  return /^0x[a-fA-F0-9]{40}$/.test(address);
188
246
  }
247
+ function validateUniversalAddress(address, chain) {
248
+ if (!address || typeof address !== "string") return false;
249
+ const value = address.trim();
250
+ if (!value) return false;
251
+ const chainRef = typeof chain === "string" ? chain.trim().toLowerCase() : "";
252
+ const namespace = chainRef.includes(":") ? chainRef.split(":")[0] : "";
253
+ if (validateWalletAddress(value)) return true;
254
+ if (namespace === "eip155") return false;
255
+ if (namespace === "solana") {
256
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
257
+ }
258
+ if (namespace === "bip122") {
259
+ return /^(bc1|tb1|bcrt1)[a-z0-9]{11,87}$/.test(value.toLowerCase()) || /^[13mn2][a-km-zA-HJ-NP-Z1-9]{25,62}$/.test(value);
260
+ }
261
+ if (namespace === "near") {
262
+ return /^[a-z0-9._-]{2,64}$/.test(value);
263
+ }
264
+ return /^[A-Za-z0-9][A-Za-z0-9._:-]{1,127}$/.test(value);
265
+ }
266
+ function toHexUtf8(value) {
267
+ const input = typeof value === "string" ? value : String(value || "");
268
+ const bytes = new TextEncoder().encode(input);
269
+ return `0x${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
270
+ }
271
+ async function signMessage({ provider, message, walletAddress, chain } = {}) {
272
+ const msg = typeof message === "string" ? message : String(message || "");
273
+ if (!msg) {
274
+ throw new SDKError("signMessage: message is required", "INVALID_ARGUMENT");
275
+ }
276
+ const resolvedProvider = provider || (typeof window !== "undefined" && window?.ethereum ? window.ethereum : null);
277
+ if (!resolvedProvider) {
278
+ throw new SDKError("signMessage: provider is required", "SIGNER_UNAVAILABLE");
279
+ }
280
+ const chainStr = typeof chain === "string" && chain.trim().length > 0 ? chain.trim() : "eip155";
281
+ const namespace = chainStr.includes(":") ? chainStr.split(":")[0] || "eip155" : "eip155";
282
+ const resolveAddress = async () => {
283
+ if (typeof walletAddress === "string" && walletAddress.trim().length > 0) return walletAddress;
284
+ if (namespace === "solana") {
285
+ if (resolvedProvider?.publicKey && typeof resolvedProvider.publicKey.toBase58 === "function") {
286
+ const pk = resolvedProvider.publicKey.toBase58();
287
+ if (typeof pk === "string" && pk) return pk;
288
+ }
289
+ if (typeof resolvedProvider.getAddress === "function") {
290
+ const addr = await resolvedProvider.getAddress().catch(() => null);
291
+ if (typeof addr === "string" && addr) return addr;
292
+ }
293
+ if (typeof resolvedProvider.address === "string" && resolvedProvider.address) return resolvedProvider.address;
294
+ return null;
295
+ }
296
+ if (typeof resolvedProvider.address === "string" && resolvedProvider.address) return resolvedProvider.address;
297
+ if (typeof resolvedProvider.getAddress === "function") return resolvedProvider.getAddress();
298
+ if (typeof resolvedProvider.request === "function") {
299
+ let accounts = await resolvedProvider.request({ method: "eth_accounts" }).catch(() => []);
300
+ if (!Array.isArray(accounts) || accounts.length === 0) {
301
+ accounts = await resolvedProvider.request({ method: "eth_requestAccounts" }).catch(() => []);
302
+ }
303
+ if (Array.isArray(accounts) && accounts[0]) return accounts[0];
304
+ }
305
+ return null;
306
+ };
307
+ if (namespace !== "eip155") {
308
+ if (typeof resolvedProvider.signMessage === "function") {
309
+ const encoded = typeof msg === "string" ? new TextEncoder().encode(msg) : msg;
310
+ const result = await resolvedProvider.signMessage(encoded);
311
+ if (typeof result === "string" && result) return result;
312
+ if (result instanceof Uint8Array) return encodeBase58Bytes(result);
313
+ if (result instanceof ArrayBuffer) return encodeBase58Bytes(new Uint8Array(result));
314
+ if (ArrayBuffer.isView(result)) return encodeBase58Bytes(result);
315
+ if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(result)) return encodeBase58Bytes(result);
316
+ }
317
+ throw new SDKError("Non-EVM signing requires provider.signMessage", "SIGNER_UNAVAILABLE");
318
+ }
319
+ const address = await resolveAddress();
320
+ if (typeof resolvedProvider.request === "function" && address) {
321
+ let firstPersonalSignError = null;
322
+ try {
323
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [msg, address] });
324
+ if (typeof sig === "string" && sig) return sig;
325
+ } catch (error) {
326
+ firstPersonalSignError = error;
327
+ }
328
+ let secondPersonalSignError = null;
329
+ try {
330
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [address, msg] });
331
+ if (typeof sig === "string" && sig) return sig;
332
+ } catch (error) {
333
+ secondPersonalSignError = error;
334
+ const signatureErrorMessage = String(
335
+ error?.message || error?.reason || firstPersonalSignError?.message || firstPersonalSignError?.reason || ""
336
+ ).toLowerCase();
337
+ const needsHex = /byte|bytes|invalid byte sequence|encoding|non-hex/i.test(signatureErrorMessage);
338
+ if (needsHex) {
339
+ try {
340
+ const hexMsg = toHexUtf8(msg);
341
+ const sig = await resolvedProvider.request({ method: "personal_sign", params: [hexMsg, address] });
342
+ if (typeof sig === "string" && sig) return sig;
343
+ } catch {
344
+ }
345
+ }
346
+ }
347
+ try {
348
+ const sig = await resolvedProvider.request({ method: "eth_sign", params: [address, msg] });
349
+ if (typeof sig === "string" && sig) return sig;
350
+ } catch {
351
+ }
352
+ if (secondPersonalSignError || firstPersonalSignError) {
353
+ const lastError = secondPersonalSignError || firstPersonalSignError;
354
+ const isUserRejection = [4001, "ACTION_REJECTED"].includes(lastError?.code);
355
+ if (isUserRejection) {
356
+ throw lastError;
357
+ }
358
+ }
359
+ }
360
+ if (typeof resolvedProvider.signMessage === "function") {
361
+ const result = await resolvedProvider.signMessage(msg);
362
+ if (typeof result === "string" && result) return result;
363
+ }
364
+ throw new SDKError("Unable to sign message with provided wallet/provider", "SIGNER_UNAVAILABLE");
365
+ }
189
366
  var NEUS_CONSTANTS = {
190
- // Hub chain (where all verifications occur)
367
+ /** Default EVM chain id for NEUS protocol signing context (`HUB_CHAIN_ID` name kept for compatibility). */
191
368
  HUB_CHAIN_ID: 84532,
192
369
  // Supported target chains for cross-chain propagation
193
370
  TESTNET_CHAINS: [
@@ -217,18 +394,31 @@ var NEUS_CONSTANTS = {
217
394
  };
218
395
 
219
396
  // client.js
397
+ var FALLBACK_PUBLIC_VERIFIER_CATALOG = {
398
+ "ownership-basic": { supportsDirectApi: true },
399
+ "ownership-pseudonym": { supportsDirectApi: true },
400
+ "ownership-dns-txt": { supportsDirectApi: true },
401
+ "ownership-social": { supportsDirectApi: false },
402
+ "ownership-org-oauth": { supportsDirectApi: false },
403
+ "contract-ownership": { supportsDirectApi: true },
404
+ "nft-ownership": { supportsDirectApi: true },
405
+ "token-holding": { supportsDirectApi: true },
406
+ "wallet-link": { supportsDirectApi: true },
407
+ "wallet-risk": { supportsDirectApi: true },
408
+ "proof-of-human": { supportsDirectApi: false },
409
+ "agent-identity": { supportsDirectApi: true },
410
+ "agent-delegation": { supportsDirectApi: true },
411
+ "ai-content-moderation": { supportsDirectApi: true }
412
+ };
413
+ var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
220
414
  var validateVerifierData = (verifierId, data) => {
221
415
  if (!data || typeof data !== "object") {
222
416
  return { valid: false, error: "Data object is required" };
223
417
  }
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
418
  switch (verifierId) {
229
419
  case "ownership-basic":
230
- if (!data.owner || !validateWalletAddress(data.owner)) {
231
- return { valid: false, error: "owner (wallet address) is required" };
420
+ if (!data.owner || !validateUniversalAddress(data.owner, typeof data.chain === "string" ? data.chain : void 0)) {
421
+ return { valid: false, error: "owner (universal wallet address) is required" };
232
422
  }
233
423
  if (data.content !== void 0 && data.content !== null) {
234
424
  if (typeof data.content !== "string") {
@@ -354,17 +544,20 @@ var validateVerifierData = (verifierId, data) => {
354
544
  }
355
545
  break;
356
546
  case "wallet-link":
357
- if (!data.primaryWalletAddress || !validateWalletAddress(data.primaryWalletAddress)) {
547
+ if (!data.primaryWalletAddress || !validateUniversalAddress(data.primaryWalletAddress, data.chain)) {
358
548
  return { valid: false, error: "primaryWalletAddress is required" };
359
549
  }
360
- if (!data.secondaryWalletAddress || !validateWalletAddress(data.secondaryWalletAddress)) {
550
+ if (!data.secondaryWalletAddress || !validateUniversalAddress(data.secondaryWalletAddress, data.chain)) {
361
551
  return { valid: false, error: "secondaryWalletAddress is required" };
362
552
  }
363
553
  if (!data.signature || typeof data.signature !== "string") {
364
554
  return { valid: false, error: "signature is required (signed by secondary wallet)" };
365
555
  }
366
- if (typeof data.chainId !== "number") {
367
- return { valid: false, error: "chainId is required" };
556
+ if (typeof data.chain !== "string" || !/^[a-z0-9]+:[^\s]+$/.test(data.chain)) {
557
+ return { valid: false, error: "chain is required (namespace:reference)" };
558
+ }
559
+ if (typeof data.signatureMethod !== "string" || !data.signatureMethod.trim()) {
560
+ return { valid: false, error: "signatureMethod is required" };
368
561
  }
369
562
  if (typeof data.signedTimestamp !== "number") {
370
563
  return { valid: false, error: "signedTimestamp is required" };
@@ -435,7 +628,10 @@ var validateVerifierData = (verifierId, data) => {
435
628
  const maxBytes = 50 * 1024;
436
629
  const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(data.content).length : String(data.content).length;
437
630
  if (bytes > maxBytes) {
438
- return { valid: false, error: `content exceeds ${maxBytes} bytes limit for ai-content-moderation verifier (text)` };
631
+ return {
632
+ valid: false,
633
+ error: `content exceeds ${maxBytes} bytes limit for ai-content-moderation verifier (text)`
634
+ };
439
635
  }
440
636
  } catch {
441
637
  }
@@ -459,7 +655,7 @@ var validateVerifierData = (verifierId, data) => {
459
655
  }
460
656
  break;
461
657
  case "wallet-risk":
462
- if (data.walletAddress && !validateWalletAddress(data.walletAddress)) {
658
+ if (data.walletAddress && !validateUniversalAddress(data.walletAddress, data.chain)) {
463
659
  return { valid: false, error: "Invalid walletAddress" };
464
660
  }
465
661
  break;
@@ -491,6 +687,21 @@ var NeusClient = class {
491
687
  if (typeof this.config.apiKey === "string" && this.config.apiKey.trim().length > 0) {
492
688
  this.defaultHeaders["Authorization"] = `Bearer ${this.config.apiKey.trim()}`;
493
689
  }
690
+ if (typeof this.config.appId === "string" && this.config.appId.trim().length > 0) {
691
+ this.defaultHeaders["X-Neus-App"] = this.config.appId.trim();
692
+ }
693
+ if (typeof this.config.paymentSignature === "string" && this.config.paymentSignature.trim().length > 0) {
694
+ this.defaultHeaders["PAYMENT-SIGNATURE"] = this.config.paymentSignature.trim();
695
+ }
696
+ if (this.config.extraHeaders && typeof this.config.extraHeaders === "object") {
697
+ for (const [k, v] of Object.entries(this.config.extraHeaders)) {
698
+ if (!k || v === void 0 || v === null) continue;
699
+ const key = String(k).trim();
700
+ const value = String(v).trim();
701
+ if (!key || !value) continue;
702
+ this.defaultHeaders[key] = value;
703
+ }
704
+ }
494
705
  try {
495
706
  if (typeof window !== "undefined" && window.location && window.location.origin) {
496
707
  this.defaultHeaders["X-Client-Origin"] = window.location.origin;
@@ -498,87 +709,183 @@ var NeusClient = class {
498
709
  } catch {
499
710
  }
500
711
  }
501
- // ============================================================================
502
- // CORE VERIFICATION METHODS
503
- // ============================================================================
712
+ _getHubChainId() {
713
+ const configured = Number(this.config?.hubChainId);
714
+ if (Number.isFinite(configured) && configured > 0) return Math.floor(configured);
715
+ return NEUS_CONSTANTS.HUB_CHAIN_ID;
716
+ }
717
+ _normalizeIdentity(value) {
718
+ let raw = String(value || "").trim();
719
+ if (!raw) return "";
720
+ const didMatch = raw.match(/^did:pkh:([^:]+):([^:]+):(.+)$/i);
721
+ if (didMatch && didMatch[3]) {
722
+ raw = String(didMatch[3]).trim();
723
+ }
724
+ return EVM_ADDRESS_RE.test(raw) ? raw.toLowerCase() : raw;
725
+ }
726
+ _inferChainForAddress(address, explicitChain) {
727
+ if (typeof explicitChain === "string" && explicitChain.includes(":")) return explicitChain.trim();
728
+ const raw = String(address || "").trim();
729
+ const didMatch = raw.match(/^did:pkh:([^:]+):([^:]+):(.+)$/i);
730
+ if (didMatch && didMatch[1] && didMatch[2]) {
731
+ return `${didMatch[1]}:${didMatch[2]}`;
732
+ }
733
+ if (EVM_ADDRESS_RE.test(raw)) {
734
+ return `eip155:${this._getHubChainId()}`;
735
+ }
736
+ return "solana:mainnet";
737
+ }
738
+ async _resolveWalletSigner(wallet) {
739
+ if (!wallet) {
740
+ throw new ConfigurationError("No wallet provider available");
741
+ }
742
+ if (wallet.address) {
743
+ return { signerWalletAddress: wallet.address, provider: wallet };
744
+ }
745
+ if (wallet.publicKey && typeof wallet.publicKey.toBase58 === "function") {
746
+ return { signerWalletAddress: wallet.publicKey.toBase58(), provider: wallet };
747
+ }
748
+ if (typeof wallet.getAddress === "function") {
749
+ const signerWalletAddress = await wallet.getAddress().catch(() => null);
750
+ return { signerWalletAddress, provider: wallet };
751
+ }
752
+ if (wallet.selectedAddress || wallet.request) {
753
+ const provider = wallet;
754
+ if (wallet.selectedAddress) {
755
+ return { signerWalletAddress: wallet.selectedAddress, provider };
756
+ }
757
+ const accounts = await provider.request({ method: "eth_accounts" });
758
+ if (!accounts || accounts.length === 0) {
759
+ throw new ConfigurationError("No wallet accounts available");
760
+ }
761
+ return { signerWalletAddress: accounts[0], provider };
762
+ }
763
+ throw new ConfigurationError("Invalid wallet provider");
764
+ }
765
+ _getDefaultBrowserWallet() {
766
+ if (typeof window === "undefined") return null;
767
+ return window.ethereum || window.solana || window.phantom && window.phantom.solana || null;
768
+ }
769
+ async _buildPrivateGateAuth({ address, wallet, chain, signatureMethod } = {}) {
770
+ const providerWallet = wallet || this._getDefaultBrowserWallet();
771
+ const { signerWalletAddress, provider } = await this._resolveWalletSigner(providerWallet);
772
+ if (!signerWalletAddress || typeof signerWalletAddress !== "string") {
773
+ throw new ConfigurationError("No wallet accounts available");
774
+ }
775
+ const normalizedSigner = this._normalizeIdentity(signerWalletAddress);
776
+ const normalizedAddress = this._normalizeIdentity(address);
777
+ if (!normalizedSigner || normalizedSigner !== normalizedAddress) {
778
+ throw new ValidationError("wallet must match address when includePrivate=true");
779
+ }
780
+ const signerIsEvm = EVM_ADDRESS_RE.test(normalizedSigner);
781
+ const resolvedChain = this._inferChainForAddress(normalizedSigner, chain);
782
+ const resolvedSignatureMethod = typeof signatureMethod === "string" && signatureMethod.trim() ? signatureMethod.trim() : signerIsEvm ? "eip191" : "ed25519";
783
+ const signedTimestamp = Date.now();
784
+ const message = constructVerificationMessage({
785
+ walletAddress: signerWalletAddress,
786
+ signedTimestamp,
787
+ data: { action: "gate_check_private_proofs", walletAddress: normalizedAddress },
788
+ verifierIds: ["ownership-basic"],
789
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain: resolvedChain }
790
+ });
791
+ let signature;
792
+ try {
793
+ signature = await signMessage({
794
+ provider,
795
+ message,
796
+ walletAddress: signerWalletAddress,
797
+ ...signerIsEvm ? {} : { chain: resolvedChain }
798
+ });
799
+ } catch (error) {
800
+ if (error.code === 4001) {
801
+ throw new ValidationError("User rejected signature request");
802
+ }
803
+ throw new ValidationError(`Failed to sign message: ${error.message}`);
804
+ }
805
+ return {
806
+ walletAddress: signerWalletAddress,
807
+ signature,
808
+ signedTimestamp,
809
+ ...signerIsEvm ? {} : { chain: resolvedChain, signatureMethod: resolvedSignatureMethod }
810
+ };
811
+ }
812
+ async createGatePrivateAuth(params = {}) {
813
+ const address = (params.address || "").toString();
814
+ if (!validateUniversalAddress(address, params.chain)) {
815
+ throw new ValidationError("Valid address is required");
816
+ }
817
+ return this._buildPrivateGateAuth({
818
+ address,
819
+ wallet: params.wallet,
820
+ chain: params.chain,
821
+ signatureMethod: params.signatureMethod
822
+ });
823
+ }
504
824
  /**
505
- * VERIFY - Standard verification (auto or manual)
506
- *
507
- * Create proofs with complete control over the verification process.
508
- * If signature and walletAddress are omitted but verifier/content are provided,
509
- * this method performs the wallet flow inline (no aliases, no secondary methods).
510
- *
825
+ * Create a verification proof.
826
+ *
827
+ * Supports two paths:
828
+ * - **Auto:** Supply `verifier`, `content`, and/or `data` with an optional
829
+ * `wallet` provider. The SDK performs signing in the client.
830
+ * - **Manual:** Supply pre-signed `verifierIds`, `data`, `walletAddress`,
831
+ * `signature`, and `signedTimestamp`.
832
+ *
511
833
  * @param {Object} params - Verification parameters
834
+ * @param {string} [params.verifier] - Verifier ID (auto path, e.g. 'ownership-basic')
835
+ * @param {string} [params.content] - Content to verify (auto path)
836
+ * @param {Object} [params.data] - Structured verification data
837
+ * @param {Object} [params.wallet] - Wallet provider (auto path)
838
+ * @param {Object} [params.options] - Additional options
512
839
  * @param {Array<string>} [params.verifierIds] - Array of verifier IDs (manual path)
513
- * @param {Object} [params.data] - Verification data object (manual path)
514
840
  * @param {string} [params.walletAddress] - Wallet address that signed the request (manual path)
515
841
  * @param {string} [params.signature] - EIP-191 signature (manual path)
516
842
  * @param {number} [params.signedTimestamp] - Unix timestamp when signature was created (manual path)
517
- * @param {number} [params.chainId] - Chain ID for verification context (optional, managed by protocol)
518
- * @param {Object} [params.options] - Additional options
519
- * @param {string} [params.verifier] - Verifier ID (auto path)
520
- * @param {string} [params.content] - Content/description (auto path)
521
- * @param {Object} [params.wallet] - Optional injected wallet/provider (auto path)
522
- * @returns {Promise<Object>} Verification result with qHash
523
- *
843
+ * @param {number} [params.chainId] - EVM signing-context hint; when omitted, resolved to the NEUS protocol primary chain for signing
844
+ * @returns {Promise<Object>} Verification result with proofId
845
+ *
524
846
  * @example
847
+ * // Auto path
848
+ * const proof = await client.verify({
849
+ * verifier: 'ownership-basic',
850
+ * content: 'Hello World',
851
+ * wallet: window.ethereum
852
+ * });
853
+ *
854
+ * @example
855
+ * // Manual path
525
856
  * const proof = await client.verify({
526
857
  * verifierIds: ['ownership-basic'],
527
- * data: {
528
- * content: "My content",
529
- * owner: walletAddress, // or ownerAddress for nft-ownership/token-holding
530
- * reference: { type: 'other', id: 'my-unique-identifier' }
531
- * },
858
+ * data: { content: "My content", owner: walletAddress },
532
859
  * walletAddress: '0x...',
533
860
  * signature: '0x...',
534
861
  * signedTimestamp: Date.now(),
535
862
  * options: { targetChains: [421614, 11155111] }
536
863
  * });
537
864
  */
538
- /**
539
- * Create a verification proof
540
- *
541
- * @param {Object} params - Verification parameters
542
- * @param {string} [params.verifier] - Verifier ID (e.g., 'ownership-basic')
543
- * @param {string} [params.content] - Content to verify
544
- * @param {Object} [params.data] - Structured verification data
545
- * @param {Object} [params.wallet] - Wallet provider
546
- * @param {Object} [params.options] - Additional options
547
- * @returns {Promise<Object>} Verification result with qHash
548
- *
549
- * @example
550
- * // Simple ownership proof
551
- * const proof = await client.verify({
552
- * verifier: 'ownership-basic',
553
- * content: 'Hello World',
554
- * wallet: window.ethereum
555
- * });
556
- */
557
865
  async verify(params) {
558
866
  if ((!params?.signature || !params?.walletAddress) && (params?.verifier || params?.content || params?.data)) {
559
867
  const { content, verifier = "ownership-basic", data: data2 = null, wallet = null, options: options2 = {} } = params;
560
868
  if (verifier === "ownership-basic" && !data2 && (!content || typeof content !== "string")) {
561
869
  throw new ValidationError("content is required and must be a string (or use data param with owner + reference)");
562
870
  }
563
- const validVerifiers = [
564
- "ownership-basic",
565
- "ownership-pseudonym",
566
- // Pseudonymous identity (public)
567
- "nft-ownership",
568
- "token-holding",
569
- "ownership-dns-txt",
570
- "wallet-link",
571
- "contract-ownership",
572
- "wallet-risk",
573
- // Wallet risk assessment (public)
574
- // AI & Agent verifiers (ERC-8004 aligned)
575
- "agent-identity",
576
- "agent-delegation",
577
- "ai-content-moderation"
578
- ];
871
+ let verifierCatalog = FALLBACK_PUBLIC_VERIFIER_CATALOG;
872
+ try {
873
+ const discovered = await this.getVerifierCatalog();
874
+ if (discovered && discovered.metadata && Object.keys(discovered.metadata).length > 0) {
875
+ verifierCatalog = discovered.metadata;
876
+ }
877
+ } catch {
878
+ }
879
+ const validVerifiers = Object.keys(verifierCatalog);
579
880
  if (!validVerifiers.includes(verifier)) {
580
881
  throw new ValidationError(`Invalid verifier '${verifier}'. Must be one of: ${validVerifiers.join(", ")}.`);
581
882
  }
883
+ if (verifierCatalog?.[verifier]?.supportsDirectApi === false) {
884
+ const hostedCheckoutUrl = options2?.hostedCheckoutUrl || "https://neus.network/verify";
885
+ throw new ValidationError(
886
+ `${verifier} requires hosted interactive checkout. Use VerifyGate or redirect to ${hostedCheckoutUrl}.`
887
+ );
888
+ }
582
889
  const requiresDataParam = [
583
890
  "ownership-dns-txt",
584
891
  "wallet-link",
@@ -596,8 +903,12 @@ var NeusClient = class {
596
903
  }
597
904
  let walletAddress2, provider;
598
905
  if (wallet) {
599
- walletAddress2 = wallet.address || wallet.selectedAddress;
906
+ walletAddress2 = wallet.address || wallet.selectedAddress || wallet.walletAddress || (typeof wallet.getAddress === "function" ? await wallet.getAddress() : null);
600
907
  provider = wallet.provider || wallet;
908
+ if (!walletAddress2 && provider && typeof provider.request === "function") {
909
+ const accounts = await provider.request({ method: "eth_accounts" });
910
+ walletAddress2 = Array.isArray(accounts) ? accounts[0] : null;
911
+ }
601
912
  } else {
602
913
  if (typeof window === "undefined" || !window.ethereum) {
603
914
  throw new ConfigurationError("No Web3 wallet detected. Please install MetaMask or provide wallet parameter.");
@@ -673,14 +984,18 @@ var NeusClient = class {
673
984
  if (!data2?.signature) {
674
985
  throw new ValidationError("wallet-link requires signature in data parameter (signed by secondary wallet)");
675
986
  }
676
- if (typeof data2?.chainId !== "number") {
677
- throw new ValidationError("wallet-link requires chainId (number) in data parameter");
987
+ if (typeof data2?.chain !== "string" || !/^[a-z0-9]+:[^\s]+$/.test(data2.chain)) {
988
+ throw new ValidationError("wallet-link requires chain (namespace:reference) in data parameter");
989
+ }
990
+ if (typeof data2?.signatureMethod !== "string" || !data2.signatureMethod.trim()) {
991
+ throw new ValidationError("wallet-link requires signatureMethod in data parameter");
678
992
  }
679
993
  verificationData = {
680
994
  primaryWalletAddress: walletAddress2,
681
995
  secondaryWalletAddress: data2.secondaryWalletAddress,
682
996
  signature: data2.signature,
683
- chainId: data2.chainId,
997
+ chain: data2.chain,
998
+ signatureMethod: data2.signatureMethod,
684
999
  signedTimestamp: data2?.signedTimestamp || Date.now()
685
1000
  };
686
1001
  } else if (verifier === "contract-ownership") {
@@ -706,7 +1021,10 @@ var NeusClient = class {
706
1021
  ...data2?.agentLabel && { agentLabel: data2.agentLabel },
707
1022
  ...data2?.agentType && { agentType: data2.agentType },
708
1023
  ...data2?.description && { description: data2.description },
709
- ...data2?.capabilities && { capabilities: data2.capabilities }
1024
+ ...data2?.capabilities && { capabilities: data2.capabilities },
1025
+ ...data2?.instructions && { instructions: data2.instructions },
1026
+ ...data2?.skills && { skills: data2.skills },
1027
+ ...data2?.services && { services: data2.services }
710
1028
  };
711
1029
  } else if (verifier === "agent-delegation") {
712
1030
  if (!data2?.agentWallet) {
@@ -719,7 +1037,11 @@ var NeusClient = class {
719
1037
  ...data2?.scope && { scope: data2.scope },
720
1038
  ...data2?.permissions && { permissions: data2.permissions },
721
1039
  ...data2?.maxSpend && { maxSpend: data2.maxSpend },
722
- ...data2?.expiresAt && { expiresAt: data2.expiresAt }
1040
+ ...data2?.allowedPaymentTypes && { allowedPaymentTypes: data2.allowedPaymentTypes },
1041
+ ...data2?.receiptDisclosure && { receiptDisclosure: data2.receiptDisclosure },
1042
+ ...data2?.expiresAt && { expiresAt: data2.expiresAt },
1043
+ ...data2?.instructions && { instructions: data2.instructions },
1044
+ ...data2?.skills && { skills: data2.skills }
723
1045
  };
724
1046
  } else if (verifier === "ai-content-moderation") {
725
1047
  if (!data2?.content) {
@@ -769,48 +1091,41 @@ var NeusClient = class {
769
1091
  signedTimestamp: signedTimestamp2,
770
1092
  data: verificationData,
771
1093
  verifierIds: verifierIds2,
772
- chainId: NEUS_CONSTANTS.HUB_CHAIN_ID
1094
+ chainId: this._getHubChainId()
773
1095
  // Protocol-managed chain
774
1096
  });
775
1097
  let signature2;
776
1098
  try {
777
- const toHexUtf8 = (s) => {
1099
+ const toHexUtf82 = (s) => {
778
1100
  try {
779
1101
  const enc = new TextEncoder();
780
1102
  const bytes = enc.encode(s);
781
1103
  let hex = "0x";
782
- for (let i = 0; i < bytes.length; i++)
783
- hex += bytes[i].toString(16).padStart(2, "0");
1104
+ for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
784
1105
  return hex;
785
1106
  } catch {
786
1107
  let hex = "0x";
787
- for (let i = 0; i < s.length; i++)
788
- hex += s.charCodeAt(i).toString(16).padStart(2, "0");
1108
+ for (let i = 0; i < s.length; i++) hex += s.charCodeAt(i).toString(16).padStart(2, "0");
789
1109
  return hex;
790
1110
  }
791
1111
  };
792
1112
  const isFarcasterWallet = (() => {
793
- if (typeof window === "undefined")
794
- return false;
1113
+ if (typeof window === "undefined") return false;
795
1114
  try {
796
1115
  const w = window;
797
1116
  const fc = w.farcaster;
798
- if (!fc || !fc.context)
799
- return false;
1117
+ if (!fc || !fc.context) return false;
800
1118
  const fcProvider = fc.provider || fc.walletProvider || fc.context && fc.context.walletProvider;
801
- if (fcProvider === provider)
802
- return true;
803
- if (w.mini && w.mini.wallet === provider && fc && fc.context)
804
- return true;
805
- if (w.ethereum === provider && fc && fc.context)
806
- return true;
1119
+ if (fcProvider === provider) return true;
1120
+ if (w.mini && w.mini.wallet === provider && fc && fc.context) return true;
1121
+ if (w.ethereum === provider && fc && fc.context) return true;
807
1122
  } catch {
808
1123
  }
809
1124
  return false;
810
1125
  })();
811
1126
  if (isFarcasterWallet) {
812
1127
  try {
813
- const hexMsg = toHexUtf8(message);
1128
+ const hexMsg = toHexUtf82(message);
814
1129
  signature2 = await provider.request({ method: "personal_sign", params: [hexMsg, walletAddress2] });
815
1130
  } catch (e) {
816
1131
  }
@@ -822,7 +1137,8 @@ var NeusClient = class {
822
1137
  const msg = String(e && (e.message || e.reason) || e || "").toLowerCase();
823
1138
  const errCode = e && (e.code || e.error && e.error.code) || null;
824
1139
  const needsHex = /byte|bytes|invalid byte sequence|encoding|non-hex/i.test(msg);
825
- const methodUnsupported = /method.*not.*supported|unsupported|not implemented|method not found|unknown method|does not support/i.test(msg) || errCode === -32601 || errCode === 4200 || msg.includes("personal_sign") && msg.includes("not") || msg.includes("request method") && msg.includes("not supported");
1140
+ const unsupportedRe = /method.*not.*supported|unsupported|not implemented|method not found|unknown method|does not support/i;
1141
+ const methodUnsupported = unsupportedRe.test(msg) || errCode === -32601 || errCode === 4200 || msg.includes("personal_sign") && msg.includes("not") || msg.includes("request method") && msg.includes("not supported");
826
1142
  if (methodUnsupported) {
827
1143
  this._log("personal_sign not supported; attempting eth_sign fallback");
828
1144
  try {
@@ -831,35 +1147,30 @@ var NeusClient = class {
831
1147
  const prefix = `Ethereum Signed Message:
832
1148
  ${bytes.length}`;
833
1149
  const full = new Uint8Array(prefix.length + bytes.length);
834
- for (let i = 0; i < prefix.length; i++)
835
- full[i] = prefix.charCodeAt(i);
1150
+ for (let i = 0; i < prefix.length; i++) full[i] = prefix.charCodeAt(i);
836
1151
  full.set(bytes, prefix.length);
837
1152
  let payloadHex = "0x";
838
- for (let i = 0; i < full.length; i++)
839
- payloadHex += full[i].toString(16).padStart(2, "0");
1153
+ for (let i = 0; i < full.length; i++) payloadHex += full[i].toString(16).padStart(2, "0");
840
1154
  try {
841
- if (typeof window !== "undefined")
842
- window.__NEUS_ALLOW_ETH_SIGN__ = true;
1155
+ if (typeof window !== "undefined") window.__NEUS_ALLOW_ETH_SIGN__ = true;
843
1156
  } catch {
844
1157
  }
845
1158
  signature2 = await provider.request({ method: "eth_sign", params: [walletAddress2, payloadHex], neusAllowEthSign: true });
846
1159
  try {
847
- if (typeof window !== "undefined")
848
- delete window.__NEUS_ALLOW_ETH_SIGN__;
1160
+ if (typeof window !== "undefined") delete window.__NEUS_ALLOW_ETH_SIGN__;
849
1161
  } catch {
850
1162
  }
851
1163
  } catch (fallbackErr) {
852
1164
  this._log("eth_sign fallback failed", { message: fallbackErr?.message || String(fallbackErr) });
853
1165
  try {
854
- if (typeof window !== "undefined")
855
- delete window.__NEUS_ALLOW_ETH_SIGN__;
1166
+ if (typeof window !== "undefined") delete window.__NEUS_ALLOW_ETH_SIGN__;
856
1167
  } catch {
857
1168
  }
858
1169
  throw e;
859
1170
  }
860
1171
  } else if (needsHex) {
861
1172
  this._log("Retrying personal_sign with hex-encoded message");
862
- const hexMsg = toHexUtf8(message);
1173
+ const hexMsg = toHexUtf82(message);
863
1174
  signature2 = await provider.request({ method: "personal_sign", params: [hexMsg, walletAddress2] });
864
1175
  } else {
865
1176
  throw e;
@@ -894,8 +1205,7 @@ ${bytes.length}`;
894
1205
  } = params;
895
1206
  const resolvedChainId = chainId || (chain ? null : NEUS_CONSTANTS.HUB_CHAIN_ID);
896
1207
  const normalizeVerifierId = (id) => {
897
- if (typeof id !== "string")
898
- return id;
1208
+ if (typeof id !== "string") return id;
899
1209
  const match = id.match(/^(.*)@\d+$/);
900
1210
  return match ? match[1] : id;
901
1211
  };
@@ -930,10 +1240,9 @@ ${bytes.length}`;
930
1240
  // Privacy and storage options (defaults)
931
1241
  privacyLevel: options?.privacyLevel || "private",
932
1242
  publicDisplay: options?.publicDisplay || false,
933
- storeOriginalContent: options?.storeOriginalContent || false
1243
+ storeOriginalContent: typeof options?.storeOriginalContent === "boolean" ? options.storeOriginalContent : true
934
1244
  };
935
- if (typeof options?.enableIpfs === "boolean")
936
- optionsPayload.enableIpfs = options.enableIpfs;
1245
+ if (typeof options?.enableIpfs === "boolean") optionsPayload.enableIpfs = options.enableIpfs;
937
1246
  const requestData = {
938
1247
  verifierIds: normalizedVerifierIds,
939
1248
  data,
@@ -951,96 +1260,94 @@ ${bytes.length}`;
951
1260
  }
952
1261
  return this._formatResponse(response);
953
1262
  }
954
- // ============================================================================
955
- // STATUS AND UTILITY METHODS
956
- // ============================================================================
957
1263
  /**
958
- * Get verification status
1264
+ * Get proof record by proof receipt id.
959
1265
  *
960
- * @param {string} qHash - Verification ID (qHash or proofId)
961
- * @returns {Promise<Object>} Verification status and data
1266
+ * @param {string} proofId - Proof receipt ID (0x + 64 hex).
1267
+ * @returns {Promise<Object>} Proof record and verification state
962
1268
  *
963
1269
  * @example
964
- * const result = await client.getStatus('0x...');
1270
+ * const result = await client.getProof('0x...');
965
1271
  * console.log('Status:', result.status);
966
1272
  */
967
- async getStatus(qHash) {
968
- if (!qHash || typeof qHash !== "string") {
969
- throw new ValidationError("qHash is required");
1273
+ async getProof(proofId) {
1274
+ if (!proofId || typeof proofId !== "string") {
1275
+ throw new ValidationError("proofId is required");
970
1276
  }
971
- const response = await this._makeRequest("GET", `/api/v1/verification/status/${qHash}`);
1277
+ const response = await this._makeRequest("GET", `/api/v1/proofs/${proofId}`);
972
1278
  if (!response.success) {
973
- throw new ApiError(`Failed to get status: ${response.error?.message || "Unknown error"}`, response.error);
1279
+ throw new ApiError(`Failed to get proof: ${response.error?.message || "Unknown error"}`, response.error);
974
1280
  }
975
1281
  return this._formatResponse(response);
976
1282
  }
977
1283
  /**
978
- * Get private proof status with wallet signature
1284
+ * Get private proof record with wallet signature
979
1285
  *
980
- * @param {string} qHash - Verification ID
1286
+ * @param {string} proofId - Proof receipt ID.
981
1287
  * @param {Object} wallet - Wallet provider (window.ethereum or ethers Wallet)
982
- * @returns {Promise<Object>} Private verification status and data
1288
+ * @returns {Promise<Object>} Private proof record and verification state
983
1289
  *
984
1290
  * @example
985
- * // Access private proof
986
- * const privateData = await client.getPrivateStatus(qHash, window.ethereum);
1291
+ * const privateData = await client.getPrivateProof(proofId, window.ethereum);
987
1292
  */
988
- async getPrivateStatus(qHash, wallet = null) {
989
- if (!qHash || typeof qHash !== "string") {
990
- throw new ValidationError("qHash is required");
1293
+ async getPrivateProof(proofId, wallet = null) {
1294
+ if (!proofId || typeof proofId !== "string") {
1295
+ throw new ValidationError("proofId is required");
991
1296
  }
992
- if (!wallet) {
993
- if (typeof window === "undefined" || !window.ethereum) {
994
- throw new ConfigurationError("No wallet provider available");
1297
+ const isPreSignedAuth = wallet && typeof wallet === "object" && typeof wallet.walletAddress === "string" && typeof wallet.signature === "string" && typeof wallet.signedTimestamp === "number";
1298
+ if (isPreSignedAuth) {
1299
+ const auth = wallet;
1300
+ const headers = {
1301
+ "x-wallet-address": String(auth.walletAddress),
1302
+ "x-signature": String(auth.signature),
1303
+ "x-signed-timestamp": String(auth.signedTimestamp),
1304
+ ...typeof auth.chain === "string" && auth.chain.trim() ? { "x-chain": auth.chain.trim() } : {},
1305
+ ...typeof auth.signatureMethod === "string" && auth.signatureMethod.trim() ? { "x-signature-method": auth.signatureMethod.trim() } : {}
1306
+ };
1307
+ const response2 = await this._makeRequest("GET", `/api/v1/proofs/${proofId}`, null, headers);
1308
+ if (!response2.success) {
1309
+ throw new ApiError(
1310
+ `Failed to access private proof: ${response2.error?.message || "Unauthorized"}`,
1311
+ response2.error
1312
+ );
995
1313
  }
996
- wallet = window.ethereum;
1314
+ return this._formatResponse(response2);
997
1315
  }
998
- let walletAddress, provider;
999
- if (wallet.address) {
1000
- walletAddress = wallet.address;
1001
- provider = wallet;
1002
- } else if (wallet.selectedAddress || wallet.request) {
1003
- provider = wallet;
1004
- if (wallet.selectedAddress) {
1005
- walletAddress = wallet.selectedAddress;
1006
- } else {
1007
- const accounts = await provider.request({ method: "eth_accounts" });
1008
- if (!accounts || accounts.length === 0) {
1009
- throw new ConfigurationError("No wallet accounts available");
1010
- }
1011
- walletAddress = accounts[0];
1012
- }
1013
- } else {
1014
- throw new ConfigurationError("Invalid wallet provider");
1316
+ const providerWallet = wallet || this._getDefaultBrowserWallet();
1317
+ const { signerWalletAddress: walletAddress, provider } = await this._resolveWalletSigner(providerWallet);
1318
+ if (!walletAddress || typeof walletAddress !== "string") {
1319
+ throw new ConfigurationError("No wallet accounts available");
1015
1320
  }
1321
+ const signerIsEvm = EVM_ADDRESS_RE.test(this._normalizeIdentity(walletAddress));
1322
+ const chain = this._inferChainForAddress(walletAddress);
1323
+ const signatureMethod = signerIsEvm ? "eip191" : "ed25519";
1016
1324
  const signedTimestamp = Date.now();
1017
1325
  const message = constructVerificationMessage({
1018
1326
  walletAddress,
1019
1327
  signedTimestamp,
1020
- data: { action: "access_private_proof", qHash },
1328
+ data: { action: "access_private_proof", qHash: proofId },
1021
1329
  verifierIds: ["ownership-basic"],
1022
- chainId: NEUS_CONSTANTS.HUB_CHAIN_ID
1330
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
1023
1331
  });
1024
1332
  let signature;
1025
1333
  try {
1026
- if (provider.signMessage) {
1027
- signature = await provider.signMessage(message);
1028
- } else {
1029
- signature = await provider.request({
1030
- method: "personal_sign",
1031
- params: [message, walletAddress]
1032
- });
1033
- }
1334
+ signature = await signMessage({
1335
+ provider,
1336
+ message,
1337
+ walletAddress,
1338
+ ...signerIsEvm ? {} : { chain }
1339
+ });
1034
1340
  } catch (error) {
1035
1341
  if (error.code === 4001) {
1036
1342
  throw new ValidationError("User rejected signature request");
1037
1343
  }
1038
1344
  throw new ValidationError(`Failed to sign message: ${error.message}`);
1039
1345
  }
1040
- const response = await this._makeRequest("GET", `/api/v1/verification/status/${qHash}`, null, {
1346
+ const response = await this._makeRequest("GET", `/api/v1/proofs/${proofId}`, null, {
1041
1347
  "x-wallet-address": walletAddress,
1042
1348
  "x-signature": signature,
1043
- "x-signed-timestamp": signedTimestamp.toString()
1349
+ "x-signed-timestamp": signedTimestamp.toString(),
1350
+ ...signerIsEvm ? {} : { "x-chain": chain, "x-signature-method": signatureMethod }
1044
1351
  });
1045
1352
  if (!response.success) {
1046
1353
  throw new ApiError(
@@ -1069,27 +1376,39 @@ ${bytes.length}`;
1069
1376
  * @returns {Promise<string[]>} Array of verifier IDs
1070
1377
  */
1071
1378
  async getVerifiers() {
1379
+ const catalog = await this.getVerifierCatalog();
1380
+ return Array.isArray(catalog?.data) ? catalog.data : [];
1381
+ }
1382
+ /**
1383
+ * Get the public verifier catalog with per-verifier capabilities.
1384
+ * @returns {Promise<{data: string[], metadata: Record<string, { supportsDirectApi?: boolean }>, meta?: object}>}
1385
+ */
1386
+ async getVerifierCatalog() {
1072
1387
  const response = await this._makeRequest("GET", "/api/v1/verification/verifiers");
1073
1388
  if (!response.success) {
1074
1389
  throw new ApiError(`Failed to get verifiers: ${response.error?.message || "Unknown error"}`, response.error);
1075
1390
  }
1076
- return Array.isArray(response.data) ? response.data : [];
1391
+ return {
1392
+ data: Array.isArray(response.data) ? response.data : [],
1393
+ metadata: response.metadata && typeof response.metadata === "object" && !Array.isArray(response.metadata) ? response.metadata : {},
1394
+ meta: response.meta && typeof response.meta === "object" && !Array.isArray(response.meta) ? response.meta : {}
1395
+ };
1077
1396
  }
1078
1397
  /**
1079
1398
  * POLL PROOF STATUS - Wait for verification completion
1080
- *
1399
+ *
1081
1400
  * Polls the verification status until it reaches a terminal state (completed or failed).
1082
1401
  * Useful for providing real-time feedback to users during verification.
1083
- *
1084
- * @param {string} qHash - Verification ID to poll
1402
+ *
1403
+ * @param {string} proofId - Proof ID to poll.
1085
1404
  * @param {Object} [options] - Polling options
1086
1405
  * @param {number} [options.interval=5000] - Polling interval in ms
1087
1406
  * @param {number} [options.timeout=120000] - Total timeout in ms
1088
1407
  * @param {Function} [options.onProgress] - Progress callback function
1089
1408
  * @returns {Promise<Object>} Final verification status
1090
- *
1409
+ *
1091
1410
  * @example
1092
- * const finalStatus = await client.pollProofStatus(qHash, {
1411
+ * const finalStatus = await client.pollProofStatus(proofId, {
1093
1412
  * interval: 3000,
1094
1413
  * timeout: 60000,
1095
1414
  * onProgress: (status) => {
@@ -1100,20 +1419,20 @@ ${bytes.length}`;
1100
1419
  * }
1101
1420
  * });
1102
1421
  */
1103
- async pollProofStatus(qHash, options = {}) {
1422
+ async pollProofStatus(proofId, options = {}) {
1104
1423
  const {
1105
1424
  interval = 5e3,
1106
1425
  timeout = 12e4,
1107
1426
  onProgress
1108
1427
  } = options;
1109
- if (!qHash || typeof qHash !== "string") {
1110
- throw new ValidationError("qHash is required");
1428
+ if (!proofId || typeof proofId !== "string") {
1429
+ throw new ValidationError("proofId is required");
1111
1430
  }
1112
1431
  const startTime = Date.now();
1113
1432
  let consecutiveRateLimits = 0;
1114
1433
  while (Date.now() - startTime < timeout) {
1115
1434
  try {
1116
- const status = await this.getStatus(qHash);
1435
+ const status = await this.getProof(proofId);
1117
1436
  consecutiveRateLimits = 0;
1118
1437
  if (onProgress && typeof onProgress === "function") {
1119
1438
  onProgress(status.data || status);
@@ -1146,7 +1465,7 @@ ${bytes.length}`;
1146
1465
  }
1147
1466
  /**
1148
1467
  * DETECT CHAIN ID - Get current wallet chain
1149
- *
1468
+ *
1150
1469
  * @returns {Promise<number>} Current chain ID
1151
1470
  */
1152
1471
  async detectChainId() {
@@ -1161,70 +1480,49 @@ ${bytes.length}`;
1161
1480
  }
1162
1481
  }
1163
1482
  /** Revoke your own proof (owner-signed) */
1164
- async revokeOwnProof(qHash, wallet) {
1165
- if (!qHash || typeof qHash !== "string") {
1166
- throw new ValidationError("qHash is required");
1483
+ async revokeOwnProof(proofId, wallet) {
1484
+ if (!proofId || typeof proofId !== "string") {
1485
+ throw new ValidationError("proofId is required");
1486
+ }
1487
+ const providerWallet = wallet || this._getDefaultBrowserWallet();
1488
+ const { signerWalletAddress: address, provider } = await this._resolveWalletSigner(providerWallet);
1489
+ if (!address || typeof address !== "string") {
1490
+ throw new ConfigurationError("No wallet accounts available");
1167
1491
  }
1168
- const address = wallet?.address || await this._getWalletAddress();
1492
+ const signerIsEvm = EVM_ADDRESS_RE.test(this._normalizeIdentity(address));
1493
+ const chain = this._inferChainForAddress(address);
1494
+ const signatureMethod = signerIsEvm ? "eip191" : "ed25519";
1169
1495
  const signedTimestamp = Date.now();
1170
- const hubChainId = NEUS_CONSTANTS.HUB_CHAIN_ID;
1171
1496
  const message = constructVerificationMessage({
1172
1497
  walletAddress: address,
1173
1498
  signedTimestamp,
1174
- data: { action: "revoke_proof", qHash },
1499
+ data: { action: "revoke_proof", qHash: proofId },
1175
1500
  verifierIds: ["ownership-basic"],
1176
- chainId: hubChainId
1501
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
1177
1502
  });
1178
1503
  let signature;
1179
1504
  try {
1180
- const toHexUtf8 = (s) => {
1181
- const enc = new TextEncoder();
1182
- const bytes = enc.encode(s);
1183
- let hex = "0x";
1184
- for (let i = 0; i < bytes.length; i++)
1185
- hex += bytes[i].toString(16).padStart(2, "0");
1186
- return hex;
1187
- };
1188
- const isFarcasterWallet = (() => {
1189
- if (typeof window === "undefined")
1190
- return false;
1191
- try {
1192
- const w = window;
1193
- const fc = w.farcaster;
1194
- if (!fc || !fc.context)
1195
- return false;
1196
- const fcProvider = fc.provider || fc.walletProvider || fc.context && fc.context.walletProvider;
1197
- if (fcProvider === w.ethereum)
1198
- return true;
1199
- if (w.mini && w.mini.wallet === w.ethereum && fc && fc.context)
1200
- return true;
1201
- if (w.ethereum && fc && fc.context)
1202
- return true;
1203
- } catch {
1204
- }
1205
- return false;
1206
- })();
1207
- if (isFarcasterWallet) {
1208
- try {
1209
- const hexMsg = toHexUtf8(message);
1210
- signature = await window.ethereum.request({ method: "personal_sign", params: [hexMsg, address] });
1211
- } catch {
1212
- }
1213
- }
1214
- if (!signature) {
1215
- signature = await window.ethereum.request({ method: "personal_sign", params: [message, address] });
1216
- }
1505
+ signature = await signMessage({
1506
+ provider,
1507
+ message,
1508
+ walletAddress: address,
1509
+ ...signerIsEvm ? {} : { chain }
1510
+ });
1217
1511
  } catch (error) {
1218
1512
  if (error.code === 4001) {
1219
1513
  throw new ValidationError("User rejected revocation signature");
1220
1514
  }
1221
1515
  throw new ValidationError(`Failed to sign revocation: ${error.message}`);
1222
1516
  }
1223
- const res = await fetch(`${this.config.apiUrl}/api/v1/proofs/${qHash}/revoke-self`, {
1517
+ const res = await fetch(`${this.config.apiUrl}/api/v1/proofs/revoke-self/${proofId}`, {
1224
1518
  method: "POST",
1225
- // SECURITY: Do not put proof signatures into Authorization headers.
1226
1519
  headers: { "Content-Type": "application/json" },
1227
- body: JSON.stringify({ walletAddress: address, signature, signedTimestamp })
1520
+ body: JSON.stringify({
1521
+ walletAddress: address,
1522
+ signature,
1523
+ signedTimestamp,
1524
+ ...signerIsEvm ? {} : { chain, signatureMethod }
1525
+ })
1228
1526
  });
1229
1527
  const json = await res.json();
1230
1528
  if (!json.success) {
@@ -1232,18 +1530,16 @@ ${bytes.length}`;
1232
1530
  }
1233
1531
  return true;
1234
1532
  }
1235
- // ============================================================================
1236
- // PROOFS & GATING METHODS
1237
- // ============================================================================
1238
1533
  /**
1239
1534
  * GET PROOFS BY WALLET - Fetch proofs for a wallet address
1240
- *
1241
- * @param {string} walletAddress - Wallet address (0x...) or DID (did:pkh:...)
1535
+ *
1536
+ * @param {string} walletAddress - Wallet identity (EVM/Solana/DID)
1242
1537
  * @param {Object} [options] - Filter options
1243
1538
  * @param {number} [options.limit] - Max results (default: 50; higher limits require owner access)
1244
1539
  * @param {number} [options.offset] - Pagination offset (default: 0)
1540
+ * @param {string} [options.qHash] - Filter to single proof by qHash
1245
1541
  * @returns {Promise<Object>} Proofs result
1246
- *
1542
+ *
1247
1543
  * @example
1248
1544
  * const result = await client.getProofsByWallet('0x...', {
1249
1545
  * limit: 50,
@@ -1257,14 +1553,13 @@ ${bytes.length}`;
1257
1553
  const id = walletAddress.trim();
1258
1554
  const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
1259
1555
  const qs = [];
1260
- if (options.limit)
1261
- qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1262
- if (options.offset)
1263
- qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1556
+ if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1557
+ if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1558
+ if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
1264
1559
  const query = qs.length ? `?${qs.join("&")}` : "";
1265
1560
  const response = await this._makeRequest(
1266
1561
  "GET",
1267
- `/api/v1/proofs/byWallet/${encodeURIComponent(pathId)}${query}`
1562
+ `/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`
1268
1563
  );
1269
1564
  if (!response.success) {
1270
1565
  throw new ApiError(`Failed to get proofs: ${response.error?.message || "Unknown error"}`, response.error);
@@ -1283,10 +1578,11 @@ ${bytes.length}`;
1283
1578
  *
1284
1579
  * Signs an owner-access intent and requests private proofs via signature headers.
1285
1580
  *
1286
- * @param {string} walletAddress - Wallet address (0x...) or DID (did:pkh:...)
1581
+ * @param {string} walletAddress - Wallet identity (EVM/Solana/DID)
1287
1582
  * @param {Object} [options]
1288
1583
  * @param {number} [options.limit] - Max results (server enforces caps)
1289
1584
  * @param {number} [options.offset] - Pagination offset
1585
+ * @param {string} [options.qHash] - Filter to single proof by qHash
1290
1586
  * @param {Object} [wallet] - Optional injected wallet/provider (MetaMask/ethers Wallet)
1291
1587
  */
1292
1588
  async getPrivateProofsByWallet(walletAddress, options = {}, wallet = null) {
@@ -1295,48 +1591,41 @@ ${bytes.length}`;
1295
1591
  }
1296
1592
  const id = walletAddress.trim();
1297
1593
  const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
1594
+ const requestedIdentity = this._normalizeIdentity(id);
1298
1595
  if (!wallet) {
1299
- if (typeof window === "undefined" || !window.ethereum) {
1596
+ const defaultWallet = this._getDefaultBrowserWallet();
1597
+ if (!defaultWallet) {
1300
1598
  throw new ConfigurationError("No wallet provider available");
1301
1599
  }
1302
- wallet = window.ethereum;
1600
+ wallet = defaultWallet;
1303
1601
  }
1304
- let signerWalletAddress, provider;
1305
- if (wallet.address) {
1306
- signerWalletAddress = wallet.address;
1307
- provider = wallet;
1308
- } else if (wallet.selectedAddress || wallet.request) {
1309
- provider = wallet;
1310
- if (wallet.selectedAddress) {
1311
- signerWalletAddress = wallet.selectedAddress;
1312
- } else {
1313
- const accounts = await provider.request({ method: "eth_accounts" });
1314
- if (!accounts || accounts.length === 0) {
1315
- throw new ConfigurationError("No wallet accounts available");
1316
- }
1317
- signerWalletAddress = accounts[0];
1318
- }
1319
- } else {
1320
- throw new ConfigurationError("Invalid wallet provider");
1602
+ const { signerWalletAddress, provider } = await this._resolveWalletSigner(wallet);
1603
+ if (!signerWalletAddress || typeof signerWalletAddress !== "string") {
1604
+ throw new ConfigurationError("No wallet accounts available");
1605
+ }
1606
+ const normalizedSigner = this._normalizeIdentity(signerWalletAddress);
1607
+ if (!normalizedSigner || normalizedSigner !== requestedIdentity) {
1608
+ throw new ValidationError("wallet must match walletAddress for private proof access");
1321
1609
  }
1610
+ const signerIsEvm = EVM_ADDRESS_RE.test(normalizedSigner);
1611
+ const chain = this._inferChainForAddress(normalizedSigner, options?.chain);
1612
+ const signatureMethod = typeof options?.signatureMethod === "string" && options.signatureMethod.trim() ? options.signatureMethod.trim() : signerIsEvm ? "eip191" : "ed25519";
1322
1613
  const signedTimestamp = Date.now();
1323
1614
  const message = constructVerificationMessage({
1324
1615
  walletAddress: signerWalletAddress,
1325
1616
  signedTimestamp,
1326
- data: { action: "access_private_proofs_by_wallet", walletAddress: signerWalletAddress.toLowerCase() },
1617
+ data: { action: "access_private_proofs_by_wallet", walletAddress: normalizedSigner },
1327
1618
  verifierIds: ["ownership-basic"],
1328
- chainId: NEUS_CONSTANTS.HUB_CHAIN_ID
1619
+ ...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
1329
1620
  });
1330
1621
  let signature;
1331
1622
  try {
1332
- if (provider.signMessage) {
1333
- signature = await provider.signMessage(message);
1334
- } else {
1335
- signature = await provider.request({
1336
- method: "personal_sign",
1337
- params: [message, signerWalletAddress]
1338
- });
1339
- }
1623
+ signature = await signMessage({
1624
+ provider,
1625
+ message,
1626
+ walletAddress: signerWalletAddress,
1627
+ ...signerIsEvm ? {} : { chain }
1628
+ });
1340
1629
  } catch (error) {
1341
1630
  if (error.code === 4001) {
1342
1631
  throw new ValidationError("User rejected signature request");
@@ -1344,15 +1633,15 @@ ${bytes.length}`;
1344
1633
  throw new ValidationError(`Failed to sign message: ${error.message}`);
1345
1634
  }
1346
1635
  const qs = [];
1347
- if (options.limit)
1348
- qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1349
- if (options.offset)
1350
- qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1636
+ if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
1637
+ if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
1638
+ if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
1351
1639
  const query = qs.length ? `?${qs.join("&")}` : "";
1352
- const response = await this._makeRequest("GET", `/api/v1/proofs/byWallet/${encodeURIComponent(pathId)}${query}`, null, {
1640
+ const response = await this._makeRequest("GET", `/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`, null, {
1353
1641
  "x-wallet-address": signerWalletAddress,
1354
1642
  "x-signature": signature,
1355
- "x-signed-timestamp": signedTimestamp.toString()
1643
+ "x-signed-timestamp": signedTimestamp.toString(),
1644
+ ...signerIsEvm ? {} : { "x-chain": chain, "x-signature-method": signatureMethod }
1356
1645
  });
1357
1646
  if (!response.success) {
1358
1647
  throw new ApiError(`Failed to get proofs: ${response.error?.message || "Unauthorized"}`, response.error);
@@ -1367,51 +1656,52 @@ ${bytes.length}`;
1367
1656
  };
1368
1657
  }
1369
1658
  /**
1370
- * GATE CHECK (API) - Minimal eligibility check
1659
+ * Gate check (HTTP API) minimal eligibility response.
1371
1660
  *
1372
- * Calls the public gate endpoint and returns a **minimal** yes/no response
1373
- * against **public + discoverable** proofs only.
1661
+ * Calls the gate endpoint and returns a **minimal** yes/no response.
1662
+ * By default this checks **public + unlisted** proofs.
1374
1663
  *
1375
- * Prefer this over `checkGate()` for server-side integrations that want the
1376
- * smallest, most stable surface area (and do NOT need full proof payloads).
1664
+ * When `includePrivate=true`, this can perform owner-signed private checks
1665
+ * (no full proof payloads returned) by providing a wallet/provider.
1666
+ *
1667
+ * Prefer this over `checkGate()` when you need the smallest, most stable
1668
+ * response shape and do not need full proof payloads.
1377
1669
  *
1378
1670
  * @param {Object} params - Gate check query params
1379
- * @param {string} params.address - Wallet address to check (0x...)
1671
+ * @param {string} params.address - Wallet identity to check (EVM/Solana/DID)
1380
1672
  * @param {Array<string>|string} [params.verifierIds] - Verifier IDs to match (array or comma-separated)
1381
1673
  * @param {boolean} [params.requireAll] - Require all verifierIds on a single proof
1382
1674
  * @param {number} [params.minCount] - Minimum number of matching proofs
1383
1675
  * @param {number} [params.sinceDays] - Optional time window in days
1384
1676
  * @param {number} [params.since] - Optional unix timestamp in ms (lower bound)
1385
1677
  * @param {number} [params.limit] - Max rows to scan (server may clamp)
1678
+ * @param {boolean} [params.includePrivate] - Include private proofs for owner-authenticated requests
1679
+ * @param {boolean} [params.includeQHashes] - Include matched qHashes in response (minimal IDs only)
1680
+ * @param {Object} [params.wallet] - Optional wallet/provider used to sign includePrivate owner checks
1386
1681
  * @returns {Promise<Object>} API response ({ success, data })
1387
1682
  */
1388
1683
  async gateCheck(params = {}) {
1389
1684
  const address = (params.address || "").toString();
1390
- if (!address || !/^0x[a-fA-F0-9]{40}$/i.test(address)) {
1685
+ if (!validateUniversalAddress(address, params.chain)) {
1391
1686
  throw new ValidationError("Valid address is required");
1392
1687
  }
1393
1688
  const qs = new URLSearchParams();
1394
1689
  qs.set("address", address);
1395
1690
  const setIfPresent = (key, value) => {
1396
- if (value === void 0 || value === null)
1397
- return;
1691
+ if (value === void 0 || value === null) return;
1398
1692
  const str = typeof value === "string" ? value : String(value);
1399
- if (str.length === 0)
1400
- return;
1693
+ if (str.length === 0) return;
1401
1694
  qs.set(key, str);
1402
1695
  };
1403
1696
  const setBoolIfPresent = (key, value) => {
1404
- if (value === void 0 || value === null)
1405
- return;
1697
+ if (value === void 0 || value === null) return;
1406
1698
  qs.set(key, value ? "true" : "false");
1407
1699
  };
1408
1700
  const setCsvIfPresent = (key, value) => {
1409
- if (value === void 0 || value === null)
1410
- return;
1701
+ if (value === void 0 || value === null) return;
1411
1702
  if (Array.isArray(value)) {
1412
1703
  const items = value.map((v) => String(v).trim()).filter(Boolean);
1413
- if (items.length)
1414
- qs.set(key, items.join(","));
1704
+ if (items.length) qs.set(key, items.join(","));
1415
1705
  return;
1416
1706
  }
1417
1707
  setIfPresent(key, value);
@@ -1422,6 +1712,8 @@ ${bytes.length}`;
1422
1712
  setIfPresent("sinceDays", params.sinceDays);
1423
1713
  setIfPresent("since", params.since);
1424
1714
  setIfPresent("limit", params.limit);
1715
+ setBoolIfPresent("includePrivate", params.includePrivate);
1716
+ setBoolIfPresent("includeQHashes", params.includeQHashes);
1425
1717
  setIfPresent("referenceType", params.referenceType);
1426
1718
  setIfPresent("referenceId", params.referenceId);
1427
1719
  setIfPresent("tag", params.tag);
@@ -1435,6 +1727,8 @@ ${bytes.length}`;
1435
1727
  setIfPresent("domain", params.domain);
1436
1728
  setIfPresent("minBalance", params.minBalance);
1437
1729
  setIfPresent("provider", params.provider);
1730
+ setIfPresent("handle", params.handle);
1731
+ setIfPresent("namespace", params.namespace);
1438
1732
  setIfPresent("ownerAddress", params.ownerAddress);
1439
1733
  setIfPresent("riskLevel", params.riskLevel);
1440
1734
  setBoolIfPresent("sanctioned", params.sanctioned);
@@ -1442,18 +1736,57 @@ ${bytes.length}`;
1442
1736
  setIfPresent("primaryWalletAddress", params.primaryWalletAddress);
1443
1737
  setIfPresent("secondaryWalletAddress", params.secondaryWalletAddress);
1444
1738
  setIfPresent("verificationMethod", params.verificationMethod);
1445
- const response = await this._makeRequest("GET", `/api/v1/proofs/gate/check?${qs.toString()}`);
1739
+ let headersOverride = null;
1740
+ if (params.includePrivate === true) {
1741
+ const provided = params.privateAuth && typeof params.privateAuth === "object" ? params.privateAuth : null;
1742
+ let auth = provided;
1743
+ if (!auth) {
1744
+ const walletCandidate = params.wallet || this._getDefaultBrowserWallet();
1745
+ if (walletCandidate) {
1746
+ auth = await this._buildPrivateGateAuth({
1747
+ address,
1748
+ wallet: walletCandidate,
1749
+ chain: params.chain,
1750
+ signatureMethod: params.signatureMethod
1751
+ });
1752
+ }
1753
+ }
1754
+ if (!auth) {
1755
+ } else {
1756
+ const normalizedAuthWallet = this._normalizeIdentity(auth.walletAddress);
1757
+ const normalizedAddress = this._normalizeIdentity(address);
1758
+ if (!normalizedAuthWallet || normalizedAuthWallet !== normalizedAddress) {
1759
+ throw new ValidationError("privateAuth.walletAddress must match address when includePrivate=true");
1760
+ }
1761
+ const authChain = typeof auth.chain === "string" && auth.chain.includes(":") ? auth.chain.trim() : null;
1762
+ const authSignatureMethod = typeof auth.signatureMethod === "string" && auth.signatureMethod.trim() ? auth.signatureMethod.trim() : null;
1763
+ headersOverride = {
1764
+ "x-wallet-address": String(auth.walletAddress),
1765
+ "x-signature": String(auth.signature),
1766
+ "x-signed-timestamp": String(auth.signedTimestamp),
1767
+ ...authChain ? { "x-chain": authChain } : {},
1768
+ ...authSignatureMethod ? { "x-signature-method": authSignatureMethod } : {}
1769
+ };
1770
+ }
1771
+ }
1772
+ const response = await this._makeRequest("GET", `/api/v1/proofs/check?${qs.toString()}`, null, headersOverride);
1446
1773
  if (!response.success) {
1447
1774
  throw new ApiError(`Gate check failed: ${response.error?.message || "Unknown error"}`, response.error);
1448
1775
  }
1449
1776
  return response;
1450
1777
  }
1451
1778
  /**
1452
- * CHECK GATE - Evaluate requirements against existing proofs
1453
- *
1779
+ * CHECK GATE Local preview against proofs you already have in memory or from `getProofsByWallet`.
1780
+ *
1781
+ * **Not authoritative for access control.** For production allow/deny, use {@link NeusClient#gateCheck}
1782
+ * (`GET /api/v1/proofs/check`), which applies the same rules as the NEUS API. This method is useful for
1783
+ * UI previews, offline-ish flows, or when you already fetched proofs and want a quick match without
1784
+ * another round trip — but it can disagree with the server (e.g. `contentHash` matching uses a local
1785
+ * approximation when proof data only has inline `content`; the API uses the standard server-side hash).
1786
+ *
1454
1787
  * Gate-first verification: checks if wallet has valid proofs satisfying requirements.
1455
1788
  * Returns which requirements are missing/expired.
1456
- *
1789
+ *
1457
1790
  * @param {Object} params - Gate check parameters
1458
1791
  * @param {string} params.walletAddress - Target wallet
1459
1792
  * @param {Array<Object>} params.requirements - Array of gate requirements
@@ -1466,12 +1799,12 @@ ${bytes.length}`;
1466
1799
  * Supports verifier-specific:
1467
1800
  * - NFT/Token: 'contractAddress', 'tokenId', 'chainId', 'ownerAddress', 'minBalance'
1468
1801
  * - DNS: 'domain', 'walletAddress'
1469
- * - Wallet-link: 'primaryWalletAddress', 'secondaryWalletAddress', 'chainId'
1802
+ * - Wallet-link: 'primaryWalletAddress', 'secondaryWalletAddress', 'chain', 'signatureMethod'
1470
1803
  * - Contract-ownership: 'contractAddress', 'chainId', 'owner', 'verificationMethod'
1471
1804
  * Note: contentHash matching uses approximation in SDK; for exact SHA-256 matching, use backend API
1472
1805
  * @param {Array} [params.proofs] - Pre-fetched proofs (skip API call)
1473
1806
  * @returns {Promise<Object>} Gate result with satisfied, missing, existing
1474
- *
1807
+ *
1475
1808
  * @example
1476
1809
  * // Basic gate check
1477
1810
  * const result = await client.checkGate({
@@ -1483,7 +1816,7 @@ ${bytes.length}`;
1483
1816
  */
1484
1817
  async checkGate(params) {
1485
1818
  const { walletAddress, requirements, proofs: preloadedProofs } = params;
1486
- if (!walletAddress || !/^0x[a-fA-F0-9]{40}$/i.test(walletAddress)) {
1819
+ if (!validateUniversalAddress(walletAddress)) {
1487
1820
  throw new ValidationError("Valid walletAddress is required");
1488
1821
  }
1489
1822
  if (!Array.isArray(requirements) || requirements.length === 0) {
@@ -1504,20 +1837,20 @@ ${bytes.length}`;
1504
1837
  const verifier = verifiedVerifiers.find(
1505
1838
  (v) => v.verifierId === verifierId && v.verified === true
1506
1839
  );
1507
- if (!verifier)
1508
- return false;
1509
- if (proof.revokedAt)
1510
- return false;
1840
+ if (!verifier) return false;
1841
+ if (proof.revokedAt) return false;
1511
1842
  if (maxAgeMs) {
1512
1843
  const proofTimestamp = proof.createdAt || proof.signedTimestamp || 0;
1513
1844
  const proofAge = now - proofTimestamp;
1514
- if (proofAge > maxAgeMs)
1515
- return false;
1845
+ if (proofAge > maxAgeMs) return false;
1516
1846
  }
1517
1847
  if (match && typeof match === "object") {
1518
1848
  const data = verifier.data || {};
1519
1849
  const input = data.input || {};
1520
- for (const [key, expected] of Object.entries(match)) {
1850
+ const matchObj = Array.isArray(match) ? Object.fromEntries(
1851
+ match.filter((m) => m && m.path && String(m.value ?? "").trim() !== "").map((m) => [String(m.path).trim(), m.value])
1852
+ ) : match;
1853
+ for (const [key, expected] of Object.entries(matchObj)) {
1521
1854
  let actualValue = null;
1522
1855
  if (key.includes(".")) {
1523
1856
  const parts = key.split(".");
@@ -1542,6 +1875,7 @@ ${bytes.length}`;
1542
1875
  actualValue = data.reference?.id || data.content;
1543
1876
  }
1544
1877
  if (actualValue === void 0) {
1878
+ const claims = data.claims || {};
1545
1879
  if (key === "contractAddress") {
1546
1880
  actualValue = input.contractAddress || data.contractAddress;
1547
1881
  } else if (key === "tokenId") {
@@ -1560,6 +1894,22 @@ ${bytes.length}`;
1560
1894
  actualValue = data.verificationMethod;
1561
1895
  } else if (key === "domain") {
1562
1896
  actualValue = data.domain;
1897
+ } else if (key === "handle") {
1898
+ actualValue = data.handle || data.pseudonymId;
1899
+ } else if (key === "namespace") {
1900
+ actualValue = data.namespace !== void 0 && data.namespace !== null && data.namespace !== "" ? data.namespace : "neus";
1901
+ } else if (key === "claims.sanctions_passed") {
1902
+ actualValue = claims.sanctions_passed ?? claims.sanctionsPassed;
1903
+ } else if (key === "claims.age_min") {
1904
+ actualValue = claims.age_min ?? claims.ageMin;
1905
+ } else if (key === "neusPersonhoodId") {
1906
+ actualValue = data.neusPersonhoodId;
1907
+ } else if (key === "riskLevel") {
1908
+ actualValue = data.riskLevel;
1909
+ } else if (key === "sanctioned") {
1910
+ actualValue = data.sanctioned;
1911
+ } else if (key === "poisoned") {
1912
+ actualValue = data.poisoned;
1563
1913
  }
1564
1914
  }
1565
1915
  if (key === "contentHash" && actualValue === void 0 && data.content) {
@@ -1571,7 +1921,7 @@ ${bytes.length}`;
1571
1921
  hash = (hash << 5) - hash + char;
1572
1922
  hash = hash & hash;
1573
1923
  }
1574
- actualValue = "0x" + Math.abs(hash).toString(16).padStart(64, "0").substring(0, 66);
1924
+ actualValue = `0x${Math.abs(hash).toString(16).padStart(64, "0").substring(0, 66)}`;
1575
1925
  } catch {
1576
1926
  actualValue = String(data.content);
1577
1927
  }
@@ -1581,11 +1931,16 @@ ${bytes.length}`;
1581
1931
  if (actualValue === void 0 || actualValue === null) {
1582
1932
  return false;
1583
1933
  }
1934
+ if (typeof actualValue === "boolean" || key && (key.includes("sanctions") || key.includes("sanctioned") || key.includes("poisoned"))) {
1935
+ const bActual = Boolean(actualValue);
1936
+ const bExpected = expected === true || expected === "true" || String(expected).toLowerCase() === "true";
1937
+ if (bActual !== bExpected) return false;
1938
+ continue;
1939
+ }
1584
1940
  if (key === "chainId" || key === "tokenId" && (typeof actualValue === "number" || !isNaN(Number(actualValue)))) {
1585
1941
  normalizedActual = Number(actualValue);
1586
1942
  normalizedExpected = Number(expected);
1587
- if (isNaN(normalizedActual) || isNaN(normalizedExpected))
1588
- return false;
1943
+ if (isNaN(normalizedActual) || isNaN(normalizedExpected)) return false;
1589
1944
  } else if (typeof actualValue === "string" && (actualValue.startsWith("0x") || actualValue.length > 20)) {
1590
1945
  normalizedActual = actualValue.toLowerCase();
1591
1946
  normalizedExpected = typeof expected === "string" ? String(expected).toLowerCase() : expected;
@@ -1614,13 +1969,6 @@ ${bytes.length}`;
1614
1969
  allProofs: proofs
1615
1970
  };
1616
1971
  }
1617
- // ============================================================================
1618
- // PRIVATE UTILITY METHODS
1619
- // ============================================================================
1620
- /**
1621
- * Get connected wallet address
1622
- * @private
1623
- */
1624
1972
  async _getWalletAddress() {
1625
1973
  if (typeof window === "undefined" || !window.ethereum) {
1626
1974
  throw new ConfigurationError("No Web3 wallet detected");
@@ -1631,10 +1979,6 @@ ${bytes.length}`;
1631
1979
  }
1632
1980
  return accounts[0];
1633
1981
  }
1634
- /**
1635
- * Make HTTP request to API
1636
- * @private
1637
- */
1638
1982
  async _makeRequest(method, endpoint, data = null, headersOverride = null) {
1639
1983
  const url = `${this.baseUrl}${endpoint}`;
1640
1984
  const controller = new AbortController();
@@ -1672,27 +2016,23 @@ ${bytes.length}`;
1672
2016
  throw new NetworkError(`Network error: ${error.message}`);
1673
2017
  }
1674
2018
  }
1675
- /**
1676
- * Format API response for consistent structure
1677
- * @private
1678
- */
1679
2019
  _formatResponse(response) {
1680
- const qHash = response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || response?.data?.id;
2020
+ const proofId = response?.data?.proofId || response?.proofId || response?.data?.resource?.proofId || response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || response?.data?.id;
2021
+ const qHash = response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || proofId || response?.data?.id;
2022
+ const finalProofId = proofId || qHash || null;
2023
+ const finalQHash = qHash || proofId || finalProofId;
1681
2024
  const status = response?.data?.status || response?.status || response?.data?.resource?.status || (response?.success ? "completed" : "unknown");
1682
2025
  return {
1683
2026
  success: response.success,
1684
- qHash,
2027
+ proofId: finalProofId,
2028
+ qHash: finalQHash,
1685
2029
  status,
1686
2030
  data: response.data,
1687
2031
  message: response.message,
1688
2032
  timestamp: Date.now(),
1689
- statusUrl: qHash ? `${this.baseUrl}/api/v1/verification/status/${qHash}` : null
2033
+ proofUrl: finalProofId ? `${this.baseUrl}/api/v1/proofs/${finalProofId}` : null
1690
2034
  };
1691
2035
  }
1692
- /**
1693
- * Check if status is terminal (completed or failed)
1694
- * @private
1695
- */
1696
2036
  _isTerminalStatus(status) {
1697
2037
  const terminalStates = [
1698
2038
  "verified",
@@ -1723,5 +2063,6 @@ ${bytes.length}`;
1723
2063
  // Annotate the CommonJS export names for ESM import in node:
1724
2064
  0 && (module.exports = {
1725
2065
  NeusClient,
2066
+ PORTABLE_PROOF_SIGNER_HEADER,
1726
2067
  constructVerificationMessage
1727
2068
  });