@oma3/omatrust 0.1.0-alpha.10 → 0.1.0-alpha.11

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/identity/index.cjs +543 -0
  2. package/dist/identity/index.cjs.map +1 -1
  3. package/dist/identity/index.d.cts +2 -1
  4. package/dist/identity/index.d.ts +2 -1
  5. package/dist/identity/index.js +527 -1
  6. package/dist/identity/index.js.map +1 -1
  7. package/dist/index-BOvk-7Ku.d.cts +235 -0
  8. package/dist/index-C2w5EvFH.d.ts +329 -0
  9. package/dist/index-QueRiudB.d.cts +329 -0
  10. package/dist/index-R78TpAhN.d.ts +235 -0
  11. package/dist/index.cjs +1475 -165
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +4 -3
  14. package/dist/index.d.ts +4 -3
  15. package/dist/index.js +1476 -166
  16. package/dist/index.js.map +1 -1
  17. package/dist/reputation/index.browser.cjs +943 -66
  18. package/dist/reputation/index.browser.cjs.map +1 -1
  19. package/dist/reputation/index.browser.d.cts +2 -2
  20. package/dist/reputation/index.browser.d.ts +2 -2
  21. package/dist/reputation/index.browser.js +941 -66
  22. package/dist/reputation/index.browser.js.map +1 -1
  23. package/dist/reputation/index.cjs +1393 -217
  24. package/dist/reputation/index.cjs.map +1 -1
  25. package/dist/reputation/index.d.cts +3 -2
  26. package/dist/reputation/index.d.ts +3 -2
  27. package/dist/reputation/index.js +1378 -217
  28. package/dist/reputation/index.js.map +1 -1
  29. package/dist/{subject-ownership-CXvzEjpH.d.cts → subject-ownership-B7cFlm8c.d.cts} +256 -26
  30. package/dist/{subject-ownership-CXvzEjpH.d.ts → subject-ownership-B7cFlm8c.d.ts} +256 -26
  31. package/dist/types-dpYxRq8N.d.cts +281 -0
  32. package/dist/types-dpYxRq8N.d.ts +281 -0
  33. package/dist/widgets/index.cjs +70 -27
  34. package/dist/widgets/index.cjs.map +1 -1
  35. package/dist/widgets/index.d.cts +42 -18
  36. package/dist/widgets/index.d.ts +42 -18
  37. package/dist/widgets/index.js +67 -26
  38. package/dist/widgets/index.js.map +1 -1
  39. package/package.json +3 -2
  40. package/dist/index-B9KW02US.d.cts +0 -119
  41. package/dist/index-DXrwBex9.d.ts +0 -119
  42. package/dist/index-QZDExA4I.d.cts +0 -90
  43. package/dist/index-QZDExA4I.d.ts +0 -90
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var ethers = require('ethers');
4
+ var jose = require('jose');
4
5
  var easSdk = require('@ethereum-attestation-service/eas-sdk');
5
6
  var canonicalize = require('canonicalize');
6
7
 
@@ -41,6 +42,11 @@ function assertString(value, name, code = "INVALID_INPUT") {
41
42
  throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
42
43
  }
43
44
  }
