@billionsnetwork/x402-human-proof 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/node/cjs/declare.cjs +42 -0
  2. package/dist/node/cjs/declare.cjs.map +1 -0
  3. package/dist/node/cjs/hooks.cjs +198 -0
  4. package/dist/node/cjs/hooks.cjs.map +1 -0
  5. package/dist/node/cjs/index.cjs +493 -0
  6. package/dist/node/cjs/index.cjs.map +1 -0
  7. package/dist/node/cjs/server.cjs +469 -0
  8. package/dist/node/cjs/server.cjs.map +1 -0
  9. package/dist/node/cjs/types.cjs +19 -0
  10. package/dist/node/cjs/types.cjs.map +1 -0
  11. package/dist/node/cjs/utils.cjs +184 -0
  12. package/dist/node/cjs/utils.cjs.map +1 -0
  13. package/dist/node/cjs/verifier.cjs +102 -0
  14. package/dist/node/cjs/verifier.cjs.map +1 -0
  15. package/dist/node/esm/declare.js +15 -0
  16. package/dist/node/esm/declare.js.map +1 -0
  17. package/dist/node/esm/hooks.js +160 -0
  18. package/dist/node/esm/hooks.js.map +1 -0
  19. package/dist/node/esm/index.js +445 -0
  20. package/dist/node/esm/index.js.map +1 -0
  21. package/dist/node/esm/server.js +427 -0
  22. package/dist/node/esm/server.js.map +1 -0
  23. package/dist/node/esm/types.js +1 -0
  24. package/dist/node/esm/types.js.map +1 -0
  25. package/dist/node/esm/utils.js +145 -0
  26. package/dist/node/esm/utils.js.map +1 -0
  27. package/dist/node/esm/verifier.js +74 -0
  28. package/dist/node/esm/verifier.js.map +1 -0
  29. package/dist/types/declare.d.ts +17 -0
  30. package/dist/types/declare.d.ts.map +1 -0
  31. package/dist/types/hooks.d.ts +36 -0
  32. package/dist/types/hooks.d.ts.map +1 -0
  33. package/dist/types/index.d.ts +9 -0
  34. package/dist/types/index.d.ts.map +1 -0
  35. package/dist/types/server.d.ts +87 -0
  36. package/dist/types/server.d.ts.map +1 -0
  37. package/dist/types/types.d.ts +99 -0
  38. package/dist/types/types.d.ts.map +1 -0
  39. package/dist/types/utils.d.ts +40 -0
  40. package/dist/types/utils.d.ts.map +1 -0
  41. package/dist/types/verifier.d.ts +13 -0
  42. package/dist/types/verifier.d.ts.map +1 -0
  43. package/package.json +36 -0
