@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
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var ethers = require('ethers');
4
+ var jose = require('jose');
4
5
  var canonicalize = require('canonicalize');
5
6
  var easSdk = require('@ethereum-attestation-service/eas-sdk');
6
7
  var promises = require('dns/promises');
@@ -56,6 +57,7 @@ function asError(err) {
56
57
  // src/identity/index.ts
57
58
  var identity_exports = {};
58
59
  __export(identity_exports, {
60
+ assertBareDid: () => assertBareDid,
59
61
  buildCaip10: () => buildCaip10,
60
62
  buildCaip2: () => buildCaip2,
61
63
  buildDidPkh: () => buildDidPkh,
@@ -66,27 +68,43 @@ __export(identity_exports, {
66
68
  canonicalizeJson: () => canonicalizeJson,
67
69
  computeDidAddress: () => computeDidAddress,
68
70
  computeDidHash: () => computeDidHash,
71
+ computeJwkThumbprint: () => computeJwkThumbprint,
72
+ didJwkToJwk: () => didJwkToJwk,
69
73
  didToAddress: () => didToAddress,
70
74
  extractAddressFromDid: () => extractAddressFromDid,
75
+ extractAuthorizationMetadata: () => extractAuthorizationMetadata,
76
+ extractControllerEvmAddress: () => extractControllerEvmAddress,
71
77
  extractDidIdentifier: () => extractDidIdentifier,
72
78
  extractDidMethod: () => extractDidMethod,
79
+ formatJktValue: () => formatJktValue,
73
80
  getAddressFromDidPkh: () => getAddressFromDidPkh,
74
81
  getChainIdFromDidPkh: () => getChainIdFromDidPkh,
75
82
  getDomainFromDidWeb: () => getDomainFromDidWeb,
76
83
  getNamespaceFromDidPkh: () => getNamespaceFromDidPkh,
77
84
  hashCanonicalizedJson: () => hashCanonicalizedJson,
85
+ isDidUrl: () => isDidUrl,
78
86
  isEvmDidPkh: () => isEvmDidPkh,
87
+ isPrivateKeyDid: () => isPrivateKeyDid,
88
+ isSameControllerId: () => isSameControllerId,
79
89
  isValidDid: () => isValidDid,
90
+ jwkToDidJwk: () => jwkToDidJwk,
80
91
  normalizeCaip10: () => normalizeCaip10,
81
92
  normalizeDid: () => normalizeDid,
82
93
  normalizeDidHandle: () => normalizeDidHandle,
94
+ normalizeDidJwk: () => normalizeDidJwk,
83
95
  normalizeDidKey: () => normalizeDidKey,
84
96
  normalizeDidPkh: () => normalizeDidPkh,
85
97
  normalizeDidWeb: () => normalizeDidWeb,
86
98
  normalizeDomain: () => normalizeDomain,
87
99
  parseCaip10: () => parseCaip10,
88
100
  parseCaip2: () => parseCaip2,
89
- validateDidAddress: () => validateDidAddress
101
+ parseDidUrl: () => parseDidUrl,
102
+ publicJwkEquals: () => publicJwkEquals,
103
+ resolveDidUrlToControllerDid: () => resolveDidUrlToControllerDid,
104
+ resolveDidUrlToPublicKey: () => resolveDidUrlToPublicKey,
105
+ validateDidAddress: () => validateDidAddress,
106
+ validatePrivateKeyDid: () => validatePrivateKeyDid,
107
+ validatePublicJwk: () => validatePublicJwk
90
108
  });
91
109
 
92
110
  // src/identity/caip.ts
@@ -234,6 +252,8 @@ function normalizeDid(input) {
234
252
  return normalizeDidHandle(trimmed);
235
253
  case "key":
236
254
  return normalizeDidKey(trimmed);
255
+ case "jwk":
256
+ return normalizeDidJwk(trimmed);
237
257
  default:
238
258
  return trimmed;
239
259
  }
@@ -337,6 +357,524 @@ function extractAddressFromDid(identifier) {
337
357
  }
338
358
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
339
359
  }
360
+ var CAIP2_NAMESPACE_REGEX = /^[a-z0-9-]{3,8}$/;
361
+ var BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
362
+ var VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
363
+ function isPrivateKeyDid(did) {
364
+ return validatePrivateKeyDid(did).valid;
365
+ }
366
+ function validatePrivateKeyDid(did) {
367
+ if (typeof did !== "string" || did.trim().length === 0) {
368
+ return { valid: false, method: null, error: "DID must be a non-empty string" };
369
+ }
370
+ const trimmed = did.trim();
371
+ const method = extractDidMethod(trimmed);
372
+ switch (method) {
373
+ case "pkh":
374
+ return validateDidPkh(trimmed);
375
+ case "jwk":
376
+ return validateDidJwk(trimmed);
377
+ default:
378
+ return {
379
+ valid: false,
380
+ method: null,
381
+ error: method ? `DID method "${method}" is not a recognized private-key method` : "Invalid DID format"
382
+ };
383
+ }
384
+ }
385
+ function validateDidPkh(did) {
386
+ const parts = did.split(":");
387
+ if (parts.length !== 5) {
388
+ return {
389
+ valid: false,
390
+ method: "pkh",
391
+ error: `did:pkh must have exactly 5 colon-separated parts, got ${parts.length}`
392
+ };
393
+ }
394
+ const [, , namespace, chainId, address] = parts;
395
+ if (!namespace) {
396
+ return { valid: false, method: "pkh", error: "Missing namespace" };
397
+ }
398
+ if (!CAIP2_NAMESPACE_REGEX.test(namespace)) {
399
+ return {
400
+ valid: false,
401
+ method: "pkh",
402
+ error: `Invalid CAIP-2 namespace "${namespace}" (must be 3-8 lowercase alphanumeric/hyphen chars)`
403
+ };
404
+ }
405
+ if (!chainId) {
406
+ return { valid: false, method: "pkh", error: "Missing chain ID (reference)" };
407
+ }
408
+ if (!address) {
409
+ return { valid: false, method: "pkh", error: "Missing address" };
410
+ }
411
+ if (namespace === "eip155") {
412
+ if (!/^\d+$/.test(chainId)) {
413
+ return {
414
+ valid: false,
415
+ method: "pkh",
416
+ error: `Invalid eip155 chain ID "${chainId}" (must be numeric)`
417
+ };
418
+ }
419
+ if (!ethers.isAddress(address)) {
420
+ return {
421
+ valid: false,
422
+ method: "pkh",
423
+ error: `Invalid EVM address "${address}" (must be 0x + 40 hex chars)`
424
+ };
425
+ }
426
+ }
427
+ return { valid: true, method: "pkh" };
428
+ }
429
+ function validateDidJwk(did) {
430
+ const parts = did.split(":");
431
+ if (parts.length !== 3) {
432
+ return {
433
+ valid: false,
434
+ method: "jwk",
435
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
436
+ };
437
+ }
438
+ const [, , encoded] = parts;
439
+ if (!encoded || encoded.length === 0) {
440
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
441
+ }
442
+ if (!BASE64URL_REGEX.test(encoded)) {
443
+ return {
444
+ valid: false,
445
+ method: "jwk",
446
+ error: "Identifier contains invalid base64url characters"
447
+ };
448
+ }
449
+ let decoded;
450
+ try {
451
+ const bytes = jose.base64url.decode(encoded);
452
+ decoded = new TextDecoder().decode(bytes);
453
+ } catch {
454
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
455
+ }
456
+ let jwk;
457
+ try {
458
+ jwk = JSON.parse(decoded);
459
+ } catch {
460
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
461
+ }
462
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
463
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
464
+ }
465
+ const kty = jwk.kty;
466
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
467
+ return {
468
+ valid: false,
469
+ method: "jwk",
470
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
471
+ };
472
+ }
473
+ if ("d" in jwk) {
474
+ return {
475
+ valid: false,
476
+ method: "jwk",
477
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
478
+ };
479
+ }
480
+ return { valid: true, method: "jwk" };
481
+ }
482
+ function normalizeDidJwk(input) {
483
+ assertString(input, "input", "INVALID_DID");
484
+ const trimmed = input.trim();
485
+ if (!trimmed.startsWith("did:jwk:")) {
486
+ throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
487
+ }
488
+ const result = validateDidJwk(trimmed);
489
+ if (!result.valid) {
490
+ throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
491
+ }
492
+ return trimmed;
493
+ }
494
+
495
+ // src/identity/did-url.ts
496
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
497
+ function parseDidUrl(input) {
498
+ assertString(input, "input", "INVALID_DID_URL");
499
+ const trimmed = input.trim();
500
+ const hashIndex = trimmed.indexOf("#");
501
+ let did;
502
+ let fragment;
503
+ if (hashIndex === -1) {
504
+ did = trimmed;
505
+ fragment = null;
506
+ } else {
507
+ did = trimmed.slice(0, hashIndex);
508
+ const rawFragment = trimmed.slice(hashIndex + 1);
509
+ if (rawFragment.length === 0) {
510
+ throw new OmaTrustError(
511
+ "INVALID_DID_URL",
512
+ "DID URL has empty fragment (trailing '#' with no identifier)",
513
+ { input }
514
+ );
515
+ }
516
+ fragment = rawFragment;
517
+ }
518
+ if (!DID_URL_REGEX.test(did)) {
519
+ throw new OmaTrustError(
520
+ "INVALID_DID_URL",
521
+ "Invalid DID URL: base DID portion is malformed",
522
+ { input, did }
523
+ );
524
+ }
525
+ return {
526
+ didUrl: trimmed,
527
+ did,
528
+ fragment
529
+ };
530
+ }
531
+ function isDidUrl(input) {
532
+ if (typeof input !== "string") return false;
533
+ const trimmed = input.trim();
534
+ return trimmed.includes("#") && DID_URL_REGEX.test(trimmed.split("#")[0]);
535
+ }
536
+ function assertBareDid(input, paramName = "did") {
537
+ assertString(input, paramName, "INVALID_DID");
538
+ if (isDidUrl(input)) {
539
+ throw new OmaTrustError(
540
+ "INVALID_DID",
541
+ `Expected a bare DID but received a DID URL with a fragment. Use parseDidUrl() to handle DID URLs.`,
542
+ { input }
543
+ );
544
+ }
545
+ }
546
+ var VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
547
+ var PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
548
+ var REQUIRED_PUBLIC_FIELDS = {
549
+ EC: ["crv", "x", "y"],
550
+ OKP: ["crv", "x"],
551
+ RSA: ["n", "e"]
552
+ };
553
+ function validatePublicJwk(jwk) {
554
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
555
+ return { valid: false, error: "JWK must be a non-null object" };
556
+ }
557
+ const obj = jwk;
558
+ const kty = obj.kty;
559
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
560
+ return {
561
+ valid: false,
562
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
563
+ };
564
+ }
565
+ for (const field of PRIVATE_KEY_FIELDS) {
566
+ if (field in obj) {
567
+ return {
568
+ valid: false,
569
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
570
+ };
571
+ }
572
+ }
573
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
574
+ if (required) {
575
+ for (const field of required) {
576
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
577
+ return {
578
+ valid: false,
579
+ error: `Missing required public key field "${field}" for kty="${kty}"`
580
+ };
581
+ }
582
+ }
583
+ }
584
+ return { valid: true };
585
+ }
586
+ function canonicalizeJwkJson(jwk) {
587
+ const sorted = Object.keys(jwk).sort();
588
+ const obj = {};
589
+ for (const key of sorted) {
590
+ obj[key] = jwk[key];
591
+ }
592
+ return JSON.stringify(obj);
593
+ }
594
+ function jwkToDidJwk(jwk) {
595
+ assertObject(jwk, "jwk", "INVALID_JWK");
596
+ const validation = validatePublicJwk(jwk);
597
+ if (!validation.valid) {
598
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
599
+ }
600
+ const canonical = canonicalizeJwkJson(jwk);
601
+ const encoded = jose.base64url.encode(new TextEncoder().encode(canonical));
602
+ return `did:jwk:${encoded}`;
603
+ }
604
+ function didJwkToJwk(didJwk) {
605
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
606
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
607
+ }
608
+ const parts = didJwk.split(":");
609
+ if (parts.length !== 3) {
610
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
611
+ input: didJwk
612
+ });
613
+ }
614
+ const encoded = parts[2];
615
+ if (!encoded || encoded.length === 0) {
616
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
617
+ input: didJwk
618
+ });
619
+ }
620
+ let decoded;
621
+ try {
622
+ const bytes = jose.base64url.decode(encoded);
623
+ decoded = new TextDecoder().decode(bytes);
624
+ } catch {
625
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
626
+ input: didJwk
627
+ });
628
+ }
629
+ let jwk;
630
+ try {
631
+ jwk = JSON.parse(decoded);
632
+ } catch {
633
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
634
+ input: didJwk
635
+ });
636
+ }
637
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
638
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
639
+ input: didJwk
640
+ });
641
+ }
642
+ const validation = validatePublicJwk(jwk);
643
+ if (!validation.valid) {
644
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
645
+ input: didJwk
646
+ });
647
+ }
648
+ return jwk;
649
+ }
650
+ function extractPublicKeyFields(jwk) {
651
+ const result = {};
652
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
653
+ for (const [key, value] of Object.entries(jwk)) {
654
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
655
+ if (metadataFields.has(key)) continue;
656
+ result[key] = value;
657
+ }
658
+ return result;
659
+ }
660
+ function publicJwkEquals(a, b) {
661
+ assertObject(a, "a", "INVALID_JWK");
662
+ assertObject(b, "b", "INVALID_JWK");
663
+ const aObj = a;
664
+ const bObj = b;
665
+ for (const field of PRIVATE_KEY_FIELDS) {
666
+ if (field in aObj) {
667
+ throw new OmaTrustError(
668
+ "INVALID_JWK",
669
+ `First JWK contains private key field "${field}"`,
670
+ { field }
671
+ );
672
+ }
673
+ if (field in bObj) {
674
+ throw new OmaTrustError(
675
+ "INVALID_JWK",
676
+ `Second JWK contains private key field "${field}"`,
677
+ { field }
678
+ );
679
+ }
680
+ }
681
+ const aPublic = extractPublicKeyFields(aObj);
682
+ const bPublic = extractPublicKeyFields(bObj);
683
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
684
+ }
685
+ async function computeJwkThumbprint(jwk, digestAlgorithm = "sha256") {
686
+ assertObject(jwk, "jwk", "INVALID_JWK");
687
+ const validation = validatePublicJwk(jwk);
688
+ if (!validation.valid) {
689
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
690
+ }
691
+ const alg = digestAlgorithm === "sha256" ? "sha256" : digestAlgorithm === "sha384" ? "sha384" : "sha512";
692
+ return jose.calculateJwkThumbprint(jwk, alg);
693
+ }
694
+ async function formatJktValue(jwk) {
695
+ const thumbprint = await computeJwkThumbprint(jwk, "sha256");
696
+ return `jkt=S256:${thumbprint}`;
697
+ }
698
+
699
+ // src/shared/did-document.ts
700
+ async function fetchDidDocument(domain) {
701
+ const normalized = domain.toLowerCase().replace(/\.$/, "");
702
+ const url = `https://${normalized}/.well-known/did.json`;
703
+ let response;
704
+ try {
705
+ response = await fetch(url, { headers: { Accept: "application/json" } });
706
+ } catch (err) {
707
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
708
+ }
709
+ if (!response.ok) {
710
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
711
+ domain,
712
+ status: response.status
713
+ });
714
+ }
715
+ return await response.json();
716
+ }
717
+
718
+ // src/identity/resolve-key.ts
719
+ function findVerificationMethod(didDocument, didUrl, fragment) {
720
+ const methods = didDocument.verificationMethod;
721
+ if (!Array.isArray(methods)) {
722
+ return null;
723
+ }
724
+ for (const method of methods) {
725
+ if (!method || typeof method !== "object") continue;
726
+ const id2 = method.id;
727
+ if (typeof id2 !== "string") continue;
728
+ if (id2 === didUrl) return method;
729
+ if (fragment && (id2 === `#${fragment}` || id2.endsWith(`#${fragment}`))) {
730
+ return method;
731
+ }
732
+ }
733
+ return null;
734
+ }
735
+ function extractMethodIdsFromDidDocument(didDocument) {
736
+ const methods = didDocument.verificationMethod;
737
+ if (!Array.isArray(methods)) return [];
738
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
739
+ }
740
+ async function resolveDidUrlToPublicKey(didUrl, options) {
741
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
742
+ const parsed = parseDidUrl(didUrl);
743
+ const method = extractDidMethod(parsed.did);
744
+ if (!method) {
745
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
746
+ didUrl,
747
+ did: parsed.did
748
+ });
749
+ }
750
+ switch (method) {
751
+ case "web":
752
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment, options);
753
+ default:
754
+ throw new OmaTrustError(
755
+ "UNSUPPORTED_DID_METHOD",
756
+ `DID URL key resolution is not supported for method "${method}"`,
757
+ { didUrl, method }
758
+ );
759
+ }
760
+ }
761
+ async function resolveDidUrlToControllerDid(didUrl, options) {
762
+ const resolved = await resolveDidUrlToPublicKey(didUrl, options);
763
+ const controllerDid = jwkToDidJwk(resolved.publicKeyJwk);
764
+ return {
765
+ ...resolved,
766
+ controllerDid
767
+ };
768
+ }
769
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
770
+ const domain = getDomainFromDidWeb(did);
771
+ if (!domain) {
772
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
773
+ didUrl,
774
+ did
775
+ });
776
+ }
777
+ const fetchFn = options?.fetchDidDocument ?? fetchDidDocument;
778
+ const didDocument = await fetchFn(domain);
779
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
780
+ if (!method) {
781
+ throw new OmaTrustError(
782
+ "KEY_NOT_FOUND",
783
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
784
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
785
+ );
786
+ }
787
+ const publicKeyJwk = method.publicKeyJwk;
788
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
789
+ throw new OmaTrustError(
790
+ "KEY_NOT_FOUND",
791
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
792
+ { didUrl, verificationMethodId: method.id }
793
+ );
794
+ }
795
+ const validation = validatePublicJwk(publicKeyJwk);
796
+ if (!validation.valid) {
797
+ throw new OmaTrustError(
798
+ "INVALID_JWK",
799
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
800
+ { didUrl, verificationMethodId: method.id }
801
+ );
802
+ }
803
+ return {
804
+ didUrl,
805
+ did,
806
+ fragment,
807
+ publicKeyJwk,
808
+ verificationMethodId: method.id
809
+ };
810
+ }
811
+
812
+ // src/identity/controller-id.ts
813
+ function isSameControllerId(a, b) {
814
+ let normalizedA = null;
815
+ let normalizedB = null;
816
+ try {
817
+ normalizedA = normalizeDid(a);
818
+ } catch {
819
+ }
820
+ try {
821
+ normalizedB = normalizeDid(b);
822
+ } catch {
823
+ }
824
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
825
+ return true;
826
+ }
827
+ const evmA = extractControllerEvmAddress(a);
828
+ const evmB = extractControllerEvmAddress(b);
829
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
830
+ return true;
831
+ }
832
+ const methodA = extractDidMethod(a);
833
+ const methodB = extractDidMethod(b);
834
+ if (methodA === "jwk" && methodB === "jwk") {
835
+ try {
836
+ const jwkA = didJwkToJwk(a);
837
+ const jwkB = didJwkToJwk(b);
838
+ return publicJwkEquals(jwkA, jwkB);
839
+ } catch {
840
+ return false;
841
+ }
842
+ }
843
+ return false;
844
+ }
845
+ function extractControllerEvmAddress(controllerDid) {
846
+ try {
847
+ const method = extractDidMethod(controllerDid);
848
+ if (method === "pkh" && controllerDid.includes("eip155")) {
849
+ return extractAddressFromDid(controllerDid);
850
+ }
851
+ } catch {
852
+ }
853
+ return null;
854
+ }
855
+
856
+ // src/identity/types.ts
857
+ function extractAuthorizationMetadata(result) {
858
+ const payload = result.payload;
859
+ const resourceUrl = typeof payload.resourceUrl === "string" ? payload.resourceUrl : null;
860
+ const issuedAt = typeof payload.issuedAt === "string" ? payload.issuedAt : null;
861
+ let subjectDid = null;
862
+ if (resourceUrl) {
863
+ try {
864
+ const url = new URL(resourceUrl);
865
+ subjectDid = `did:web:${url.hostname}`;
866
+ } catch {
867
+ }
868
+ }
869
+ return {
870
+ controllerDid: result.publicKeyDid,
871
+ subjectDid,
872
+ resourceUrl,
873
+ issuedAt,
874
+ kid: result.kid,
875
+ publicKeyJwk: result.publicKeyJwk
876
+ };
877
+ }
340
878
  function canonicalizeJson(obj) {
341
879
  const jcs = canonicalize__default.default(obj);
342
880
  if (!jcs) {
@@ -360,6 +898,8 @@ function hashCanonicalizedJson(obj, algorithm) {
360
898
  // src/reputation/index.ts
361
899
  var reputation_exports = {};
362
900
  __export(reputation_exports, {
901
+ EIP1967_ADMIN_SLOT: () => EIP1967_ADMIN_SLOT,
902
+ OWNERSHIP_PATTERNS: () => OWNERSHIP_PATTERNS,
363
903
  buildDelegatedAttestationTypedData: () => buildDelegatedAttestationTypedData,
364
904
  buildDelegatedTypedDataFromEncoded: () => buildDelegatedTypedDataFromEncoded,
365
905
  buildDnsTxtRecord: () => buildDnsTxtRecord,
@@ -378,9 +918,12 @@ __export(reputation_exports, {
378
918
  createX402ReceiptProof: () => createX402ReceiptProof,
379
919
  decodeAttestationData: () => decodeAttestationData,
380
920
  deduplicateReviews: () => deduplicateReviews,
921
+ discoverContractOwner: () => discoverContractOwner,
922
+ discoverControllingWalletDid: () => discoverControllingWalletDid,
381
923
  encodeAttestationData: () => encodeAttestationData,
382
- extractAddressesFromDidDocument: () => extractAddressesFromDidDocument,
924
+ extractEvmAddressesFromDidDocument: () => extractEvmAddressesFromDidDocument,
383
925
  extractExpirationTime: () => extractExpirationTime,
926
+ extractJwksFromDidDocument: () => extractJwksFromDidDocument,
384
927
  fetchDidDocument: () => fetchDidDocument,
385
928
  formatSchemaUid: () => formatSchemaUid,
386
929
  formatTransferAmount: () => formatTransferAmount,
@@ -388,6 +931,7 @@ __export(reputation_exports, {
388
931
  getAttestationsByAttester: () => getAttestationsByAttester,
389
932
  getAttestationsForDid: () => getAttestationsForDid,
390
933
  getChainConstants: () => getChainConstants,
934
+ getControllerAuthorization: () => getControllerAuthorization,
391
935
  getExplorerAddressUrl: () => getExplorerAddressUrl,
392
936
  getExplorerTxUrl: () => getExplorerTxUrl,
393
937
  getLatestAttestations: () => getLatestAttestations,
@@ -401,6 +945,8 @@ __export(reputation_exports, {
401
945
  normalizeSchema: () => normalizeSchema,
402
946
  parseDnsTxtRecord: () => parseDnsTxtRecord,
403
947
  prepareDelegatedAttestation: () => prepareDelegatedAttestation,
948
+ readOwnerFromContract: () => readOwnerFromContract,
949
+ requestControllerWitness: () => requestControllerWitness,
404
950
  revokeAttestation: () => revokeAttestation,
405
951
  schemaToString: () => schemaToString,
406
952
  splitSignature: () => splitSignature,
@@ -412,11 +958,18 @@ __export(reputation_exports, {
412
958
  verifyDidJsonControllerDid: () => verifyDidJsonControllerDid,
413
959
  verifyDidPkhOwnership: () => verifyDidPkhOwnership,
414
960
  verifyDidWebOwnership: () => verifyDidWebOwnership,
415
- verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid,
961
+ verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid2,
416
962
  verifyEip712Signature: () => verifyEip712Signature,
417
963
  verifyProof: () => verifyProof,
418
964
  verifySchemaExists: () => verifySchemaExists,
419
- verifySubjectOwnership: () => verifySubjectOwnership
965
+ verifySubjectOwnership: () => verifySubjectOwnership,
966
+ verifyTransferProof: () => verifyTransferProof,
967
+ verifyX402Eip712Artifact: () => verifyX402Eip712Artifact,
968
+ verifyX402Eip712Offer: () => verifyX402Eip712Offer,
969
+ verifyX402Eip712Receipt: () => verifyX402Eip712Receipt,
970
+ verifyX402JwsArtifact: () => verifyX402JwsArtifact,
971
+ verifyX402JwsOffer: () => verifyX402JwsOffer,
972
+ verifyX402JwsReceipt: () => verifyX402JwsReceipt
420
973
  });
421
974
  function normalizeSchema(schema) {
422
975
  if (Array.isArray(schema)) {
@@ -875,19 +1428,32 @@ async function submitDelegatedAttestation(params) {
875
1428
  payload = {};
876
1429
  }
877
1430
  if (!response.ok) {
878
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
879
- status: response.status,
1431
+ const relayError = {
1432
+ httpStatus: response.status,
1433
+ code: payload.code,
1434
+ error: payload.error ?? payload.message,
880
1435
  payload
881
- });
1436
+ };
1437
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
882
1438
  }
883
1439
  const uid = payload.uid ?? ZERO_UID;
884
1440
  const txHash = payload.txHash;
885
- const status = payload.status ?? "submitted";
886
- return {
887
- uid,
888
- txHash,
889
- status
890
- };
1441
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
1442
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
1443
+ const relay = {};
1444
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
1445
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
1446
+ if (typeof payload.success === "boolean") relay.success = payload.success;
1447
+ for (const key of Object.keys(payload)) {
1448
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
1449
+ relay[key] = payload[key];
1450
+ }
1451
+ }
1452
+ const result = { uid, txHash, status };
1453
+ if (Object.keys(relay).length > 0) {
1454
+ result.relay = relay;
1455
+ }
1456
+ return result;
891
1457
  }
892
1458
  var EAS_EVENT_ABI = [
893
1459
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
@@ -989,7 +1555,7 @@ async function getAttestationsForDid(params) {
989
1555
  provider,
990
1556
  fromBlock,
991
1557
  toBlock,
992
- { recipient: didToAddress(params.did), schemas: params.schemas }
1558
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
993
1559
  );
994
1560
  }
995
1561
  async function getAttestationsByAttester(params) {
@@ -1230,25 +1796,7 @@ function getExplorerTxUrl(chainId, txHash) {
1230
1796
  function getExplorerAddressUrl(chainId, address) {
1231
1797
  return `${getConfig(chainId).explorer}/address/${address}`;
1232
1798
  }
1233
- async function fetchDidDocument(domain) {
1234
- const normalized = domain.toLowerCase().replace(/\.$/, "");
1235
- const url = `https://${normalized}/.well-known/did.json`;
1236
- let response;
1237
- try {
1238
- response = await fetch(url, { headers: { Accept: "application/json" } });
1239
- } catch (err) {
1240
- throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
1241
- }
1242
- if (!response.ok) {
1243
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1244
- domain,
1245
- status: response.status
1246
- });
1247
- }
1248
- const body = await response.json();
1249
- return body;
1250
- }
1251
- function extractAddressesFromDidDocument(didDocument) {
1799
+ function extractEvmAddressesFromDidDocument(didDocument) {
1252
1800
  const methods = didDocument.verificationMethod;
1253
1801
  if (!Array.isArray(methods)) {
1254
1802
  return [];
@@ -1272,21 +1820,59 @@ function extractAddressesFromDidDocument(didDocument) {
1272
1820
  }
1273
1821
  return [...addresses];
1274
1822
  }
1275
- function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1276
- let expectedAddress;
1277
- try {
1278
- expectedAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1279
- } catch {
1280
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
1823
+ function extractJwksFromDidDocument(didDocument) {
1824
+ const methods = didDocument.verificationMethod;
1825
+ if (!Array.isArray(methods)) {
1826
+ return [];
1281
1827
  }
1282
- const addresses = extractAddressesFromDidDocument(didDocument);
1283
- if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1284
- return { valid: true };
1828
+ const jwks = [];
1829
+ for (const method of methods) {
1830
+ const publicKeyJwk = method.publicKeyJwk;
1831
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
1832
+ jwks.push(publicKeyJwk);
1833
+ }
1285
1834
  }
1286
- return {
1287
- valid: false,
1288
- reason: `No matching address found in DID document (expected ${expectedAddress})`
1289
- };
1835
+ return jwks;
1836
+ }
1837
+ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1838
+ const method = extractDidMethod(expectedControllerDid);
1839
+ if (method === "jwk") {
1840
+ try {
1841
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
1842
+ const documentJwks = extractJwksFromDidDocument(didDocument);
1843
+ for (const docJwk of documentJwks) {
1844
+ try {
1845
+ if (publicJwkEquals(docJwk, expectedJwk)) {
1846
+ return { valid: true };
1847
+ }
1848
+ } catch {
1849
+ }
1850
+ }
1851
+ return {
1852
+ valid: false,
1853
+ reason: "No matching publicKeyJwk found in DID document verification methods"
1854
+ };
1855
+ } catch {
1856
+ return {
1857
+ valid: false,
1858
+ reason: "Failed to decode did:jwk controller DID"
1859
+ };
1860
+ }
1861
+ }
1862
+ let expectedAddress;
1863
+ try {
1864
+ expectedAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1865
+ } catch {
1866
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
1867
+ }
1868
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
1869
+ if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1870
+ return { valid: true };
1871
+ }
1872
+ return {
1873
+ valid: false,
1874
+ reason: `No matching address found in DID document (expected ${expectedAddress})`
1875
+ };
1290
1876
  }
1291
1877
  async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1292
1878
  if (!domain || typeof domain !== "string") {
@@ -1335,27 +1921,353 @@ function parseDnsTxtRecord(record) {
1335
1921
  }
1336
1922
  const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
1337
1923
  const parsed = {};
1924
+ const controllers = [];
1338
1925
  for (const entry of entries) {
1339
- const [key, ...valueParts] = entry.split("=");
1340
- if (!key) {
1341
- continue;
1342
- }
1343
- const value = valueParts.join("=");
1344
- if (!value) {
1345
- continue;
1926
+ const eqIndex = entry.indexOf("=");
1927
+ if (eqIndex === -1) continue;
1928
+ const key = entry.slice(0, eqIndex).trim();
1929
+ const value = entry.slice(eqIndex + 1).trim();
1930
+ if (!key || !value) continue;
1931
+ if (key === "controller") {
1932
+ controllers.push(value);
1933
+ } else {
1934
+ parsed[key] = value;
1346
1935
  }
1347
- parsed[key.trim()] = value.trim();
1348
1936
  }
1349
1937
  return {
1350
1938
  version: parsed.v,
1351
- controller: parsed.controller,
1352
- ...parsed
1939
+ controller: controllers[0],
1940
+ controllers
1353
1941
  };
1354
1942
  }
1355
1943
  function buildDnsTxtRecord(controllerDid) {
1356
1944
  const normalized = normalizeDid(controllerDid);
1357
1945
  return `v=1;controller=${normalized}`;
1358
1946
  }
1947
+ var REQUIRED_OFFER_FIELDS = [
1948
+ "version",
1949
+ "resourceUrl",
1950
+ "scheme",
1951
+ "network",
1952
+ "asset",
1953
+ "payTo",
1954
+ "amount"
1955
+ ];
1956
+ var REQUIRED_RECEIPT_FIELDS = [
1957
+ "version",
1958
+ "network",
1959
+ "resourceUrl",
1960
+ "payer",
1961
+ "issuedAt"
1962
+ ];
1963
+ function validateOfferPayload(payload) {
1964
+ for (const field of REQUIRED_OFFER_FIELDS) {
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 validateReceiptPayload(payload) {
1972
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
1973
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1974
+ return { valid: false, field };
1975
+ }
1976
+ }
1977
+ return { valid: true };
1978
+ }
1979
+ function failure(code, message, partial) {
1980
+ return {
1981
+ valid: false,
1982
+ error: { code, message },
1983
+ ...partial
1984
+ };
1985
+ }
1986
+ async function verifyX402JwsArtifact(artifact, options) {
1987
+ if (!artifact || typeof artifact !== "object") {
1988
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
1989
+ }
1990
+ if (artifact.format !== "jws") {
1991
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
1992
+ }
1993
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1994
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
1995
+ }
1996
+ const compactJws = artifact.signature;
1997
+ const parts = compactJws.split(".");
1998
+ if (parts.length !== 3) {
1999
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
2000
+ }
2001
+ let header;
2002
+ try {
2003
+ header = jose.decodeProtectedHeader(compactJws);
2004
+ } catch {
2005
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
2006
+ }
2007
+ if (!header.alg || typeof header.alg !== "string") {
2008
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
2009
+ }
2010
+ const kid = typeof header.kid === "string" ? header.kid : null;
2011
+ const embeddedJwk = header.jwk;
2012
+ if (!kid && !embeddedJwk) {
2013
+ return failure(
2014
+ "MISSING_KEY_MATERIAL",
2015
+ "JWS header must include at least one of kid or jwk",
2016
+ { header }
2017
+ );
2018
+ }
2019
+ let publicKeyJwk;
2020
+ let publicKeySource;
2021
+ if (embeddedJwk) {
2022
+ const validation = validatePublicJwk(embeddedJwk);
2023
+ if (!validation.valid) {
2024
+ return failure(
2025
+ "INVALID_EMBEDDED_JWK",
2026
+ `Embedded JWK is invalid: ${validation.error}`,
2027
+ { header }
2028
+ );
2029
+ }
2030
+ publicKeyJwk = embeddedJwk;
2031
+ publicKeySource = "embedded-jwk";
2032
+ if (kid) {
2033
+ try {
2034
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
2035
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
2036
+ return failure(
2037
+ "KEY_CONFLICT",
2038
+ "Embedded jwk conflicts with public key resolved from kid",
2039
+ { header, kid }
2040
+ );
2041
+ }
2042
+ } catch (err) {
2043
+ }
2044
+ }
2045
+ } else {
2046
+ publicKeySource = "kid-resolution";
2047
+ try {
2048
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
2049
+ publicKeyJwk = resolved.publicKeyJwk;
2050
+ } catch (err) {
2051
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
2052
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
2053
+ }
2054
+ }
2055
+ let payload;
2056
+ try {
2057
+ const key = await jose.importJWK(publicKeyJwk, header.alg);
2058
+ const result = await jose.compactVerify(compactJws, key);
2059
+ const payloadText = new TextDecoder().decode(result.payload);
2060
+ payload = JSON.parse(payloadText);
2061
+ } catch (err) {
2062
+ const message = err instanceof Error ? err.message : "Signature verification failed";
2063
+ return failure("SIGNATURE_INVALID", message, { header, kid });
2064
+ }
2065
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
2066
+ return {
2067
+ valid: true,
2068
+ header,
2069
+ payload,
2070
+ kid,
2071
+ publicKeyJwk,
2072
+ publicKeySource,
2073
+ publicKeyDid
2074
+ };
2075
+ }
2076
+ async function verifyX402JwsOffer(artifact, options) {
2077
+ const result = await verifyX402JwsArtifact(artifact, options);
2078
+ if (!result.valid) return result;
2079
+ const payloadCheck = validateOfferPayload(result.payload);
2080
+ if (!payloadCheck.valid) {
2081
+ return failure(
2082
+ "INVALID_OFFER_PAYLOAD",
2083
+ `Missing required offer field: ${payloadCheck.field}`,
2084
+ {
2085
+ header: result.header,
2086
+ payload: result.payload,
2087
+ kid: result.kid,
2088
+ publicKeyJwk: result.publicKeyJwk,
2089
+ publicKeySource: result.publicKeySource,
2090
+ publicKeyDid: result.publicKeyDid
2091
+ }
2092
+ );
2093
+ }
2094
+ return result;
2095
+ }
2096
+ async function verifyX402JwsReceipt(artifact, options) {
2097
+ const result = await verifyX402JwsArtifact(artifact, options);
2098
+ if (!result.valid) return result;
2099
+ const payloadCheck = validateReceiptPayload(result.payload);
2100
+ if (!payloadCheck.valid) {
2101
+ return failure(
2102
+ "INVALID_RECEIPT_PAYLOAD",
2103
+ `Missing required receipt field: ${payloadCheck.field}`,
2104
+ {
2105
+ header: result.header,
2106
+ payload: result.payload,
2107
+ kid: result.kid,
2108
+ publicKeyJwk: result.publicKeyJwk,
2109
+ publicKeySource: result.publicKeySource,
2110
+ publicKeyDid: result.publicKeyDid
2111
+ }
2112
+ );
2113
+ }
2114
+ return result;
2115
+ }
2116
+ var OFFER_DOMAIN = {
2117
+ name: "x402 offer",
2118
+ version: "1",
2119
+ chainId: 1
2120
+ };
2121
+ var RECEIPT_DOMAIN = {
2122
+ name: "x402 receipt",
2123
+ version: "1",
2124
+ chainId: 1
2125
+ };
2126
+ var OFFER_TYPES = {
2127
+ Offer: [
2128
+ { name: "version", type: "uint256" },
2129
+ { name: "resourceUrl", type: "string" },
2130
+ { name: "scheme", type: "string" },
2131
+ { name: "network", type: "string" },
2132
+ { name: "asset", type: "string" },
2133
+ { name: "payTo", type: "string" },
2134
+ { name: "amount", type: "string" },
2135
+ { name: "validUntil", type: "uint256" }
2136
+ ]
2137
+ };
2138
+ var RECEIPT_TYPES = {
2139
+ Receipt: [
2140
+ { name: "version", type: "uint256" },
2141
+ { name: "network", type: "string" },
2142
+ { name: "resourceUrl", type: "string" },
2143
+ { name: "payer", type: "string" },
2144
+ { name: "issuedAt", type: "uint256" },
2145
+ { name: "transaction", type: "string" }
2146
+ ]
2147
+ };
2148
+ var REQUIRED_OFFER_FIELDS2 = [
2149
+ "version",
2150
+ "resourceUrl",
2151
+ "scheme",
2152
+ "network",
2153
+ "asset",
2154
+ "payTo",
2155
+ "amount"
2156
+ ];
2157
+ var REQUIRED_RECEIPT_FIELDS2 = [
2158
+ "version",
2159
+ "network",
2160
+ "resourceUrl",
2161
+ "payer",
2162
+ "issuedAt"
2163
+ ];
2164
+ function validateOfferPayload2(payload) {
2165
+ for (const field of REQUIRED_OFFER_FIELDS2) {
2166
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2167
+ return { valid: false, field };
2168
+ }
2169
+ }
2170
+ return { valid: true };
2171
+ }
2172
+ function validateReceiptPayload2(payload) {
2173
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
2174
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2175
+ return { valid: false, field };
2176
+ }
2177
+ }
2178
+ return { valid: true };
2179
+ }
2180
+ function failure2(code, message, partial) {
2181
+ return {
2182
+ valid: false,
2183
+ error: { code, message },
2184
+ ...partial
2185
+ };
2186
+ }
2187
+ function prepareOfferMessage(payload) {
2188
+ return {
2189
+ version: payload.version,
2190
+ resourceUrl: payload.resourceUrl,
2191
+ scheme: payload.scheme,
2192
+ network: payload.network,
2193
+ asset: payload.asset,
2194
+ payTo: payload.payTo,
2195
+ amount: payload.amount,
2196
+ validUntil: payload.validUntil ?? 0
2197
+ };
2198
+ }
2199
+ function prepareReceiptMessage(payload) {
2200
+ return {
2201
+ version: payload.version,
2202
+ network: payload.network,
2203
+ resourceUrl: payload.resourceUrl,
2204
+ payer: payload.payer,
2205
+ issuedAt: payload.issuedAt,
2206
+ transaction: payload.transaction ?? ""
2207
+ };
2208
+ }
2209
+ function verifyX402Eip712Artifact(artifact, artifactType) {
2210
+ if (!artifact || typeof artifact !== "object") {
2211
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
2212
+ }
2213
+ if (artifact.format !== "eip712") {
2214
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
2215
+ }
2216
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
2217
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
2218
+ }
2219
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2220
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
2221
+ }
2222
+ const payload = artifact.payload;
2223
+ if (artifactType === "offer") {
2224
+ const check = validateOfferPayload2(payload);
2225
+ if (!check.valid) {
2226
+ return failure2(
2227
+ "INVALID_OFFER_PAYLOAD",
2228
+ `Missing required offer field: ${check.field}`,
2229
+ { payload }
2230
+ );
2231
+ }
2232
+ } else {
2233
+ const check = validateReceiptPayload2(payload);
2234
+ if (!check.valid) {
2235
+ return failure2(
2236
+ "INVALID_RECEIPT_PAYLOAD",
2237
+ `Missing required receipt field: ${check.field}`,
2238
+ { payload }
2239
+ );
2240
+ }
2241
+ }
2242
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
2243
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
2244
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
2245
+ let signer;
2246
+ try {
2247
+ signer = ethers.verifyTypedData(
2248
+ domain,
2249
+ types,
2250
+ message,
2251
+ artifact.signature
2252
+ );
2253
+ signer = ethers.getAddress(signer);
2254
+ } catch (err) {
2255
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
2256
+ return failure2("SIGNATURE_INVALID", message2, { payload });
2257
+ }
2258
+ return {
2259
+ valid: true,
2260
+ payload,
2261
+ signer,
2262
+ artifactType
2263
+ };
2264
+ }
2265
+ function verifyX402Eip712Offer(artifact) {
2266
+ return verifyX402Eip712Artifact(artifact, "offer");
2267
+ }
2268
+ function verifyX402Eip712Receipt(artifact) {
2269
+ return verifyX402Eip712Artifact(artifact, "receipt");
2270
+ }
1359
2271
 
1360
2272
  // src/reputation/verify.ts
1361
2273
  function parseChainId(input) {
@@ -1414,18 +2326,18 @@ function decodeJwtPayload(compactJws) {
1414
2326
  return JSON.parse(json);
1415
2327
  }
1416
2328
  async function verifyProof(params) {
1417
- const { proof, provider, expectedSubject, expectedController } = params;
2329
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1418
2330
  try {
1419
2331
  switch (proof.proofType) {
1420
2332
  case "tx-encoded-value": {
1421
2333
  if (!provider) {
1422
2334
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1423
2335
  }
1424
- if (!expectedSubject || !expectedController) {
2336
+ if (!expectedSubjectDid || !expectedControllerDid) {
1425
2337
  return {
1426
2338
  valid: false,
1427
2339
  proofType: proof.proofType,
1428
- reason: "expectedSubject and expectedController are required"
2340
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1429
2341
  };
1430
2342
  }
1431
2343
  const proofObject = proof.proofObject;
@@ -1437,13 +2349,13 @@ async function verifyProof(params) {
1437
2349
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1438
2350
  }
1439
2351
  const expectedAmount = calculateTransferAmount(
1440
- expectedSubject,
1441
- expectedController,
2352
+ expectedSubjectDid,
2353
+ expectedControllerDid,
1442
2354
  chainId,
1443
2355
  getProofPurpose(proof)
1444
2356
  );
1445
- const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubject));
1446
- const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedController));
2357
+ const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubjectDid));
2358
+ const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1447
2359
  if (tx.from && ethers.getAddress(tx.from) !== subjectAddress) {
1448
2360
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1449
2361
  }
@@ -1517,6 +2429,38 @@ async function verifyProof(params) {
1517
2429
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1518
2430
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1519
2431
  }
2432
+ const proofObj = proof.proofObject;
2433
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2434
+ const artifact = {
2435
+ format: "jws",
2436
+ signature: proofObj.signature
2437
+ };
2438
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2439
+ if (!jwsResult.valid) {
2440
+ return {
2441
+ valid: false,
2442
+ proofType: proof.proofType,
2443
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2444
+ };
2445
+ }
2446
+ return { valid: true, proofType: proof.proofType };
2447
+ }
2448
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2449
+ const artifact = {
2450
+ format: "eip712",
2451
+ payload: proofObj.payload,
2452
+ signature: proofObj.signature
2453
+ };
2454
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2455
+ if (!eip712Result.valid) {
2456
+ return {
2457
+ valid: false,
2458
+ proofType: proof.proofType,
2459
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2460
+ };
2461
+ }
2462
+ return { valid: true, proofType: proof.proofType };
2463
+ }
1520
2464
  return { valid: true, proofType: proof.proofType };
1521
2465
  }