45
+ function assertObject(value, name, code = "INVALID_INPUT") {
46
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
47
+ throw new OmaTrustError(code, `${name} must be an object`, { value });
48
+ }
49
+ }
44
50
  var init_assert = __esm({
45
51
  "src/shared/assert.ts"() {
46
52
  init_errors();
@@ -156,6 +162,8 @@ function normalizeDid(input) {
156
162
  return normalizeDidHandle(trimmed);
157
163
  case "key":
158
164
  return normalizeDidKey(trimmed);
165
+ case "jwk":
166
+ return normalizeDidJwk(trimmed);
159
167
  default:
160
168
  return trimmed;
161
169
  }
@@ -245,13 +253,226 @@ function extractAddressFromDid(identifier) {
245
253
  }
246
254
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
247
255
  }
248
- var DID_REGEX;
256
+ function validateDidJwk(did) {
257
+ const parts = did.split(":");
258
+ if (parts.length !== 3) {
259
+ return {
260
+ valid: false,
261
+ method: "jwk",
262
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
263
+ };
264
+ }
265
+ const [, , encoded] = parts;
266
+ if (!encoded || encoded.length === 0) {
267
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
268
+ }
269
+ if (!BASE64URL_REGEX.test(encoded)) {
270
+ return {
271
+ valid: false,
272
+ method: "jwk",
273
+ error: "Identifier contains invalid base64url characters"
274
+ };
275
+ }
276
+ let decoded;
277
+ try {
278
+ const bytes = jose.base64url.decode(encoded);
279
+ decoded = new TextDecoder().decode(bytes);
280
+ } catch {
281
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
282
+ }
283
+ let jwk;
284
+ try {
285
+ jwk = JSON.parse(decoded);
286
+ } catch {
287
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
288
+ }
289
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
290
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
291
+ }
292
+ const kty = jwk.kty;
293
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
294
+ return {
295
+ valid: false,
296
+ method: "jwk",
297
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
298
+ };
299
+ }
300
+ if ("d" in jwk) {
301
+ return {
302
+ valid: false,
303
+ method: "jwk",
304
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
305
+ };
306
+ }
307
+ return { valid: true, method: "jwk" };
308
+ }
309
+ function normalizeDidJwk(input) {
310
+ assertString(input, "input", "INVALID_DID");
311
+ const trimmed = input.trim();
312
+ if (!trimmed.startsWith("did:jwk:")) {
313
+ throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
314
+ }
315
+ const result = validateDidJwk(trimmed);
316
+ if (!result.valid) {
317
+ throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
318
+ }
319
+ return trimmed;
320
+ }
321
+ var DID_REGEX, BASE64URL_REGEX, VALID_JWK_KTY;
249
322
  var init_did = __esm({
250
323
  "src/identity/did.ts"() {
251
324
  init_errors();
252
325
  init_assert();
253
326
  init_caip();
254
327
  DID_REGEX = /^did:[a-z0-9]+:.+$/i;
328
+ BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
329
+ VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
330
+ }
331
+ });
332
+ function validatePublicJwk(jwk) {
333
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
334
+ return { valid: false, error: "JWK must be a non-null object" };
335
+ }
336
+ const obj = jwk;
337
+ const kty = obj.kty;
338
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
339
+ return {
340
+ valid: false,
341
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
342
+ };
343
+ }
344
+ for (const field of PRIVATE_KEY_FIELDS) {
345
+ if (field in obj) {
346
+ return {
347
+ valid: false,
348
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
349
+ };
350
+ }
351
+ }
352
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
353
+ if (required) {
354
+ for (const field of required) {
355
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
356
+ return {
357
+ valid: false,
358
+ error: `Missing required public key field "${field}" for kty="${kty}"`
359
+ };
360
+ }
361
+ }
362
+ }
363
+ return { valid: true };
364
+ }
365
+ function canonicalizeJwkJson(jwk) {
366
+ const sorted = Object.keys(jwk).sort();
367
+ const obj = {};
368
+ for (const key of sorted) {
369
+ obj[key] = jwk[key];
370
+ }
371
+ return JSON.stringify(obj);
372
+ }
373
+ function jwkToDidJwk(jwk) {
374
+ assertObject(jwk, "jwk", "INVALID_JWK");
375
+ const validation = validatePublicJwk(jwk);
376
+ if (!validation.valid) {
377
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
378
+ }
379
+ const canonical = canonicalizeJwkJson(jwk);
380
+ const encoded = jose.base64url.encode(new TextEncoder().encode(canonical));
381
+ return `did:jwk:${encoded}`;
382
+ }
383
+ function didJwkToJwk(didJwk) {
384
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
385
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
386
+ }
387
+ const parts = didJwk.split(":");
388
+ if (parts.length !== 3) {
389
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
390
+ input: didJwk
391
+ });
392
+ }
393
+ const encoded = parts[2];
394
+ if (!encoded || encoded.length === 0) {
395
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
396
+ input: didJwk
397
+ });
398
+ }
399
+ let decoded;
400
+ try {
401
+ const bytes = jose.base64url.decode(encoded);
402
+ decoded = new TextDecoder().decode(bytes);
403
+ } catch {
404
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
405
+ input: didJwk
406
+ });
407
+ }
408
+ let jwk;
409
+ try {
410
+ jwk = JSON.parse(decoded);
411
+ } catch {
412
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
413
+ input: didJwk
414
+ });
415
+ }
416
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
417
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
418
+ input: didJwk
419
+ });
420
+ }
421
+ const validation = validatePublicJwk(jwk);
422
+ if (!validation.valid) {
423
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
424
+ input: didJwk
425
+ });
426
+ }
427
+ return jwk;
428
+ }
429
+ function extractPublicKeyFields(jwk) {
430
+ const result = {};
431
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
432
+ for (const [key, value] of Object.entries(jwk)) {
433
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
434
+ if (metadataFields.has(key)) continue;
435
+ result[key] = value;
436
+ }
437
+ return result;
438
+ }
439
+ function publicJwkEquals(a, b) {
440
+ assertObject(a, "a", "INVALID_JWK");
441
+ assertObject(b, "b", "INVALID_JWK");
442
+ const aObj = a;
443
+ const bObj = b;
444
+ for (const field of PRIVATE_KEY_FIELDS) {
445
+ if (field in aObj) {
446
+ throw new OmaTrustError(
447
+ "INVALID_JWK",
448
+ `First JWK contains private key field "${field}"`,
449
+ { field }
450
+ );
451
+ }
452
+ if (field in bObj) {
453
+ throw new OmaTrustError(
454
+ "INVALID_JWK",
455
+ `Second JWK contains private key field "${field}"`,
456
+ { field }
457
+ );
458
+ }
459
+ }
460
+ const aPublic = extractPublicKeyFields(aObj);
461
+ const bPublic = extractPublicKeyFields(bObj);
462
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
463
+ }
464
+ var VALID_KTY, PRIVATE_KEY_FIELDS, REQUIRED_PUBLIC_FIELDS;
465
+ var init_jwk = __esm({
466
+ "src/identity/jwk.ts"() {
467
+ init_errors();
468
+ init_assert();
469
+ VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
470
+ PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
471
+ REQUIRED_PUBLIC_FIELDS = {
472
+ EC: ["crv", "x", "y"],
473
+ OKP: ["crv", "x"],
474
+ RSA: ["n", "e"]
475
+ };
255
476
  }
256
477
  });
257
478
 
@@ -262,21 +483,23 @@ function parseDnsTxtRecord(record) {
262
483
  }
263
484
  const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
264
485
  const parsed = {};
486
+ const controllers = [];
265
487
  for (const entry of entries) {
266
- const [key, ...valueParts] = entry.split("=");
267
- if (!key) {
268
- continue;
269
- }
270
- const value = valueParts.join("=");
271
- if (!value) {
272
- continue;
488
+ const eqIndex = entry.indexOf("=");
489
+ if (eqIndex === -1) continue;
490
+ const key = entry.slice(0, eqIndex).trim();
491
+ const value = entry.slice(eqIndex + 1).trim();
492
+ if (!key || !value) continue;
493
+ if (key === "controller") {
494
+ controllers.push(value);
495
+ } else {
496
+ parsed[key] = value;
273
497
  }
274
- parsed[key.trim()] = value.trim();
275
498
  }
276
499
  return {
277
500
  version: parsed.v,
278
- controller: parsed.controller,
279
- ...parsed
501
+ controller: controllers[0],
502
+ controllers
280
503
  };
281
504
  }