@@ -0,0 +1,427 @@
1
+ // src/utils.ts
2
+ import { recoverPublicKey, hashMessage, recoverMessageAddress } from "viem";
3
+ import bs58 from "bs58";
4
+ import nacl from "tweetnacl";
5
+ function formatResources(resources) {
6
+ if (!resources?.length) return "";
7
+ const lines = resources.map((resource) => `- ${resource}`).join("\n");
8
+ return `
9
+ Resources:
10
+ ${lines}`;
11
+ }
12
+ function formatSIWEMessage(payload) {
13
+ const statementBlock = payload.statement ? `
14
+ ${payload.statement}
15
+ ` : "\n";
16
+ const expirationLine = payload.expirationTime ? `
17
+ Expiration Time: ${payload.expirationTime}` : "";
18
+ return `${payload.domain} wants you to sign in with your Ethereum account:
19
+ ${payload.address}${statementBlock}
20
+ URI: ${payload.uri ?? ""}
21
+ Version: ${payload.version}
22
+ Chain ID: ${payload.chainId}
23
+ Nonce: ${payload.nonce}
24
+ Issued At: ${payload.issuedAt}${expirationLine}${formatResources(payload.resources)}`;
25
+ }
26
+ function formatSIWSMessage(payload) {
27
+ const statementBlock = payload.statement ? `
28
+ ${payload.statement}
29
+ ` : "\n";
30
+ const expirationLine = payload.expirationTime ? `
31
+ Expiration Time: ${payload.expirationTime}` : "";
32
+ return `${payload.domain} wants you to sign in with your Solana account:
33
+ ${payload.address}${statementBlock}
34
+ URI: ${payload.uri ?? ""}
35
+ Version: ${payload.version}
36
+ Chain ID: ${payload.chainId}
37
+ Nonce: ${payload.nonce}
38
+ Issued At: ${payload.issuedAt}${expirationLine}${formatResources(payload.resources)}`;
39
+ }
40
+ function decodeBase58(input) {
41
+ return bs58.decode(input);
42
+ }
43
+ function buildHumanProofSchema() {
44
+ return {
45
+ type: "object",
46
+ required: ["domain", "version", "nonce", "issuedAt", "chainId", "type", "address", "signature", "requiredAttestations"],
47
+ properties: {
48
+ domain: { type: "string" },
49
+ uri: { type: "string" },
50
+ version: { type: "string" },
51
+ nonce: { type: "string" },
52
+ issuedAt: { type: "string" },
53
+ expirationTime: { type: "string" },
54
+ statement: { type: "string" },
55
+ resources: { type: "array", items: { type: "string" } },
56
+ chainId: { type: "string" },
57
+ type: { enum: ["eip191", "ed25519"] },
58
+ address: { type: "string" },
59
+ signature: { type: "string" },
60
+ requiredAttestations: { type: "array", items: { type: "string" } }
61
+ },
62
+ additionalProperties: true
63
+ };
64
+ }
65
+ function parseHumanProofHeader(headerValue) {
66
+ const parsed = JSON.parse(headerValue);
67
+ if (!parsed || typeof parsed !== "object") {
68
+ throw new Error("Invalid header payload");
69
+ }
70
+ const payload = parsed;
71
+ if (!payload.address || !payload.signature || !payload.chainId || !payload.type) {
72
+ throw new Error("Missing required fields in header payload");
73
+ }
74
+ return payload;
75
+ }
76
+ async function validateHumanProofMessage(payload, resource) {
77
+ if (!payload.domain || !payload.version || !payload.nonce || !payload.issuedAt) {
78
+ return { valid: false, error: "invalid_message" };
79
+ }
80
+ if (!payload.uri || payload.uri !== resource) {
81
+ return { valid: false, error: "resource_mismatch" };
82
+ }
83
+ const issuedAtMillis = Date.parse(payload.issuedAt);
84
+ if (Number.isNaN(issuedAtMillis)) {
85
+ return { valid: false, error: "invalid_issued_at" };
86
+ }
87
+ if (payload.expirationTime) {
88
+ const expirationMillis = Date.parse(payload.expirationTime);
89
+ if (Number.isNaN(expirationMillis)) {
90
+ return { valid: false, error: "invalid_expiration_time" };
91
+ }
92
+ if (expirationMillis < Date.now()) {
93
+ return { valid: false, error: "message_expired" };
94
+ }
95
+ }
96
+ return { valid: true };
97
+ }
98
+ async function verifyHumanProofSignature(payload) {
99
+ if (payload.type === "eip191") {
100
+ if (!payload.signature.startsWith("0x")) {
101
+ return { valid: false, error: "invalid_signature" };
102
+ }
103
+ try {
104
+ const message = formatSIWEMessage(payload);
105
+ const signature = payload.signature;
106
+ const recovered = await recoverMessageAddress({ message, signature });
107
+ if (recovered.toLowerCase() !== payload.address.toLowerCase()) {
108
+ return { valid: false, error: "invalid_signature" };
109
+ }
110
+ const publicKey = await recoverPublicKey({ hash: hashMessage(message), signature });
111
+ return { valid: true, address: payload.address, pubKey: publicKey };
112
+ } catch {
113
+ return { valid: false, error: "invalid_signature" };
114
+ }
115
+ }
116
+ if (payload.type === "ed25519") {
117
+ try {
118
+ const message = new TextEncoder().encode(formatSIWSMessage(payload));
119
+ const publicKey = decodeBase58(payload.address);
120
+ const signature = decodeBase58(payload.signature);
121
+ const valid = nacl.sign.detached.verify(message, signature, publicKey);
122
+ if (!valid) return { valid: false, error: "invalid_signature" };
123
+ return { valid: true, address: payload.address, pubKey: bs58.encode(publicKey) };
124
+ } catch {
125
+ return { valid: false, error: "invalid_signature" };
126
+ }
127
+ }
128
+ return { valid: false, error: "invalid_signature_type" };
129
+ }
130
+
131
+ // src/server.ts
132
+ import { randomBytes } from "crypto";
133
+
134
+ // src/declare.ts
135
+ var HUMAN_PROOF = "human-proof";
136
+ function getSignatureTypes(network) {
137
+ if (network.startsWith("solana:")) return ["ed25519"];
138
+ return ["eip191"];
139
+ }
140
+
141
+ // src/hooks.ts
142
+ import { Blockchain, buildDIDType, DidMethod, NetworkId } from "@iden3/js-iden3-core";
143
+ import { buildDIDFromEthAddress } from "@0xpolygonid/js-sdk";
144
+ async function verifyHumanProofRequest(verifier, context) {
145
+ const { resource, humanProofHeader, onEvent } = context;
146
+ if (!humanProofHeader) {
147
+ return { allowed: false, reason: "missing_header" };
148
+ }
149
+ let payload;
150
+ try {
151
+ payload = parseHumanProofHeader(humanProofHeader);
152
+ } catch {
153
+ return { allowed: false, reason: "invalid_header" };
154
+ }
155
+ const validation = await validateHumanProofMessage(payload, resource);
156
+ if (!validation.valid) {
157
+ return { allowed: false, reason: validation.error ?? "invalid_message" };
158
+ }
159
+ const sigResult = await verifyHumanProofSignature(payload);
160
+ if (!sigResult.valid || !sigResult.address) {
161
+ return { allowed: false, reason: sigResult.error ?? "invalid_signature" };
162
+ }
163
+ const didType = buildDIDType(DidMethod.Iden3, Blockchain.Billions, NetworkId.Main);
164
+ const didString = buildDIDFromEthAddress(didType, sigResult.address).string();
165
+ const resolution = await verifier.lookupHuman(didString, payload.chainId);
166
+ if (!resolution) {
167
+ await onEvent?.({
168
+ type: "human_not_registered",
169
+ resource,
170
+ did: didString
171
+ });
172
+ return { allowed: false, reason: "not_registered", did: didString };
173
+ }
174
+ await onEvent?.({
175
+ type: "human_verified",
176
+ resource,
177
+ humanId: resolution.humanId,
178
+ verifiedAt: resolution.verifiedAt,
179
+ attestationId: resolution.attestationId,
180
+ humanDid: resolution.humanDid,
181
+ humanAddress: resolution.humanAddress,
182
+ agentDid: resolution.agentDid,
183
+ agentAddress: resolution.agentAddress
184
+ });
185
+ return { allowed: true, resolution, did: didString, chainId: payload.chainId };
186
+ }
187
+
188
+ // src/verifier.ts
189
+ var DEFAULT_ATTESTATIONS_API_BASE_URL = "https://attestations-api.billions.network/api/v1/attestations";
190
+ var DEFAULT_NULLIFIER_API_BASE_URL = "https://attestations-api.billions.network/api/v1/nullifier";
191
+ var DEFAULT_AGENT_OWNERSHIP_SCHEMA = "0xca354bee6dc5eded165461d15ccb13aceb6f77ebbb1fd3fe45aca686097f2911";
192
+ async function fetchExplorerPage(baseUrl, schema, recipientDid) {
193
+ const url = new URL(baseUrl);
194
+ url.searchParams.set("schemaId", schema);
195
+ url.searchParams.set("recipientDid", recipientDid);
196
+ const response = await fetch(url.toString());
197
+ if (!response.ok) {
198
+ throw new Error(`Attestations explorer lookup failed (${response.status})`);
199
+ }
200
+ const json = await response.json();
201
+ return json;
202
+ }
203
+ async function fetchNullifier(nullifierBaseUrl, userId) {
204
+ const url = new URL(nullifierBaseUrl);
205
+ url.searchParams.set("userId", userId);
206
+ const response = await fetch(url.toString());
207
+ if (!response.ok) {
208
+ throw new Error(`Nullifier lookup failed (${response.status})`);
209
+ }
210
+ const json = await response.json();
211
+ return json.nullifier ?? null;
212
+ }
213
+ async function lookupHumanFromExplorer(did, options) {
214
+ const payload = await fetchExplorerPage(
215
+ options.baseUrl,
216
+ options.schema,
217
+ did
218
+ );
219
+ const sorted = (payload.data ?? []).sort((a, b) => (b.creationTime ?? 0) - (a.creationTime ?? 0));
220
+ const match = sorted[0];
221
+ if (match?.fromId) {
222
+ const nullifier = await fetchNullifier(options.nullifierBaseUrl, match.fromId);
223
+ if (nullifier) {
224
+ return {
225
+ humanId: nullifier,
226
+ verifiedAt: new Date(match.creationTime * 1e3).toISOString(),
227
+ attestationId: match.id,
228
+ humanDid: match.fromDid,
229
+ humanAddress: match.fromEthereumAddress,
230
+ agentDid: match.toDid,
231
+ agentAddress: match.toEthereumAddress
232
+ };
233
+ }
234
+ }
235
+ return null;
236
+ }
237
+ function createPoUVerifier(options = {}) {
238
+ const baseUrl = options.attestationsApiBaseUrl ?? DEFAULT_ATTESTATIONS_API_BASE_URL;
239
+ const nullifierBaseUrl = options.nullifierApiBaseUrl ?? DEFAULT_NULLIFIER_API_BASE_URL;
240
+ const ownershipSchema = options.agentOwnershipSchema ?? DEFAULT_AGENT_OWNERSHIP_SCHEMA;
241
+ return {
242
+ ownershipSchema,
243
+ async lookupHuman(did, _chainId) {
244
+ return lookupHumanFromExplorer(did, { baseUrl, schema: ownershipSchema, nullifierBaseUrl });
245
+ },
246
+ async hasAttestation(did, _chainId, schema) {
247
+ if (options.hasAttestation) {
248
+ return options.hasAttestation(did, _chainId, schema);
249
+ }
250
+ const payload = await fetchExplorerPage(baseUrl, schema, did);
251
+ return (payload.data ?? []).length > 0;
252
+ }
253
+ };
254
+ }
255
+
256
+ // src/server.ts
257
+ function createHumanProofExtension(options = {}) {
258
+ const ownershipSchema = options.agentOwnershipSchema ?? DEFAULT_AGENT_OWNERSHIP_SCHEMA;
259
+ return {
260
+ key: HUMAN_PROOF,
261
+ enrichPaymentRequiredResponse: async (declaration, context) => {
262
+ const decl = declaration;
263
+ const opts = decl._options ?? {};
264
+ const resourceUri = opts.resourceUri ?? context.resourceInfo.url;
265
+ let domain = opts.domain;
266
+ if (!domain) {
267
+ try {
268
+ domain = new URL(resourceUri).hostname;
269
+ } catch {
270
+ throw new Error(
271
+ `Cannot derive domain from resourceUri "${resourceUri}". Set opts.domain explicitly in declareHumanProofExtension.`
272
+ );
273
+ }
274
+ }
275
+ let networks;
276
+ if (opts.network) {
277
+ networks = Array.isArray(opts.network) ? opts.network : [opts.network];
278
+ } else {
279
+ networks = [...new Set(context.requirements.map((r) => r.network))];
280
+ }
281
+ const nonce = randomBytes(16).toString("hex");
282
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
283
+ const expirationSeconds = opts.expirationSeconds;
284
+ const expirationTime = expirationSeconds !== void 0 ? new Date(Date.now() + expirationSeconds * 1e3).toISOString() : void 0;
285
+ const requiredAttestations = [ownershipSchema];
286
+ const info = {
287
+ domain,
288
+ uri: resourceUri,
289
+ version: opts.version ?? "1",
290
+ nonce,
291
+ issuedAt,
292
+ requiredAttestations
293
+ };
294
+ if (resourceUri) info.resources = [resourceUri];
295
+ if (expirationTime) info.expirationTime = expirationTime;
296
+ if (opts.statement) info.statement = opts.statement;
297
+ const supportedChains = networks.filter((network) => network.startsWith("eip155:")).flatMap((network) => getSignatureTypes(network).map((type) => ({ chainId: network, type })));
298
+ const result = {
299
+ info,
300
+ supportedChains,
301
+ schema: buildHumanProofSchema()
302
+ };
303
+ return result;
304
+ }
305
+ };
306
+ }
307
+ function createVerifyHumanProofHook(options = {}) {
308
+ const { onEvent, storage } = options;
309
+ const verifier = options.verifier ?? createPoUVerifier(options.verifierOptions ?? {});
310
+ return async (context) => {
311
+ const humanProofHeader = context.paymentPayload.extensions?.[HUMAN_PROOF];
312
+ const resource = context.paymentPayload.resource?.url ?? "";
313
+ const requiredAttestations = context.requirements?.extra?.requiredAttestations;
314
+ if (!requiredAttestations?.length) {
315
+ return;
316
+ }
317
+ const result = await verifyHumanProofRequest(verifier, {
318
+ resource,
319
+ humanProofHeader: humanProofHeader ?? null,
320
+ ...onEvent ? { onEvent } : {}
321
+ });
322
+ if (!result.allowed) {
323
+ return { abort: true, reason: result.reason };
324
+ }
325
+ const extraSchemas = requiredAttestations?.filter((s) => s !== verifier.ownershipSchema);
326
+ if (extraSchemas?.length) {
327
+ if (!verifier.hasAttestation) {
328
+ return { abort: true, reason: "verifier_cannot_check_attestations" };
329
+ }
330
+ for (const schema of extraSchemas) {
331
+ const has = await verifier.hasAttestation(result.did, result.chainId, schema);
332
+ if (!has) {
333
+ return { abort: true, reason: "missing_required_attestation" };
334
+ }
335
+ }
336
+ }
337
+ const maxUseRaw = context.requirements?.extra?.maxUse;
338
+ if (maxUseRaw === void 0) return;
339
+ if (typeof maxUseRaw !== "number" || !Number.isFinite(maxUseRaw) || !Number.isInteger(maxUseRaw) || maxUseRaw < 1) {
340
+ return { abort: true, reason: "invalid_max_use_value" };
341
+ }
342
+ const maxUse = maxUseRaw;
343
+ if (!storage) {
344
+ return { abort: true, reason: "misconfigured_max_use_storage" };
345
+ }
346
+ const scopeRaw = context.requirements?.extra?.scope;
347
+ const scope = typeof scopeRaw === "string" && scopeRaw ? scopeRaw : resource;
348
+ const count = await storage.incrementIfBelow(result.resolution.humanId, maxUse, scope);
349
+ if (count === null) {
350
+ await onEvent?.({ type: "max_use_exceeded", resource, humanId: result.resolution.humanId, maxUse });
351
+ return { abort: true, reason: "max_use_exceeded" };
352
+ }
353
+ };
354
+ }
355
+ async function resolveUsageContext(context, verifier) {
356
+ const requiredAttestations = context.requirements?.extra?.requiredAttestations;
357
+ if (!requiredAttestations?.length) return null;
358
+ const maxUseRaw = context.requirements?.extra?.maxUse;
359
+ if (typeof maxUseRaw !== "number" || !Number.isFinite(maxUseRaw) || !Number.isInteger(maxUseRaw) || maxUseRaw < 1) {
360
+ return null;
361
+ }
362
+ const humanProofHeader = context.paymentPayload.extensions?.[HUMAN_PROOF];
363
+ const resource = context.paymentPayload.resource?.url ?? "";
364
+ const result = await verifyHumanProofRequest(verifier, {
365
+ resource,
366
+ humanProofHeader: humanProofHeader ?? null
367
+ });
368
+ if (!result.allowed) return null;
369
+ const scopeRaw = context.requirements?.extra?.scope;
370
+ const scope = typeof scopeRaw === "string" && scopeRaw ? scopeRaw : resource;
371
+ return { humanId: result.resolution.humanId, scope };
372
+ }
373
+ function createAfterVerifyFailureRollbackHook(options) {
374
+ const { storage, verifier } = options;
375
+ return async (context) => {
376
+ if (context.result.isValid !== false) return;
377
+ const resolved = await resolveUsageContext(context, verifier);
378
+ if (!resolved) return;
379
+ await storage.decrementIfAboveZero(resolved.humanId, resolved.scope);
380
+ };
381
+ }
382
+ function createVerifyFailureRollbackHook(options) {
383
+ const { storage, verifier } = options;
384
+ return async (context) => {
385
+ const resolved = await resolveUsageContext(context, verifier);
386
+ if (!resolved) return;
387
+ await storage.decrementIfAboveZero(resolved.humanId, resolved.scope);
388
+ };
389
+ }
390
+ function createSettleFailureRollbackHook(options) {
391
+ const { storage, verifier } = options;
392
+ return async (context) => {
393
+ const resolved = await resolveUsageContext(context, verifier);
394
+ if (!resolved) return;
395
+ await storage.decrementIfAboveZero(resolved.humanId, resolved.scope);
396
+ };
397
+ }
398
+ function configureHumanProofServer(server, options = {}) {
399
+ const {
400
+ extensionOptions,
401
+ verifier,
402
+ verifierOptions,
403
+ onEvent,
404
+ storage
405
+ } = options;
406
+ const resolvedVerifier = verifier ?? createPoUVerifier(verifierOptions ?? {});
407
+ server.registerExtension(createHumanProofExtension(extensionOptions));
408
+ server.onBeforeVerify(createVerifyHumanProofHook({
409
+ verifier: resolvedVerifier,
410
+ ...onEvent ? { onEvent } : {},
411
+ ...storage ? { storage } : {}
412
+ }));
413
+ if (storage) {
414
+ server.onAfterVerify(createAfterVerifyFailureRollbackHook({ storage, verifier: resolvedVerifier }));
415
+ server.onVerifyFailure(createVerifyFailureRollbackHook({ storage, verifier: resolvedVerifier }));
416
+ server.onSettleFailure(createSettleFailureRollbackHook({ storage, verifier: resolvedVerifier }));
417
+ }
418
+ }
419
+ export {
420
+ configureHumanProofServer,
421
+ createAfterVerifyFailureRollbackHook,
422
+ createHumanProofExtension,
423
+ createSettleFailureRollbackHook,
424
+ createVerifyFailureRollbackHook,
425
+ createVerifyHumanProofHook
426
+ };
427
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils.ts","../../../src/server.ts","../../../src/declare.ts","../../../src/hooks.ts","../../../src/verifier.ts"],"sourcesContent":["import { recoverPublicKey, hashMessage, recoverMessageAddress } from 'viem'\nimport bs58 from 'bs58'\nimport nacl from 'tweetnacl'\nimport type { PaywallProvider } from '@x402/core/server'\nimport type { PaymentRequired } from '@x402/core/types'\n\nexport type SignatureType = 'eip191' | 'ed25519'\n\nexport type SupportedChain = {\n chainId: string\n type: SignatureType\n}\n\nexport type HumanProofExtensionInfo = {\n domain: string\n uri?: string\n version: string\n nonce: string\n issuedAt: string\n expirationTime?: string\n statement?: string\n resources?: string[]\n requiredAttestations: string[]\n}\n\nexport type HumanProofExtensionPayload = HumanProofExtensionInfo & {\n chainId: string\n type: SignatureType\n address: string\n signature: string\n}\n\ntype ValidationResult = {\n valid: boolean\n error?: string\n}\n\ntype SignatureVerificationResult = {\n valid: boolean\n address?: string\n pubKey?: string\n error?: string\n}\n\nfunction formatResources(resources?: string[]): string {\n if (!resources?.length) return ''\n const lines = resources.map(resource => `- ${resource}`).join('\\n')\n return `\\nResources:\\n${lines}`\n}\n\nfunction formatSIWEMessage(payload: HumanProofExtensionPayload): string {\n const statementBlock = payload.statement ? `\\n${payload.statement}\\n` : '\\n'\n const expirationLine = payload.expirationTime ? `\\nExpiration Time: ${payload.expirationTime}` : ''\n\n return (\n `${payload.domain} wants you to sign in with your Ethereum account:\\n` +\n `${payload.address}${statementBlock}\\n` +\n `URI: ${payload.uri ?? ''}\\n` +\n `Version: ${payload.version}\\n` +\n `Chain ID: ${payload.chainId}\\n` +\n `Nonce: ${payload.nonce}\\n` +\n `Issued At: ${payload.issuedAt}` +\n `${expirationLine}` +\n `${formatResources(payload.resources)}`\n )\n}\n\nfunction formatSIWSMessage(payload: HumanProofExtensionPayload): string {\n const statementBlock = payload.statement ? `\\n${payload.statement}\\n` : '\\n'\n const expirationLine = payload.expirationTime ? `\\nExpiration Time: ${payload.expirationTime}` : ''\n\n return (\n `${payload.domain} wants you to sign in with your Solana account:\\n` +\n `${payload.address}${statementBlock}\\n` +\n `URI: ${payload.uri ?? ''}\\n` +\n `Version: ${payload.version}\\n` +\n `Chain ID: ${payload.chainId}\\n` +\n `Nonce: ${payload.nonce}\\n` +\n `Issued At: ${payload.issuedAt}` +\n `${expirationLine}` +\n `${formatResources(payload.resources)}`\n )\n}\n\nfunction decodeBase58(input: string): Uint8Array {\n return bs58.decode(input)\n}\n\nexport function buildHumanProofSchema(): Record<string, unknown> {\n return {\n type: 'object',\n required: ['domain', 'version', 'nonce', 'issuedAt', 'chainId', 'type', 'address', 'signature', 'requiredAttestations'],\n properties: {\n domain: { type: 'string' },\n uri: { type: 'string' },\n version: { type: 'string' },\n nonce: { type: 'string' },\n issuedAt: { type: 'string' },\n expirationTime: { type: 'string' },\n statement: { type: 'string' },\n resources: { type: 'array', items: { type: 'string' } },\n chainId: { type: 'string' },\n type: { enum: ['eip191', 'ed25519'] },\n address: { type: 'string' },\n signature: { type: 'string' },\n requiredAttestations: { type: 'array', items: { type: 'string' }},\n },\n additionalProperties: true,\n }\n}\n\nexport function parseHumanProofHeader(headerValue: string): HumanProofExtensionPayload {\n const parsed = JSON.parse(headerValue) as unknown\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('Invalid header payload')\n }\n\n const payload = parsed as Partial<HumanProofExtensionPayload>\n if (!payload.address || !payload.signature || !payload.chainId || !payload.type) {\n throw new Error('Missing required fields in header payload')\n }\n\n return payload as HumanProofExtensionPayload\n}\n\nexport async function validateHumanProofMessage(\n payload: HumanProofExtensionPayload,\n resource: string,\n): Promise<ValidationResult> {\n if (!payload.domain || !payload.version || !payload.nonce || !payload.issuedAt) {\n return { valid: false, error: 'invalid_message' }\n }\n\n if (!payload.uri || payload.uri !== resource) {\n return { valid: false, error: 'resource_mismatch' }\n }\n\n const issuedAtMillis = Date.parse(payload.issuedAt)\n if (Number.isNaN(issuedAtMillis)) {\n return { valid: false, error: 'invalid_issued_at' }\n }\n\n if (payload.expirationTime) {\n const expirationMillis = Date.parse(payload.expirationTime)\n if (Number.isNaN(expirationMillis)) {\n return { valid: false, error: 'invalid_expiration_time' }\n }\n if (expirationMillis < Date.now()) {\n return { valid: false, error: 'message_expired' }\n }\n }\n\n return { valid: true }\n}\n\nexport async function verifyHumanProofSignature(\n payload: HumanProofExtensionPayload,\n): Promise<SignatureVerificationResult> {\n if (payload.type === 'eip191') {\n if (!payload.signature.startsWith('0x')) {\n return { valid: false, error: 'invalid_signature' }\n }\n try {\n const message = formatSIWEMessage(payload)\n const signature = payload.signature as `0x${string}`\n\n const recovered = await recoverMessageAddress({ message, signature })\n if (recovered.toLowerCase() !== payload.address.toLowerCase()) {\n return { valid: false, error: 'invalid_signature' }\n }\n\n const publicKey = await recoverPublicKey({ hash: hashMessage(message), signature })\n return { valid: true, address: payload.address, pubKey: publicKey }\n } catch {\n return { valid: false, error: 'invalid_signature' }\n }\n }\n\n if (payload.type === 'ed25519') {\n try {\n const message = new TextEncoder().encode(formatSIWSMessage(payload))\n const publicKey = decodeBase58(payload.address)\n const signature = decodeBase58(payload.signature)\n\n const valid = nacl.sign.detached.verify(message, signature, publicKey)\n if (!valid) return { valid: false, error: 'invalid_signature' }\n return { valid: true, address: payload.address, pubKey: bs58.encode(publicKey) }\n } catch {\n return { valid: false, error: 'invalid_signature' }\n }\n }\n\n return { valid: false, error: 'invalid_signature_type' }\n}\n\nconst DEFAULT_PAYWALL_HTML =\n \"<html><body>\" +\n \"<h1>Payment Required</h1>\" +\n \"<p>Please complete the payment to access this resource.</p>\" +\n \"<p>The payment depends on verified-agent-identity plugin. \" +\n \"That can be downloaded from: \" +\n '<a href=\"https://clawhub.ai/obrezhniev/verified-agent-identity\">clawhub.ai</a> ' +\n '<a href=\"https://skills.sh/billionsnetwork/verified-agent-identity/verified-agent-identity\">skills.sh</a>' +\n \"</p></body></html>\"\n\nexport function paywallInstructions(html?: string): PaywallProvider {\n return new (class implements PaywallProvider {\n public generateHtml(_: PaymentRequired): string {\n return html ?? DEFAULT_PAYWALL_HTML\n }\n })()\n}\n","import { buildHumanProofSchema } from './utils.js'\nimport { randomBytes } from 'crypto'\nimport { HUMAN_PROOF, getSignatureTypes } from './declare.js'\nimport { verifyHumanProofRequest } from './hooks.js'\nimport type { HumanProofDeclaration, HumanProofEvent, HumanUsageStorage, PoUVerifier, PoUVerifierOptions } from './types.js'\nimport type { ResourceServerExtension, PaymentRequiredContext } from '@x402/core/types'\nimport type { HumanProofExtensionInfo, SupportedChain } from './utils.js'\nimport { createPoUVerifier, DEFAULT_AGENT_OWNERSHIP_SCHEMA } from './verifier.js'\nimport type { x402ResourceServer } from '@x402/core/server'\n\nexport type CreateHumanProofExtensionOptions = {\n /** Must match the agentOwnershipSchema configured in your PoUVerifier, if customised. */\n agentOwnershipSchema?: string\n}\n\nexport function createHumanProofExtension(options: CreateHumanProofExtensionOptions = {}): ResourceServerExtension {\n const ownershipSchema = options.agentOwnershipSchema ?? DEFAULT_AGENT_OWNERSHIP_SCHEMA\n return {\n key: HUMAN_PROOF,\n enrichPaymentRequiredResponse: async (\n declaration: unknown,\n context: PaymentRequiredContext,\n ): Promise<unknown> => {\n const decl = declaration as HumanProofDeclaration\n const opts = decl._options ?? {}\n\n const resourceUri = opts.resourceUri ?? context.resourceInfo.url\n\n let domain = opts.domain\n if (!domain) {\n try {\n domain = new URL(resourceUri).hostname\n } catch {\n throw new Error(\n `Cannot derive domain from resourceUri \"${resourceUri}\". ` +\n `Set opts.domain explicitly in declareHumanProofExtension.`,\n )\n }\n }\n\n let networks: string[]\n if (opts.network) {\n networks = Array.isArray(opts.network) ? opts.network : [opts.network]\n } else {\n networks = [...new Set(context.requirements.map(r => r.network))]\n }\n\n const nonce = randomBytes(16).toString('hex')\n const issuedAt = new Date().toISOString()\n\n const expirationSeconds = opts.expirationSeconds\n const expirationTime =\n expirationSeconds !== undefined\n ? new Date(Date.now() + expirationSeconds * 1000).toISOString()\n : undefined\n\n const requiredAttestations: string[] = [ownershipSchema]\n\n const info: HumanProofExtensionInfo = {\n domain,\n uri: resourceUri,\n version: opts.version ?? '1',\n nonce,\n issuedAt,\n requiredAttestations,\n }\n\n if (resourceUri) info.resources = [resourceUri]\n if (expirationTime) info.expirationTime = expirationTime\n if (opts.statement) info.statement = opts.statement\n\n // Only eip155 networks support the CAIP-122 signing flow; Solana routes still\n // accept payment but are not listed as human-proof signing options.\n const supportedChains: SupportedChain[] = networks\n .filter(network => network.startsWith('eip155:'))\n .flatMap(network => getSignatureTypes(network).map(type => ({ chainId: network, type })))\n\n const result: Record<string, unknown> = {\n info,\n supportedChains,\n schema: buildHumanProofSchema(),\n }\n return result\n },\n }\n}\n\nexport type CreateVerifyHumanProofHookOptions = {\n verifier?: PoUVerifier\n verifierOptions?: PoUVerifierOptions\n onEvent?: (event: HumanProofEvent) => void | Promise<void>\n storage?: HumanUsageStorage\n}\n\nexport function createVerifyHumanProofHook(\n options: CreateVerifyHumanProofHookOptions = {},\n) {\n const { onEvent, storage } = options\n const verifier = options.verifier ?? createPoUVerifier(options.verifierOptions ?? {})\n\n return async (context: {\n paymentPayload: { extensions?: Record<string, unknown>; resource?: { url: string } }\n requirements?: { extra?: Record<string, unknown> }\n }): Promise<void | { abort: true; reason: string }> => {\n const humanProofHeader = context.paymentPayload.extensions?.[HUMAN_PROOF] as string | undefined\n const resource = context.paymentPayload.resource?.url ?? ''\n\n\n // if no extra attestation requirements are specified, we can skip the human proof verification\n const requiredAttestations = context.requirements?.extra?.requiredAttestations as string[] | undefined\n if (!requiredAttestations?.length) {\n return\n }\n const result = await verifyHumanProofRequest(verifier, {\n resource,\n humanProofHeader: humanProofHeader ?? null,\n ...(onEvent ? { onEvent } : {}),\n })\n\n if (!result.allowed) {\n return { abort: true, reason: result.reason }\n }\n\n const extraSchemas = requiredAttestations?.filter(s => s !== verifier.ownershipSchema)\n if (extraSchemas?.length) {\n if (!verifier.hasAttestation) {\n return { abort: true, reason: 'verifier_cannot_check_attestations' }\n }\n for (const schema of extraSchemas) {\n const has = await verifier.hasAttestation(result.did, result.chainId, schema)\n if (!has) {\n return { abort: true, reason: 'missing_required_attestation' }\n }\n }\n }\n\n const maxUseRaw = context.requirements?.extra?.maxUse\n if (maxUseRaw === undefined) return\n if (typeof maxUseRaw !== 'number' || !Number.isFinite(maxUseRaw) || !Number.isInteger(maxUseRaw) || maxUseRaw < 1) {\n return { abort: true, reason: 'invalid_max_use_value' }\n }\n const maxUse = maxUseRaw\n if (!storage) {\n return { abort: true, reason: 'misconfigured_max_use_storage' }\n }\n const scopeRaw = context.requirements?.extra?.scope\n const scope = typeof scopeRaw === 'string' && scopeRaw ? scopeRaw : resource\n const count = await storage.incrementIfBelow(result.resolution.humanId, maxUse, scope)\n if (count === null) {\n await onEvent?.({ type: 'max_use_exceeded', resource, humanId: result.resolution.humanId, maxUse })\n return { abort: true, reason: 'max_use_exceeded' }\n }\n }\n}\n\nasync function resolveUsageContext(\n context: {\n paymentPayload: { extensions?: Record<string, unknown>; resource?: { url: string } }\n requirements?: { extra?: Record<string, unknown> }\n },\n verifier: PoUVerifier,\n): Promise<{ humanId: string; scope: string } | null> {\n const requiredAttestations = context.requirements?.extra?.requiredAttestations as string[] | undefined\n if (!requiredAttestations?.length) return null\n\n const maxUseRaw = context.requirements?.extra?.maxUse\n if (typeof maxUseRaw !== 'number' || !Number.isFinite(maxUseRaw) || !Number.isInteger(maxUseRaw) || maxUseRaw < 1) {\n return null\n }\n\n const humanProofHeader = context.paymentPayload.extensions?.[HUMAN_PROOF] as string | undefined\n const resource = context.paymentPayload.resource?.url ?? ''\n\n const result = await verifyHumanProofRequest(verifier, {\n resource,\n humanProofHeader: humanProofHeader ?? null,\n })\n if (!result.allowed) return null\n\n const scopeRaw = context.requirements?.extra?.scope\n const scope = typeof scopeRaw === 'string' && scopeRaw ? scopeRaw : resource\n return { humanId: result.resolution.humanId, scope }\n}\n\nexport type CreateAfterVerifyFailureRollbackHookOptions = {\n storage: HumanUsageStorage\n verifier: PoUVerifier\n}\n\nexport function createAfterVerifyFailureRollbackHook(\n options: CreateAfterVerifyFailureRollbackHookOptions,\n) {\n const { storage, verifier } = options\n\n return async (context: {\n paymentPayload: { extensions?: Record<string, unknown>; resource?: { url: string } }\n requirements?: { extra?: Record<string, unknown> }\n result: { isValid?: boolean }\n }): Promise<void> => {\n if (context.result.isValid !== false) return\n const resolved = await resolveUsageContext(context, verifier)\n if (!resolved) return\n await storage.decrementIfAboveZero(resolved.humanId, resolved.scope)\n }\n}\n\nexport type CreateVerifyFailureRollbackHookOptions = {\n storage: HumanUsageStorage\n verifier: PoUVerifier\n}\n\nexport function createVerifyFailureRollbackHook(\n options: CreateVerifyFailureRollbackHookOptions,\n) {\n const { storage, verifier } = options\n\n return async (context: {\n paymentPayload: { extensions?: Record<string, unknown>; resource?: { url: string } }\n requirements?: { extra?: Record<string, unknown> }\n error?: Error\n }): Promise<void> => {\n const resolved = await resolveUsageContext(context, verifier)\n if (!resolved) return\n await storage.decrementIfAboveZero(resolved.humanId, resolved.scope)\n }\n}\n\nexport type CreateSettleFailureRollbackHookOptions = {\n storage: HumanUsageStorage\n verifier: PoUVerifier\n}\n\nexport function createSettleFailureRollbackHook(\n options: CreateSettleFailureRollbackHookOptions,\n) {\n const { storage, verifier } = options\n\n return async (context: {\n paymentPayload: { extensions?: Record<string, unknown>; resource?: { url: string } }\n requirements?: { extra?: Record<string, unknown> }\n error?: Error\n }): Promise<void> => {\n const resolved = await resolveUsageContext(context, verifier)\n if (!resolved) return\n await storage.decrementIfAboveZero(resolved.humanId, resolved.scope)\n }\n}\n\nexport type ConfigureHumanProofServerOptions = {\n extensionOptions?: CreateHumanProofExtensionOptions\n verifier?: PoUVerifier\n verifierOptions?: PoUVerifierOptions\n onEvent?: (event: HumanProofEvent) => void | Promise<void>\n storage?: HumanUsageStorage\n}\n\nexport function configureHumanProofServer(\n server: x402ResourceServer,\n options: ConfigureHumanProofServerOptions = {},\n): void {\n const {\n extensionOptions,\n verifier,\n verifierOptions,\n onEvent,\n storage,\n } = options\n\n const resolvedVerifier = verifier ?? createPoUVerifier(verifierOptions ?? {})\n\n server.registerExtension(createHumanProofExtension(extensionOptions))\n server.onBeforeVerify(createVerifyHumanProofHook({\n verifier: resolvedVerifier,\n ...(onEvent ? { onEvent } : {}),\n ...(storage ? { storage } : {}),\n }))\n\n if (storage) {\n server.onAfterVerify(createAfterVerifyFailureRollbackHook({ storage, verifier: resolvedVerifier }))\n server.onVerifyFailure(createVerifyFailureRollbackHook({ storage, verifier: resolvedVerifier }))\n server.onSettleFailure(createSettleFailureRollbackHook({ storage, verifier: resolvedVerifier }))\n }\n}\n","import type { SignatureType } from './utils.js'\nimport type { DeclareHumanProofOptions, HumanProofDeclaration } from './types.js'\n\nexport const HUMAN_PROOF = 'human-proof'\n\n/**\n *\n * @example\n * extensions: declareHumanProofExtension({\n * statement: 'Verify your agent is backed by a verified human',\n * })\n */\nexport function declareHumanProofExtension(options: DeclareHumanProofOptions): HumanProofDeclaration {\n return { _options: options, _type: 'human-proof' }\n}\n\n/**\n * Returns the supported signature types for a given CAIP-2 network.\n * Solana uses ed25519 (SIWS); all EVM networks use eip191 (SIWE).\n */\nexport function getSignatureTypes(network: string): SignatureType[] {\n if (network.startsWith('solana:')) return ['ed25519']\n return ['eip191']\n}\n","import {\n parseHumanProofHeader,\n validateHumanProofMessage,\n verifyHumanProofSignature,\n} from './utils.js'\nimport type { HumanResolution, HumanProofEvent, PoUVerifier } from './types.js'\nimport { Blockchain, buildDIDType, DidMethod, NetworkId } from '@iden3/js-iden3-core'\nimport { buildDIDFromEthAddress } from '@0xpolygonid/js-sdk'\n\nexport const HUMAN_PROOF_HEADER = 'HUMAN-PROOF'\n\nexport type HumanProofRequestContext = {\n /** The resource URI being accessed (used to validate the CAIP-122 message). */\n resource: string\n /** Value of the HUMAN-PROOF request header, or null if absent. */\n humanProofHeader: string | null\n /** Optional callback fired on human_verified and human_not_registered events. */\n onEvent?: (event: HumanProofEvent) => void | Promise<void>\n}\n\nexport type HumanProofRequestResult =\n | { allowed: true; resolution: HumanResolution; did: string; chainId: string }\n | {\n allowed: false\n reason:\n | 'missing_header'\n | 'invalid_header'\n | 'invalid_message'\n | 'invalid_signature'\n | 'not_registered'\n | string\n did?: string\n humanId?: string\n }\n\n/**\n * Verify a HUMAN-PROOF request header against the PoU registry.\n *\n * Call this inside a request hook (BeforeVerifyHook or framework middleware)\n * to gate access based on human verification.\n *\n * Flow:\n * 1. Parse the CAIP-122 signed payload from the HUMAN-PROOF header\n * 2. Validate message fields (resource URI, nonce expiry)\n * 3. Verify the cryptographic signature (EVM eip191 or Solana ed25519)\n * 4. Resolve the wallet address to a HumanResolution via the on-chain registry\n * 5. Fire hook events\n */\nexport async function verifyHumanProofRequest(\n verifier: PoUVerifier,\n context: HumanProofRequestContext,\n): Promise<HumanProofRequestResult> {\n const { resource, humanProofHeader, onEvent } = context\n\n if (!humanProofHeader) {\n return { allowed: false, reason: 'missing_header' }\n }\n\n let payload\n try {\n payload = parseHumanProofHeader(humanProofHeader)\n } catch {\n return { allowed: false, reason: 'invalid_header' }\n }\n\n // Validate CAIP-122 message fields (URI match, nonce, expiry)\n const validation = await validateHumanProofMessage(payload, resource)\n if (!validation.valid) {\n return { allowed: false, reason: validation.error ?? 'invalid_message' }\n }\n\n // Verify cryptographic signature — handles both eip191 (SIWE) and ed25519 (SIWS)\n const sigResult = await verifyHumanProofSignature(payload)\n if (!sigResult.valid || !sigResult.address) {\n return { allowed: false, reason: sigResult.error ?? 'invalid_signature' }\n }\n\n const didType = buildDIDType(DidMethod.Iden3!, Blockchain.Billions!, NetworkId.Main!)\n const didString = buildDIDFromEthAddress(didType, sigResult.address!).string()\n // Resolve did → human via resolver\n const resolution = await verifier.lookupHuman(didString, payload.chainId)\n if (!resolution) {\n await onEvent?.({\n type: 'human_not_registered',\n resource,\n did: didString,\n })\n\n return { allowed: false, reason: 'not_registered', did: didString }\n }\n\n await onEvent?.({\n type: 'human_verified',\n resource,\n humanId: resolution.humanId,\n verifiedAt: resolution.verifiedAt,\n attestationId: resolution.attestationId,\n humanDid: resolution.humanDid,\n humanAddress: resolution.humanAddress,\n agentDid: resolution.agentDid,\n agentAddress: resolution.agentAddress, \n })\n\n return { allowed: true, resolution, did: didString, chainId: payload.chainId }\n}\n","import type {\n HumanResolution,\n PoUVerifier,\n PoUVerifierOptions,\n} from './types.js'\n\nexport const DEFAULT_ATTESTATIONS_API_BASE_URL =\n 'https://attestations-api.billions.network/api/v1/attestations'\n\nexport const DEFAULT_NULLIFIER_API_BASE_URL =\n 'https://attestations-api.billions.network/api/v1/nullifier'\n\nexport const DEFAULT_AGENT_OWNERSHIP_SCHEMA =\n '0xca354bee6dc5eded165461d15ccb13aceb6f77ebbb1fd3fe45aca686097f2911'\n\ntype ExplorerAttestationItem = {\n creationTime: number\n time?: number\n id: string\n schemaId: string\n fromId?: string\n recipientEthereumAddress?: string\n attesterEthereumAddress?: string\n toDid: string\n toEthereumAddress: string\n fromDid: string\n fromEthereumAddress: string\n}\n\ntype ExplorerAttestationsResponse = {\n data?: ExplorerAttestationItem[]\n nextPage?: number | null\n}\n\nasync function fetchExplorerPage(\n baseUrl: string,\n schema: string,\n recipientDid: string,\n): Promise<ExplorerAttestationsResponse> {\n const url = new URL(baseUrl)\n\n url.searchParams.set('schemaId', schema)\n url.searchParams.set('recipientDid', recipientDid)\n\n const response = await fetch(url.toString())\n if (!response.ok) {\n throw new Error(`Attestations explorer lookup failed (${response.status})`)\n }\n\n const json = (await response.json()) as ExplorerAttestationsResponse\n return json\n}\n\nasync function fetchNullifier(nullifierBaseUrl: string, userId: string): Promise<string | null> {\n const url = new URL(nullifierBaseUrl)\n url.searchParams.set('userId', userId)\n\n const response = await fetch(url.toString())\n if (!response.ok) {\n throw new Error(`Nullifier lookup failed (${response.status})`)\n }\n\n const json = (await response.json()) as { userId: string; nullifier: string }\n return json.nullifier ?? null\n}\n\nasync function lookupHumanFromExplorer(\n did: string,\n options: {\n baseUrl: string\n schema: string\n nullifierBaseUrl: string\n },\n): Promise<HumanResolution | null> {\n const payload = await fetchExplorerPage(\n options.baseUrl,\n options.schema,\n did,\n )\n\n const sorted = (payload.data ?? []).sort((a, b) => (b.creationTime ?? 0) - (a.creationTime ?? 0))\n const match = sorted[0]\n\n if (match?.fromId) {\n const nullifier = await fetchNullifier(options.nullifierBaseUrl, match.fromId)\n if (nullifier) {\n return {\n humanId: nullifier,\n verifiedAt: new Date(match.creationTime * 1000).toISOString(),\n attestationId: match.id,\n humanDid: match.fromDid,\n humanAddress: match.fromEthereumAddress,\n agentDid: match.toDid,\n agentAddress: match.toEthereumAddress,\n }\n }\n }\n\n return null\n}\n\n/**\n * Creates a PoU (Proof of Uniqueness) verifier that resolves a DID\n * to a HumanResolution.\n * @example\n * const verifier = createPoUVerifier()\n * const resolution = await verifier.lookupHuman('did:iden3:...', 'eip155:137')\n */\nexport function createPoUVerifier(options: PoUVerifierOptions = {}): PoUVerifier {\n const baseUrl = options.attestationsApiBaseUrl ?? DEFAULT_ATTESTATIONS_API_BASE_URL\n const nullifierBaseUrl = options.nullifierApiBaseUrl ?? DEFAULT_NULLIFIER_API_BASE_URL\n const ownershipSchema = options.agentOwnershipSchema ?? DEFAULT_AGENT_OWNERSHIP_SCHEMA\n\n return {\n ownershipSchema,\n\n async lookupHuman(did: string, _chainId: string): Promise<HumanResolution | null> {\n return lookupHumanFromExplorer(did, { baseUrl, schema: ownershipSchema, nullifierBaseUrl })\n },\n\n async hasAttestation(did: string, _chainId: string, schema: string): Promise<boolean> {\n if (options.hasAttestation) {\n return options.hasAttestation(did, _chainId, schema)\n }\n const payload = await fetchExplorerPage(baseUrl, schema, did)\n return (payload.data ?? []).length > 0\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,145 @@
1
+ // src/utils.ts
2
+ import { recoverPublicKey, hashMessage, recoverMessageAddress } from "viem";
3
+ import bs58 from "bs58";
4
+ import nacl from "tweetnacl";
5
+ function formatResources(resources) {
6
+ if (!resources?.length) return "";
7
+ const lines = resources.map((resource) => `- ${resource}`).join("\n");
8
+ return `
9
+ Resources:
10
+ ${lines}`;
11
+ }
12
+ function formatSIWEMessage(payload) {
13
+ const statementBlock = payload.statement ? `
14
+ ${payload.statement}
15
+ ` : "\n";
16
+ const expirationLine = payload.expirationTime ? `
17
+ Expiration Time: ${payload.expirationTime}` : "";
18
+ return `${payload.domain} wants you to sign in with your Ethereum account:
19
+ ${payload.address}${statementBlock}
20
+ URI: ${payload.uri ?? ""}
21
+ Version: ${payload.version}
22
+ Chain ID: ${payload.chainId}
23
+ Nonce: ${payload.nonce}
24
+ Issued At: ${payload.issuedAt}${expirationLine}${formatResources(payload.resources)}`;
25
+ }
26
+ function formatSIWSMessage(payload) {
27
+ const statementBlock = payload.statement ? `
28
+ ${payload.statement}
29
+ ` : "\n";
30
+ const expirationLine = payload.expirationTime ? `
31
+ Expiration Time: ${payload.expirationTime}` : "";
32
+ return `${payload.domain} wants you to sign in with your Solana account:
33
+ ${payload.address}${statementBlock}
34
+ URI: ${payload.uri ?? ""}
35
+ Version: ${payload.version}
36
+ Chain ID: ${payload.chainId}
37
+ Nonce: ${payload.nonce}
38
+ Issued At: ${payload.issuedAt}${expirationLine}${formatResources(payload.resources)}`;
39
+ }
40
+ function decodeBase58(input) {
41
+ return bs58.decode(input);
42
+ }
43
+ function buildHumanProofSchema() {
44
+ return {
45
+ type: "object",
46
+ required: ["domain", "version", "nonce", "issuedAt", "chainId", "type", "address", "signature", "requiredAttestations"],
47
+ properties: {
48
+ domain: { type: "string" },
49
+ uri: { type: "string" },
50
+ version: { type: "string" },
51
+ nonce: { type: "string" },
52
+ issuedAt: { type: "string" },
53
+ expirationTime: { type: "string" },
54
+ statement: { type: "string" },
55
+ resources: { type: "array", items: { type: "string" } },
56
+ chainId: { type: "string" },
57
+ type: { enum: ["eip191", "ed25519"] },
58
+ address: { type: "string" },
59
+ signature: { type: "string" },
60
+ requiredAttestations: { type: "array", items: { type: "string" } }
61
+ },
62
+ additionalProperties: true
63
+ };
64
+ }
65
+ function parseHumanProofHeader(headerValue) {
66
+ const parsed = JSON.parse(headerValue);
67
+ if (!parsed || typeof parsed !== "object") {
68
+ throw new Error("Invalid header payload");
69
+ }
70
+ const payload = parsed;
71
+ if (!payload.address || !payload.signature || !payload.chainId || !payload.type) {
72
+ throw new Error("Missing required fields in header payload");
73
+ }
74
+ return payload;
75
+ }
76
+ async function validateHumanProofMessage(payload, resource) {
77
+ if (!payload.domain || !payload.version || !payload.nonce || !payload.issuedAt) {
78
+ return { valid: false, error: "invalid_message" };
79
+ }
80
+ if (!payload.uri || payload.uri !== resource) {
81
+ return { valid: false, error: "resource_mismatch" };
82
+ }
83
+ const issuedAtMillis = Date.parse(payload.issuedAt);
84
+ if (Number.isNaN(issuedAtMillis)) {
85
+ return { valid: false, error: "invalid_issued_at" };
86
+ }
87
+ if (payload.expirationTime) {
88
+ const expirationMillis = Date.parse(payload.expirationTime);
89
+ if (Number.isNaN(expirationMillis)) {
90
+ return { valid: false, error: "invalid_expiration_time" };
91
+ }
92
+ if (expirationMillis < Date.now()) {
93
+ return { valid: false, error: "message_expired" };
94
+ }
95
+ }
96
+ return { valid: true };
97
+ }
98
+ async function verifyHumanProofSignature(payload) {
99
+ if (payload.type === "eip191") {
100
+ if (!payload.signature.startsWith("0x")) {
101
+ return { valid: false, error: "invalid_signature" };
102
+ }
103
+ try {
104
+ const message = formatSIWEMessage(payload);
105
+ const signature = payload.signature;
106
+ const recovered = await recoverMessageAddress({ message, signature });
107
+ if (recovered.toLowerCase() !== payload.address.toLowerCase()) {
108
+ return { valid: false, error: "invalid_signature" };
109
+ }
110
+ const publicKey = await recoverPublicKey({ hash: hashMessage(message), signature });
111
+ return { valid: true, address: payload.address, pubKey: publicKey };
112
+ } catch {
113
+ return { valid: false, error: "invalid_signature" };
114
+ }
115
+ }
116
+ if (payload.type === "ed25519") {
117
+ try {
118
+ const message = new TextEncoder().encode(formatSIWSMessage(payload));
119
+ const publicKey = decodeBase58(payload.address);
120
+ const signature = decodeBase58(payload.signature);
121
+ const valid = nacl.sign.detached.verify(message, signature, publicKey);
122
+ if (!valid) return { valid: false, error: "invalid_signature" };
123
+ return { valid: true, address: payload.address, pubKey: bs58.encode(publicKey) };
124
+ } catch {
125
+ return { valid: false, error: "invalid_signature" };
126
+ }
127
+ }
128
+ return { valid: false, error: "invalid_signature_type" };
129
+ }
130
+ var DEFAULT_PAYWALL_HTML = '<html><body><h1>Payment Required</h1><p>Please complete the payment to access this resource.</p><p>The payment depends on verified-agent-identity plugin. That can be downloaded from: <a href="https://clawhub.ai/obrezhniev/verified-agent-identity">clawhub.ai</a> <a href="https://skills.sh/billionsnetwork/verified-agent-identity/verified-agent-identity">skills.sh</a></p></body></html>';
131
+ function paywallInstructions(html) {
132
+ return new class {
133
+ generateHtml(_) {
134
+ return html ?? DEFAULT_PAYWALL_HTML;
135
+ }
136
+ }();
137
+ }
138
+ export {
139
+ buildHumanProofSchema,
140
+ parseHumanProofHeader,
141
+ paywallInstructions,
142
+ validateHumanProofMessage,
143
+ verifyHumanProofSignature
144
+ };
145
+ //# sourceMappingURL=utils.js.map