1522
2466
  case "evidence-pointer": {
@@ -1528,9 +2472,9 @@ async function verifyProof(params) {
1528
2472
  if (!response.ok) {
1529
2473
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1530
2474
  }
1531
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2475
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1532
2476
  const didDoc = await response.json();
1533
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2477
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1534
2478
  return {
1535
2479
  valid: didCheck.valid,
1536
2480
  proofType: proof.proofType,
@@ -1538,10 +2482,10 @@ async function verifyProof(params) {
1538
2482
  };
1539
2483
  }
1540
2484
  const body = await response.text();
1541
- if (expectedController && !body.includes(expectedController)) {
2485
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1542
2486
  try {
1543
2487
  const parsed = parseDnsTxtRecord(body);
1544
- if (parsed.controller !== expectedController) {
2488
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1545
2489
  return {
1546
2490
  valid: false,
1547
2491
  proofType: proof.proofType,
@@ -1597,8 +2541,8 @@ async function verifyAttestation(params) {
1597
2541
  const result = await verifyProof({
1598
2542
  proof,
1599
2543
  provider: params.provider,
1600
- expectedSubject: params.context?.subject,
1601
- expectedController: params.context?.controller
2544
+ expectedSubjectDid: params.context?.subjectDid,
2545
+ expectedControllerDid: params.context?.controllerDid
1602
2546
  });
1603
2547
  checks[proof.proofType] = result.valid;
1604
2548
  if (!result.valid) {
@@ -1701,6 +2645,460 @@ async function callControllerWitness(params) {
1701
2645
  method: "did-json"
1702
2646
  };
1703
2647
  }
2648
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
2649
+ async function requestControllerWitness(params) {
2650
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
2651
+ const body = {
2652
+ subjectDid: params.subjectDid,
2653
+ controllerDid: params.controllerDid
2654
+ };
2655
+ if (params.chainId !== void 0) {
2656
+ body.chainId = params.chainId;
2657
+ }
2658
+ let response;
2659
+ try {
2660
+ response = await fetch(url, {
2661
+ method: "POST",
2662
+ headers: { "Content-Type": "application/json" },
2663
+ credentials: "include",
2664
+ body: JSON.stringify(body),
2665
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
2666
+ });
2667
+ } catch (err) {
2668
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
2669
+ }
2670
+ if (!response.ok) {
2671
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
2672
+ throw new OmaTrustError(
2673
+ "API_ERROR",
2674
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
2675
+ { status: response.status, ...errorBody }
2676
+ );
2677
+ }
2678
+ return await response.json();
2679
+ }
2680
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
2681
+ var OWNERSHIP_PATTERNS = [
2682
+ { method: "owner", signature: "function owner() view returns (address)" },
2683
+ { method: "admin", signature: "function admin() view returns (address)" },
2684
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
2685
+ ];
2686
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
2687
+ try {
2688
+ const iface = new ethers.Interface([signature]);
2689
+ const data = iface.encodeFunctionData(method, []);
2690
+ const result = await provider.call({ to: contractAddress, data });
2691
+ const [value] = iface.decodeFunctionResult(method, result);
2692
+ if (typeof value === "string" && ethers.isAddress(value) && value !== ethers.ZeroAddress) {
2693
+ return ethers.getAddress(value);
2694
+ }
2695
+ } catch {
2696
+ }
2697
+ return null;
2698
+ }
2699
+ async function discoverContractOwner(provider, contractAddress) {
2700
+ try {
2701
+ const code = await provider.getCode(contractAddress);
2702
+ if (code === "0x" || code === "0x0") {
2703
+ return null;
2704
+ }
2705
+ } catch {
2706
+ return null;
2707
+ }
2708
+ for (const pattern of OWNERSHIP_PATTERNS) {
2709
+ const address = await readOwnerFromContract(
2710
+ provider,
2711
+ contractAddress,
2712
+ pattern.signature,
2713
+ pattern.method
2714
+ );
2715
+ if (address) {
2716
+ return address;
2717
+ }
2718
+ }
2719
+ try {
2720
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
2721
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
2722
+ const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
2723
+ if (adminAddress !== ethers.ZeroAddress) {
2724
+ return adminAddress;
2725
+ }
2726
+ }
2727
+ } catch {
2728
+ }
2729
+ return null;
2730
+ }
2731
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
2732
+ const owner = await discoverContractOwner(provider, contractAddress);
2733
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
2734
+ }
2735
+ async function verifyTransferProof(provider, txHash, subjectDid, attesterAddress, chainId) {
2736
+ try {
2737
+ const tx = await provider.getTransaction(txHash);
2738
+ if (!tx || !tx.from || !tx.to) {
2739
+ return false;
2740
+ }
2741
+ if (ethers.getAddress(tx.to) !== ethers.getAddress(attesterAddress)) {
2742
+ return false;
2743
+ }
2744
+ const attesterDid = buildEvmDidPkh(chainId, attesterAddress);
2745
+ const expectedAmount = calculateTransferAmount(
2746
+ subjectDid,
2747
+ attesterDid,
2748
+ chainId,
2749
+ "shared-control"
2750
+ );
2751
+ const actualValue = BigInt(tx.value ?? 0n);
2752
+ return actualValue === expectedAmount;
2753
+ } catch {
2754
+ return false;
2755
+ }
2756
+ }
2757
+
2758
+ // src/shared/trust-anchors.ts
2759
+ var DEFAULT_TRUST_ANCHORS_URL = "https://api.omatrust.org/v1/trust-anchors";
2760
+ var TRUST_ANCHORS_URL = DEFAULT_TRUST_ANCHORS_URL;
2761
+ var cachedAnchors = null;
2762
+ var cacheTimestamp = 0;
2763
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
2764
+ async function fetchTrustAnchors(url) {
2765
+ const now = Date.now();
2766
+ if (cachedAnchors && now - cacheTimestamp < CACHE_TTL_MS) {
2767
+ return cachedAnchors;
2768
+ }
2769
+ let res;
2770
+ try {
2771
+ res = await fetch(url ?? TRUST_ANCHORS_URL);
2772
+ } catch (err) {
2773
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch trust anchors", { err });
2774
+ }
2775
+ if (!res.ok) {
2776
+ throw new OmaTrustError("NETWORK_ERROR", `Failed to fetch trust anchors: ${res.status} ${res.statusText}`);
2777
+ }
2778
+ const anchors = await res.json();
2779
+ if (!anchors.version || !anchors.chains || typeof anchors.chains !== "object") {
2780
+ throw new OmaTrustError("INVALID_INPUT", "Invalid trust anchors format");
2781
+ }
2782
+ cachedAnchors = anchors;
2783
+ cacheTimestamp = now;
2784
+ return anchors;
2785
+ }
2786
+ function getChainAnchors(anchors, caip2) {
2787
+ const chain = anchors.chains[caip2];
2788
+ if (!chain) {
2789
+ throw new OmaTrustError("UNSUPPORTED_CHAIN", `Chain ${caip2} is not in the trust anchors`, {
2790
+ caip2,
2791
+ supportedChains: Object.keys(anchors.chains)
2792
+ });
2793
+ }
2794
+ return chain;
2795
+ }
2796
+
2797
+ // src/reputation/proof/dns-txt-shared.ts
2798
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
2799
+ if (!domain || typeof domain !== "string") {
2800
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
2801
+ }
2802
+ if (!options.resolveTxt) {
2803
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
2804
+ }
2805
+ const prefix = options.recordPrefix ?? "_controllers";
2806
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
2807
+ let records;
2808
+ try {
2809
+ records = await options.resolveTxt(host);
2810
+ } catch (err) {
2811
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
2812
+ }
2813
+ for (const recordParts of records) {
2814
+ const record = recordParts.join("");
2815
+ const parsed = parseDnsTxtRecord(record);
2816
+ if (parsed.version !== "1") continue;
2817
+ for (const recordController of parsed.controllers) {
2818
+ if (isSameControllerId(recordController, expectedControllerDid)) {
2819
+ return { valid: true, record };
2820
+ }
2821
+ }
2822
+ }
2823
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
2824
+ }
2825
+
2826
+ // src/reputation/attester-authorization.ts
2827
+ var DEFAULT_CHAIN = "eip155:6623";
2828
+ var DEFAULT_PURPOSES = ["authentication", "assertionMethod"];
2829
+ async function getControllerAuthorization(params) {
2830
+ const chain = params.chain ?? DEFAULT_CHAIN;
2831
+ const purposes = params.purpose && params.purpose.length > 0 ? params.purpose : DEFAULT_PURPOSES;
2832
+ const controllerDid = normalizeDid(params.controllerDid);
2833
+ const controllerMethod = extractDidMethod(controllerDid);
2834
+ const controllerEvmAddress = extractControllerEvmAddress(controllerDid);
2835
+ const anchors = await fetchTrustAnchors();
2836
+ const chainAnchors = getChainAnchors(anchors, chain);
2837
+ const easContractAddress = params.easContractAddress ?? chainAnchors.easContract;
2838
+ const provider = params.provider;
2839
+ const controllerWitnessSchemaUid = chainAnchors.schemas["controller-witness"];
2840
+ const keyBindingSchemaUid = chainAnchors.schemas["key-binding"];
2841
+ const subjectAddress = didToAddress(params.subjectDid);
2842
+ let relevantWitnesses = [];
2843
+ if (controllerWitnessSchemaUid) {
2844
+ const rawWitnesses = await queryByRecipientAndSchema(
2845
+ easContractAddress,
2846
+ provider,
2847
+ subjectAddress,
2848
+ [controllerWitnessSchemaUid],
2849
+ params.fromBlock ?? 0
2850
+ );
2851
+ relevantWitnesses = rawWitnesses.filter((att) => {
2852
+ const witnessController = att.data?.controller;
2853
+ if (typeof witnessController !== "string") return false;
2854
+ return isSameControllerId(witnessController, controllerDid);
2855
+ });
2856
+ relevantWitnesses.sort((a, b) => Number(a.time - b.time));
2857
+ }
2858
+ const controllerWitnesses = relevantWitnesses.map((att) => ({
2859
+ uid: att.uid,
2860
+ issuedAt: att.time,
2861
+ attester: att.attester,
2862
+ method: resolveWitnessMethod(att.data?.method)
2863
+ }));
2864
+ let keyBindingUid = null;
2865
+ let until = null;
2866
+ let keyPurposeStatus = "not-required";
2867
+ if (keyBindingSchemaUid) {
2868
+ const keyBindings = await queryByRecipientAndSchema(
2869
+ easContractAddress,
2870
+ provider,
2871
+ subjectAddress,
2872
+ [keyBindingSchemaUid],
2873
+ params.fromBlock ?? 0
2874
+ );
2875
+ const relevantKeyBindings = keyBindings.filter((att) => controllerMatchesKeyBinding(att, controllerDid, controllerMethod)).sort((a, b) => Number(b.time - a.time));
2876
+ const relevantKeyBinding = relevantKeyBindings[0] ?? null;
2877
+ if (relevantKeyBinding) {
2878
+ keyBindingUid = relevantKeyBinding.uid;
2879
+ if (relevantKeyBinding.revocationTime > 0n) {
2880
+ until = relevantKeyBinding.revocationTime;
2881
+ }
2882
+ const keyPurposes = relevantKeyBinding.data?.keyPurpose;
2883
+ if (!Array.isArray(keyPurposes) || keyPurposes.length === 0) {
2884
+ keyPurposeStatus = "unknown";
2885
+ } else {
2886
+ const allMatched = purposes.every((p) => keyPurposes.includes(p));
2887
+ keyPurposeStatus = allMatched ? "matched" : "mismatch";
2888
+ }
2889
+ }
2890
+ }
2891
+ let currentlyVerified = false;
2892
+ let liveMethod = null;
2893
+ let transferProofVerified = false;
2894
+ let transferProofAnchor = null;
2895
+ const subjectMethod = extractDidMethod(params.subjectDid);
2896
+ if (subjectMethod === "web") {
2897
+ const domain = getDomainFromDidWeb(params.subjectDid);
2898
+ if (domain) {
2899
+ try {
2900
+ const dnsResult = await verifyDnsTxtControllerDid(domain, controllerDid, {
2901
+ resolveTxt: params.resolveTxt
2902
+ });
2903
+ if (dnsResult.valid) {
2904
+ currentlyVerified = true;
2905
+ liveMethod = "dns";
2906
+ }
2907
+ } catch {
2908
+ }
2909
+ if (!currentlyVerified) {
2910
+ try {
2911
+ const fetchDoc = params.fetchDidDocument ?? fetchDidDocument;
2912
+ const didDoc = await fetchDoc(domain);
2913
+ const didJsonResult = verifyDidDocumentControllerDid(didDoc, controllerDid);
2914
+ if (didJsonResult.valid) {
2915
+ currentlyVerified = true;
2916
+ liveMethod = "did-document";
2917
+ }
2918
+ } catch {
2919
+ }
2920
+ }
2921
+ }
2922
+ } else if (subjectMethod === "pkh") {
2923
+ if (isEvmDidPkh(params.subjectDid) && controllerEvmAddress) {
2924
+ const subjectContractAddress = getAddressFromDidPkh(params.subjectDid);
2925
+ const subjectChainId = getChainIdFromDidPkh(params.subjectDid);
2926
+ const chainIdNum = subjectChainId ? Number(subjectChainId) : null;
2927
+ if (subjectContractAddress && chainIdNum) {
2928
+ const ownerAddress = await discoverContractOwner(
2929
+ provider,
2930
+ subjectContractAddress
2931
+ );
2932
+ if (ownerAddress && ethers.getAddress(ownerAddress) === ethers.getAddress(controllerEvmAddress)) {
2933
+ currentlyVerified = true;
2934
+ liveMethod = "contract-ownership";
2935
+ }
2936
+ if (!currentlyVerified && keyBindingUid) {
2937
+ const relevantKeyBinding = await findKeyBindingWithTransferProof(
2938
+ keyBindingSchemaUid,
2939
+ easContractAddress,
2940
+ provider,
2941
+ subjectAddress,
2942
+ controllerDid,
2943
+ controllerMethod,
2944
+ params.fromBlock ?? 0
2945
+ );
2946
+ if (relevantKeyBinding) {
2947
+ const proofTxHash = relevantKeyBinding.data?.proofTxHash;
2948
+ if (proofTxHash && /^0x[0-9a-fA-F]{64}$/.test(proofTxHash)) {
2949
+ const verified = await verifyTransferProof(
2950
+ provider,
2951
+ proofTxHash,
2952
+ params.subjectDid,
2953
+ controllerEvmAddress,
2954
+ chainIdNum
2955
+ );
2956
+ if (verified) {
2957
+ transferProofVerified = true;
2958
+ transferProofAnchor = relevantKeyBinding.time;
2959
+ }
2960
+ }
2961
+ }
2962
+ }
2963
+ }
2964
+ }
2965
+ }
2966
+ const witnessAnchor = relevantWitnesses.length > 0 ? relevantWitnesses[0].time : null;
2967
+ const anchoredFrom = witnessAnchor ?? transferProofAnchor;
2968
+ const purposeBlocks = keyPurposeStatus === "mismatch";
2969
+ const hasOpenWindow = relevantWitnesses.length > 0 && until === null;
2970
+ const hasKeyBindingWithProof = keyBindingUid !== null && transferProofVerified && until === null;
2971
+ const authorized = !purposeBlocks && (hasOpenWindow || currentlyVerified && until === null || hasKeyBindingWithProof);
2972
+ return {
2973
+ authorized,
2974
+ anchoredFrom,
2975
+ until,
2976
+ currentlyVerified,
2977
+ liveMethod,
2978
+ controllerWitnesses,
2979
+ keyBindingUid,
2980
+ keyPurposeStatus,
2981
+ transferProofVerified: transferProofVerified || void 0
2982
+ };
2983
+ }
2984
+ function controllerMatchesKeyBinding(att, controllerDid, controllerMethod) {
2985
+ const keyId = att.data?.keyId;
2986
+ if (typeof keyId === "string") {
2987
+ if (isSameControllerId(keyId, controllerDid)) {
2988
+ return true;
2989
+ }
2990
+ }
2991
+ if (controllerMethod === "jwk") {
2992
+ const publicKeyJwkRaw = att.data?.publicKeyJwk;
2993
+ if (publicKeyJwkRaw) {
2994
+ try {
2995
+ const onChainJwk = typeof publicKeyJwkRaw === "string" ? JSON.parse(publicKeyJwkRaw) : publicKeyJwkRaw;
2996
+ const controllerJwk = didJwkToJwk(controllerDid);
2997
+ return publicJwkEquals(onChainJwk, controllerJwk);
2998
+ } catch {
2999
+ }
3000
+ }
3001
+ }
3002
+ return false;
3003
+ }
3004
+ var EAS_EVENT_ABI2 = [
3005
+ "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
3006
+ ];
3007
+ function resolveWitnessMethod(value) {
3008
+ if (typeof value !== "string") return void 0;
3009
+ switch (value) {
3010
+ case "dns":
3011
+ case "dns-txt":
3012
+ return "dns";
3013
+ case "did-document":
3014
+ case "did-json":
3015
+ return "did-document";
3016
+ case "manual":
3017
+ return "manual";
3018
+ default:
3019
+ return "other";
3020
+ }
3021
+ }
3022
+ async function queryByRecipientAndSchema(easContractAddress, provider, recipient, schemas, fromBlock) {
3023
+ const contract = new ethers.Contract(easContractAddress, EAS_EVENT_ABI2, provider);
3024
+ const filter = contract.filters.Attested(recipient, null);
3025
+ const toBlock = await provider.getBlockNumber();
3026
+ let events;
3027
+ try {
3028
+ events = await contract.queryFilter(filter, fromBlock, toBlock);
3029
+ } catch (err) {
3030
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
3031
+ }
3032
+ const eas = new easSdk.EAS(easContractAddress);
3033
+ eas.connect(provider);
3034
+ const schemaFilter = schemas.map((s) => s.toLowerCase());
3035
+ const results = [];
3036
+ for (const event of events) {
3037
+ if (!("args" in event) || !Array.isArray(event.args)) continue;
3038
+ const args = event.args;
3039
+ const uid = args?.[2];
3040
+ const schemaUid = args?.[3];
3041
+ if (!uid || !schemaUid) continue;
3042
+ if (!schemaFilter.includes(schemaUid.toLowerCase())) continue;
3043
+ const attestation = await eas.getAttestation(uid);
3044
+ if (!attestation || !attestation.uid) continue;
3045
+ let data = {};
3046
+ const rawData = attestation.data;
3047
+ if (rawData && rawData !== "0x") {
3048
+ try {
3049
+ data = decodeAttestationData(
3050
+ "string subject, string controller, string method",
3051
+ rawData
3052
+ );
3053
+ } catch {
3054
+ try {
3055
+ data = decodeAttestationData(
3056
+ "string subject, string keyId, string publicKeyJwk, string[] keyPurpose, string[] proofs, uint256 issuedAt, uint256 effectiveAt, uint256 expiresAt",
3057
+ rawData
3058
+ );
3059
+ } catch {
3060
+ }
3061
+ }
3062
+ }
3063
+ const toBigIntSafe = (value) => {
3064
+ if (typeof value === "bigint") return value;
3065
+ if (typeof value === "number") return BigInt(value);
3066
+ if (typeof value === "string" && value.length > 0) return BigInt(value);
3067
+ return 0n;
3068
+ };
3069
+ results.push({
3070
+ uid: attestation.uid,
3071
+ schema: attestation.schema,
3072
+ attester: attestation.attester,
3073
+ recipient: attestation.recipient,
3074
+ revocable: Boolean(attestation.revocable),
3075
+ revocationTime: toBigIntSafe(attestation.revocationTime),
3076
+ expirationTime: toBigIntSafe(attestation.expirationTime),
3077
+ time: toBigIntSafe(attestation.time),
3078
+ refUID: attestation.refUID,
3079
+ data
3080
+ });
3081
+ }
3082
+ return results;
3083
+ }
3084
+ async function findKeyBindingWithTransferProof(keyBindingSchemaUid, easContractAddress, provider, subjectAddress, controllerDid, controllerMethod, fromBlock) {
3085
+ const keyBindings = await queryByRecipientAndSchema(
3086
+ easContractAddress,
3087
+ provider,
3088
+ subjectAddress,
3089
+ [keyBindingSchemaUid],
3090
+ fromBlock
3091
+ );
3092
+ const withProof = keyBindings.filter((att) => {
3093
+ if (!controllerMatchesKeyBinding(att, controllerDid, controllerMethod)) return false;
3094
+ const proofs = att.data?.proofs;
3095
+ if (Array.isArray(proofs)) {
3096
+ return proofs.some((p) => typeof p === "string" && /^0x[0-9a-fA-F]{64}$/.test(p));
3097
+ }
3098
+ return typeof att.data?.proofTxHash === "string";
3099
+ }).sort((a, b) => Number(b.time - a.time));
3100
+ return withProof[0] ?? null;
3101
+ }
1704
3102
 
1705
3103
  // src/reputation/proof/tx-interaction.ts
1706
3104
  function createTxInteractionProof(chainId, txHash) {
@@ -1822,63 +3220,12 @@ function createEvidencePointerProof(url) {
1822
3220
  issuedAt: Math.floor(Date.now() / 1e3)
1823
3221
  };
1824
3222
  }
1825
- async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
1826
- if (!domain || typeof domain !== "string") {
1827
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1828
- }
1829
- const expected = normalizeDid(expectedControllerDid);
1830
- const prefix = options.recordPrefix ?? "_controllers";
1831
- const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1832
- let records;
1833
- try {
1834
- records = await (options.resolveTxt ?? promises.resolveTxt)(host);
1835
- } catch (err) {
1836
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1837
- }
1838
- for (const recordParts of records) {
1839
- const record = recordParts.join("");
1840
- const parsed = parseDnsTxtRecord(record);
1841
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1842
- return { valid: true, record };
1843
- }
1844
- }
1845
- return { valid: false, reason: "No TXT record matched expected controller DID" };
1846
- }
1847
-
1848
- // src/reputation/proof/dns-txt-shared.ts
1849
- async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
1850
- if (!domain || typeof domain !== "string") {
1851
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1852
- }
1853
- if (!options.resolveTxt) {
1854
- throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
1855
- }
1856
- const expected = normalizeDid(expectedControllerDid);
1857
- const prefix = options.recordPrefix ?? "_controllers";
1858
- const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1859
- let records;
1860
- try {
1861
- records = await options.resolveTxt(host);
1862
- } catch (err) {
1863
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1864
- }
1865
- for (const recordParts of records) {
1866
- const record = recordParts.join("");
1867
- const parsed = parseDnsTxtRecord(record);
1868
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1869
- return { valid: true, record };
1870
- }
1871
- }
1872
- return { valid: false, reason: "No TXT record matched expected controller DID" };
3223
+ function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
3224
+ return verifyDnsTxtControllerDid(domain, expectedControllerDid, {
3225
+ ...options,
3226
+ resolveTxt: options.resolveTxt ?? promises.resolveTxt
3227
+ });
1873
3228
  }