282
505
  function buildDnsTxtRecord(controllerDid) {
@@ -290,6 +513,56 @@ var init_dns_txt_record = __esm({
290
513
  }
291
514
  });
292
515
 
516
+ // src/identity/controller-id.ts
517
+ function isSameControllerId(a, b) {
518
+ let normalizedA = null;
519
+ let normalizedB = null;
520
+ try {
521
+ normalizedA = normalizeDid(a);
522
+ } catch {
523
+ }
524
+ try {
525
+ normalizedB = normalizeDid(b);
526
+ } catch {
527
+ }
528
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
529
+ return true;
530
+ }
531
+ const evmA = extractControllerEvmAddress(a);
532
+ const evmB = extractControllerEvmAddress(b);
533
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
534
+ return true;
535
+ }
536
+ const methodA = extractDidMethod(a);
537
+ const methodB = extractDidMethod(b);
538
+ if (methodA === "jwk" && methodB === "jwk") {
539
+ try {
540
+ const jwkA = didJwkToJwk(a);
541
+ const jwkB = didJwkToJwk(b);
542
+ return publicJwkEquals(jwkA, jwkB);
543
+ } catch {
544
+ return false;
545
+ }
546
+ }
547
+ return false;
548
+ }
549
+ function extractControllerEvmAddress(controllerDid) {
550
+ try {
551
+ const method = extractDidMethod(controllerDid);
552
+ if (method === "pkh" && controllerDid.includes("eip155")) {
553
+ return extractAddressFromDid(controllerDid);
554
+ }
555
+ } catch {
556
+ }
557
+ return null;
558
+ }
559
+ var init_controller_id = __esm({
560
+ "src/identity/controller-id.ts"() {
561
+ init_did();
562
+ init_jwk();
563
+ }
564
+ });
565
+
293
566
  // src/reputation/proof/dns-txt-shared.ts
294
567
  var dns_txt_shared_exports = {};
295
568
  __export(dns_txt_shared_exports, {
@@ -302,7 +575,6 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
302
575
  if (!options.resolveTxt) {
303
576
  throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
304
577
  }
305
- const expected = normalizeDid(expectedControllerDid);
306
578
  const prefix = options.recordPrefix ?? "_controllers";
307
579
  const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
308
580
  let records;
@@ -314,15 +586,18 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
314
586
  for (const recordParts of records) {
315
587
  const record = recordParts.join("");
316
588
  const parsed = parseDnsTxtRecord(record);
317
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
318
- return { valid: true, record };
589
+ if (parsed.version !== "1") continue;
590
+ for (const recordController of parsed.controllers) {
591
+ if (isSameControllerId(recordController, expectedControllerDid)) {
592
+ return { valid: true, record };
593
+ }
319
594
  }
320
595
  }
321
- return { valid: false, reason: "No TXT record matched expected controller DID" };
596
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
322
597
  }
323
598
  var init_dns_txt_shared = __esm({
324
599
  "src/reputation/proof/dns-txt-shared.ts"() {
325
- init_did();
600
+ init_controller_id();
326
601
  init_errors();
327
602
  init_dns_txt_record();
328
603
  }
@@ -793,19 +1068,32 @@ async function submitDelegatedAttestation(params) {
793
1068
  payload = {};
794
1069
  }
795
1070
  if (!response.ok) {
796
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
797
- status: response.status,
1071
+ const relayError = {
1072
+ httpStatus: response.status,
1073
+ code: payload.code,
1074
+ error: payload.error ?? payload.message,
798
1075
  payload
799
- });
1076
+ };
1077
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
800
1078
  }
801
1079
  const uid = payload.uid ?? ZERO_UID;
802
1080
  const txHash = payload.txHash;
803
- const status = payload.status ?? "submitted";
804
- return {
805
- uid,
806
- txHash,
807
- status
808
- };
1081
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
1082
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
1083
+ const relay = {};
1084
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
1085
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
1086
+ if (typeof payload.success === "boolean") relay.success = payload.success;
1087
+ for (const key of Object.keys(payload)) {
1088
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
1089
+ relay[key] = payload[key];
1090
+ }
1091
+ }
1092
+ const result = { uid, txHash, status };
1093
+ if (Object.keys(relay).length > 0) {
1094
+ result.relay = relay;
1095
+ }
1096
+ return result;
809
1097
  }
810
1098
 
811
1099
  // src/reputation/query.ts
@@ -911,7 +1199,7 @@ async function getAttestationsForDid(params) {
911
1199
  provider,
912
1200
  fromBlock,
913
1201
  toBlock,
914
- { recipient: didToAddress(params.did), schemas: params.schemas }
1202
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
915
1203
  );
916
1204
  }
917
1205
  async function getAttestationsByAttester(params) {
@@ -1163,6 +1451,10 @@ function getExplorerAddressUrl(chainId, address) {
1163
1451
 
1164
1452
  // src/reputation/proof/did-json.ts
1165
1453
  init_did();
1454
+ init_jwk();
1455
+ init_errors();
1456
+
1457
+ // src/shared/did-document.ts
1166
1458
  init_errors();
1167
1459
  async function fetchDidDocument(domain) {
1168
1460
  const normalized = domain.toLowerCase().replace(/\.$/, "");
@@ -1174,15 +1466,16 @@ async function fetchDidDocument(domain) {
1174
1466
  throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
1175
1467
  }
1176
1468
  if (!response.ok) {
1177
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1469
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
1178
1470
  domain,
1179
1471
  status: response.status
1180
1472
  });
1181
1473
  }
1182
- const body = await response.json();
1183
- return body;
1474
+ return await response.json();
1184
1475
  }
