@neus/sdk 1.0.3 → 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/README.md +90 -150
- package/SECURITY.md +33 -29
- package/cjs/client.cjs +235 -264
- package/cjs/index.cjs +330 -310
- package/cjs/utils.cjs +138 -97
- package/cli/neus.mjs +58 -0
- package/client.js +464 -457
- package/errors.js +5 -5
- package/gates.js +18 -18
- package/index.js +79 -75
- package/package.json +16 -10
- package/types.d.ts +124 -59
- package/utils.js +1291 -1180
- package/widgets/README.md +45 -64
- package/widgets/index.js +1 -1
- package/widgets/verify-gate/dist/ProofBadge.js +10 -18
- package/widgets/verify-gate/dist/VerifyGate.js +108 -97
- package/widgets/verify-gate/index.js +5 -5
package/cjs/client.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var client_exports = {};
|
|
22
22
|
__export(client_exports, {
|
|
23
23
|
NeusClient: () => NeusClient,
|
|
24
|
+
PORTABLE_PROOF_SIGNER_HEADER: () => PORTABLE_PROOF_SIGNER_HEADER,
|
|
24
25
|
constructVerificationMessage: () => constructVerificationMessage
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(client_exports);
|
|
@@ -129,6 +130,7 @@ var ConfigurationError = class extends SDKError {
|
|
|
129
130
|
};
|
|
130
131
|
|
|
131
132
|
// utils.js
|
|
133
|
+
var PORTABLE_PROOF_SIGNER_HEADER = "Portable Proof Verification Request";
|
|
132
134
|
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
133
135
|
function encodeBase58Bytes(input) {
|
|
134
136
|
let source;
|
|
@@ -143,8 +145,7 @@ function encodeBase58Bytes(input) {
|
|
|
143
145
|
} else {
|
|
144
146
|
throw new SDKError("Unsupported non-EVM signature byte format", "INVALID_SIGNATURE_FORMAT");
|
|
145
147
|
}
|
|
146
|
-
if (source.length === 0)
|
|
147
|
-
return "";
|
|
148
|
+
if (source.length === 0) return "";
|
|
148
149
|
let zeroes = 0;
|
|
149
150
|
while (zeroes < source.length && source[zeroes] === 0) {
|
|
150
151
|
zeroes++;
|
|
@@ -178,16 +179,28 @@ function deterministicStringify(obj) {
|
|
|
178
179
|
return JSON.stringify(obj);
|
|
179
180
|
}
|
|
180
181
|
if (typeof obj !== "object") {
|
|
182
|
+
if (typeof obj === "string") return JSON.stringify(obj.normalize("NFC"));
|
|
181
183
|
return JSON.stringify(obj);
|
|
182
184
|
}
|
|
183
185
|
if (Array.isArray(obj)) {
|
|
184
|
-
return
|
|
186
|
+
return `[${obj.map((item) => item === void 0 ? "null" : deterministicStringify(item)).join(",")}]`;
|
|
185
187
|
}
|
|
186
|
-
const sortedKeys = Object.keys(obj).sort();
|
|
188
|
+
const sortedKeys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
187
189
|
const pairs = sortedKeys.map(
|
|
188
|
-
(key) => JSON.stringify(key)
|
|
190
|
+
(key) => `${JSON.stringify(key)}:${deterministicStringify(obj[key])}`
|
|
189
191
|
);
|
|
190
|
-
return
|
|
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");
|
|
191
204
|
}
|
|
192
205
|
function constructVerificationMessage({ walletAddress, signedTimestamp, data, verifierIds, chainId, chain }) {
|
|
193
206
|
if (!walletAddress || typeof walletAddress !== "string") {
|
|
@@ -202,28 +215,28 @@ function constructVerificationMessage({ walletAddress, signedTimestamp, data, ve
|
|
|
202
215
|
if (!Array.isArray(verifierIds) || verifierIds.length === 0) {
|
|
203
216
|
throw new SDKError("verifierIds is required and must be a non-empty array", "INVALID_VERIFIER_IDS");
|
|
204
217
|
}
|
|
205
|
-
|
|
206
|
-
if (!chainContext) {
|
|
218
|
+
if ((typeof chain !== "string" || !chain.length) && !(typeof chainId === "number" && chainId > 0)) {
|
|
207
219
|
throw new SDKError("chainId is required (or provide chain for universal mode)", "INVALID_CHAIN_CONTEXT");
|
|
208
220
|
}
|
|
209
|
-
if (
|
|
210
|
-
throw new SDKError("chainId must be a number when provided", "INVALID_CHAIN_ID");
|
|
211
|
-
}
|
|
212
|
-
if (chainContext === chain && (typeof chain !== "string" || !chain.includes(":"))) {
|
|
221
|
+
if (typeof chain === "string" && chain.length > 0 && !chain.includes(":")) {
|
|
213
222
|
throw new SDKError('chain must be a "namespace:reference" string', "INVALID_CHAIN");
|
|
214
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);
|
|
215
228
|
const namespace = typeof chain === "string" && chain.includes(":") ? chain.split(":")[0] : "eip155";
|
|
216
229
|
const normalizedWalletAddress = namespace === "eip155" ? walletAddress.toLowerCase() : walletAddress;
|
|
217
230
|
const dataString = deterministicStringify(data);
|
|
218
231
|
const messageComponents = [
|
|
219
|
-
|
|
232
|
+
PORTABLE_PROOF_SIGNER_HEADER,
|
|
220
233
|
`Wallet: ${normalizedWalletAddress}`,
|
|
221
|
-
`Chain: ${
|
|
234
|
+
`Chain: ${chainLine}`,
|
|
222
235
|
`Verifiers: ${verifierIds.join(",")}`,
|
|
223
236
|
`Data: ${dataString}`,
|
|
224
237
|
`Timestamp: ${signedTimestamp}`
|
|
225
238
|
];
|
|
226
|
-
return messageComponents.join("\n");
|
|
239
|
+
return messageComponents.join("\n").normalize("NFC");
|
|
227
240
|
}
|
|
228
241
|
function validateWalletAddress(address) {
|
|
229
242
|
if (!address || typeof address !== "string") {
|
|
@@ -232,17 +245,13 @@ function validateWalletAddress(address) {
|
|
|
232
245
|
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
233
246
|
}
|
|
234
247
|
function validateUniversalAddress(address, chain) {
|
|
235
|
-
if (!address || typeof address !== "string")
|
|
236
|
-
return false;
|
|
248
|
+
if (!address || typeof address !== "string") return false;
|
|
237
249
|
const value = address.trim();
|
|
238
|
-
if (!value)
|
|
239
|
-
return false;
|
|
250
|
+
if (!value) return false;
|
|
240
251
|
const chainRef = typeof chain === "string" ? chain.trim().toLowerCase() : "";
|
|
241
252
|
const namespace = chainRef.includes(":") ? chainRef.split(":")[0] : "";
|
|
242
|
-
if (validateWalletAddress(value))
|
|
243
|
-
|
|
244
|
-
if (namespace === "eip155")
|
|
245
|
-
return false;
|
|
253
|
+
if (validateWalletAddress(value)) return true;
|
|
254
|
+
if (namespace === "eip155") return false;
|
|
246
255
|
if (namespace === "solana") {
|
|
247
256
|
return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
|
|
248
257
|
}
|
|
@@ -271,34 +280,27 @@ async function signMessage({ provider, message, walletAddress, chain } = {}) {
|
|
|
271
280
|
const chainStr = typeof chain === "string" && chain.trim().length > 0 ? chain.trim() : "eip155";
|
|
272
281
|
const namespace = chainStr.includes(":") ? chainStr.split(":")[0] || "eip155" : "eip155";
|
|
273
282
|
const resolveAddress = async () => {
|
|
274
|
-
if (typeof walletAddress === "string" && walletAddress.trim().length > 0)
|
|
275
|
-
return walletAddress;
|
|
283
|
+
if (typeof walletAddress === "string" && walletAddress.trim().length > 0) return walletAddress;
|
|
276
284
|
if (namespace === "solana") {
|
|
277
285
|
if (resolvedProvider?.publicKey && typeof resolvedProvider.publicKey.toBase58 === "function") {
|
|
278
286
|
const pk = resolvedProvider.publicKey.toBase58();
|
|
279
|
-
if (typeof pk === "string" && pk)
|
|
280
|
-
return pk;
|
|
287
|
+
if (typeof pk === "string" && pk) return pk;
|
|
281
288
|
}
|
|
282
289
|
if (typeof resolvedProvider.getAddress === "function") {
|
|
283
290
|
const addr = await resolvedProvider.getAddress().catch(() => null);
|
|
284
|
-
if (typeof addr === "string" && addr)
|
|
285
|
-
return addr;
|
|
291
|
+
if (typeof addr === "string" && addr) return addr;
|
|
286
292
|
}
|
|
287
|
-
if (typeof resolvedProvider.address === "string" && resolvedProvider.address)
|
|
288
|
-
return resolvedProvider.address;
|
|
293
|
+
if (typeof resolvedProvider.address === "string" && resolvedProvider.address) return resolvedProvider.address;
|
|
289
294
|
return null;
|
|
290
295
|
}
|
|
291
|
-
if (typeof resolvedProvider.address === "string" && resolvedProvider.address)
|
|
292
|
-
|
|
293
|
-
if (typeof resolvedProvider.getAddress === "function")
|
|
294
|
-
return await resolvedProvider.getAddress();
|
|
296
|
+
if (typeof resolvedProvider.address === "string" && resolvedProvider.address) return resolvedProvider.address;
|
|
297
|
+
if (typeof resolvedProvider.getAddress === "function") return resolvedProvider.getAddress();
|
|
295
298
|
if (typeof resolvedProvider.request === "function") {
|
|
296
299
|
let accounts = await resolvedProvider.request({ method: "eth_accounts" }).catch(() => []);
|
|
297
300
|
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
298
301
|
accounts = await resolvedProvider.request({ method: "eth_requestAccounts" }).catch(() => []);
|
|
299
302
|
}
|
|
300
|
-
if (Array.isArray(accounts) && accounts[0])
|
|
301
|
-
return accounts[0];
|
|
303
|
+
if (Array.isArray(accounts) && accounts[0]) return accounts[0];
|
|
302
304
|
}
|
|
303
305
|
return null;
|
|
304
306
|
};
|
|
@@ -306,16 +308,11 @@ async function signMessage({ provider, message, walletAddress, chain } = {}) {
|
|
|
306
308
|
if (typeof resolvedProvider.signMessage === "function") {
|
|
307
309
|
const encoded = typeof msg === "string" ? new TextEncoder().encode(msg) : msg;
|
|
308
310
|
const result = await resolvedProvider.signMessage(encoded);
|
|
309
|
-
if (typeof result === "string" && result)
|
|
310
|
-
|
|
311
|
-
if (result instanceof Uint8Array)
|
|
312
|
-
|
|
313
|
-
if (result
|
|
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);
|
|
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);
|
|
319
316
|
}
|
|
320
317
|
throw new SDKError("Non-EVM signing requires provider.signMessage", "SIGNER_UNAVAILABLE");
|
|
321
318
|
}
|
|
@@ -324,16 +321,14 @@ async function signMessage({ provider, message, walletAddress, chain } = {}) {
|
|
|
324
321
|
let firstPersonalSignError = null;
|
|
325
322
|
try {
|
|
326
323
|
const sig = await resolvedProvider.request({ method: "personal_sign", params: [msg, address] });
|
|
327
|
-
if (typeof sig === "string" && sig)
|
|
328
|
-
return sig;
|
|
324
|
+
if (typeof sig === "string" && sig) return sig;
|
|
329
325
|
} catch (error) {
|
|
330
326
|
firstPersonalSignError = error;
|
|
331
327
|
}
|
|
332
328
|
let secondPersonalSignError = null;
|
|
333
329
|
try {
|
|
334
330
|
const sig = await resolvedProvider.request({ method: "personal_sign", params: [address, msg] });
|
|
335
|
-
if (typeof sig === "string" && sig)
|
|
336
|
-
return sig;
|
|
331
|
+
if (typeof sig === "string" && sig) return sig;
|
|
337
332
|
} catch (error) {
|
|
338
333
|
secondPersonalSignError = error;
|
|
339
334
|
const signatureErrorMessage = String(
|
|
@@ -344,16 +339,14 @@ async function signMessage({ provider, message, walletAddress, chain } = {}) {
|
|
|
344
339
|
try {
|
|
345
340
|
const hexMsg = toHexUtf8(msg);
|
|
346
341
|
const sig = await resolvedProvider.request({ method: "personal_sign", params: [hexMsg, address] });
|
|
347
|
-
if (typeof sig === "string" && sig)
|
|
348
|
-
return sig;
|
|
342
|
+
if (typeof sig === "string" && sig) return sig;
|
|
349
343
|
} catch {
|
|
350
344
|
}
|
|
351
345
|
}
|
|
352
346
|
}
|
|
353
347
|
try {
|
|
354
348
|
const sig = await resolvedProvider.request({ method: "eth_sign", params: [address, msg] });
|
|
355
|
-
if (typeof sig === "string" && sig)
|
|
356
|
-
return sig;
|
|
349
|
+
if (typeof sig === "string" && sig) return sig;
|
|
357
350
|
} catch {
|
|
358
351
|
}
|
|
359
352
|
if (secondPersonalSignError || firstPersonalSignError) {
|
|
@@ -366,13 +359,12 @@ async function signMessage({ provider, message, walletAddress, chain } = {}) {
|
|
|
366
359
|
}
|
|
367
360
|
if (typeof resolvedProvider.signMessage === "function") {
|
|
368
361
|
const result = await resolvedProvider.signMessage(msg);
|
|
369
|
-
if (typeof result === "string" && result)
|
|
370
|
-
return result;
|
|
362
|
+
if (typeof result === "string" && result) return result;
|
|
371
363
|
}
|
|
372
364
|
throw new SDKError("Unable to sign message with provided wallet/provider", "SIGNER_UNAVAILABLE");
|
|
373
365
|
}
|
|
374
366
|
var NEUS_CONSTANTS = {
|
|
375
|
-
|
|
367
|
+
/** Default EVM chain id for NEUS protocol signing context (`HUB_CHAIN_ID` name kept for compatibility). */
|
|
376
368
|
HUB_CHAIN_ID: 84532,
|
|
377
369
|
// Supported target chains for cross-chain propagation
|
|
378
370
|
TESTNET_CHAINS: [
|
|
@@ -402,27 +394,22 @@ var NEUS_CONSTANTS = {
|
|
|
402
394
|
};
|
|
403
395
|
|
|
404
396
|
// client.js
|
|
405
|
-
var
|
|
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
|
-
]);
|
|
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
|
+
};
|
|
426
413
|
var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
427
414
|
var validateVerifierData = (verifierId, data) => {
|
|
428
415
|
if (!data || typeof data !== "object") {
|
|
@@ -641,7 +628,10 @@ var validateVerifierData = (verifierId, data) => {
|
|
|
641
628
|
const maxBytes = 50 * 1024;
|
|
642
629
|
const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(data.content).length : String(data.content).length;
|
|
643
630
|
if (bytes > maxBytes) {
|
|
644
|
-
return {
|
|
631
|
+
return {
|
|
632
|
+
valid: false,
|
|
633
|
+
error: `content exceeds ${maxBytes} bytes limit for ai-content-moderation verifier (text)`
|
|
634
|
+
};
|
|
645
635
|
}
|
|
646
636
|
} catch {
|
|
647
637
|
}
|
|
@@ -700,20 +690,15 @@ var NeusClient = class {
|
|
|
700
690
|
if (typeof this.config.appId === "string" && this.config.appId.trim().length > 0) {
|
|
701
691
|
this.defaultHeaders["X-Neus-App"] = this.config.appId.trim();
|
|
702
692
|
}
|
|
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
693
|
if (typeof this.config.paymentSignature === "string" && this.config.paymentSignature.trim().length > 0) {
|
|
707
694
|
this.defaultHeaders["PAYMENT-SIGNATURE"] = this.config.paymentSignature.trim();
|
|
708
695
|
}
|
|
709
696
|
if (this.config.extraHeaders && typeof this.config.extraHeaders === "object") {
|
|
710
697
|
for (const [k, v] of Object.entries(this.config.extraHeaders)) {
|
|
711
|
-
if (!k || v === void 0 || v === null)
|
|
712
|
-
continue;
|
|
698
|
+
if (!k || v === void 0 || v === null) continue;
|
|
713
699
|
const key = String(k).trim();
|
|
714
700
|
const value = String(v).trim();
|
|
715
|
-
if (!key || !value)
|
|
716
|
-
continue;
|
|
701
|
+
if (!key || !value) continue;
|
|
717
702
|
this.defaultHeaders[key] = value;
|
|
718
703
|
}
|
|
719
704
|
}
|
|
@@ -726,14 +711,12 @@ var NeusClient = class {
|
|
|
726
711
|
}
|
|
727
712
|
_getHubChainId() {
|
|
728
713
|
const configured = Number(this.config?.hubChainId);
|
|
729
|
-
if (Number.isFinite(configured) && configured > 0)
|
|
730
|
-
return Math.floor(configured);
|
|
714
|
+
if (Number.isFinite(configured) && configured > 0) return Math.floor(configured);
|
|
731
715
|
return NEUS_CONSTANTS.HUB_CHAIN_ID;
|
|
732
716
|
}
|
|
733
717
|
_normalizeIdentity(value) {
|
|
734
718
|
let raw = String(value || "").trim();
|
|
735
|
-
if (!raw)
|
|
736
|
-
return "";
|
|
719
|
+
if (!raw) return "";
|
|
737
720
|
const didMatch = raw.match(/^did:pkh:([^:]+):([^:]+):(.+)$/i);
|
|
738
721
|
if (didMatch && didMatch[3]) {
|
|
739
722
|
raw = String(didMatch[3]).trim();
|
|
@@ -741,8 +724,7 @@ var NeusClient = class {
|
|
|
741
724
|
return EVM_ADDRESS_RE.test(raw) ? raw.toLowerCase() : raw;
|
|
742
725
|
}
|
|
743
726
|
_inferChainForAddress(address, explicitChain) {
|
|
744
|
-
if (typeof explicitChain === "string" && explicitChain.includes(":"))
|
|
745
|
-
return explicitChain.trim();
|
|
727
|
+
if (typeof explicitChain === "string" && explicitChain.includes(":")) return explicitChain.trim();
|
|
746
728
|
const raw = String(address || "").trim();
|
|
747
729
|
const didMatch = raw.match(/^did:pkh:([^:]+):([^:]+):(.+)$/i);
|
|
748
730
|
if (didMatch && didMatch[1] && didMatch[2]) {
|
|
@@ -781,8 +763,7 @@ var NeusClient = class {
|
|
|
781
763
|
throw new ConfigurationError("Invalid wallet provider");
|
|
782
764
|
}
|
|
783
765
|
_getDefaultBrowserWallet() {
|
|
784
|
-
if (typeof window === "undefined")
|
|
785
|
-
return null;
|
|
766
|
+
if (typeof window === "undefined") return null;
|
|
786
767
|
return window.ethereum || window.solana || window.phantom && window.phantom.solana || null;
|
|
787
768
|
}
|
|
788
769
|
async _buildPrivateGateAuth({ address, wallet, chain, signatureMethod } = {}) {
|
|
@@ -840,80 +821,66 @@ var NeusClient = class {
|
|
|
840
821
|
signatureMethod: params.signatureMethod
|
|
841
822
|
});
|
|
842
823
|
}
|
|
843
|
-
// ============================================================================
|
|
844
|
-
// CORE VERIFICATION METHODS
|
|
845
|
-
// ============================================================================
|
|
846
824
|
/**
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
*
|
|
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
|
+
*
|
|
853
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
|
|
854
839
|
* @param {Array<string>} [params.verifierIds] - Array of verifier IDs (manual path)
|
|
855
|
-
* @param {Object} [params.data] - Verification data object (manual path)
|
|
856
840
|
* @param {string} [params.walletAddress] - Wallet address that signed the request (manual path)
|
|
857
841
|
* @param {string} [params.signature] - EIP-191 signature (manual path)
|
|
858
842
|
* @param {number} [params.signedTimestamp] - Unix timestamp when signature was created (manual path)
|
|
859
|
-
* @param {number} [params.chainId] -
|
|
860
|
-
* @
|
|
861
|
-
*
|
|
862
|
-
* @
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
*
|
|
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
|
+
*
|
|
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
|
+
*
|
|
866
854
|
* @example
|
|
855
|
+
* // Manual path
|
|
867
856
|
* const proof = await client.verify({
|
|
868
857
|
* verifierIds: ['ownership-basic'],
|
|
869
|
-
* data: {
|
|
870
|
-
* content: "My content",
|
|
871
|
-
* owner: walletAddress, // or ownerAddress for nft-ownership/token-holding
|
|
872
|
-
* reference: { type: 'other', id: 'my-unique-identifier' }
|
|
873
|
-
* },
|
|
858
|
+
* data: { content: "My content", owner: walletAddress },
|
|
874
859
|
* walletAddress: '0x...',
|
|
875
860
|
* signature: '0x...',
|
|
876
861
|
* signedTimestamp: Date.now(),
|
|
877
862
|
* options: { targetChains: [421614, 11155111] }
|
|
878
863
|
* });
|
|
879
864
|
*/
|
|
880
|
-
/**
|
|
881
|
-
* Create a verification proof
|
|
882
|
-
*
|
|
883
|
-
* @param {Object} params - Verification parameters
|
|
884
|
-
* @param {string} [params.verifier] - Verifier ID (e.g., 'ownership-basic')
|
|
885
|
-
* @param {string} [params.content] - Content to verify
|
|
886
|
-
* @param {Object} [params.data] - Structured verification data
|
|
887
|
-
* @param {Object} [params.wallet] - Wallet provider
|
|
888
|
-
* @param {Object} [params.options] - Additional options
|
|
889
|
-
* @returns {Promise<Object>} Verification result with proofId (qHash is a deprecated alias)
|
|
890
|
-
*
|
|
891
|
-
* @example
|
|
892
|
-
* // Simple ownership proof
|
|
893
|
-
* const proof = await client.verify({
|
|
894
|
-
* verifier: 'ownership-basic',
|
|
895
|
-
* content: 'Hello World',
|
|
896
|
-
* wallet: window.ethereum
|
|
897
|
-
* });
|
|
898
|
-
*/
|
|
899
865
|
async verify(params) {
|
|
900
866
|
if ((!params?.signature || !params?.walletAddress) && (params?.verifier || params?.content || params?.data)) {
|
|
901
867
|
const { content, verifier = "ownership-basic", data: data2 = null, wallet = null, options: options2 = {} } = params;
|
|
902
868
|
if (verifier === "ownership-basic" && !data2 && (!content || typeof content !== "string")) {
|
|
903
869
|
throw new ValidationError("content is required and must be a string (or use data param with owner + reference)");
|
|
904
870
|
}
|
|
905
|
-
let
|
|
871
|
+
let verifierCatalog = FALLBACK_PUBLIC_VERIFIER_CATALOG;
|
|
906
872
|
try {
|
|
907
|
-
const discovered = await this.
|
|
908
|
-
if (
|
|
909
|
-
|
|
873
|
+
const discovered = await this.getVerifierCatalog();
|
|
874
|
+
if (discovered && discovered.metadata && Object.keys(discovered.metadata).length > 0) {
|
|
875
|
+
verifierCatalog = discovered.metadata;
|
|
910
876
|
}
|
|
911
877
|
} catch {
|
|
912
878
|
}
|
|
879
|
+
const validVerifiers = Object.keys(verifierCatalog);
|
|
913
880
|
if (!validVerifiers.includes(verifier)) {
|
|
914
881
|
throw new ValidationError(`Invalid verifier '${verifier}'. Must be one of: ${validVerifiers.join(", ")}.`);
|
|
915
882
|
}
|
|
916
|
-
if (
|
|
883
|
+
if (verifierCatalog?.[verifier]?.supportsDirectApi === false) {
|
|
917
884
|
const hostedCheckoutUrl = options2?.hostedCheckoutUrl || "https://neus.network/verify";
|
|
918
885
|
throw new ValidationError(
|
|
919
886
|
`${verifier} requires hosted interactive checkout. Use VerifyGate or redirect to ${hostedCheckoutUrl}.`
|
|
@@ -1054,7 +1021,10 @@ var NeusClient = class {
|
|
|
1054
1021
|
...data2?.agentLabel && { agentLabel: data2.agentLabel },
|
|
1055
1022
|
...data2?.agentType && { agentType: data2.agentType },
|
|
1056
1023
|
...data2?.description && { description: data2.description },
|
|
1057
|
-
...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 }
|
|
1058
1028
|
};
|
|
1059
1029
|
} else if (verifier === "agent-delegation") {
|
|
1060
1030
|
if (!data2?.agentWallet) {
|
|
@@ -1067,7 +1037,11 @@ var NeusClient = class {
|
|
|
1067
1037
|
...data2?.scope && { scope: data2.scope },
|
|
1068
1038
|
...data2?.permissions && { permissions: data2.permissions },
|
|
1069
1039
|
...data2?.maxSpend && { maxSpend: data2.maxSpend },
|
|
1070
|
-
...data2?.
|
|
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 }
|
|
1071
1045
|
};
|
|
1072
1046
|
} else if (verifier === "ai-content-moderation") {
|
|
1073
1047
|
if (!data2?.content) {
|
|
@@ -1127,31 +1101,24 @@ var NeusClient = class {
|
|
|
1127
1101
|
const enc = new TextEncoder();
|
|
1128
1102
|
const bytes = enc.encode(s);
|
|
1129
1103
|
let hex = "0x";
|
|
1130
|
-
for (let i = 0; i < bytes.length; i++)
|
|
1131
|
-
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");
|
|
1132
1105
|
return hex;
|
|
1133
1106
|
} catch {
|
|
1134
1107
|
let hex = "0x";
|
|
1135
|
-
for (let i = 0; i < s.length; i++)
|
|
1136
|
-
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");
|
|
1137
1109
|
return hex;
|
|
1138
1110
|
}
|
|
1139
1111
|
};
|
|
1140
1112
|
const isFarcasterWallet = (() => {
|
|
1141
|
-
if (typeof window === "undefined")
|
|
1142
|
-
return false;
|
|
1113
|
+
if (typeof window === "undefined") return false;
|
|
1143
1114
|
try {
|
|
1144
1115
|
const w = window;
|
|
1145
1116
|
const fc = w.farcaster;
|
|
1146
|
-
if (!fc || !fc.context)
|
|
1147
|
-
return false;
|
|
1117
|
+
if (!fc || !fc.context) return false;
|
|
1148
1118
|
const fcProvider = fc.provider || fc.walletProvider || fc.context && fc.context.walletProvider;
|
|
1149
|
-
if (fcProvider === provider)
|
|
1150
|
-
|
|
1151
|
-
if (w.
|
|
1152
|
-
return true;
|
|
1153
|
-
if (w.ethereum === provider && fc && fc.context)
|
|
1154
|
-
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;
|
|
1155
1122
|
} catch {
|
|
1156
1123
|
}
|
|
1157
1124
|
return false;
|
|
@@ -1170,7 +1137,8 @@ var NeusClient = class {
|
|
|
1170
1137
|
const msg = String(e && (e.message || e.reason) || e || "").toLowerCase();
|
|
1171
1138
|
const errCode = e && (e.code || e.error && e.error.code) || null;
|
|
1172
1139
|
const needsHex = /byte|bytes|invalid byte sequence|encoding|non-hex/i.test(msg);
|
|
1173
|
-
const
|
|
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");
|
|
1174
1142
|
if (methodUnsupported) {
|
|
1175
1143
|
this._log("personal_sign not supported; attempting eth_sign fallback");
|
|
1176
1144
|
try {
|
|
@@ -1179,28 +1147,23 @@ var NeusClient = class {
|
|
|
1179
1147
|
const prefix = `Ethereum Signed Message:
|
|
1180
1148
|
${bytes.length}`;
|
|
1181
1149
|
const full = new Uint8Array(prefix.length + bytes.length);
|
|
1182
|
-
for (let i = 0; i < prefix.length; i++)
|
|
1183
|
-
full[i] = prefix.charCodeAt(i);
|
|
1150
|
+
for (let i = 0; i < prefix.length; i++) full[i] = prefix.charCodeAt(i);
|
|
1184
1151
|
full.set(bytes, prefix.length);
|
|
1185
1152
|
let payloadHex = "0x";
|
|
1186
|
-
for (let i = 0; i < full.length; i++)
|
|
1187
|
-
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");
|
|
1188
1154
|
try {
|
|
1189
|
-
if (typeof window !== "undefined")
|
|
1190
|
-
window.__NEUS_ALLOW_ETH_SIGN__ = true;
|
|
1155
|
+
if (typeof window !== "undefined") window.__NEUS_ALLOW_ETH_SIGN__ = true;
|
|
1191
1156
|
} catch {
|
|
1192
1157
|
}
|
|
1193
1158
|
signature2 = await provider.request({ method: "eth_sign", params: [walletAddress2, payloadHex], neusAllowEthSign: true });
|
|
1194
1159
|
try {
|
|
1195
|
-
if (typeof window !== "undefined")
|
|
1196
|
-
delete window.__NEUS_ALLOW_ETH_SIGN__;
|
|
1160
|
+
if (typeof window !== "undefined") delete window.__NEUS_ALLOW_ETH_SIGN__;
|
|
1197
1161
|
} catch {
|
|
1198
1162
|
}
|
|
1199
1163
|
} catch (fallbackErr) {
|
|
1200
1164
|
this._log("eth_sign fallback failed", { message: fallbackErr?.message || String(fallbackErr) });
|
|
1201
1165
|
try {
|
|
1202
|
-
if (typeof window !== "undefined")
|
|
1203
|
-
delete window.__NEUS_ALLOW_ETH_SIGN__;
|
|
1166
|
+
if (typeof window !== "undefined") delete window.__NEUS_ALLOW_ETH_SIGN__;
|
|
1204
1167
|
} catch {
|
|
1205
1168
|
}
|
|
1206
1169
|
throw e;
|
|
@@ -1242,8 +1205,7 @@ ${bytes.length}`;
|
|
|
1242
1205
|
} = params;
|
|
1243
1206
|
const resolvedChainId = chainId || (chain ? null : NEUS_CONSTANTS.HUB_CHAIN_ID);
|
|
1244
1207
|
const normalizeVerifierId = (id) => {
|
|
1245
|
-
if (typeof id !== "string")
|
|
1246
|
-
return id;
|
|
1208
|
+
if (typeof id !== "string") return id;
|
|
1247
1209
|
const match = id.match(/^(.*)@\d+$/);
|
|
1248
1210
|
return match ? match[1] : id;
|
|
1249
1211
|
};
|
|
@@ -1278,10 +1240,9 @@ ${bytes.length}`;
|
|
|
1278
1240
|
// Privacy and storage options (defaults)
|
|
1279
1241
|
privacyLevel: options?.privacyLevel || "private",
|
|
1280
1242
|
publicDisplay: options?.publicDisplay || false,
|
|
1281
|
-
storeOriginalContent: options?.storeOriginalContent
|
|
1243
|
+
storeOriginalContent: typeof options?.storeOriginalContent === "boolean" ? options.storeOriginalContent : true
|
|
1282
1244
|
};
|
|
1283
|
-
if (typeof options?.enableIpfs === "boolean")
|
|
1284
|
-
optionsPayload.enableIpfs = options.enableIpfs;
|
|
1245
|
+
if (typeof options?.enableIpfs === "boolean") optionsPayload.enableIpfs = options.enableIpfs;
|
|
1285
1246
|
const requestData = {
|
|
1286
1247
|
verifierIds: normalizedVerifierIds,
|
|
1287
1248
|
data,
|
|
@@ -1299,41 +1260,37 @@ ${bytes.length}`;
|
|
|
1299
1260
|
}
|
|
1300
1261
|
return this._formatResponse(response);
|
|
1301
1262
|
}
|
|
1302
|
-
// ============================================================================
|
|
1303
|
-
// STATUS AND UTILITY METHODS
|
|
1304
|
-
// ============================================================================
|
|
1305
1263
|
/**
|
|
1306
|
-
* Get
|
|
1264
|
+
* Get proof record by proof receipt id.
|
|
1307
1265
|
*
|
|
1308
|
-
* @param {string} proofId - Proof ID (
|
|
1309
|
-
* @returns {Promise<Object>}
|
|
1266
|
+
* @param {string} proofId - Proof receipt ID (0x + 64 hex).
|
|
1267
|
+
* @returns {Promise<Object>} Proof record and verification state
|
|
1310
1268
|
*
|
|
1311
1269
|
* @example
|
|
1312
|
-
* const result = await client.
|
|
1270
|
+
* const result = await client.getProof('0x...');
|
|
1313
1271
|
* console.log('Status:', result.status);
|
|
1314
1272
|
*/
|
|
1315
|
-
async
|
|
1273
|
+
async getProof(proofId) {
|
|
1316
1274
|
if (!proofId || typeof proofId !== "string") {
|
|
1317
1275
|
throw new ValidationError("proofId is required");
|
|
1318
1276
|
}
|
|
1319
|
-
const response = await this._makeRequest("GET", `/api/v1/
|
|
1277
|
+
const response = await this._makeRequest("GET", `/api/v1/proofs/${proofId}`);
|
|
1320
1278
|
if (!response.success) {
|
|
1321
|
-
throw new ApiError(`Failed to get
|
|
1279
|
+
throw new ApiError(`Failed to get proof: ${response.error?.message || "Unknown error"}`, response.error);
|
|
1322
1280
|
}
|
|
1323
1281
|
return this._formatResponse(response);
|
|
1324
1282
|
}
|
|
1325
1283
|
/**
|
|
1326
|
-
* Get private proof
|
|
1284
|
+
* Get private proof record with wallet signature
|
|
1327
1285
|
*
|
|
1328
|
-
* @param {string} proofId - Proof ID
|
|
1286
|
+
* @param {string} proofId - Proof receipt ID.
|
|
1329
1287
|
* @param {Object} wallet - Wallet provider (window.ethereum or ethers Wallet)
|
|
1330
|
-
* @returns {Promise<Object>} Private
|
|
1288
|
+
* @returns {Promise<Object>} Private proof record and verification state
|
|
1331
1289
|
*
|
|
1332
1290
|
* @example
|
|
1333
|
-
*
|
|
1334
|
-
* const privateData = await client.getPrivateStatus(proofId, window.ethereum);
|
|
1291
|
+
* const privateData = await client.getPrivateProof(proofId, window.ethereum);
|
|
1335
1292
|
*/
|
|
1336
|
-
async
|
|
1293
|
+
async getPrivateProof(proofId, wallet = null) {
|
|
1337
1294
|
if (!proofId || typeof proofId !== "string") {
|
|
1338
1295
|
throw new ValidationError("proofId is required");
|
|
1339
1296
|
}
|
|
@@ -1347,7 +1304,7 @@ ${bytes.length}`;
|
|
|
1347
1304
|
...typeof auth.chain === "string" && auth.chain.trim() ? { "x-chain": auth.chain.trim() } : {},
|
|
1348
1305
|
...typeof auth.signatureMethod === "string" && auth.signatureMethod.trim() ? { "x-signature-method": auth.signatureMethod.trim() } : {}
|
|
1349
1306
|
};
|
|
1350
|
-
const response2 = await this._makeRequest("GET", `/api/v1/
|
|
1307
|
+
const response2 = await this._makeRequest("GET", `/api/v1/proofs/${proofId}`, null, headers);
|
|
1351
1308
|
if (!response2.success) {
|
|
1352
1309
|
throw new ApiError(
|
|
1353
1310
|
`Failed to access private proof: ${response2.error?.message || "Unauthorized"}`,
|
|
@@ -1386,7 +1343,7 @@ ${bytes.length}`;
|
|
|
1386
1343
|
}
|
|
1387
1344
|
throw new ValidationError(`Failed to sign message: ${error.message}`);
|
|
1388
1345
|
}
|
|
1389
|
-
const response = await this._makeRequest("GET", `/api/v1/
|
|
1346
|
+
const response = await this._makeRequest("GET", `/api/v1/proofs/${proofId}`, null, {
|
|
1390
1347
|
"x-wallet-address": walletAddress,
|
|
1391
1348
|
"x-signature": signature,
|
|
1392
1349
|
"x-signed-timestamp": signedTimestamp.toString(),
|
|
@@ -1419,25 +1376,37 @@ ${bytes.length}`;
|
|
|
1419
1376
|
* @returns {Promise<string[]>} Array of verifier IDs
|
|
1420
1377
|
*/
|
|
1421
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() {
|
|
1422
1387
|
const response = await this._makeRequest("GET", "/api/v1/verification/verifiers");
|
|
1423
1388
|
if (!response.success) {
|
|
1424
1389
|
throw new ApiError(`Failed to get verifiers: ${response.error?.message || "Unknown error"}`, response.error);
|
|
1425
1390
|
}
|
|
1426
|
-
return
|
|
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
|
+
};
|
|
1427
1396
|
}
|
|
1428
1397
|
/**
|
|
1429
1398
|
* POLL PROOF STATUS - Wait for verification completion
|
|
1430
|
-
*
|
|
1399
|
+
*
|
|
1431
1400
|
* Polls the verification status until it reaches a terminal state (completed or failed).
|
|
1432
1401
|
* Useful for providing real-time feedback to users during verification.
|
|
1433
|
-
*
|
|
1434
|
-
* @param {string} proofId - Proof ID to poll
|
|
1402
|
+
*
|
|
1403
|
+
* @param {string} proofId - Proof ID to poll.
|
|
1435
1404
|
* @param {Object} [options] - Polling options
|
|
1436
1405
|
* @param {number} [options.interval=5000] - Polling interval in ms
|
|
1437
1406
|
* @param {number} [options.timeout=120000] - Total timeout in ms
|
|
1438
1407
|
* @param {Function} [options.onProgress] - Progress callback function
|
|
1439
1408
|
* @returns {Promise<Object>} Final verification status
|
|
1440
|
-
*
|
|
1409
|
+
*
|
|
1441
1410
|
* @example
|
|
1442
1411
|
* const finalStatus = await client.pollProofStatus(proofId, {
|
|
1443
1412
|
* interval: 3000,
|
|
@@ -1463,7 +1432,7 @@ ${bytes.length}`;
|
|
|
1463
1432
|
let consecutiveRateLimits = 0;
|
|
1464
1433
|
while (Date.now() - startTime < timeout) {
|
|
1465
1434
|
try {
|
|
1466
|
-
const status = await this.
|
|
1435
|
+
const status = await this.getProof(proofId);
|
|
1467
1436
|
consecutiveRateLimits = 0;
|
|
1468
1437
|
if (onProgress && typeof onProgress === "function") {
|
|
1469
1438
|
onProgress(status.data || status);
|
|
@@ -1496,7 +1465,7 @@ ${bytes.length}`;
|
|
|
1496
1465
|
}
|
|
1497
1466
|
/**
|
|
1498
1467
|
* DETECT CHAIN ID - Get current wallet chain
|
|
1499
|
-
*
|
|
1468
|
+
*
|
|
1500
1469
|
* @returns {Promise<number>} Current chain ID
|
|
1501
1470
|
*/
|
|
1502
1471
|
async detectChainId() {
|
|
@@ -1527,7 +1496,6 @@ ${bytes.length}`;
|
|
|
1527
1496
|
const message = constructVerificationMessage({
|
|
1528
1497
|
walletAddress: address,
|
|
1529
1498
|
signedTimestamp,
|
|
1530
|
-
// Keep wire payload key `qHash` for backwards compatibility.
|
|
1531
1499
|
data: { action: "revoke_proof", qHash: proofId },
|
|
1532
1500
|
verifierIds: ["ownership-basic"],
|
|
1533
1501
|
...signerIsEvm ? { chainId: this._getHubChainId() } : { chain }
|
|
@@ -1546,9 +1514,8 @@ ${bytes.length}`;
|
|
|
1546
1514
|
}
|
|
1547
1515
|
throw new ValidationError(`Failed to sign revocation: ${error.message}`);
|
|
1548
1516
|
}
|
|
1549
|
-
const res = await fetch(`${this.config.apiUrl}/api/v1/proofs/${proofId}
|
|
1517
|
+
const res = await fetch(`${this.config.apiUrl}/api/v1/proofs/revoke-self/${proofId}`, {
|
|
1550
1518
|
method: "POST",
|
|
1551
|
-
// SECURITY: Do not put proof signatures into Authorization headers.
|
|
1552
1519
|
headers: { "Content-Type": "application/json" },
|
|
1553
1520
|
body: JSON.stringify({
|
|
1554
1521
|
walletAddress: address,
|
|
@@ -1563,18 +1530,16 @@ ${bytes.length}`;
|
|
|
1563
1530
|
}
|
|
1564
1531
|
return true;
|
|
1565
1532
|
}
|
|
1566
|
-
// ============================================================================
|
|
1567
|
-
// PROOFS & GATING METHODS
|
|
1568
|
-
// ============================================================================
|
|
1569
1533
|
/**
|
|
1570
1534
|
* GET PROOFS BY WALLET - Fetch proofs for a wallet address
|
|
1571
|
-
*
|
|
1535
|
+
*
|
|
1572
1536
|
* @param {string} walletAddress - Wallet identity (EVM/Solana/DID)
|
|
1573
1537
|
* @param {Object} [options] - Filter options
|
|
1574
1538
|
* @param {number} [options.limit] - Max results (default: 50; higher limits require owner access)
|
|
1575
1539
|
* @param {number} [options.offset] - Pagination offset (default: 0)
|
|
1540
|
+
* @param {string} [options.qHash] - Filter to single proof by qHash
|
|
1576
1541
|
* @returns {Promise<Object>} Proofs result
|
|
1577
|
-
*
|
|
1542
|
+
*
|
|
1578
1543
|
* @example
|
|
1579
1544
|
* const result = await client.getProofsByWallet('0x...', {
|
|
1580
1545
|
* limit: 50,
|
|
@@ -1588,14 +1553,13 @@ ${bytes.length}`;
|
|
|
1588
1553
|
const id = walletAddress.trim();
|
|
1589
1554
|
const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
|
|
1590
1555
|
const qs = [];
|
|
1591
|
-
if (options.limit)
|
|
1592
|
-
|
|
1593
|
-
if (options.
|
|
1594
|
-
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())}`);
|
|
1595
1559
|
const query = qs.length ? `?${qs.join("&")}` : "";
|
|
1596
1560
|
const response = await this._makeRequest(
|
|
1597
1561
|
"GET",
|
|
1598
|
-
`/api/v1/proofs/
|
|
1562
|
+
`/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`
|
|
1599
1563
|
);
|
|
1600
1564
|
if (!response.success) {
|
|
1601
1565
|
throw new ApiError(`Failed to get proofs: ${response.error?.message || "Unknown error"}`, response.error);
|
|
@@ -1618,6 +1582,7 @@ ${bytes.length}`;
|
|
|
1618
1582
|
* @param {Object} [options]
|
|
1619
1583
|
* @param {number} [options.limit] - Max results (server enforces caps)
|
|
1620
1584
|
* @param {number} [options.offset] - Pagination offset
|
|
1585
|
+
* @param {string} [options.qHash] - Filter to single proof by qHash
|
|
1621
1586
|
* @param {Object} [wallet] - Optional injected wallet/provider (MetaMask/ethers Wallet)
|
|
1622
1587
|
*/
|
|
1623
1588
|
async getPrivateProofsByWallet(walletAddress, options = {}, wallet = null) {
|
|
@@ -1668,12 +1633,11 @@ ${bytes.length}`;
|
|
|
1668
1633
|
throw new ValidationError(`Failed to sign message: ${error.message}`);
|
|
1669
1634
|
}
|
|
1670
1635
|
const qs = [];
|
|
1671
|
-
if (options.limit)
|
|
1672
|
-
|
|
1673
|
-
if (options.
|
|
1674
|
-
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())}`);
|
|
1675
1639
|
const query = qs.length ? `?${qs.join("&")}` : "";
|
|
1676
|
-
const response = await this._makeRequest("GET", `/api/v1/proofs/
|
|
1640
|
+
const response = await this._makeRequest("GET", `/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`, null, {
|
|
1677
1641
|
"x-wallet-address": signerWalletAddress,
|
|
1678
1642
|
"x-signature": signature,
|
|
1679
1643
|
"x-signed-timestamp": signedTimestamp.toString(),
|
|
@@ -1692,16 +1656,16 @@ ${bytes.length}`;
|
|
|
1692
1656
|
};
|
|
1693
1657
|
}
|
|
1694
1658
|
/**
|
|
1695
|
-
*
|
|
1659
|
+
* Gate check (HTTP API) — minimal eligibility response.
|
|
1696
1660
|
*
|
|
1697
1661
|
* Calls the gate endpoint and returns a **minimal** yes/no response.
|
|
1698
|
-
* By default this checks **public +
|
|
1662
|
+
* By default this checks **public + unlisted** proofs.
|
|
1699
1663
|
*
|
|
1700
1664
|
* When `includePrivate=true`, this can perform owner-signed private checks
|
|
1701
1665
|
* (no full proof payloads returned) by providing a wallet/provider.
|
|
1702
1666
|
*
|
|
1703
|
-
* Prefer this over `checkGate()`
|
|
1704
|
-
*
|
|
1667
|
+
* Prefer this over `checkGate()` when you need the smallest, most stable
|
|
1668
|
+
* response shape and do not need full proof payloads.
|
|
1705
1669
|
*
|
|
1706
1670
|
* @param {Object} params - Gate check query params
|
|
1707
1671
|
* @param {string} params.address - Wallet identity to check (EVM/Solana/DID)
|
|
@@ -1724,25 +1688,20 @@ ${bytes.length}`;
|
|
|
1724
1688
|
const qs = new URLSearchParams();
|
|
1725
1689
|
qs.set("address", address);
|
|
1726
1690
|
const setIfPresent = (key, value) => {
|
|
1727
|
-
if (value === void 0 || value === null)
|
|
1728
|
-
return;
|
|
1691
|
+
if (value === void 0 || value === null) return;
|
|
1729
1692
|
const str = typeof value === "string" ? value : String(value);
|
|
1730
|
-
if (str.length === 0)
|
|
1731
|
-
return;
|
|
1693
|
+
if (str.length === 0) return;
|
|
1732
1694
|
qs.set(key, str);
|
|
1733
1695
|
};
|
|
1734
1696
|
const setBoolIfPresent = (key, value) => {
|
|
1735
|
-
if (value === void 0 || value === null)
|
|
1736
|
-
return;
|
|
1697
|
+
if (value === void 0 || value === null) return;
|
|
1737
1698
|
qs.set(key, value ? "true" : "false");
|
|
1738
1699
|
};
|
|
1739
1700
|
const setCsvIfPresent = (key, value) => {
|
|
1740
|
-
if (value === void 0 || value === null)
|
|
1741
|
-
return;
|
|
1701
|
+
if (value === void 0 || value === null) return;
|
|
1742
1702
|
if (Array.isArray(value)) {
|
|
1743
1703
|
const items = value.map((v) => String(v).trim()).filter(Boolean);
|
|
1744
|
-
if (items.length)
|
|
1745
|
-
qs.set(key, items.join(","));
|
|
1704
|
+
if (items.length) qs.set(key, items.join(","));
|
|
1746
1705
|
return;
|
|
1747
1706
|
}
|
|
1748
1707
|
setIfPresent(key, value);
|
|
@@ -1768,6 +1727,8 @@ ${bytes.length}`;
|
|
|
1768
1727
|
setIfPresent("domain", params.domain);
|
|
1769
1728
|
setIfPresent("minBalance", params.minBalance);
|
|
1770
1729
|
setIfPresent("provider", params.provider);
|
|
1730
|
+
setIfPresent("handle", params.handle);
|
|
1731
|
+
setIfPresent("namespace", params.namespace);
|
|
1771
1732
|
setIfPresent("ownerAddress", params.ownerAddress);
|
|
1772
1733
|
setIfPresent("riskLevel", params.riskLevel);
|
|
1773
1734
|
setBoolIfPresent("sanctioned", params.sanctioned);
|
|
@@ -1815,11 +1776,17 @@ ${bytes.length}`;
|
|
|
1815
1776
|
return response;
|
|
1816
1777
|
}
|
|
1817
1778
|
/**
|
|
1818
|
-
* CHECK GATE
|
|
1819
|
-
*
|
|
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
|
+
*
|
|
1820
1787
|
* Gate-first verification: checks if wallet has valid proofs satisfying requirements.
|
|
1821
1788
|
* Returns which requirements are missing/expired.
|
|
1822
|
-
*
|
|
1789
|
+
*
|
|
1823
1790
|
* @param {Object} params - Gate check parameters
|
|
1824
1791
|
* @param {string} params.walletAddress - Target wallet
|
|
1825
1792
|
* @param {Array<Object>} params.requirements - Array of gate requirements
|
|
@@ -1837,7 +1804,7 @@ ${bytes.length}`;
|
|
|
1837
1804
|
* Note: contentHash matching uses approximation in SDK; for exact SHA-256 matching, use backend API
|
|
1838
1805
|
* @param {Array} [params.proofs] - Pre-fetched proofs (skip API call)
|
|
1839
1806
|
* @returns {Promise<Object>} Gate result with satisfied, missing, existing
|
|
1840
|
-
*
|
|
1807
|
+
*
|
|
1841
1808
|
* @example
|
|
1842
1809
|
* // Basic gate check
|
|
1843
1810
|
* const result = await client.checkGate({
|
|
@@ -1870,20 +1837,20 @@ ${bytes.length}`;
|
|
|
1870
1837
|
const verifier = verifiedVerifiers.find(
|
|
1871
1838
|
(v) => v.verifierId === verifierId && v.verified === true
|
|
1872
1839
|
);
|
|
1873
|
-
if (!verifier)
|
|
1874
|
-
|
|
1875
|
-
if (proof.revokedAt)
|
|
1876
|
-
return false;
|
|
1840
|
+
if (!verifier) return false;
|
|
1841
|
+
if (proof.revokedAt) return false;
|
|
1877
1842
|
if (maxAgeMs) {
|
|
1878
1843
|
const proofTimestamp = proof.createdAt || proof.signedTimestamp || 0;
|
|
1879
1844
|
const proofAge = now - proofTimestamp;
|
|
1880
|
-
if (proofAge > maxAgeMs)
|
|
1881
|
-
return false;
|
|
1845
|
+
if (proofAge > maxAgeMs) return false;
|
|
1882
1846
|
}
|
|
1883
1847
|
if (match && typeof match === "object") {
|
|
1884
1848
|
const data = verifier.data || {};
|
|
1885
1849
|
const input = data.input || {};
|
|
1886
|
-
|
|
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)) {
|
|
1887
1854
|
let actualValue = null;
|
|
1888
1855
|
if (key.includes(".")) {
|
|
1889
1856
|
const parts = key.split(".");
|
|
@@ -1908,6 +1875,7 @@ ${bytes.length}`;
|
|
|
1908
1875
|
actualValue = data.reference?.id || data.content;
|
|
1909
1876
|
}
|
|
1910
1877
|
if (actualValue === void 0) {
|
|
1878
|
+
const claims = data.claims || {};
|
|
1911
1879
|
if (key === "contractAddress") {
|
|
1912
1880
|
actualValue = input.contractAddress || data.contractAddress;
|
|
1913
1881
|
} else if (key === "tokenId") {
|
|
@@ -1926,6 +1894,22 @@ ${bytes.length}`;
|
|
|
1926
1894
|
actualValue = data.verificationMethod;
|
|
1927
1895
|
} else if (key === "domain") {
|
|
1928
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;
|
|
1929
1913
|
}
|
|
1930
1914
|
}
|
|
1931
1915
|
if (key === "contentHash" && actualValue === void 0 && data.content) {
|
|
@@ -1937,7 +1921,7 @@ ${bytes.length}`;
|
|
|
1937
1921
|
hash = (hash << 5) - hash + char;
|
|
1938
1922
|
hash = hash & hash;
|
|
1939
1923
|
}
|
|
1940
|
-
actualValue =
|
|
1924
|
+
actualValue = `0x${Math.abs(hash).toString(16).padStart(64, "0").substring(0, 66)}`;
|
|
1941
1925
|
} catch {
|
|
1942
1926
|
actualValue = String(data.content);
|
|
1943
1927
|
}
|
|
@@ -1947,11 +1931,16 @@ ${bytes.length}`;
|
|
|
1947
1931
|
if (actualValue === void 0 || actualValue === null) {
|
|
1948
1932
|
return false;
|
|
1949
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
|
+
}
|
|
1950
1940
|
if (key === "chainId" || key === "tokenId" && (typeof actualValue === "number" || !isNaN(Number(actualValue)))) {
|
|
1951
1941
|
normalizedActual = Number(actualValue);
|
|
1952
1942
|
normalizedExpected = Number(expected);
|
|
1953
|
-
if (isNaN(normalizedActual) || isNaN(normalizedExpected))
|
|
1954
|
-
return false;
|
|
1943
|
+
if (isNaN(normalizedActual) || isNaN(normalizedExpected)) return false;
|
|
1955
1944
|
} else if (typeof actualValue === "string" && (actualValue.startsWith("0x") || actualValue.length > 20)) {
|
|
1956
1945
|
normalizedActual = actualValue.toLowerCase();
|
|
1957
1946
|
normalizedExpected = typeof expected === "string" ? String(expected).toLowerCase() : expected;
|
|
@@ -1980,13 +1969,6 @@ ${bytes.length}`;
|
|
|
1980
1969
|
allProofs: proofs
|
|
1981
1970
|
};
|
|
1982
1971
|
}
|
|
1983
|
-
// ============================================================================
|
|
1984
|
-
// PRIVATE UTILITY METHODS
|
|
1985
|
-
// ============================================================================
|
|
1986
|
-
/**
|
|
1987
|
-
* Get connected wallet address
|
|
1988
|
-
* @private
|
|
1989
|
-
*/
|
|
1990
1972
|
async _getWalletAddress() {
|
|
1991
1973
|
if (typeof window === "undefined" || !window.ethereum) {
|
|
1992
1974
|
throw new ConfigurationError("No Web3 wallet detected");
|
|
@@ -1997,10 +1979,6 @@ ${bytes.length}`;
|
|
|
1997
1979
|
}
|
|
1998
1980
|
return accounts[0];
|
|
1999
1981
|
}
|
|
2000
|
-
/**
|
|
2001
|
-
* Make HTTP request to API
|
|
2002
|
-
* @private
|
|
2003
|
-
*/
|
|
2004
1982
|
async _makeRequest(method, endpoint, data = null, headersOverride = null) {
|
|
2005
1983
|
const url = `${this.baseUrl}${endpoint}`;
|
|
2006
1984
|
const controller = new AbortController();
|
|
@@ -2038,10 +2016,6 @@ ${bytes.length}`;
|
|
|
2038
2016
|
throw new NetworkError(`Network error: ${error.message}`);
|
|
2039
2017
|
}
|
|
2040
2018
|
}
|
|
2041
|
-
/**
|
|
2042
|
-
* Format API response for consistent structure
|
|
2043
|
-
* @private
|
|
2044
|
-
*/
|
|
2045
2019
|
_formatResponse(response) {
|
|
2046
2020
|
const proofId = response?.data?.proofId || response?.proofId || response?.data?.resource?.proofId || response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || response?.data?.id;
|
|
2047
2021
|
const qHash = response?.data?.qHash || response?.qHash || response?.data?.resource?.qHash || proofId || response?.data?.id;
|
|
@@ -2056,13 +2030,9 @@ ${bytes.length}`;
|
|
|
2056
2030
|
data: response.data,
|
|
2057
2031
|
message: response.message,
|
|
2058
2032
|
timestamp: Date.now(),
|
|
2059
|
-
|
|
2033
|
+
proofUrl: finalProofId ? `${this.baseUrl}/api/v1/proofs/${finalProofId}` : null
|
|
2060
2034
|
};
|
|
2061
2035
|
}
|
|
2062
|
-
/**
|
|
2063
|
-
* Check if status is terminal (completed or failed)
|
|
2064
|
-
* @private
|
|
2065
|
-
*/
|
|
2066
2036
|
_isTerminalStatus(status) {
|
|
2067
2037
|
const terminalStates = [
|
|
2068
2038
|
"verified",
|
|
@@ -2093,5 +2063,6 @@ ${bytes.length}`;
|
|
|
2093
2063
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2094
2064
|
0 && (module.exports = {
|
|
2095
2065
|
NeusClient,
|
|
2066
|
+
PORTABLE_PROOF_SIGNER_HEADER,
|
|
2096
2067
|
constructVerificationMessage
|
|
2097
2068
|
});
|