1874
-
1875
- // src/reputation/proof/subject-ownership.ts
1876
- var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1877
- var OWNERSHIP_PATTERNS = [
1878
- { method: "owner", signature: "function owner() view returns (address)" },
1879
- { method: "admin", signature: "function admin() view returns (address)" },
1880
- { method: "getOwner", signature: "function getOwner() view returns (address)" }
1881
- ];
1882
3229
  function assertConnectedWalletDid(input) {
1883
3230
  const normalized = normalizeDid(input);
1884
3231
  if (extractDidMethod(normalized) !== "pkh") {
@@ -1891,43 +3238,6 @@ function assertConnectedWalletDid(input) {
1891
3238
  function normalizeSubjectDid(input) {
1892
3239
  return normalizeDid(input);
1893
3240
  }
1894
- async function readAddressFromContract(provider, contractAddress, signature, method) {
1895
- try {
1896
- const iface = new ethers.Interface([signature]);
1897
- const data = iface.encodeFunctionData(method, []);
1898
- const result = await provider.call({ to: contractAddress, data });
1899
- const [value] = iface.decodeFunctionResult(method, result);
1900
- if (typeof value === "string" && ethers.isAddress(value) && value !== ethers.ZeroAddress) {
1901
- return ethers.getAddress(value);
1902
- }
1903
- } catch {
1904
- }
1905
- return null;
1906
- }
1907
- async function discoverControllingWallet(provider, contractAddress, chainId) {
1908
- for (const pattern of OWNERSHIP_PATTERNS) {
1909
- const address = await readAddressFromContract(
1910
- provider,
1911
- contractAddress,
1912
- pattern.signature,
1913
- pattern.method
1914
- );
1915
- if (address) {
1916
- return buildEvmDidPkh(chainId, address);
1917
- }
1918
- }
1919
- try {
1920
- const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
1921
- if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1922
- const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
1923
- if (adminAddress !== ethers.ZeroAddress) {
1924
- return buildEvmDidPkh(chainId, adminAddress);
1925
- }
1926
- }
1927
- } catch {
1928
- }
1929
- return null;
1930
- }
1931
3241
  async function verifyDidWebOwnership(params) {
1932
3242
  const subjectDid = normalizeSubjectDid(params.subjectDid);
1933
3243
  const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
@@ -1939,7 +3249,7 @@ async function verifyDidWebOwnership(params) {
1939
3249
  }
1940
3250
  let dnsReason;
1941
3251
  try {
1942
- const dnsResult = await verifyDnsTxtControllerDid2(domain, connectedWalletDid, {
3252
+ const dnsResult = await verifyDnsTxtControllerDid(domain, connectedWalletDid, {
1943
3253
  resolveTxt: params.resolveTxt,
1944
3254
  recordPrefix: params.recordPrefix
1945
3255
  });
@@ -2025,7 +3335,7 @@ async function verifyDidPkhOwnership(params) {
2025
3335
  connectedWalletDid
2026
3336
  };
2027
3337
  }
2028
- const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
3338
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
2029
3339
  if (params.txHash) {
2030
3340
  if (!controllingWalletDid) {
2031
3341
  return {
@@ -2108,7 +3418,7 @@ async function verifyDidPkhOwnership(params) {
2108
3418
  };
2109
3419
  }
2110
3420
  for (const pattern of OWNERSHIP_PATTERNS) {
2111
- const ownerAddress = await readAddressFromContract(
3421
+ const ownerAddress = await readOwnerFromContract(
2112
3422
  params.provider,
2113
3423
  subjectAddress,
2114
3424
  pattern.signature,