1185
- function extractAddressesFromDidDocument(didDocument) {
1476
+
1477
+ // src/reputation/proof/did-json.ts
1478
+ function extractEvmAddressesFromDidDocument(didDocument) {
1186
1479
  const methods = didDocument.verificationMethod;
1187
1480
  if (!Array.isArray(methods)) {
1188
1481
  return [];
@@ -1206,14 +1499,52 @@ function extractAddressesFromDidDocument(didDocument) {
1206
1499
  }
1207
1500
  return [...addresses];
1208
1501
  }
1502
+ function extractJwksFromDidDocument(didDocument) {
1503
+ const methods = didDocument.verificationMethod;
1504
+ if (!Array.isArray(methods)) {
1505
+ return [];
1506
+ }
1507
+ const jwks = [];
1508
+ for (const method of methods) {
1509
+ const publicKeyJwk = method.publicKeyJwk;
1510
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
1511
+ jwks.push(publicKeyJwk);
1512
+ }
1513
+ }
1514
+ return jwks;
1515
+ }
1209
1516
  function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1517
+ const method = extractDidMethod(expectedControllerDid);
1518
+ if (method === "jwk") {
1519
+ try {
1520
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
1521
+ const documentJwks = extractJwksFromDidDocument(didDocument);
1522
+ for (const docJwk of documentJwks) {
1523
+ try {
1524
+ if (publicJwkEquals(docJwk, expectedJwk)) {
1525
+ return { valid: true };
1526
+ }
1527
+ } catch {
1528
+ }
1529
+ }
1530
+ return {
1531
+ valid: false,
1532
+ reason: "No matching publicKeyJwk found in DID document verification methods"
1533
+ };
1534
+ } catch {
1535
+ return {
1536
+ valid: false,
1537
+ reason: "Failed to decode did:jwk controller DID"
1538
+ };
1539
+ }
1540
+ }
1210
1541
  let expectedAddress;
1211
1542
  try {
1212
1543
  expectedAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1213
1544
  } catch {
1214
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
1545
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
1215
1546
  }
1216
- const addresses = extractAddressesFromDidDocument(didDocument);
1547
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
1217
1548
  if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1218
1549
  return { valid: true };
1219
1550
  }
@@ -1267,6 +1598,469 @@ function verifyEip712Signature(typedData, signature) {
1267
1598
 
1268
1599
  // src/reputation/verify.ts
1269
1600
  init_dns_txt_record();
1601
+
1602
+ // src/reputation/proof/x402-jws.ts
1603
+ init_errors();
1604
+ init_jwk();
1605
+
1606
+ // src/identity/resolve-key.ts
1607
+ init_errors();
1608
+ init_assert();
1609
+
1610
+ // src/identity/did-url.ts
1611
+ init_errors();
1612
+ init_assert();
1613
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
1614
+ function parseDidUrl(input) {
1615
+ assertString(input, "input", "INVALID_DID_URL");
1616
+ const trimmed = input.trim();
1617
+ const hashIndex = trimmed.indexOf("#");
1618
+ let did;
1619
+ let fragment;
1620
+ if (hashIndex === -1) {
1621
+ did = trimmed;
1622
+ fragment = null;
1623
+ } else {
1624
+ did = trimmed.slice(0, hashIndex);
1625
+ const rawFragment = trimmed.slice(hashIndex + 1);
1626
+ if (rawFragment.length === 0) {
1627
+ throw new OmaTrustError(
1628
+ "INVALID_DID_URL",
1629
+ "DID URL has empty fragment (trailing '#' with no identifier)",
1630
+ { input }
1631
+ );
1632
+ }
1633
+ fragment = rawFragment;
1634
+ }
1635
+ if (!DID_URL_REGEX.test(did)) {
1636
+ throw new OmaTrustError(
1637
+ "INVALID_DID_URL",
1638
+ "Invalid DID URL: base DID portion is malformed",
1639
+ { input, did }
1640
+ );
1641
+ }
1642
+ return {
1643
+ didUrl: trimmed,
1644
+ did,
1645
+ fragment
1646
+ };
1647
+ }
1648
+
1649
+ // src/identity/resolve-key.ts
1650
+ init_did();
1651
+ init_jwk();
1652
+ function findVerificationMethod(didDocument, didUrl, fragment) {
1653
+ const methods = didDocument.verificationMethod;
1654
+ if (!Array.isArray(methods)) {
1655
+ return null;
1656
+ }
1657
+ for (const method of methods) {
1658
+ if (!method || typeof method !== "object") continue;
1659
+ const id = method.id;
1660
+ if (typeof id !== "string") continue;
1661
+ if (id === didUrl) return method;
1662
+ if (fragment && (id === `#${fragment}` || id.endsWith(`#${fragment}`))) {
1663
+ return method;
1664
+ }
1665
+ }
1666
+ return null;
1667
+ }
1668
+ function extractMethodIdsFromDidDocument(didDocument) {
1669
+ const methods = didDocument.verificationMethod;
1670
+ if (!Array.isArray(methods)) return [];
1671
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
1672
+ }
1673
+ async function resolveDidUrlToPublicKey(didUrl, options) {
1674
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
1675
+ const parsed = parseDidUrl(didUrl);
1676
+ const method = extractDidMethod(parsed.did);
1677
+ if (!method) {
1678
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
1679
+ didUrl,
1680
+ did: parsed.did
1681
+ });
1682
+ }
1683
+ switch (method) {
1684
+ case "web":
1685
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment);
1686
+ default:
1687
+ throw new OmaTrustError(
1688
+ "UNSUPPORTED_DID_METHOD",
1689
+ `DID URL key resolution is not supported for method "${method}"`,
1690
+ { didUrl, method }
1691
+ );
1692
+ }
1693
+ }
1694
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
1695
+ const domain = getDomainFromDidWeb(did);
1696
+ if (!domain) {
1697
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
1698
+ didUrl,
1699
+ did
1700
+ });
1701
+ }
1702
+ const fetchFn = fetchDidDocument;
1703
+ const didDocument = await fetchFn(domain);
1704
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
1705
+ if (!method) {
1706
+ throw new OmaTrustError(
1707
+ "KEY_NOT_FOUND",
1708
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
1709
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
1710
+ );
1711
+ }
1712
+ const publicKeyJwk = method.publicKeyJwk;
1713
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
1714
+ throw new OmaTrustError(
1715
+ "KEY_NOT_FOUND",
1716
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
1717
+ { didUrl, verificationMethodId: method.id }
1718
+ );
1719
+ }
1720
+ const validation = validatePublicJwk(publicKeyJwk);
1721
+ if (!validation.valid) {
1722
+ throw new OmaTrustError(
1723
+ "INVALID_JWK",
1724
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
1725
+ { didUrl, verificationMethodId: method.id }
1726
+ );
1727
+ }
1728
+ return {
1729
+ didUrl,
1730
+ did,
1731
+ fragment,
1732
+ publicKeyJwk,
1733
+ verificationMethodId: method.id
1734
+ };
1735
+ }
1736
+
1737
+ // src/reputation/proof/x402-jws.ts
1738
+ var REQUIRED_OFFER_FIELDS = [
1739
+ "version",
1740
+ "resourceUrl",
1741
+ "scheme",
1742
+ "network",
1743
+ "asset",
1744
+ "payTo",
1745
+ "amount"
1746
+ ];
1747
+ var REQUIRED_RECEIPT_FIELDS = [
1748
+ "version",
1749
+ "network",
1750
+ "resourceUrl",
1751
+ "payer",
1752
+ "issuedAt"
1753
+ ];
1754
+ function validateOfferPayload(payload) {
1755
+ for (const field of REQUIRED_OFFER_FIELDS) {
1756
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1757
+ return { valid: false, field };
1758
+ }
1759
+ }
1760
+ return { valid: true };
1761
+ }
1762
+ function validateReceiptPayload(payload) {
1763
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
1764
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1765
+ return { valid: false, field };
1766
+ }
1767
+ }
1768
+ return { valid: true };
1769
+ }
1770
+ function failure(code, message, partial) {
1771
+ return {
1772
+ valid: false,
1773
+ error: { code, message },
1774
+ ...partial
1775
+ };
1776
+ }
1777
+ async function verifyX402JwsArtifact(artifact, options) {
1778
+ if (!artifact || typeof artifact !== "object") {
1779
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
1780
+ }
1781
+ if (artifact.format !== "jws") {
1782
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
1783
+ }
1784
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1785
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
1786
+ }
1787
+ const compactJws = artifact.signature;
1788
+ const parts = compactJws.split(".");
1789
+ if (parts.length !== 3) {
1790
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
1791
+ }
1792
+ let header;
1793
+ try {
1794
+ header = jose.decodeProtectedHeader(compactJws);
1795
+ } catch {
1796
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
1797
+ }
1798
+ if (!header.alg || typeof header.alg !== "string") {
1799
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
1800
+ }
1801
+ const kid = typeof header.kid === "string" ? header.kid : null;
1802
+ const embeddedJwk = header.jwk;
1803
+ if (!kid && !embeddedJwk) {
1804
+ return failure(
1805
+ "MISSING_KEY_MATERIAL",
1806
+ "JWS header must include at least one of kid or jwk",
1807
+ { header }
1808
+ );
1809
+ }
1810
+ let publicKeyJwk;
1811
+ let publicKeySource;
1812
+ if (embeddedJwk) {
1813
+ const validation = validatePublicJwk(embeddedJwk);
1814
+ if (!validation.valid) {
1815
+ return failure(
1816
+ "INVALID_EMBEDDED_JWK",
1817
+ `Embedded JWK is invalid: ${validation.error}`,
1818
+ { header }
1819
+ );
1820
+ }
1821
+ publicKeyJwk = embeddedJwk;
1822
+ publicKeySource = "embedded-jwk";
1823
+ if (kid) {
1824
+ try {
1825
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1826
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
1827
+ return failure(
1828
+ "KEY_CONFLICT",
1829
+ "Embedded jwk conflicts with public key resolved from kid",
1830
+ { header, kid }
1831
+ );
1832
+ }
1833
+ } catch (err) {
1834
+ }
1835
+ }
1836
+ } else {
1837
+ publicKeySource = "kid-resolution";
1838
+ try {
1839
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1840
+ publicKeyJwk = resolved.publicKeyJwk;
1841
+ } catch (err) {
1842
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
1843
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
1844
+ }
1845
+ }
1846
+ let payload;
1847
+ try {
1848
+ const key = await jose.importJWK(publicKeyJwk, header.alg);
1849
+ const result = await jose.compactVerify(compactJws, key);
1850
+ const payloadText = new TextDecoder().decode(result.payload);
1851
+ payload = JSON.parse(payloadText);
1852
+ } catch (err) {
1853
+ const message = err instanceof Error ? err.message : "Signature verification failed";
1854
+ return failure("SIGNATURE_INVALID", message, { header, kid });
1855
+ }
1856
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
1857
+ return {
1858
+ valid: true,
1859
+ header,
1860
+ payload,
1861
+ kid,
1862
+ publicKeyJwk,
1863
+ publicKeySource,
1864
+ publicKeyDid
1865
+ };
1866
+ }
1867
+ async function verifyX402JwsOffer(artifact, options) {
1868
+ const result = await verifyX402JwsArtifact(artifact, options);
1869
+ if (!result.valid) return result;
1870
+ const payloadCheck = validateOfferPayload(result.payload);
1871
+ if (!payloadCheck.valid) {
1872
+ return failure(
1873
+ "INVALID_OFFER_PAYLOAD",
1874
+ `Missing required offer field: ${payloadCheck.field}`,
1875
+ {
1876
+ header: result.header,
1877
+ payload: result.payload,
1878
+ kid: result.kid,
1879
+ publicKeyJwk: result.publicKeyJwk,
1880
+ publicKeySource: result.publicKeySource,
1881
+ publicKeyDid: result.publicKeyDid
1882
+ }
1883
+ );
1884
+ }
1885
+ return result;
1886
+ }
1887
+ async function verifyX402JwsReceipt(artifact, options) {
1888
+ const result = await verifyX402JwsArtifact(artifact, options);
1889
+ if (!result.valid) return result;
1890
+ const payloadCheck = validateReceiptPayload(result.payload);
1891
+ if (!payloadCheck.valid) {
1892
+ return failure(
1893
+ "INVALID_RECEIPT_PAYLOAD",
1894
+ `Missing required receipt field: ${payloadCheck.field}`,
1895
+ {
1896
+ header: result.header,
1897
+ payload: result.payload,
1898
+ kid: result.kid,
1899
+ publicKeyJwk: result.publicKeyJwk,
1900
+ publicKeySource: result.publicKeySource,
1901
+ publicKeyDid: result.publicKeyDid
1902
+ }
1903
+ );
1904
+ }
1905
+ return result;
1906
+ }
1907
+ var OFFER_DOMAIN = {
1908
+ name: "x402 offer",
1909
+ version: "1",
1910
+ chainId: 1
1911
+ };
1912
+ var RECEIPT_DOMAIN = {
1913
+ name: "x402 receipt",
1914
+ version: "1",
1915
+ chainId: 1
1916
+ };
1917
+ var OFFER_TYPES = {
1918
+ Offer: [
1919
+ { name: "version", type: "uint256" },
1920
+ { name: "resourceUrl", type: "string" },
1921
+ { name: "scheme", type: "string" },
1922
+ { name: "network", type: "string" },
1923
+ { name: "asset", type: "string" },
1924
+ { name: "payTo", type: "string" },
1925
+ { name: "amount", type: "string" },
1926
+ { name: "validUntil", type: "uint256" }
1927
+ ]
1928
+ };
1929
+ var RECEIPT_TYPES = {
1930
+ Receipt: [
1931
+ { name: "version", type: "uint256" },
1932
+ { name: "network", type: "string" },
1933
+ { name: "resourceUrl", type: "string" },
1934
+ { name: "payer", type: "string" },
1935
+ { name: "issuedAt", type: "uint256" },
1936
+ { name: "transaction", type: "string" }
1937
+ ]
1938
+ };
1939
+ var REQUIRED_OFFER_FIELDS2 = [
1940
+ "version",
1941
+ "resourceUrl",
1942
+ "scheme",
1943
+ "network",
1944
+ "asset",
1945
+ "payTo",
1946
+ "amount"
1947
+ ];
1948
+ var REQUIRED_RECEIPT_FIELDS2 = [
1949
+ "version",
1950
+ "network",
1951
+ "resourceUrl",
1952
+ "payer",
1953
+ "issuedAt"
1954
+ ];
1955
+ function validateOfferPayload2(payload) {
1956
+ for (const field of REQUIRED_OFFER_FIELDS2) {
1957
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1958
+ return { valid: false, field };
1959
+ }
1960
+ }
1961
+ return { valid: true };
1962
+ }
1963
+ function validateReceiptPayload2(payload) {
1964
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
1965
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1966
+ return { valid: false, field };
1967
+ }
1968
+ }
1969
+ return { valid: true };
1970
+ }
1971
+ function failure2(code, message, partial) {
1972
+ return {
1973
+ valid: false,
1974
+ error: { code, message },
1975
+ ...partial
1976
+ };
1977
+ }
1978
+ function prepareOfferMessage(payload) {
1979
+ return {
1980
+ version: payload.version,
1981
+ resourceUrl: payload.resourceUrl,
1982
+ scheme: payload.scheme,
1983
+ network: payload.network,
1984
+ asset: payload.asset,
1985
+ payTo: payload.payTo,
1986
+ amount: payload.amount,
1987
+ validUntil: payload.validUntil ?? 0
1988
+ };
1989
+ }
1990
+ function prepareReceiptMessage(payload) {
1991
+ return {
1992
+ version: payload.version,
1993
+ network: payload.network,
1994
+ resourceUrl: payload.resourceUrl,
1995
+ payer: payload.payer,
1996
+ issuedAt: payload.issuedAt,
1997
+ transaction: payload.transaction ?? ""
1998
+ };
1999
+ }
2000
+ function verifyX402Eip712Artifact(artifact, artifactType) {
2001
+ if (!artifact || typeof artifact !== "object") {
2002
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
2003
+ }
2004
+ if (artifact.format !== "eip712") {
2005
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
2006
+ }
2007
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
2008
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
2009
+ }
2010
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2011
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
2012
+ }
2013
+ const payload = artifact.payload;
2014
+ if (artifactType === "offer") {
2015
+ const check = validateOfferPayload2(payload);
2016
+ if (!check.valid) {
2017
+ return failure2(
2018
+ "INVALID_OFFER_PAYLOAD",
2019
+ `Missing required offer field: ${check.field}`,
2020
+ { payload }
2021
+ );
2022
+ }
2023
+ } else {
2024
+ const check = validateReceiptPayload2(payload);
2025
+ if (!check.valid) {
2026
+ return failure2(
2027
+ "INVALID_RECEIPT_PAYLOAD",
2028
+ `Missing required receipt field: ${check.field}`,
2029
+ { payload }
2030
+ );
2031
+ }
2032
+ }
2033
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
2034
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
2035
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
2036
+ let signer;
2037
+ try {
2038
+ signer = ethers.verifyTypedData(
2039
+ domain,
2040
+ types,
2041
+ message,
2042
+ artifact.signature
2043
+ );
2044
+ signer = ethers.getAddress(signer);
2045
+ } catch (err) {
2046
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
2047
+ return failure2("SIGNATURE_INVALID", message2, { payload });
2048
+ }
2049
+ return {
2050
+ valid: true,
2051
+ payload,
2052
+ signer,
2053
+ artifactType
2054
+ };
2055
+ }
2056
+ function verifyX402Eip712Offer(artifact) {
2057
+ return verifyX402Eip712Artifact(artifact, "offer");
2058
+ }
2059
+ function verifyX402Eip712Receipt(artifact) {
2060
+ return verifyX402Eip712Artifact(artifact, "receipt");
2061
+ }
2062
+
2063
+ // src/reputation/verify.ts
1270
2064
  function parseChainId(input) {
1271
2065
  if (typeof input === "number") {
1272
2066
  return input;
@@ -1323,18 +2117,18 @@ function decodeJwtPayload(compactJws) {
1323
2117
  return JSON.parse(json);
1324
2118
  }
1325
2119
  async function verifyProof(params) {
1326
- const { proof, provider, expectedSubject, expectedController } = params;
2120
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1327
2121
  try {
1328
2122
  switch (proof.proofType) {
1329
2123
  case "tx-encoded-value": {
1330
2124
  if (!provider) {
1331
2125
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1332
2126
  }
1333
- if (!expectedSubject || !expectedController) {
2127
+ if (!expectedSubjectDid || !expectedControllerDid) {
1334
2128
  return {
1335
2129
  valid: false,
1336
2130
  proofType: proof.proofType,
1337
- reason: "expectedSubject and expectedController are required"
2131
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1338
2132
  };
1339
2133
  }
1340
2134
  const proofObject = proof.proofObject;
@@ -1346,13 +2140,13 @@ async function verifyProof(params) {
1346
2140
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1347
2141
  }
1348
2142
  const expectedAmount = calculateTransferAmount(
1349
- expectedSubject,
1350
- expectedController,
2143
+ expectedSubjectDid,
2144
+ expectedControllerDid,
1351
2145
  chainId,
1352
2146
  getProofPurpose(proof)
1353
2147
  );
1354
- const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubject));
1355
- const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedController));
2148
+ const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubjectDid));
2149
+ const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1356
2150
  if (tx.from && ethers.getAddress(tx.from) !== subjectAddress) {
1357
2151
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1358
2152
  }
@@ -1426,6 +2220,38 @@ async function verifyProof(params) {
1426
2220
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1427
2221
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1428
2222
  }
2223
+ const proofObj = proof.proofObject;
2224
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2225
+ const artifact = {
2226
+ format: "jws",
2227
+ signature: proofObj.signature
2228
+ };
2229
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2230
+ if (!jwsResult.valid) {
2231
+ return {
2232
+ valid: false,
2233
+ proofType: proof.proofType,
2234
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2235
+ };
2236
+ }
2237
+ return { valid: true, proofType: proof.proofType };
2238
+ }
2239
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2240
+ const artifact = {
2241
+ format: "eip712",
2242
+ payload: proofObj.payload,
2243
+ signature: proofObj.signature
2244
+ };
2245
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2246
+ if (!eip712Result.valid) {
2247
+ return {
2248
+ valid: false,
2249
+ proofType: proof.proofType,
2250
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2251
+ };
2252
+ }
2253
+ return { valid: true, proofType: proof.proofType };
2254
+ }
1429
2255
  return { valid: true, proofType: proof.proofType };
1430
2256
  }
1431
2257
  case "evidence-pointer": {
@@ -1437,9 +2263,9 @@ async function verifyProof(params) {
1437
2263
  if (!response.ok) {
1438
2264
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1439
2265
  }
1440
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2266
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1441
2267
  const didDoc = await response.json();
1442
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2268
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1443
2269
  return {
1444
2270
  valid: didCheck.valid,
1445
2271
  proofType: proof.proofType,
@@ -1447,10 +2273,10 @@ async function verifyProof(params) {
1447
2273
  };
1448
2274
  }
1449
2275
  const body = await response.text();
1450
- if (expectedController && !body.includes(expectedController)) {
2276
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1451
2277
  try {
1452
2278
  const parsed = parseDnsTxtRecord(body);
1453
- if (parsed.controller !== expectedController) {
2279
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1454
2280
  return {
1455
2281
  valid: false,
1456
2282
  proofType: proof.proofType,
@@ -1506,8 +2332,8 @@ async function verifyAttestation(params) {
1506
2332
  const result = await verifyProof({
1507
2333
  proof,
1508
2334
  provider: params.provider,
1509
- expectedSubject: params.context?.subject,
1510
- expectedController: params.context?.controller
2335
+ expectedSubjectDid: params.context?.subjectDid,
2336
+ expectedControllerDid: params.context?.controllerDid
1511
2337
  });
1512
2338
  checks[proof.proofType] = result.valid;
1513
2339
  if (!result.valid) {
@@ -1612,6 +2438,38 @@ async function callControllerWitness(params) {
1612
2438
  method: "did-json"
1613
2439
  };
1614
2440
  }
2441
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
2442
+ async function requestControllerWitness(params) {
2443
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
2444
+ const body = {
2445
+ subjectDid: params.subjectDid,
2446
+ controllerDid: params.controllerDid
2447
+ };
2448
+ if (params.chainId !== void 0) {
2449
+ body.chainId = params.chainId;
2450
+ }
2451
+ let response;
2452
+ try {
2453
+ response = await fetch(url, {
2454
+ method: "POST",
2455
+ headers: { "Content-Type": "application/json" },
2456
+ credentials: "include",
2457
+ body: JSON.stringify(body),
2458
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
2459
+ });
2460
+ } catch (err) {
2461
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
2462
+ }
2463
+ if (!response.ok) {
2464
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
2465
+ throw new OmaTrustError(
2466
+ "API_ERROR",
2467
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
2468
+ { status: response.status, ...errorBody }
2469
+ );
2470
+ }
2471
+ return await response.json();
2472
+ }
1615
2473
 
1616
2474
  // src/reputation/proof/tx-interaction.ts
1617
2475
  init_errors();
@@ -1758,29 +2616,20 @@ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options
1758
2616
  );
1759
2617
  }
1760
2618
 
1761
- // src/reputation/proof/subject-ownership.ts
2619
+ // src/reputation/subject-ownership.ts
1762
2620
  init_did();
1763
2621
  init_errors();
1764
2622
  init_dns_txt_shared();
2623
+
2624
+ // src/reputation/contract-ownership.ts
2625
+ init_did();
1765
2626
  var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1766
2627
  var OWNERSHIP_PATTERNS = [
1767
2628
  { method: "owner", signature: "function owner() view returns (address)" },
1768
2629
  { method: "admin", signature: "function admin() view returns (address)" },
1769
2630
  { method: "getOwner", signature: "function getOwner() view returns (address)" }
1770
2631
  ];
1771
- function assertConnectedWalletDid(input) {
1772
- const normalized = normalizeDid(input);
1773
- if (extractDidMethod(normalized) !== "pkh") {
1774
- throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
1775
- connectedWalletDid: input
1776
- });
1777
- }
1778
- return normalized;
1779
- }
1780
- function normalizeSubjectDid(input) {
1781
- return normalizeDid(input);
1782
- }
1783
- async function readAddressFromContract(provider, contractAddress, signature, method) {
2632
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
1784
2633
  try {
1785
2634
  const iface = new ethers.Interface([signature]);
1786
2635
  const data = iface.encodeFunctionData(method, []);
@@ -1793,16 +2642,24 @@ async function readAddressFromContract(provider, contractAddress, signature, met
1793
2642
  }
1794
2643
  return null;
1795
2644
  }
1796
- async function discoverControllingWallet(provider, contractAddress, chainId) {
2645
+ async function discoverContractOwner(provider, contractAddress) {
2646
+ try {
2647
+ const code = await provider.getCode(contractAddress);
2648
+ if (code === "0x" || code === "0x0") {
2649
+ return null;
2650
+ }
2651
+ } catch {
2652
+ return null;
2653
+ }
1797
2654
  for (const pattern of OWNERSHIP_PATTERNS) {
1798
- const address = await readAddressFromContract(
2655
+ const address = await readOwnerFromContract(
1799
2656
  provider,
1800
2657
  contractAddress,
1801
2658
  pattern.signature,
1802
2659
  pattern.method
1803
2660
  );
1804
2661
  if (address) {
1805
- return buildEvmDidPkh(chainId, address);
2662
+ return address;
1806
2663
  }
1807
2664
  }
1808
2665
  try {
@@ -1810,13 +2667,31 @@ async function discoverControllingWallet(provider, contractAddress, chainId) {
1810
2667
  if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1811
2668
  const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
1812
2669
  if (adminAddress !== ethers.ZeroAddress) {
1813
- return buildEvmDidPkh(chainId, adminAddress);
2670
+ return adminAddress;
1814
2671
  }
1815
2672
  }
1816
2673
  } catch {
1817
2674
  }
1818
2675
  return null;
1819
2676
  }
2677
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
2678
+ const owner = await discoverContractOwner(provider, contractAddress);
2679
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
2680
+ }
2681
+
2682
+ // src/reputation/subject-ownership.ts
2683
+ function assertConnectedWalletDid(input) {
2684
+ const normalized = normalizeDid(input);
2685
+ if (extractDidMethod(normalized) !== "pkh") {
2686
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
2687
+ connectedWalletDid: input
2688
+ });
2689
+ }
2690
+ return normalized;
2691
+ }
2692
+ function normalizeSubjectDid(input) {
2693
+ return normalizeDid(input);
2694
+ }
1820
2695
  async function verifyDidWebOwnership(params) {
1821
2696
  const subjectDid = normalizeSubjectDid(params.subjectDid);
1822
2697
  const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
@@ -1914,7 +2789,7 @@ async function verifyDidPkhOwnership(params) {
1914
2789
  connectedWalletDid
1915
2790
  };
1916
2791
  }
1917
- const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
2792
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
1918
2793
  if (params.txHash) {
1919
2794
  if (!controllingWalletDid) {
1920
2795
  return {
@@ -1997,7 +2872,7 @@ async function verifyDidPkhOwnership(params) {
1997
2872
  };
1998
2873
  }
1999
2874
  for (const pattern of OWNERSHIP_PATTERNS) {
2000
- const ownerAddress = await readAddressFromContract(
2875
+ const ownerAddress = await readOwnerFromContract(
2001
2876
  params.provider,
2002
2877
  subjectAddress,
2003
2878
  pattern.signature,
@@ -2091,8 +2966,9 @@ exports.createX402ReceiptProof = createX402ReceiptProof;
2091
2966
  exports.decodeAttestationData = decodeAttestationData;
2092
2967
  exports.deduplicateReviews = deduplicateReviews;
2093
2968
  exports.encodeAttestationData = encodeAttestationData;
2094
- exports.extractAddressesFromDidDocument = extractAddressesFromDidDocument;
2969
+ exports.extractEvmAddressesFromDidDocument = extractEvmAddressesFromDidDocument;
2095
2970
  exports.extractExpirationTime = extractExpirationTime;
2971
+ exports.extractJwksFromDidDocument = extractJwksFromDidDocument;
2096
2972
  exports.fetchDidDocument = fetchDidDocument;
2097
2973
  exports.formatSchemaUid = formatSchemaUid;
2098
2974
  exports.formatTransferAmount = formatTransferAmount;
@@ -2113,6 +2989,7 @@ exports.listAttestations = listAttestations;
2113
2989
  exports.normalizeSchema = normalizeSchema;
2114
2990
  exports.parseDnsTxtRecord = parseDnsTxtRecord;
2115
2991
  exports.prepareDelegatedAttestation = prepareDelegatedAttestation;
2992
+ exports.requestControllerWitness = requestControllerWitness;
2116
2993
  exports.revokeAttestation = revokeAttestation;
2117
2994
  exports.schemaToString = schemaToString;
2118
2995
  exports.splitSignature = splitSignature;