@oma3/omatrust 0.1.0-alpha.1 → 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 (44) hide show
  1. package/README.md +21 -7
  2. package/dist/identity/index.cjs +544 -1
  3. package/dist/identity/index.cjs.map +1 -1
  4. package/dist/identity/index.d.cts +2 -1
  5. package/dist/identity/index.d.ts +2 -1
  6. package/dist/identity/index.js +528 -2
  7. package/dist/identity/index.js.map +1 -1
  8. package/dist/index-BOvk-7Ku.d.cts +235 -0
  9. package/dist/index-C2w5EvFH.d.ts +329 -0
  10. package/dist/index-QueRiudB.d.cts +329 -0
  11. package/dist/index-R78TpAhN.d.ts +235 -0
  12. package/dist/index.cjs +2020 -145
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +4 -2
  15. package/dist/index.d.ts +4 -2
  16. package/dist/index.js +2021 -146
  17. package/dist/index.js.map +1 -1
  18. package/dist/reputation/index.browser.cjs +3010 -0
  19. package/dist/reputation/index.browser.cjs.map +1 -0
  20. package/dist/reputation/index.browser.d.cts +14 -0
  21. package/dist/reputation/index.browser.d.ts +14 -0
  22. package/dist/reputation/index.browser.js +2946 -0
  23. package/dist/reputation/index.browser.js.map +1 -0
  24. package/dist/reputation/index.cjs +1884 -123
  25. package/dist/reputation/index.cjs.map +1 -1
  26. package/dist/reputation/index.d.cts +3 -1
  27. package/dist/reputation/index.d.ts +3 -1
  28. package/dist/reputation/index.js +1861 -123
  29. package/dist/reputation/index.js.map +1 -1
  30. package/dist/subject-ownership-B7cFlm8c.d.cts +664 -0
  31. package/dist/subject-ownership-B7cFlm8c.d.ts +664 -0
  32. package/dist/types-dpYxRq8N.d.cts +281 -0
  33. package/dist/types-dpYxRq8N.d.ts +281 -0
  34. package/dist/widgets/index.cjs +238 -0
  35. package/dist/widgets/index.cjs.map +1 -0
  36. package/dist/widgets/index.d.cts +158 -0
  37. package/dist/widgets/index.d.ts +158 -0
  38. package/dist/widgets/index.js +226 -0
  39. package/dist/widgets/index.js.map +1 -0
  40. package/package.json +35 -7
  41. package/dist/index-ChbJxwOA.d.cts +0 -415
  42. package/dist/index-ChbJxwOA.d.ts +0 -415
  43. package/dist/index-QZDExA4I.d.cts +0 -90
  44. package/dist/index-QZDExA4I.d.ts +0 -90
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { keccak256, toUtf8Bytes, isAddress, getAddress, sha256, ZeroAddress, Signature, Contract, formatUnits, verifyTypedData, hexlify, randomBytes, id } from 'ethers';
1
+ import { keccak256, toUtf8Bytes, isAddress, getAddress, sha256, ZeroAddress, Signature, formatUnits, verifyTypedData, Interface, Contract, hexlify, randomBytes, id } from 'ethers';
2
+ import { base64url, calculateJwkThumbprint, decodeProtectedHeader, importJWK, compactVerify } from 'jose';
2
3
  import canonicalize from 'canonicalize';
3
4
  import { SchemaEncoder, EAS } from '@ethereum-attestation-service/eas-sdk';
4
5
  import { resolveTxt } from 'dns/promises';
@@ -50,6 +51,7 @@ function asError(err) {
50
51
  // src/identity/index.ts
51
52
  var identity_exports = {};
52
53
  __export(identity_exports, {
54
+ assertBareDid: () => assertBareDid,
53
55
  buildCaip10: () => buildCaip10,
54
56
  buildCaip2: () => buildCaip2,
55
57
  buildDidPkh: () => buildDidPkh,
@@ -60,27 +62,43 @@ __export(identity_exports, {
60
62
  canonicalizeJson: () => canonicalizeJson,
61
63
  computeDidAddress: () => computeDidAddress,
62
64
  computeDidHash: () => computeDidHash,
65
+ computeJwkThumbprint: () => computeJwkThumbprint,
66
+ didJwkToJwk: () => didJwkToJwk,
63
67
  didToAddress: () => didToAddress,
64
68
  extractAddressFromDid: () => extractAddressFromDid,
69
+ extractAuthorizationMetadata: () => extractAuthorizationMetadata,
70
+ extractControllerEvmAddress: () => extractControllerEvmAddress,
65
71
  extractDidIdentifier: () => extractDidIdentifier,
66
72
  extractDidMethod: () => extractDidMethod,
73
+ formatJktValue: () => formatJktValue,
67
74
  getAddressFromDidPkh: () => getAddressFromDidPkh,
68
75
  getChainIdFromDidPkh: () => getChainIdFromDidPkh,
69
76
  getDomainFromDidWeb: () => getDomainFromDidWeb,
70
77
  getNamespaceFromDidPkh: () => getNamespaceFromDidPkh,
71
78
  hashCanonicalizedJson: () => hashCanonicalizedJson,
79
+ isDidUrl: () => isDidUrl,
72
80
  isEvmDidPkh: () => isEvmDidPkh,
81
+ isPrivateKeyDid: () => isPrivateKeyDid,
82
+ isSameControllerId: () => isSameControllerId,
73
83
  isValidDid: () => isValidDid,
84
+ jwkToDidJwk: () => jwkToDidJwk,
74
85
  normalizeCaip10: () => normalizeCaip10,
75
86
  normalizeDid: () => normalizeDid,
76
87
  normalizeDidHandle: () => normalizeDidHandle,
88
+ normalizeDidJwk: () => normalizeDidJwk,
77
89
  normalizeDidKey: () => normalizeDidKey,
78
90
  normalizeDidPkh: () => normalizeDidPkh,
79
91
  normalizeDidWeb: () => normalizeDidWeb,
80
92
  normalizeDomain: () => normalizeDomain,
81
93
  parseCaip10: () => parseCaip10,
82
94
  parseCaip2: () => parseCaip2,
83
- validateDidAddress: () => validateDidAddress
95
+ parseDidUrl: () => parseDidUrl,
96
+ publicJwkEquals: () => publicJwkEquals,
97
+ resolveDidUrlToControllerDid: () => resolveDidUrlToControllerDid,
98
+ resolveDidUrlToPublicKey: () => resolveDidUrlToPublicKey,
99
+ validateDidAddress: () => validateDidAddress,
100
+ validatePrivateKeyDid: () => validatePrivateKeyDid,
101
+ validatePublicJwk: () => validatePublicJwk
84
102
  });
85
103
 
86
104
  // src/identity/caip.ts
@@ -152,7 +170,7 @@ function extractDidIdentifier(did) {
152
170
  }
153
171
  function normalizeDomain(domain) {
154
172
  assertString(domain, "domain", "INVALID_DID");
155
- return domain.trim().toLowerCase().replace(/\.$/, "");
173
+ return domain.trim().toLowerCase().replace(/\.$/, "").replace(/^www\./, "");
156
174
  }
157
175
  function normalizeDidWeb(input) {
158
176
  assertString(input, "input", "INVALID_DID");
@@ -228,6 +246,8 @@ function normalizeDid(input) {
228
246
  return normalizeDidHandle(trimmed);
229
247
  case "key":
230
248
  return normalizeDidKey(trimmed);
249
+ case "jwk":
250
+ return normalizeDidJwk(trimmed);
231
251
  default:
232
252
  return trimmed;
233
253
  }
@@ -331,6 +351,524 @@ function extractAddressFromDid(identifier) {
331
351
  }
332
352
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
333
353
  }
354
+ var CAIP2_NAMESPACE_REGEX = /^[a-z0-9-]{3,8}$/;
355
+ var BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
356
+ var VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
357
+ function isPrivateKeyDid(did) {
358
+ return validatePrivateKeyDid(did).valid;
359
+ }
360
+ function validatePrivateKeyDid(did) {
361
+ if (typeof did !== "string" || did.trim().length === 0) {
362
+ return { valid: false, method: null, error: "DID must be a non-empty string" };
363
+ }
364
+ const trimmed = did.trim();
365
+ const method = extractDidMethod(trimmed);
366
+ switch (method) {
367
+ case "pkh":
368
+ return validateDidPkh(trimmed);
369
+ case "jwk":
370
+ return validateDidJwk(trimmed);
371
+ default:
372
+ return {
373
+ valid: false,
374
+ method: null,
375
+ error: method ? `DID method "${method}" is not a recognized private-key method` : "Invalid DID format"
376
+ };
377
+ }
378
+ }
379
+ function validateDidPkh(did) {
380
+ const parts = did.split(":");
381
+ if (parts.length !== 5) {
382
+ return {
383
+ valid: false,
384
+ method: "pkh",
385
+ error: `did:pkh must have exactly 5 colon-separated parts, got ${parts.length}`
386
+ };
387
+ }
388
+ const [, , namespace, chainId, address] = parts;
389
+ if (!namespace) {
390
+ return { valid: false, method: "pkh", error: "Missing namespace" };
391
+ }
392
+ if (!CAIP2_NAMESPACE_REGEX.test(namespace)) {
393
+ return {
394
+ valid: false,
395
+ method: "pkh",
396
+ error: `Invalid CAIP-2 namespace "${namespace}" (must be 3-8 lowercase alphanumeric/hyphen chars)`
397
+ };
398
+ }
399
+ if (!chainId) {
400
+ return { valid: false, method: "pkh", error: "Missing chain ID (reference)" };
401
+ }
402
+ if (!address) {
403
+ return { valid: false, method: "pkh", error: "Missing address" };
404
+ }
405
+ if (namespace === "eip155") {
406
+ if (!/^\d+$/.test(chainId)) {
407
+ return {
408
+ valid: false,
409
+ method: "pkh",
410
+ error: `Invalid eip155 chain ID "${chainId}" (must be numeric)`
411
+ };
412
+ }
413
+ if (!isAddress(address)) {
414
+ return {
415
+ valid: false,
416
+ method: "pkh",
417
+ error: `Invalid EVM address "${address}" (must be 0x + 40 hex chars)`
418
+ };
419
+ }
420
+ }
421
+ return { valid: true, method: "pkh" };
422
+ }
423
+ function validateDidJwk(did) {
424
+ const parts = did.split(":");
425
+ if (parts.length !== 3) {
426
+ return {
427
+ valid: false,
428
+ method: "jwk",
429
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
430
+ };
431
+ }
432
+ const [, , encoded] = parts;
433
+ if (!encoded || encoded.length === 0) {
434
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
435
+ }
436
+ if (!BASE64URL_REGEX.test(encoded)) {
437
+ return {
438
+ valid: false,
439
+ method: "jwk",
440
+ error: "Identifier contains invalid base64url characters"
441
+ };
442
+ }
443
+ let decoded;
444
+ try {
445
+ const bytes = base64url.decode(encoded);
446
+ decoded = new TextDecoder().decode(bytes);
447
+ } catch {
448
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
449
+ }
450
+ let jwk;
451
+ try {
452
+ jwk = JSON.parse(decoded);
453
+ } catch {
454
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
455
+ }
456
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
457
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
458
+ }
459
+ const kty = jwk.kty;
460
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
461
+ return {
462
+ valid: false,
463
+ method: "jwk",
464
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
465
+ };
466
+ }
467
+ if ("d" in jwk) {
468
+ return {
469
+ valid: false,
470
+ method: "jwk",
471
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
472
+ };
473
+ }
474
+ return { valid: true, method: "jwk" };
475
+ }
476
+ function normalizeDidJwk(input) {
477
+ assertString(input, "input", "INVALID_DID");
478
+ const trimmed = input.trim();
479
+ if (!trimmed.startsWith("did:jwk:")) {
480
+ throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
481
+ }
482
+ const result = validateDidJwk(trimmed);
483
+ if (!result.valid) {
484
+ throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
485
+ }
486
+ return trimmed;
487
+ }
488
+
489
+ // src/identity/did-url.ts
490
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
491
+ function parseDidUrl(input) {
492
+ assertString(input, "input", "INVALID_DID_URL");
493
+ const trimmed = input.trim();
494
+ const hashIndex = trimmed.indexOf("#");
495
+ let did;
496
+ let fragment;
497
+ if (hashIndex === -1) {
498
+ did = trimmed;
499
+ fragment = null;
500
+ } else {
501
+ did = trimmed.slice(0, hashIndex);
502
+ const rawFragment = trimmed.slice(hashIndex + 1);
503
+ if (rawFragment.length === 0) {
504
+ throw new OmaTrustError(
505
+ "INVALID_DID_URL",
506
+ "DID URL has empty fragment (trailing '#' with no identifier)",
507
+ { input }
508
+ );
509
+ }
510
+ fragment = rawFragment;
511
+ }
512
+ if (!DID_URL_REGEX.test(did)) {
513
+ throw new OmaTrustError(
514
+ "INVALID_DID_URL",
515
+ "Invalid DID URL: base DID portion is malformed",
516
+ { input, did }
517
+ );
518
+ }
519
+ return {
520
+ didUrl: trimmed,
521
+ did,
522
+ fragment
523
+ };
524
+ }
525
+ function isDidUrl(input) {
526
+ if (typeof input !== "string") return false;
527
+ const trimmed = input.trim();
528
+ return trimmed.includes("#") && DID_URL_REGEX.test(trimmed.split("#")[0]);
529
+ }
530
+ function assertBareDid(input, paramName = "did") {
531
+ assertString(input, paramName, "INVALID_DID");
532
+ if (isDidUrl(input)) {
533
+ throw new OmaTrustError(
534
+ "INVALID_DID",
535
+ `Expected a bare DID but received a DID URL with a fragment. Use parseDidUrl() to handle DID URLs.`,
536
+ { input }
537
+ );
538
+ }
539
+ }
540
+ var VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
541
+ var PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
542
+ var REQUIRED_PUBLIC_FIELDS = {
543
+ EC: ["crv", "x", "y"],
544
+ OKP: ["crv", "x"],
545
+ RSA: ["n", "e"]
546
+ };
547
+ function validatePublicJwk(jwk) {
548
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
549
+ return { valid: false, error: "JWK must be a non-null object" };
550
+ }
551
+ const obj = jwk;
552
+ const kty = obj.kty;
553
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
554
+ return {
555
+ valid: false,
556
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
557
+ };
558
+ }
559
+ for (const field of PRIVATE_KEY_FIELDS) {
560
+ if (field in obj) {
561
+ return {
562
+ valid: false,
563
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
564
+ };
565
+ }
566
+ }
567
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
568
+ if (required) {
569
+ for (const field of required) {
570
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
571
+ return {
572
+ valid: false,
573
+ error: `Missing required public key field "${field}" for kty="${kty}"`
574
+ };
575
+ }
576
+ }
577
+ }
578
+ return { valid: true };
579
+ }
580
+ function canonicalizeJwkJson(jwk) {
581
+ const sorted = Object.keys(jwk).sort();
582
+ const obj = {};
583
+ for (const key of sorted) {
584
+ obj[key] = jwk[key];
585
+ }
586
+ return JSON.stringify(obj);
587
+ }
588
+ function jwkToDidJwk(jwk) {
589
+ assertObject(jwk, "jwk", "INVALID_JWK");
590
+ const validation = validatePublicJwk(jwk);
591
+ if (!validation.valid) {
592
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
593
+ }
594
+ const canonical = canonicalizeJwkJson(jwk);
595
+ const encoded = base64url.encode(new TextEncoder().encode(canonical));
596
+ return `did:jwk:${encoded}`;
597
+ }
598
+ function didJwkToJwk(didJwk) {
599
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
600
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
601
+ }
602
+ const parts = didJwk.split(":");
603
+ if (parts.length !== 3) {
604
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
605
+ input: didJwk
606
+ });
607
+ }
608
+ const encoded = parts[2];
609
+ if (!encoded || encoded.length === 0) {
610
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
611
+ input: didJwk
612
+ });
613
+ }
614
+ let decoded;
615
+ try {
616
+ const bytes = base64url.decode(encoded);
617
+ decoded = new TextDecoder().decode(bytes);
618
+ } catch {
619
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
620
+ input: didJwk
621
+ });
622
+ }
623
+ let jwk;
624
+ try {
625
+ jwk = JSON.parse(decoded);
626
+ } catch {
627
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
628
+ input: didJwk
629
+ });
630
+ }
631
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
632
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
633
+ input: didJwk
634
+ });
635
+ }
636
+ const validation = validatePublicJwk(jwk);
637
+ if (!validation.valid) {
638
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
639
+ input: didJwk
640
+ });
641
+ }
642
+ return jwk;
643
+ }
644
+ function extractPublicKeyFields(jwk) {
645
+ const result = {};
646
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
647
+ for (const [key, value] of Object.entries(jwk)) {
648
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
649
+ if (metadataFields.has(key)) continue;
650
+ result[key] = value;
651
+ }
652
+ return result;
653
+ }
654
+ function publicJwkEquals(a, b) {
655
+ assertObject(a, "a", "INVALID_JWK");
656
+ assertObject(b, "b", "INVALID_JWK");
657
+ const aObj = a;
658
+ const bObj = b;
659
+ for (const field of PRIVATE_KEY_FIELDS) {
660
+ if (field in aObj) {
661
+ throw new OmaTrustError(
662
+ "INVALID_JWK",
663
+ `First JWK contains private key field "${field}"`,
664
+ { field }
665
+ );
666
+ }
667
+ if (field in bObj) {
668
+ throw new OmaTrustError(
669
+ "INVALID_JWK",
670
+ `Second JWK contains private key field "${field}"`,
671
+ { field }
672
+ );
673
+ }
674
+ }
675
+ const aPublic = extractPublicKeyFields(aObj);
676
+ const bPublic = extractPublicKeyFields(bObj);
677
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
678
+ }
679
+ async function computeJwkThumbprint(jwk, digestAlgorithm = "sha256") {
680
+ assertObject(jwk, "jwk", "INVALID_JWK");
681
+ const validation = validatePublicJwk(jwk);
682
+ if (!validation.valid) {
683
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
684
+ }
685
+ const alg = digestAlgorithm === "sha256" ? "sha256" : digestAlgorithm === "sha384" ? "sha384" : "sha512";
686
+ return calculateJwkThumbprint(jwk, alg);
687
+ }
688
+ async function formatJktValue(jwk) {
689
+ const thumbprint = await computeJwkThumbprint(jwk, "sha256");
690
+ return `jkt=S256:${thumbprint}`;
691
+ }
692
+
693
+ // src/shared/did-document.ts
694
+ async function fetchDidDocument(domain) {
695
+ const normalized = domain.toLowerCase().replace(/\.$/, "");
696
+ const url = `https://${normalized}/.well-known/did.json`;
697
+ let response;
698
+ try {
699
+ response = await fetch(url, { headers: { Accept: "application/json" } });
700
+ } catch (err) {
701
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
702
+ }
703
+ if (!response.ok) {
704
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
705
+ domain,
706
+ status: response.status
707
+ });
708
+ }
709
+ return await response.json();
710
+ }
711
+
712
+ // src/identity/resolve-key.ts
713
+ function findVerificationMethod(didDocument, didUrl, fragment) {
714
+ const methods = didDocument.verificationMethod;
715
+ if (!Array.isArray(methods)) {
716
+ return null;
717
+ }
718
+ for (const method of methods) {
719
+ if (!method || typeof method !== "object") continue;
720
+ const id2 = method.id;
721
+ if (typeof id2 !== "string") continue;
722
+ if (id2 === didUrl) return method;
723
+ if (fragment && (id2 === `#${fragment}` || id2.endsWith(`#${fragment}`))) {
724
+ return method;
725
+ }
726
+ }
727
+ return null;
728
+ }
729
+ function extractMethodIdsFromDidDocument(didDocument) {
730
+ const methods = didDocument.verificationMethod;
731
+ if (!Array.isArray(methods)) return [];
732
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
733
+ }
734
+ async function resolveDidUrlToPublicKey(didUrl, options) {
735
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
736
+ const parsed = parseDidUrl(didUrl);
737
+ const method = extractDidMethod(parsed.did);
738
+ if (!method) {
739
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
740
+ didUrl,
741
+ did: parsed.did
742
+ });
743
+ }
744
+ switch (method) {
745
+ case "web":
746
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment, options);
747
+ default:
748
+ throw new OmaTrustError(
749
+ "UNSUPPORTED_DID_METHOD",
750
+ `DID URL key resolution is not supported for method "${method}"`,
751
+ { didUrl, method }
752
+ );
753
+ }
754
+ }
755
+ async function resolveDidUrlToControllerDid(didUrl, options) {
756
+ const resolved = await resolveDidUrlToPublicKey(didUrl, options);
757
+ const controllerDid = jwkToDidJwk(resolved.publicKeyJwk);
758
+ return {
759
+ ...resolved,
760
+ controllerDid
761
+ };
762
+ }
763
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
764
+ const domain = getDomainFromDidWeb(did);
765
+ if (!domain) {
766
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
767
+ didUrl,
768
+ did
769
+ });
770
+ }
771
+ const fetchFn = options?.fetchDidDocument ?? fetchDidDocument;
772
+ const didDocument = await fetchFn(domain);
773
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
774
+ if (!method) {
775
+ throw new OmaTrustError(
776
+ "KEY_NOT_FOUND",
777
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
778
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
779
+ );
780
+ }
781
+ const publicKeyJwk = method.publicKeyJwk;
782
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
783
+ throw new OmaTrustError(
784
+ "KEY_NOT_FOUND",
785
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
786
+ { didUrl, verificationMethodId: method.id }
787
+ );
788
+ }
789
+ const validation = validatePublicJwk(publicKeyJwk);
790
+ if (!validation.valid) {
791
+ throw new OmaTrustError(
792
+ "INVALID_JWK",
793
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
794
+ { didUrl, verificationMethodId: method.id }
795
+ );
796
+ }
797
+ return {
798
+ didUrl,
799
+ did,
800
+ fragment,
801
+ publicKeyJwk,
802
+ verificationMethodId: method.id
803
+ };
804
+ }
805
+
806
+ // src/identity/controller-id.ts
807
+ function isSameControllerId(a, b) {
808
+ let normalizedA = null;
809
+ let normalizedB = null;
810
+ try {
811
+ normalizedA = normalizeDid(a);
812
+ } catch {
813
+ }
814
+ try {
815
+ normalizedB = normalizeDid(b);
816
+ } catch {
817
+ }
818
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
819
+ return true;
820
+ }
821
+ const evmA = extractControllerEvmAddress(a);
822
+ const evmB = extractControllerEvmAddress(b);
823
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
824
+ return true;
825
+ }
826
+ const methodA = extractDidMethod(a);
827
+ const methodB = extractDidMethod(b);
828
+ if (methodA === "jwk" && methodB === "jwk") {
829
+ try {
830
+ const jwkA = didJwkToJwk(a);
831
+ const jwkB = didJwkToJwk(b);
832
+ return publicJwkEquals(jwkA, jwkB);
833
+ } catch {
834
+ return false;
835
+ }
836
+ }
837
+ return false;
838
+ }
839
+ function extractControllerEvmAddress(controllerDid) {
840
+ try {
841
+ const method = extractDidMethod(controllerDid);
842
+ if (method === "pkh" && controllerDid.includes("eip155")) {
843
+ return extractAddressFromDid(controllerDid);
844
+ }
845
+ } catch {
846
+ }
847
+ return null;
848
+ }
849
+
850
+ // src/identity/types.ts
851
+ function extractAuthorizationMetadata(result) {
852
+ const payload = result.payload;
853
+ const resourceUrl = typeof payload.resourceUrl === "string" ? payload.resourceUrl : null;
854
+ const issuedAt = typeof payload.issuedAt === "string" ? payload.issuedAt : null;
855
+ let subjectDid = null;
856
+ if (resourceUrl) {
857
+ try {
858
+ const url = new URL(resourceUrl);
859
+ subjectDid = `did:web:${url.hostname}`;
860
+ } catch {
861
+ }
862
+ }
863
+ return {
864
+ controllerDid: result.publicKeyDid,
865
+ subjectDid,
866
+ resourceUrl,
867
+ issuedAt,
868
+ kid: result.kid,
869
+ publicKeyJwk: result.publicKeyJwk
870
+ };
871
+ }
334
872
  function canonicalizeJson(obj) {
335
873
  const jcs = canonicalize(obj);
336
874
  if (!jcs) {
@@ -354,7 +892,10 @@ function hashCanonicalizedJson(obj, algorithm) {
354
892
  // src/reputation/index.ts
355
893
  var reputation_exports = {};
356
894
  __export(reputation_exports, {
895
+ EIP1967_ADMIN_SLOT: () => EIP1967_ADMIN_SLOT,
896
+ OWNERSHIP_PATTERNS: () => OWNERSHIP_PATTERNS,
357
897
  buildDelegatedAttestationTypedData: () => buildDelegatedAttestationTypedData,
898
+ buildDelegatedTypedDataFromEncoded: () => buildDelegatedTypedDataFromEncoded,
358
899
  buildDnsTxtRecord: () => buildDnsTxtRecord,
359
900
  buildEip712Domain: () => buildEip712Domain,
360
901
  calculateAverageUserReviewRating: () => calculateAverageUserReviewRating,
@@ -371,15 +912,20 @@ __export(reputation_exports, {
371
912
  createX402ReceiptProof: () => createX402ReceiptProof,
372
913
  decodeAttestationData: () => decodeAttestationData,
373
914
  deduplicateReviews: () => deduplicateReviews,
915
+ discoverContractOwner: () => discoverContractOwner,
916
+ discoverControllingWalletDid: () => discoverControllingWalletDid,
374
917
  encodeAttestationData: () => encodeAttestationData,
375
- extractAddressesFromDidDocument: () => extractAddressesFromDidDocument,
918
+ extractEvmAddressesFromDidDocument: () => extractEvmAddressesFromDidDocument,
376
919
  extractExpirationTime: () => extractExpirationTime,
920
+ extractJwksFromDidDocument: () => extractJwksFromDidDocument,
377
921
  fetchDidDocument: () => fetchDidDocument,
378
922
  formatSchemaUid: () => formatSchemaUid,
379
923
  formatTransferAmount: () => formatTransferAmount,
380
924
  getAttestation: () => getAttestation,
925
+ getAttestationsByAttester: () => getAttestationsByAttester,
381
926
  getAttestationsForDid: () => getAttestationsForDid,
382
927
  getChainConstants: () => getChainConstants,
928
+ getControllerAuthorization: () => getControllerAuthorization,
383
929
  getExplorerAddressUrl: () => getExplorerAddressUrl,
384
930
  getExplorerTxUrl: () => getExplorerTxUrl,
385
931
  getLatestAttestations: () => getLatestAttestations,
@@ -393,16 +939,31 @@ __export(reputation_exports, {
393
939
  normalizeSchema: () => normalizeSchema,
394
940
  parseDnsTxtRecord: () => parseDnsTxtRecord,
395
941
  prepareDelegatedAttestation: () => prepareDelegatedAttestation,
942
+ readOwnerFromContract: () => readOwnerFromContract,
943
+ requestControllerWitness: () => requestControllerWitness,
944
+ revokeAttestation: () => revokeAttestation,
396
945
  schemaToString: () => schemaToString,
397
946
  splitSignature: () => splitSignature,
398
947
  submitAttestation: () => submitAttestation,
399
948
  submitDelegatedAttestation: () => submitDelegatedAttestation,
949
+ validateAttestationData: () => validateAttestationData,
400
950
  verifyAttestation: () => verifyAttestation,
401
951
  verifyDidDocumentControllerDid: () => verifyDidDocumentControllerDid,
402
- verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid,
952
+ verifyDidJsonControllerDid: () => verifyDidJsonControllerDid,
953
+ verifyDidPkhOwnership: () => verifyDidPkhOwnership,
954
+ verifyDidWebOwnership: () => verifyDidWebOwnership,
955
+ verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid2,
403
956
  verifyEip712Signature: () => verifyEip712Signature,
404
957
  verifyProof: () => verifyProof,
405
- verifySchemaExists: () => verifySchemaExists
958
+ verifySchemaExists: () => verifySchemaExists,
959
+ verifySubjectOwnership: () => verifySubjectOwnership,
960
+ verifyTransferProof: () => verifyTransferProof,
961
+ verifyX402Eip712Artifact: () => verifyX402Eip712Artifact,
962
+ verifyX402Eip712Offer: () => verifyX402Eip712Offer,
963
+ verifyX402Eip712Receipt: () => verifyX402Eip712Receipt,
964
+ verifyX402JwsArtifact: () => verifyX402JwsArtifact,
965
+ verifyX402JwsOffer: () => verifyX402JwsOffer,
966
+ verifyX402JwsReceipt: () => verifyX402JwsReceipt
406
967
  });
407
968
  function normalizeSchema(schema) {
408
969
  if (Array.isArray(schema)) {
@@ -438,6 +999,13 @@ function encodeAttestationData(schema, data) {
438
999
  if (!data || typeof data !== "object") {
439
1000
  throw new OmaTrustError("INVALID_INPUT", "data must be an object");
440
1001
  }
1002
+ const validationErrors = validateAttestationData(schema, data);
1003
+ if (validationErrors.length > 0) {
1004
+ const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
1005
+ throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
1006
+ errors: validationErrors
1007
+ });
1008
+ }
441
1009
  const fields = normalizeSchema(schema);
442
1010
  const schemaString = schemaToString(fields);
443
1011
  const encoder = new SchemaEncoder(schemaString);
@@ -450,6 +1018,99 @@ function encodeAttestationData(schema, data) {
450
1018
  );
451
1019
  return encoded;
452
1020
  }
1021
+ function getActualType(value) {
1022
+ if (value === null) {
1023
+ return "null";
1024
+ }
1025
+ if (value === void 0) {
1026
+ return "undefined";
1027
+ }
1028
+ if (Array.isArray(value)) {
1029
+ return "array";
1030
+ }
1031
+ if (typeof value === "number") {
1032
+ return Number.isFinite(value) ? "number" : "non-finite-number";
1033
+ }
1034
+ return typeof value;
1035
+ }
1036
+ function isNumericValue(value, allowNegative) {
1037
+ if (typeof value === "bigint") {
1038
+ return allowNegative || value >= 0n;
1039
+ }
1040
+ if (typeof value === "number") {
1041
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
1042
+ return false;
1043
+ }
1044
+ return allowNegative || value >= 0;
1045
+ }
1046
+ if (typeof value === "string") {
1047
+ if (value.length === 0) {
1048
+ return false;
1049
+ }
1050
+ const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
1051
+ return pattern.test(value);
1052
+ }
1053
+ return false;
1054
+ }
1055
+ function isHex(value) {
1056
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
1057
+ }
1058
+ function validateFieldValue(type, value) {
1059
+ const normalizedType = type.trim().toLowerCase();
1060
+ const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
1061
+ if (/^uint\d*$/.test(normalizedType)) {
1062
+ return isNumericValue(value, false);
1063
+ }
1064
+ if (/^int\d*$/.test(normalizedType)) {
1065
+ return isNumericValue(value, true);
1066
+ }
1067
+ if (normalizedType === "string") {
1068
+ return typeof value === "string";
1069
+ }
1070
+ if (normalizedType === "string[]") {
1071
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
1072
+ }
1073
+ if (normalizedType === "bool") {
1074
+ return typeof value === "boolean";
1075
+ }
1076
+ if (normalizedType === "address") {
1077
+ return typeof value === "string" && isAddress(value);
1078
+ }
1079
+ if (normalizedType === "bytes") {
1080
+ return isHex(value) && (value.length - 2) % 2 === 0;
1081
+ }
1082
+ if (bytesNMatch) {
1083
+ const expectedBytes = Number(bytesNMatch[1]);
1084
+ return isHex(value) && value.length === 2 + expectedBytes * 2;
1085
+ }
1086
+ return true;
1087
+ }
1088
+ function validateAttestationData(schema, data) {
1089
+ if (!data || typeof data !== "object") {
1090
+ return [{
1091
+ schemaFieldName: "data",
1092
+ expectedType: "object",
1093
+ providedType: getActualType(data),
1094
+ providedValue: data
1095
+ }];
1096
+ }
1097
+ const fields = normalizeSchema(schema);
1098
+ const errors = [];
1099
+ for (const field of fields) {
1100
+ const value = data[field.name];
1101
+ const isValid = validateFieldValue(field.type, value);
1102
+ if (isValid) {
1103
+ continue;
1104
+ }
1105
+ errors.push({
1106
+ schemaFieldName: field.name,
1107
+ expectedType: field.type,
1108
+ providedType: getActualType(value),
1109
+ providedValue: value
1110
+ });
1111
+ }
1112
+ return errors;
1113
+ }
453
1114
  function decodeAttestationData(schema, encodedData) {
454
1115
  if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
455
1116
  throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
@@ -470,22 +1131,11 @@ function decodeAttestationData(schema, encodedData) {
470
1131
  return result;
471
1132
  }
472
1133
  function extractExpirationTime(data) {
473
- const keys = ["expirationTime", "expiration", "validUntil", "expiresAt", "expires"];
474
- for (const key of keys) {
475
- const value = data[key];
476
- if (value === void 0 || value === null) {
477
- continue;
478
- }
479
- if (typeof value === "bigint") {
480
- return value;
481
- }
482
- if (typeof value === "number") {
483
- return value;
484
- }
485
- if (typeof value === "string" && /^\d+$/.test(value)) {
486
- return BigInt(value);
487
- }
488
- }
1134
+ const value = data["expiresAt"];
1135
+ if (value === void 0 || value === null) return void 0;
1136
+ if (typeof value === "bigint") return value;
1137
+ if (typeof value === "number") return value;
1138
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
489
1139
  return void 0;
490
1140
  }
491
1141
  var ZERO_UID = "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -526,6 +1176,53 @@ function resolveRecipientAddress(data) {
526
1176
  return ZeroAddress;
527
1177
  }
528
1178
 
1179
+ // src/reputation/eas-adapter.ts
1180
+ function isRecord(value) {
1181
+ return Boolean(value) && typeof value === "object";
1182
+ }
1183
+ function isHex32(value) {
1184
+ return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
1185
+ }
1186
+ function getEasTransactionReceipt(tx) {
1187
+ if (!isRecord(tx)) {
1188
+ return void 0;
1189
+ }
1190
+ const candidates = [tx.receipt, tx.tx, tx.transaction];
1191
+ for (const candidate of candidates) {
1192
+ if (isRecord(candidate)) {
1193
+ return candidate;
1194
+ }
1195
+ }
1196
+ return void 0;
1197
+ }
1198
+ function getEasTransactionHash(tx) {
1199
+ const receipt = getEasTransactionReceipt(tx);
1200
+ if (receipt) {
1201
+ const receiptHash = receipt.hash;
1202
+ if (isHex32(receiptHash)) {
1203
+ return receiptHash;
1204
+ }
1205
+ const transactionHash = receipt.transactionHash;
1206
+ if (isHex32(transactionHash)) {
1207
+ return transactionHash;
1208
+ }
1209
+ }
1210
+ if (isRecord(tx) && isHex32(tx.hash)) {
1211
+ return tx.hash;
1212
+ }
1213
+ return void 0;
1214
+ }
1215
+ function isEasSchemaNotFoundError(err) {
1216
+ if (!isRecord(err)) {
1217
+ return false;
1218
+ }
1219
+ const message = err.message;
1220
+ if (typeof message !== "string" || message.length === 0) {
1221
+ return false;
1222
+ }
1223
+ return /schema not found/i.test(message);
1224
+ }
1225
+
529
1226
  // src/reputation/submit.ts
530
1227
  async function submitAttestation(params) {
531
1228
  if (!params || typeof params !== "object") {
@@ -556,25 +1253,46 @@ async function submitAttestation(params) {
556
1253
  }
557
1254
  });
558
1255
  const uid = await tx.wait();
559
- const txAny = tx;
560
- const txHash = txAny.tx?.hash ?? txAny.hash ?? txAny.transaction?.hash ?? ZERO_UID;
1256
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
1257
+ const receipt = getEasTransactionReceipt(tx);
561
1258
  return {
562
1259
  uid,
563
1260
  txHash,
564
- receipt: txAny.tx
1261
+ receipt
565
1262
  };
566
1263
  } catch (err) {
567
1264
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
568
1265
  }
569
1266
  }
570
- function buildDelegatedAttestationTypedData(params) {
571
- const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
572
- const encodedData = encodeAttestationData(params.schema, dataWithHash);
573
- const recipient = resolveRecipientAddress(dataWithHash);
574
- const expiration = toBigIntOrDefault(
575
- params.expirationTime ?? extractExpirationTime(dataWithHash),
576
- 0n
577
- );
1267
+ async function revokeAttestation(params) {
1268
+ if (!params || typeof params !== "object") {
1269
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
1270
+ }
1271
+ if (!params.signer) {
1272
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
1273
+ }
1274
+ const eas = new EAS(params.easContractAddress);
1275
+ eas.connect(params.signer);
1276
+ try {
1277
+ const tx = await eas.revoke({
1278
+ schema: params.schemaUid,
1279
+ data: {
1280
+ uid: params.uid,
1281
+ value: toBigIntOrDefault(params.value, 0n)
1282
+ }
1283
+ });
1284
+ await tx.wait();
1285
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
1286
+ const receipt = getEasTransactionReceipt(tx);
1287
+ return {
1288
+ txHash,
1289
+ receipt
1290
+ };
1291
+ } catch (err) {
1292
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
1293
+ }
1294
+ }
1295
+ function buildDelegatedTypedData(params) {
578
1296
  return {
579
1297
  domain: {
580
1298
  name: "EAS",
@@ -599,17 +1317,52 @@ function buildDelegatedAttestationTypedData(params) {
599
1317
  message: {
600
1318
  attester: params.attester,
601
1319
  schema: params.schemaUid,
602
- recipient,
603
- expirationTime: expiration,
1320
+ recipient: params.recipient,
1321
+ expirationTime: toBigIntOrDefault(params.expirationTime, 0n),
604
1322
  revocable: params.revocable ?? true,
605
1323
  refUID: params.refUid ?? ZERO_UID,
606
- data: encodedData,
1324
+ data: params.encodedData,
607
1325
  value: toBigIntOrDefault(params.value, 0n),
608
1326
  nonce: toBigIntOrDefault(params.nonce, 0n),
609
1327
  deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
610
1328
  }
611
1329
  };
612
1330
  }
1331
+ function buildDelegatedAttestationTypedData(params) {
1332
+ const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
1333
+ const encodedData = encodeAttestationData(params.schema, dataWithHash);
1334
+ const recipient = resolveRecipientAddress(dataWithHash);
1335
+ return buildDelegatedTypedData({
1336
+ chainId: params.chainId,
1337
+ easContractAddress: params.easContractAddress,
1338
+ schemaUid: params.schemaUid,
1339
+ encodedData,
1340
+ recipient,
1341
+ attester: params.attester,
1342
+ nonce: params.nonce,
1343
+ revocable: params.revocable,
1344
+ expirationTime: params.expirationTime ?? extractExpirationTime(dataWithHash),
1345
+ refUid: params.refUid,
1346
+ value: params.value,
1347
+ deadline: params.deadline
1348
+ });
1349
+ }
1350
+ function buildDelegatedTypedDataFromEncoded(params) {
1351
+ return buildDelegatedTypedData({
1352
+ chainId: params.chainId,
1353
+ easContractAddress: params.easContractAddress,
1354
+ schemaUid: params.schemaUid,
1355
+ encodedData: params.encodedData,
1356
+ recipient: params.recipient,
1357
+ attester: params.attester,
1358
+ nonce: params.nonce,
1359
+ revocable: params.revocable,
1360
+ expirationTime: params.expirationTime,
1361
+ refUid: params.refUid,
1362
+ value: params.value,
1363
+ deadline: params.deadline
1364
+ });
1365
+ }
613
1366
  function splitSignature(signature) {
614
1367
  try {
615
1368
  const parsed = Signature.from(signature);
@@ -669,24 +1422,44 @@ async function submitDelegatedAttestation(params) {
669
1422
  payload = {};
670
1423
  }
671
1424
  if (!response.ok) {
672
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
673
- status: response.status,
1425
+ const relayError = {
1426
+ httpStatus: response.status,
1427
+ code: payload.code,
1428
+ error: payload.error ?? payload.message,
674
1429
  payload
675
- });
1430
+ };
1431
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
676
1432
  }
677
1433
  const uid = payload.uid ?? ZERO_UID;
678
1434
  const txHash = payload.txHash;
679
- const status = payload.status ?? "submitted";
680
- return {
681
- uid,
682
- txHash,
683
- status
684
- };
1435
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
1436
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
1437
+ const relay = {};
1438
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
1439
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
1440
+ if (typeof payload.success === "boolean") relay.success = payload.success;
1441
+ for (const key of Object.keys(payload)) {
1442
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
1443
+ relay[key] = payload[key];
1444
+ }
1445
+ }
1446
+ const result = { uid, txHash, status };
1447
+ if (Object.keys(relay).length > 0) {
1448
+ result.relay = relay;
1449
+ }
1450
+ return result;
685
1451
  }
686
1452
  var EAS_EVENT_ABI = [
687
1453
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
688
1454
  ];
689
- function parseAttestation(attestation, schema) {
1455
+ function parseEventTxHash(event) {
1456
+ const txHash = event.transactionHash;
1457
+ if (typeof txHash !== "string") {
1458
+ return void 0;
1459
+ }
1460
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
1461
+ }
1462
+ function parseAttestation(attestation, schema, txHash) {
690
1463
  const rawData = attestation.data ?? "0x";
691
1464
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
692
1465
  const toBigIntSafe = (value) => {
@@ -706,6 +1479,7 @@ function parseAttestation(attestation, schema) {
706
1479
  schema: attestation.schema,
707
1480
  attester: attestation.attester,
708
1481
  recipient: attestation.recipient,
1482
+ txHash,
709
1483
  revocable: Boolean(attestation.revocable),
710
1484
  revocationTime: toBigIntSafe(attestation.revocationTime),
711
1485
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -731,21 +1505,18 @@ async function getAttestation(params) {
731
1505
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
732
1506
  }
733
1507
  }
734
- async function getAttestationsForDid(params) {
735
- const provider = params.provider;
736
- const contract = new Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
737
- const toBlock = params.toBlock ?? await provider.getBlockNumber();
738
- const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
739
- const filter = contract.filters.Attested(didToAddress(params.did));
1508
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
1509
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI, provider);
1510
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
740
1511
  let events;
741
1512
  try {
742
1513
  events = await contract.queryFilter(filter, fromBlock, toBlock);
743
1514
  } catch (err) {
744
1515
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
745
1516
  }
746
- const eas = new EAS(params.easContractAddress);
1517
+ const eas = new EAS(easContractAddress);
747
1518
  eas.connect(provider);
748
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
1519
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
749
1520
  const results = [];
750
1521
  for (const event of events) {
751
1522
  if (!("args" in event && Array.isArray(event.args))) {
@@ -764,11 +1535,39 @@ async function getAttestationsForDid(params) {
764
1535
  if (!attestation || !attestation.uid) {
765
1536
  continue;
766
1537
  }
767
- results.push(parseAttestation(attestation));
1538
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
768
1539
  }
769
1540
  results.sort((a, b) => Number(b.time - a.time));
770
1541
  return results;
771
1542
  }
1543
+ async function getAttestationsForDid(params) {
1544
+ const provider = params.provider;
1545
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
1546
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
1547
+ return queryAttestationEvents(
1548
+ params.easContractAddress,
1549
+ provider,
1550
+ fromBlock,
1551
+ toBlock,
1552
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
1553
+ );
1554
+ }
1555
+ async function getAttestationsByAttester(params) {
1556
+ const provider = params.provider;
1557
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
1558
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
1559
+ if (params.limit !== void 0 && params.limit <= 0) {
1560
+ return [];
1561
+ }
1562
+ const results = await queryAttestationEvents(
1563
+ params.easContractAddress,
1564
+ provider,
1565
+ fromBlock,
1566
+ toBlock,
1567
+ { attester: params.attester, schemas: params.schemas }
1568
+ );
1569
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
1570
+ }
772
1571
  async function listAttestations(params) {
773
1572
  const limit = params.limit ?? 20;
774
1573
  if (limit <= 0) {
@@ -779,34 +1578,15 @@ async function listAttestations(params) {
779
1578
  }
780
1579
  async function getLatestAttestations(params) {
781
1580
  const provider = params.provider;
782
- const contract = new Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
783
- const currentBlock = await provider.getBlockNumber();
784
- const fromBlock = params.fromBlock ?? Math.max(0, currentBlock - 5e4);
785
- const events = await contract.queryFilter(contract.filters.Attested(), fromBlock, currentBlock);
786
- const eas = new EAS(params.easContractAddress);
787
- eas.connect(provider);
788
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
789
- const results = [];
790
- for (const event of events) {
791
- if (!("args" in event && Array.isArray(event.args))) {
792
- continue;
793
- }
794
- const args = event.args;
795
- const uid = args?.[2];
796
- const schemaUid = args?.[3];
797
- if (!uid || !schemaUid) {
798
- continue;
799
- }
800
- if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
801
- continue;
802
- }
803
- const attestation = await eas.getAttestation(uid);
804
- if (!attestation || !attestation.uid) {
805
- continue;
806
- }
807
- results.push(parseAttestation(attestation));
808
- }
809
- results.sort((a, b) => Number(b.time - a.time));
1581
+ const toBlock = await provider.getBlockNumber();
1582
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
1583
+ const results = await queryAttestationEvents(
1584
+ params.easContractAddress,
1585
+ provider,
1586
+ fromBlock,
1587
+ toBlock,
1588
+ { schemas: params.schemas }
1589
+ );
810
1590
  return results.slice(0, params.limit ?? 20);
811
1591
  }
812
1592
  function deduplicateReviews(attestations) {
@@ -1010,25 +1790,7 @@ function getExplorerTxUrl(chainId, txHash) {
1010
1790
  function getExplorerAddressUrl(chainId, address) {
1011
1791
  return `${getConfig(chainId).explorer}/address/${address}`;
1012
1792
  }
1013
- async function fetchDidDocument(domain) {
1014
- const normalized = domain.toLowerCase().replace(/\.$/, "");
1015
- const url = `https://${normalized}/.well-known/did.json`;
1016
- let response;
1017
- try {
1018
- response = await fetch(url, { headers: { Accept: "application/json" } });
1019
- } catch (err) {
1020
- throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
1021
- }
1022
- if (!response.ok) {
1023
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1024
- domain,
1025
- status: response.status
1026
- });
1027
- }
1028
- const body = await response.json();
1029
- return body;
1030
- }
1031
- function extractAddressesFromDidDocument(didDocument) {
1793
+ function extractEvmAddressesFromDidDocument(didDocument) {
1032
1794
  const methods = didDocument.verificationMethod;
1033
1795
  if (!Array.isArray(methods)) {
1034
1796
  return [];
@@ -1052,14 +1814,52 @@ function extractAddressesFromDidDocument(didDocument) {
1052
1814
  }
1053
1815
  return [...addresses];
1054
1816
  }
1817
+ function extractJwksFromDidDocument(didDocument) {
1818
+ const methods = didDocument.verificationMethod;
1819
+ if (!Array.isArray(methods)) {
1820
+ return [];
1821
+ }
1822
+ const jwks = [];
1823
+ for (const method of methods) {
1824
+ const publicKeyJwk = method.publicKeyJwk;
1825
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
1826
+ jwks.push(publicKeyJwk);
1827
+ }
1828
+ }
1829
+ return jwks;
1830
+ }
1055
1831
  function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1832
+ const method = extractDidMethod(expectedControllerDid);
1833
+ if (method === "jwk") {
1834
+ try {
1835
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
1836
+ const documentJwks = extractJwksFromDidDocument(didDocument);
1837
+ for (const docJwk of documentJwks) {
1838
+ try {
1839
+ if (publicJwkEquals(docJwk, expectedJwk)) {
1840
+ return { valid: true };
1841
+ }
1842
+ } catch {
1843
+ }
1844
+ }
1845
+ return {
1846
+ valid: false,
1847
+ reason: "No matching publicKeyJwk found in DID document verification methods"
1848
+ };
1849
+ } catch {
1850
+ return {
1851
+ valid: false,
1852
+ reason: "Failed to decode did:jwk controller DID"
1853
+ };
1854
+ }
1855
+ }
1056
1856
  let expectedAddress;
1057
1857
  try {
1058
1858
  expectedAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1059
1859
  } catch {
1060
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
1860
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
1061
1861
  }
1062
- const addresses = extractAddressesFromDidDocument(didDocument);
1862
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
1063
1863
  if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1064
1864
  return { valid: true };
1065
1865
  }
@@ -1068,6 +1868,13 @@ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1068
1868
  reason: `No matching address found in DID document (expected ${expectedAddress})`
1069
1869
  };
1070
1870
  }
1871
+ async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1872
+ if (!domain || typeof domain !== "string") {
1873
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1874
+ }
1875
+ const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
1876
+ return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
1877
+ }
1071
1878
  function buildEip712Domain(name, version, chainId, verifyingContract) {
1072
1879
  return { name, version, chainId, verifyingContract };
1073
1880
  }
@@ -1100,53 +1907,360 @@ function verifyEip712Signature(typedData, signature) {
1100
1907
  throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
1101
1908
  }
1102
1909
  }
1910
+
1911
+ // src/reputation/proof/dns-txt-record.ts
1103
1912
  function parseDnsTxtRecord(record) {
1104
1913
  if (!record || typeof record !== "string") {
1105
1914
  throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
1106
1915
  }
1107
1916
  const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
1108
1917
  const parsed = {};
1918
+ const controllers = [];
1109
1919
  for (const entry of entries) {
1110
- const [key, ...valueParts] = entry.split("=");
1111
- if (!key) {
1112
- continue;
1920
+ const eqIndex = entry.indexOf("=");
1921
+ if (eqIndex === -1) continue;
1922
+ const key = entry.slice(0, eqIndex).trim();
1923
+ const value = entry.slice(eqIndex + 1).trim();
1924
+ if (!key || !value) continue;
1925
+ if (key === "controller") {
1926
+ controllers.push(value);
1927
+ } else {
1928
+ parsed[key] = value;
1929
+ }
1930
+ }
1931
+ return {
1932
+ version: parsed.v,
1933
+ controller: controllers[0],
1934
+ controllers
1935
+ };
1936
+ }
1937
+ function buildDnsTxtRecord(controllerDid) {
1938
+ const normalized = normalizeDid(controllerDid);
1939
+ return `v=1;controller=${normalized}`;
1940
+ }
1941
+ var REQUIRED_OFFER_FIELDS = [
1942
+ "version",
1943
+ "resourceUrl",
1944
+ "scheme",
1945
+ "network",
1946
+ "asset",
1947
+ "payTo",
1948
+ "amount"
1949
+ ];
1950
+ var REQUIRED_RECEIPT_FIELDS = [
1951
+ "version",
1952
+ "network",
1953
+ "resourceUrl",
1954
+ "payer",
1955
+ "issuedAt"
1956
+ ];
1957
+ function validateOfferPayload(payload) {
1958
+ for (const field of REQUIRED_OFFER_FIELDS) {
1959
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1960
+ return { valid: false, field };
1961
+ }
1962
+ }
1963
+ return { valid: true };
1964
+ }
1965
+ function validateReceiptPayload(payload) {
1966
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
1967
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1968
+ return { valid: false, field };
1969
+ }
1970
+ }
1971
+ return { valid: true };
1972
+ }
1973
+ function failure(code, message, partial) {
1974
+ return {
1975
+ valid: false,
1976
+ error: { code, message },
1977
+ ...partial
1978
+ };
1979
+ }
1980
+ async function verifyX402JwsArtifact(artifact, options) {
1981
+ if (!artifact || typeof artifact !== "object") {
1982
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
1983
+ }
1984
+ if (artifact.format !== "jws") {
1985
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
1986
+ }
1987
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1988
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
1989
+ }
1990
+ const compactJws = artifact.signature;
1991
+ const parts = compactJws.split(".");
1992
+ if (parts.length !== 3) {
1993
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
1994
+ }
1995
+ let header;
1996
+ try {
1997
+ header = decodeProtectedHeader(compactJws);
1998
+ } catch {
1999
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
2000
+ }
2001
+ if (!header.alg || typeof header.alg !== "string") {
2002
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
2003
+ }
2004
+ const kid = typeof header.kid === "string" ? header.kid : null;
2005
+ const embeddedJwk = header.jwk;
2006
+ if (!kid && !embeddedJwk) {
2007
+ return failure(
2008
+ "MISSING_KEY_MATERIAL",
2009
+ "JWS header must include at least one of kid or jwk",
2010
+ { header }
2011
+ );
2012
+ }
2013
+ let publicKeyJwk;
2014
+ let publicKeySource;
2015
+ if (embeddedJwk) {
2016
+ const validation = validatePublicJwk(embeddedJwk);
2017
+ if (!validation.valid) {
2018
+ return failure(
2019
+ "INVALID_EMBEDDED_JWK",
2020
+ `Embedded JWK is invalid: ${validation.error}`,
2021
+ { header }
2022
+ );
2023
+ }
2024
+ publicKeyJwk = embeddedJwk;
2025
+ publicKeySource = "embedded-jwk";
2026
+ if (kid) {
2027
+ try {
2028
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
2029
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
2030
+ return failure(
2031
+ "KEY_CONFLICT",
2032
+ "Embedded jwk conflicts with public key resolved from kid",
2033
+ { header, kid }
2034
+ );
2035
+ }
2036
+ } catch (err) {
2037
+ }
2038
+ }
2039
+ } else {
2040
+ publicKeySource = "kid-resolution";
2041
+ try {
2042
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
2043
+ publicKeyJwk = resolved.publicKeyJwk;
2044
+ } catch (err) {
2045
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
2046
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
2047
+ }
2048
+ }
2049
+ let payload;
2050
+ try {
2051
+ const key = await importJWK(publicKeyJwk, header.alg);
2052
+ const result = await compactVerify(compactJws, key);
2053
+ const payloadText = new TextDecoder().decode(result.payload);
2054
+ payload = JSON.parse(payloadText);
2055
+ } catch (err) {
2056
+ const message = err instanceof Error ? err.message : "Signature verification failed";
2057
+ return failure("SIGNATURE_INVALID", message, { header, kid });
2058
+ }
2059
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
2060
+ return {
2061
+ valid: true,
2062
+ header,
2063
+ payload,
2064
+ kid,
2065
+ publicKeyJwk,
2066
+ publicKeySource,
2067
+ publicKeyDid
2068
+ };
2069
+ }
2070
+ async function verifyX402JwsOffer(artifact, options) {
2071
+ const result = await verifyX402JwsArtifact(artifact, options);
2072
+ if (!result.valid) return result;
2073
+ const payloadCheck = validateOfferPayload(result.payload);
2074
+ if (!payloadCheck.valid) {
2075
+ return failure(
2076
+ "INVALID_OFFER_PAYLOAD",
2077
+ `Missing required offer field: ${payloadCheck.field}`,
2078
+ {
2079
+ header: result.header,
2080
+ payload: result.payload,
2081
+ kid: result.kid,
2082
+ publicKeyJwk: result.publicKeyJwk,
2083
+ publicKeySource: result.publicKeySource,
2084
+ publicKeyDid: result.publicKeyDid
2085
+ }
2086
+ );
2087
+ }
2088
+ return result;
2089
+ }
2090
+ async function verifyX402JwsReceipt(artifact, options) {
2091
+ const result = await verifyX402JwsArtifact(artifact, options);
2092
+ if (!result.valid) return result;
2093
+ const payloadCheck = validateReceiptPayload(result.payload);
2094
+ if (!payloadCheck.valid) {
2095
+ return failure(
2096
+ "INVALID_RECEIPT_PAYLOAD",
2097
+ `Missing required receipt field: ${payloadCheck.field}`,
2098
+ {
2099
+ header: result.header,
2100
+ payload: result.payload,
2101
+ kid: result.kid,
2102
+ publicKeyJwk: result.publicKeyJwk,
2103
+ publicKeySource: result.publicKeySource,
2104
+ publicKeyDid: result.publicKeyDid
2105
+ }
2106
+ );
2107
+ }
2108
+ return result;
2109
+ }
2110
+ var OFFER_DOMAIN = {
2111
+ name: "x402 offer",
2112
+ version: "1",
2113
+ chainId: 1
2114
+ };
2115
+ var RECEIPT_DOMAIN = {
2116
+ name: "x402 receipt",
2117
+ version: "1",
2118
+ chainId: 1
2119
+ };
2120
+ var OFFER_TYPES = {
2121
+ Offer: [
2122
+ { name: "version", type: "uint256" },
2123
+ { name: "resourceUrl", type: "string" },
2124
+ { name: "scheme", type: "string" },
2125
+ { name: "network", type: "string" },
2126
+ { name: "asset", type: "string" },
2127
+ { name: "payTo", type: "string" },
2128
+ { name: "amount", type: "string" },
2129
+ { name: "validUntil", type: "uint256" }
2130
+ ]
2131
+ };
2132
+ var RECEIPT_TYPES = {
2133
+ Receipt: [
2134
+ { name: "version", type: "uint256" },
2135
+ { name: "network", type: "string" },
2136
+ { name: "resourceUrl", type: "string" },
2137
+ { name: "payer", type: "string" },
2138
+ { name: "issuedAt", type: "uint256" },
2139
+ { name: "transaction", type: "string" }
2140
+ ]
2141
+ };
2142
+ var REQUIRED_OFFER_FIELDS2 = [
2143
+ "version",
2144
+ "resourceUrl",
2145
+ "scheme",
2146
+ "network",
2147
+ "asset",
2148
+ "payTo",
2149
+ "amount"
2150
+ ];
2151
+ var REQUIRED_RECEIPT_FIELDS2 = [
2152
+ "version",
2153
+ "network",
2154
+ "resourceUrl",
2155
+ "payer",
2156
+ "issuedAt"
2157
+ ];
2158
+ function validateOfferPayload2(payload) {
2159
+ for (const field of REQUIRED_OFFER_FIELDS2) {
2160
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2161
+ return { valid: false, field };
1113
2162
  }
1114
- const value = valueParts.join("=");
1115
- if (!value) {
1116
- continue;
2163
+ }
2164
+ return { valid: true };
2165
+ }
2166
+ function validateReceiptPayload2(payload) {
2167
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
2168
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
2169
+ return { valid: false, field };
1117
2170
  }
1118
- parsed[key.trim()] = value.trim();
1119
2171
  }
2172
+ return { valid: true };
2173
+ }
2174
+ function failure2(code, message, partial) {
1120
2175
  return {
1121
- version: parsed.v,
1122
- controller: parsed.controller,
1123
- ...parsed
2176
+ valid: false,
2177
+ error: { code, message },
2178
+ ...partial
1124
2179
  };
1125
2180
  }
1126
- function buildDnsTxtRecord(controllerDid) {
1127
- const normalized = normalizeDid(controllerDid);
1128
- return `v=1;controller=${normalized}`;
2181
+ function prepareOfferMessage(payload) {
2182
+ return {
2183
+ version: payload.version,
2184
+ resourceUrl: payload.resourceUrl,
2185
+ scheme: payload.scheme,
2186
+ network: payload.network,
2187
+ asset: payload.asset,
2188
+ payTo: payload.payTo,
2189
+ amount: payload.amount,
2190
+ validUntil: payload.validUntil ?? 0
2191
+ };
1129
2192
  }
1130
- async function verifyDnsTxtControllerDid(domain, expectedControllerDid) {
1131
- if (!domain || typeof domain !== "string") {
1132
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
2193
+ function prepareReceiptMessage(payload) {
2194
+ return {
2195
+ version: payload.version,
2196
+ network: payload.network,
2197
+ resourceUrl: payload.resourceUrl,
2198
+ payer: payload.payer,
2199
+ issuedAt: payload.issuedAt,
2200
+ transaction: payload.transaction ?? ""
2201
+ };
2202
+ }
2203
+ function verifyX402Eip712Artifact(artifact, artifactType) {
2204
+ if (!artifact || typeof artifact !== "object") {
2205
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
1133
2206
  }
1134
- const expected = normalizeDid(expectedControllerDid);
1135
- const host = `_omatrust.${domain.toLowerCase().replace(/\.$/, "")}`;
1136
- let records;
1137
- try {
1138
- records = await resolveTxt(host);
1139
- } catch (err) {
1140
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
2207
+ if (artifact.format !== "eip712") {
2208
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
1141
2209
  }
1142
- for (const recordParts of records) {
1143
- const record = recordParts.join("");
1144
- const parsed = parseDnsTxtRecord(record);
1145
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1146
- return { valid: true, record };
2210
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
2211
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
2212
+ }
2213
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2214
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
2215
+ }
2216
+ const payload = artifact.payload;
2217
+ if (artifactType === "offer") {
2218
+ const check = validateOfferPayload2(payload);
2219
+ if (!check.valid) {
2220
+ return failure2(
2221
+ "INVALID_OFFER_PAYLOAD",
2222
+ `Missing required offer field: ${check.field}`,
2223
+ { payload }
2224
+ );
1147
2225
  }
2226
+ } else {
2227
+ const check = validateReceiptPayload2(payload);
2228
+ if (!check.valid) {
2229
+ return failure2(
2230
+ "INVALID_RECEIPT_PAYLOAD",
2231
+ `Missing required receipt field: ${check.field}`,
2232
+ { payload }
2233
+ );
2234
+ }
2235
+ }
2236
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
2237
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
2238
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
2239
+ let signer;
2240
+ try {
2241
+ signer = verifyTypedData(
2242
+ domain,
2243
+ types,
2244
+ message,
2245
+ artifact.signature
2246
+ );
2247
+ signer = getAddress(signer);
2248
+ } catch (err) {
2249
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
2250
+ return failure2("SIGNATURE_INVALID", message2, { payload });
1148
2251
  }
1149
- return { valid: false, reason: "No TXT record matched expected controller DID" };
2252
+ return {
2253
+ valid: true,
2254
+ payload,
2255
+ signer,
2256
+ artifactType
2257
+ };
2258
+ }
2259
+ function verifyX402Eip712Offer(artifact) {
2260
+ return verifyX402Eip712Artifact(artifact, "offer");
2261
+ }
2262
+ function verifyX402Eip712Receipt(artifact) {
2263
+ return verifyX402Eip712Artifact(artifact, "receipt");
1150
2264
  }
1151
2265
 
1152
2266
  // src/reputation/verify.ts
@@ -1206,18 +2320,18 @@ function decodeJwtPayload(compactJws) {
1206
2320
  return JSON.parse(json);
1207
2321
  }
1208
2322
  async function verifyProof(params) {
1209
- const { proof, provider, expectedSubject, expectedController } = params;
2323
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1210
2324
  try {
1211
2325
  switch (proof.proofType) {
1212
2326
  case "tx-encoded-value": {
1213
2327
  if (!provider) {
1214
2328
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1215
2329
  }
1216
- if (!expectedSubject || !expectedController) {
2330
+ if (!expectedSubjectDid || !expectedControllerDid) {
1217
2331
  return {
1218
2332
  valid: false,
1219
2333
  proofType: proof.proofType,
1220
- reason: "expectedSubject and expectedController are required"
2334
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1221
2335
  };
1222
2336
  }
1223
2337
  const proofObject = proof.proofObject;
@@ -1229,13 +2343,13 @@ async function verifyProof(params) {
1229
2343
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1230
2344
  }
1231
2345
  const expectedAmount = calculateTransferAmount(
1232
- expectedSubject,
1233
- expectedController,
2346
+ expectedSubjectDid,
2347
+ expectedControllerDid,
1234
2348
  chainId,
1235
2349
  getProofPurpose(proof)
1236
2350
  );
1237
- const subjectAddress = getAddress(extractAddressFromDid(expectedSubject));
1238
- const controllerAddress = getAddress(extractAddressFromDid(expectedController));
2351
+ const subjectAddress = getAddress(extractAddressFromDid(expectedSubjectDid));
2352
+ const controllerAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1239
2353
  if (tx.from && getAddress(tx.from) !== subjectAddress) {
1240
2354
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1241
2355
  }
@@ -1309,6 +2423,38 @@ async function verifyProof(params) {
1309
2423
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1310
2424
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1311
2425
  }
2426
+ const proofObj = proof.proofObject;
2427
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2428
+ const artifact = {
2429
+ format: "jws",
2430
+ signature: proofObj.signature
2431
+ };
2432
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2433
+ if (!jwsResult.valid) {
2434
+ return {
2435
+ valid: false,
2436
+ proofType: proof.proofType,
2437
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2438
+ };
2439
+ }
2440
+ return { valid: true, proofType: proof.proofType };
2441
+ }
2442
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2443
+ const artifact = {
2444
+ format: "eip712",
2445
+ payload: proofObj.payload,
2446
+ signature: proofObj.signature
2447
+ };
2448
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2449
+ if (!eip712Result.valid) {
2450
+ return {
2451
+ valid: false,
2452
+ proofType: proof.proofType,
2453
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2454
+ };
2455
+ }
2456
+ return { valid: true, proofType: proof.proofType };
2457
+ }
1312
2458
  return { valid: true, proofType: proof.proofType };
1313
2459
  }
1314
2460
  case "evidence-pointer": {
@@ -1320,9 +2466,9 @@ async function verifyProof(params) {
1320
2466
  if (!response.ok) {
1321
2467
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1322
2468
  }
1323
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2469
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1324
2470
  const didDoc = await response.json();
1325
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2471
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1326
2472
  return {
1327
2473
  valid: didCheck.valid,
1328
2474
  proofType: proof.proofType,
@@ -1330,10 +2476,10 @@ async function verifyProof(params) {
1330
2476
  };
1331
2477
  }
1332
2478
  const body = await response.text();
1333
- if (expectedController && !body.includes(expectedController)) {
2479
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1334
2480
  try {
1335
2481
  const parsed = parseDnsTxtRecord(body);
1336
- if (parsed.controller !== expectedController) {
2482
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1337
2483
  return {
1338
2484
  valid: false,
1339
2485
  proofType: proof.proofType,
@@ -1389,8 +2535,8 @@ async function verifyAttestation(params) {
1389
2535
  const result = await verifyProof({
1390
2536
  proof,
1391
2537
  provider: params.provider,
1392
- expectedSubject: params.context?.subject,
1393
- expectedController: params.context?.controller
2538
+ expectedSubjectDid: params.context?.subjectDid,
2539
+ expectedControllerDid: params.context?.controllerDid
1394
2540
  });
1395
2541
  checks[proof.proofType] = result.valid;
1396
2542
  if (!result.valid) {
@@ -1428,6 +2574,9 @@ async function getSchemaDetails(schemaRegistry, schemaUid) {
1428
2574
  revocable: Boolean(details.revocable)
1429
2575
  };
1430
2576
  } catch (err) {
2577
+ if (isEasSchemaNotFoundError(err)) {
2578
+ throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
2579
+ }
1431
2580
  if (err instanceof OmaTrustError) {
1432
2581
  throw err;
1433
2582
  }
@@ -1490,6 +2639,460 @@ async function callControllerWitness(params) {
1490
2639
  method: "did-json"
1491
2640
  };
1492
2641
  }
2642
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
2643
+ async function requestControllerWitness(params) {
2644
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
2645
+ const body = {
2646
+ subjectDid: params.subjectDid,
2647
+ controllerDid: params.controllerDid
2648
+ };
2649
+ if (params.chainId !== void 0) {
2650
+ body.chainId = params.chainId;
2651
+ }
2652
+ let response;
2653
+ try {
2654
+ response = await fetch(url, {
2655
+ method: "POST",
2656
+ headers: { "Content-Type": "application/json" },
2657
+ credentials: "include",
2658
+ body: JSON.stringify(body),
2659
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
2660
+ });
2661
+ } catch (err) {
2662
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
2663
+ }
2664
+ if (!response.ok) {
2665
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
2666
+ throw new OmaTrustError(
2667
+ "API_ERROR",
2668
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
2669
+ { status: response.status, ...errorBody }
2670
+ );
2671
+ }
2672
+ return await response.json();
2673
+ }
2674
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
2675
+ var OWNERSHIP_PATTERNS = [
2676
+ { method: "owner", signature: "function owner() view returns (address)" },
2677
+ { method: "admin", signature: "function admin() view returns (address)" },
2678
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
2679
+ ];
2680
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
2681
+ try {
2682
+ const iface = new Interface([signature]);
2683
+ const data = iface.encodeFunctionData(method, []);
2684
+ const result = await provider.call({ to: contractAddress, data });
2685
+ const [value] = iface.decodeFunctionResult(method, result);
2686
+ if (typeof value === "string" && isAddress(value) && value !== ZeroAddress) {
2687
+ return getAddress(value);
2688
+ }
2689
+ } catch {
2690
+ }
2691
+ return null;
2692
+ }
2693
+ async function discoverContractOwner(provider, contractAddress) {
2694
+ try {
2695
+ const code = await provider.getCode(contractAddress);
2696
+ if (code === "0x" || code === "0x0") {
2697
+ return null;
2698
+ }
2699
+ } catch {
2700
+ return null;
2701
+ }
2702
+ for (const pattern of OWNERSHIP_PATTERNS) {
2703
+ const address = await readOwnerFromContract(
2704
+ provider,
2705
+ contractAddress,
2706
+ pattern.signature,
2707
+ pattern.method
2708
+ );
2709
+ if (address) {
2710
+ return address;
2711
+ }
2712
+ }
2713
+ try {
2714
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
2715
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
2716
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
2717
+ if (adminAddress !== ZeroAddress) {
2718
+ return adminAddress;
2719
+ }
2720
+ }
2721
+ } catch {
2722
+ }
2723
+ return null;
2724
+ }
2725
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
2726
+ const owner = await discoverContractOwner(provider, contractAddress);
2727
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
2728
+ }
2729
+ async function verifyTransferProof(provider, txHash, subjectDid, attesterAddress, chainId) {
2730
+ try {
2731
+ const tx = await provider.getTransaction(txHash);
2732
+ if (!tx || !tx.from || !tx.to) {
2733
+ return false;
2734
+ }
2735
+ if (getAddress(tx.to) !== getAddress(attesterAddress)) {
2736
+ return false;
2737
+ }
2738
+ const attesterDid = buildEvmDidPkh(chainId, attesterAddress);
2739
+ const expectedAmount = calculateTransferAmount(
2740
+ subjectDid,
2741
+ attesterDid,
2742
+ chainId,
2743
+ "shared-control"
2744
+ );
2745
+ const actualValue = BigInt(tx.value ?? 0n);
2746
+ return actualValue === expectedAmount;
2747
+ } catch {
2748
+ return false;
2749
+ }
2750
+ }
2751
+
2752
+ // src/shared/trust-anchors.ts
2753
+ var DEFAULT_TRUST_ANCHORS_URL = "https://api.omatrust.org/v1/trust-anchors";
2754
+ var TRUST_ANCHORS_URL = DEFAULT_TRUST_ANCHORS_URL;
2755
+ var cachedAnchors = null;
2756
+ var cacheTimestamp = 0;
2757
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
2758
+ async function fetchTrustAnchors(url) {
2759
+ const now = Date.now();
2760
+ if (cachedAnchors && now - cacheTimestamp < CACHE_TTL_MS) {
2761
+ return cachedAnchors;
2762
+ }
2763
+ let res;
2764
+ try {
2765
+ res = await fetch(url ?? TRUST_ANCHORS_URL);
2766
+ } catch (err) {
2767
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch trust anchors", { err });
2768
+ }
2769
+ if (!res.ok) {
2770
+ throw new OmaTrustError("NETWORK_ERROR", `Failed to fetch trust anchors: ${res.status} ${res.statusText}`);
2771
+ }
2772
+ const anchors = await res.json();
2773
+ if (!anchors.version || !anchors.chains || typeof anchors.chains !== "object") {
2774
+ throw new OmaTrustError("INVALID_INPUT", "Invalid trust anchors format");
2775
+ }
2776
+ cachedAnchors = anchors;
2777
+ cacheTimestamp = now;
2778
+ return anchors;
2779
+ }
2780
+ function getChainAnchors(anchors, caip2) {
2781
+ const chain = anchors.chains[caip2];
2782
+ if (!chain) {
2783
+ throw new OmaTrustError("UNSUPPORTED_CHAIN", `Chain ${caip2} is not in the trust anchors`, {
2784
+ caip2,
2785
+ supportedChains: Object.keys(anchors.chains)
2786
+ });
2787
+ }
2788
+ return chain;
2789
+ }
2790
+
2791
+ // src/reputation/proof/dns-txt-shared.ts
2792
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
2793
+ if (!domain || typeof domain !== "string") {
2794
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
2795
+ }
2796
+ if (!options.resolveTxt) {
2797
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
2798
+ }
2799
+ const prefix = options.recordPrefix ?? "_controllers";
2800
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
2801
+ let records;
2802
+ try {
2803
+ records = await options.resolveTxt(host);
2804
+ } catch (err) {
2805
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
2806
+ }
2807
+ for (const recordParts of records) {
2808
+ const record = recordParts.join("");
2809
+ const parsed = parseDnsTxtRecord(record);
2810
+ if (parsed.version !== "1") continue;
2811
+ for (const recordController of parsed.controllers) {
2812
+ if (isSameControllerId(recordController, expectedControllerDid)) {
2813
+ return { valid: true, record };
2814
+ }
2815
+ }
2816
+ }
2817
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
2818
+ }
2819
+
2820
+ // src/reputation/attester-authorization.ts
2821
+ var DEFAULT_CHAIN = "eip155:6623";
2822
+ var DEFAULT_PURPOSES = ["authentication", "assertionMethod"];
2823
+ async function getControllerAuthorization(params) {
2824
+ const chain = params.chain ?? DEFAULT_CHAIN;
2825
+ const purposes = params.purpose && params.purpose.length > 0 ? params.purpose : DEFAULT_PURPOSES;
2826
+ const controllerDid = normalizeDid(params.controllerDid);
2827
+ const controllerMethod = extractDidMethod(controllerDid);
2828
+ const controllerEvmAddress = extractControllerEvmAddress(controllerDid);
2829
+ const anchors = await fetchTrustAnchors();
2830
+ const chainAnchors = getChainAnchors(anchors, chain);
2831
+ const easContractAddress = params.easContractAddress ?? chainAnchors.easContract;
2832
+ const provider = params.provider;
2833
+ const controllerWitnessSchemaUid = chainAnchors.schemas["controller-witness"];
2834
+ const keyBindingSchemaUid = chainAnchors.schemas["key-binding"];
2835
+ const subjectAddress = didToAddress(params.subjectDid);
2836
+ let relevantWitnesses = [];
2837
+ if (controllerWitnessSchemaUid) {
2838
+ const rawWitnesses = await queryByRecipientAndSchema(
2839
+ easContractAddress,
2840
+ provider,
2841
+ subjectAddress,
2842
+ [controllerWitnessSchemaUid],
2843
+ params.fromBlock ?? 0
2844
+ );
2845
+ relevantWitnesses = rawWitnesses.filter((att) => {
2846
+ const witnessController = att.data?.controller;
2847
+ if (typeof witnessController !== "string") return false;
2848
+ return isSameControllerId(witnessController, controllerDid);
2849
+ });
2850
+ relevantWitnesses.sort((a, b) => Number(a.time - b.time));
2851
+ }
2852
+ const controllerWitnesses = relevantWitnesses.map((att) => ({
2853
+ uid: att.uid,
2854
+ issuedAt: att.time,
2855
+ attester: att.attester,
2856
+ method: resolveWitnessMethod(att.data?.method)
2857
+ }));
2858
+ let keyBindingUid = null;
2859
+ let until = null;
2860
+ let keyPurposeStatus = "not-required";
2861
+ if (keyBindingSchemaUid) {
2862
+ const keyBindings = await queryByRecipientAndSchema(
2863
+ easContractAddress,
2864
+ provider,
2865
+ subjectAddress,
2866
+ [keyBindingSchemaUid],
2867
+ params.fromBlock ?? 0
2868
+ );
2869
+ const relevantKeyBindings = keyBindings.filter((att) => controllerMatchesKeyBinding(att, controllerDid, controllerMethod)).sort((a, b) => Number(b.time - a.time));
2870
+ const relevantKeyBinding = relevantKeyBindings[0] ?? null;
2871
+ if (relevantKeyBinding) {
2872
+ keyBindingUid = relevantKeyBinding.uid;
2873
+ if (relevantKeyBinding.revocationTime > 0n) {
2874
+ until = relevantKeyBinding.revocationTime;
2875
+ }
2876
+ const keyPurposes = relevantKeyBinding.data?.keyPurpose;
2877
+ if (!Array.isArray(keyPurposes) || keyPurposes.length === 0) {
2878
+ keyPurposeStatus = "unknown";
2879
+ } else {
2880
+ const allMatched = purposes.every((p) => keyPurposes.includes(p));
2881
+ keyPurposeStatus = allMatched ? "matched" : "mismatch";
2882
+ }
2883
+ }
2884
+ }
2885
+ let currentlyVerified = false;
2886
+ let liveMethod = null;
2887
+ let transferProofVerified = false;
2888
+ let transferProofAnchor = null;
2889
+ const subjectMethod = extractDidMethod(params.subjectDid);
2890
+ if (subjectMethod === "web") {
2891
+ const domain = getDomainFromDidWeb(params.subjectDid);
2892
+ if (domain) {
2893
+ try {
2894
+ const dnsResult = await verifyDnsTxtControllerDid(domain, controllerDid, {
2895
+ resolveTxt: params.resolveTxt
2896
+ });
2897
+ if (dnsResult.valid) {
2898
+ currentlyVerified = true;
2899
+ liveMethod = "dns";
2900
+ }
2901
+ } catch {
2902
+ }
2903
+ if (!currentlyVerified) {
2904
+ try {
2905
+ const fetchDoc = params.fetchDidDocument ?? fetchDidDocument;
2906
+ const didDoc = await fetchDoc(domain);
2907
+ const didJsonResult = verifyDidDocumentControllerDid(didDoc, controllerDid);
2908
+ if (didJsonResult.valid) {
2909
+ currentlyVerified = true;
2910
+ liveMethod = "did-document";
2911
+ }
2912
+ } catch {
2913
+ }
2914
+ }
2915
+ }
2916
+ } else if (subjectMethod === "pkh") {
2917
+ if (isEvmDidPkh(params.subjectDid) && controllerEvmAddress) {
2918
+ const subjectContractAddress = getAddressFromDidPkh(params.subjectDid);
2919
+ const subjectChainId = getChainIdFromDidPkh(params.subjectDid);
2920
+ const chainIdNum = subjectChainId ? Number(subjectChainId) : null;
2921
+ if (subjectContractAddress && chainIdNum) {
2922
+ const ownerAddress = await discoverContractOwner(
2923
+ provider,
2924
+ subjectContractAddress
2925
+ );
2926
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(controllerEvmAddress)) {
2927
+ currentlyVerified = true;
2928
+ liveMethod = "contract-ownership";
2929
+ }
2930
+ if (!currentlyVerified && keyBindingUid) {
2931
+ const relevantKeyBinding = await findKeyBindingWithTransferProof(
2932
+ keyBindingSchemaUid,
2933
+ easContractAddress,
2934
+ provider,
2935
+ subjectAddress,
2936
+ controllerDid,
2937
+ controllerMethod,
2938
+ params.fromBlock ?? 0
2939
+ );
2940
+ if (relevantKeyBinding) {
2941
+ const proofTxHash = relevantKeyBinding.data?.proofTxHash;
2942
+ if (proofTxHash && /^0x[0-9a-fA-F]{64}$/.test(proofTxHash)) {
2943
+ const verified = await verifyTransferProof(
2944
+ provider,
2945
+ proofTxHash,
2946
+ params.subjectDid,
2947
+ controllerEvmAddress,
2948
+ chainIdNum
2949
+ );
2950
+ if (verified) {
2951
+ transferProofVerified = true;
2952
+ transferProofAnchor = relevantKeyBinding.time;
2953
+ }
2954
+ }
2955
+ }
2956
+ }
2957
+ }
2958
+ }
2959
+ }
2960
+ const witnessAnchor = relevantWitnesses.length > 0 ? relevantWitnesses[0].time : null;
2961
+ const anchoredFrom = witnessAnchor ?? transferProofAnchor;
2962
+ const purposeBlocks = keyPurposeStatus === "mismatch";
2963
+ const hasOpenWindow = relevantWitnesses.length > 0 && until === null;
2964
+ const hasKeyBindingWithProof = keyBindingUid !== null && transferProofVerified && until === null;
2965
+ const authorized = !purposeBlocks && (hasOpenWindow || currentlyVerified && until === null || hasKeyBindingWithProof);
2966
+ return {
2967
+ authorized,
2968
+ anchoredFrom,
2969
+ until,
2970
+ currentlyVerified,
2971
+ liveMethod,
2972
+ controllerWitnesses,
2973
+ keyBindingUid,
2974
+ keyPurposeStatus,
2975
+ transferProofVerified: transferProofVerified || void 0
2976
+ };
2977
+ }
2978
+ function controllerMatchesKeyBinding(att, controllerDid, controllerMethod) {
2979
+ const keyId = att.data?.keyId;
2980
+ if (typeof keyId === "string") {
2981
+ if (isSameControllerId(keyId, controllerDid)) {
2982
+ return true;
2983
+ }
2984
+ }
2985
+ if (controllerMethod === "jwk") {
2986
+ const publicKeyJwkRaw = att.data?.publicKeyJwk;
2987
+ if (publicKeyJwkRaw) {
2988
+ try {
2989
+ const onChainJwk = typeof publicKeyJwkRaw === "string" ? JSON.parse(publicKeyJwkRaw) : publicKeyJwkRaw;
2990
+ const controllerJwk = didJwkToJwk(controllerDid);
2991
+ return publicJwkEquals(onChainJwk, controllerJwk);
2992
+ } catch {
2993
+ }
2994
+ }
2995
+ }
2996
+ return false;
2997
+ }
2998
+ var EAS_EVENT_ABI2 = [
2999
+ "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
3000
+ ];
3001
+ function resolveWitnessMethod(value) {
3002
+ if (typeof value !== "string") return void 0;
3003
+ switch (value) {
3004
+ case "dns":
3005
+ case "dns-txt":
3006
+ return "dns";
3007
+ case "did-document":
3008
+ case "did-json":
3009
+ return "did-document";
3010
+ case "manual":
3011
+ return "manual";
3012
+ default:
3013
+ return "other";
3014
+ }
3015
+ }
3016
+ async function queryByRecipientAndSchema(easContractAddress, provider, recipient, schemas, fromBlock) {
3017
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI2, provider);
3018
+ const filter = contract.filters.Attested(recipient, null);
3019
+ const toBlock = await provider.getBlockNumber();
3020
+ let events;
3021
+ try {
3022
+ events = await contract.queryFilter(filter, fromBlock, toBlock);
3023
+ } catch (err) {
3024
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
3025
+ }
3026
+ const eas = new EAS(easContractAddress);
3027
+ eas.connect(provider);
3028
+ const schemaFilter = schemas.map((s) => s.toLowerCase());
3029
+ const results = [];
3030
+ for (const event of events) {
3031
+ if (!("args" in event) || !Array.isArray(event.args)) continue;
3032
+ const args = event.args;
3033
+ const uid = args?.[2];
3034
+ const schemaUid = args?.[3];
3035
+ if (!uid || !schemaUid) continue;
3036
+ if (!schemaFilter.includes(schemaUid.toLowerCase())) continue;
3037
+ const attestation = await eas.getAttestation(uid);
3038
+ if (!attestation || !attestation.uid) continue;
3039
+ let data = {};
3040
+ const rawData = attestation.data;
3041
+ if (rawData && rawData !== "0x") {
3042
+ try {
3043
+ data = decodeAttestationData(
3044
+ "string subject, string controller, string method",
3045
+ rawData
3046
+ );
3047
+ } catch {
3048
+ try {
3049
+ data = decodeAttestationData(
3050
+ "string subject, string keyId, string publicKeyJwk, string[] keyPurpose, string[] proofs, uint256 issuedAt, uint256 effectiveAt, uint256 expiresAt",
3051
+ rawData
3052
+ );
3053
+ } catch {
3054
+ }
3055
+ }
3056
+ }
3057
+ const toBigIntSafe = (value) => {
3058
+ if (typeof value === "bigint") return value;
3059
+ if (typeof value === "number") return BigInt(value);
3060
+ if (typeof value === "string" && value.length > 0) return BigInt(value);
3061
+ return 0n;
3062
+ };
3063
+ results.push({
3064
+ uid: attestation.uid,
3065
+ schema: attestation.schema,
3066
+ attester: attestation.attester,
3067
+ recipient: attestation.recipient,
3068
+ revocable: Boolean(attestation.revocable),
3069
+ revocationTime: toBigIntSafe(attestation.revocationTime),
3070
+ expirationTime: toBigIntSafe(attestation.expirationTime),
3071
+ time: toBigIntSafe(attestation.time),
3072
+ refUID: attestation.refUID,
3073
+ data
3074
+ });
3075
+ }
3076
+ return results;
3077
+ }
3078
+ async function findKeyBindingWithTransferProof(keyBindingSchemaUid, easContractAddress, provider, subjectAddress, controllerDid, controllerMethod, fromBlock) {
3079
+ const keyBindings = await queryByRecipientAndSchema(
3080
+ easContractAddress,
3081
+ provider,
3082
+ subjectAddress,
3083
+ [keyBindingSchemaUid],
3084
+ fromBlock
3085
+ );
3086
+ const withProof = keyBindings.filter((att) => {
3087
+ if (!controllerMatchesKeyBinding(att, controllerDid, controllerMethod)) return false;
3088
+ const proofs = att.data?.proofs;
3089
+ if (Array.isArray(proofs)) {
3090
+ return proofs.some((p) => typeof p === "string" && /^0x[0-9a-fA-F]{64}$/.test(p));
3091
+ }
3092
+ return typeof att.data?.proofTxHash === "string";
3093
+ }).sort((a, b) => Number(b.time - a.time));
3094
+ return withProof[0] ?? null;
3095
+ }
1493
3096
 
1494
3097
  // src/reputation/proof/tx-interaction.ts
1495
3098
  function createTxInteractionProof(chainId, txHash) {
@@ -1611,6 +3214,278 @@ function createEvidencePointerProof(url) {
1611
3214
  issuedAt: Math.floor(Date.now() / 1e3)
1612
3215
  };
1613
3216
  }
3217
+ function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
3218
+ return verifyDnsTxtControllerDid(domain, expectedControllerDid, {
3219
+ ...options,
3220
+ resolveTxt: options.resolveTxt ?? resolveTxt
3221
+ });
3222
+ }
3223
+ function assertConnectedWalletDid(input) {
3224
+ const normalized = normalizeDid(input);
3225
+ if (extractDidMethod(normalized) !== "pkh") {
3226
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
3227
+ connectedWalletDid: input
3228
+ });
3229
+ }
3230
+ return normalized;
3231
+ }
3232
+ function normalizeSubjectDid(input) {
3233
+ return normalizeDid(input);
3234
+ }
3235
+ async function verifyDidWebOwnership(params) {
3236
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
3237
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
3238
+ const domain = getDomainFromDidWeb(subjectDid);
3239
+ if (!domain) {
3240
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be a did:web DID", {
3241
+ subjectDid: params.subjectDid
3242
+ });
3243
+ }
3244
+ let dnsReason;
3245
+ try {
3246
+ const dnsResult = await verifyDnsTxtControllerDid(domain, connectedWalletDid, {
3247
+ resolveTxt: params.resolveTxt,
3248
+ recordPrefix: params.recordPrefix
3249
+ });
3250
+ if (dnsResult.valid) {
3251
+ return {
3252
+ valid: true,
3253
+ method: "dns",
3254
+ details: `Verified via DNS TXT record at ${params.recordPrefix ?? "_controllers"}.${domain}`,
3255
+ subjectDid,
3256
+ connectedWalletDid
3257
+ };
3258
+ }
3259
+ dnsReason = dnsResult.reason;
3260
+ } catch (error) {
3261
+ dnsReason = error instanceof Error ? error.message : "DNS TXT verification failed";
3262
+ }
3263
+ let didDocReason;
3264
+ try {
3265
+ const didDocumentResult = await verifyDidJsonControllerDid(domain, connectedWalletDid, {
3266
+ fetchDidDocument: params.fetchDidDocument
3267
+ });
3268
+ if (didDocumentResult.valid) {
3269
+ return {
3270
+ valid: true,
3271
+ method: "did-document",
3272
+ details: `Verified via DID document at https://${domain}/.well-known/did.json`,
3273
+ subjectDid,
3274
+ connectedWalletDid
3275
+ };
3276
+ }
3277
+ didDocReason = didDocumentResult.reason;
3278
+ } catch (error) {
3279
+ didDocReason = error instanceof Error ? error.message : "DID document verification failed";
3280
+ }
3281
+ return {
3282
+ valid: false,
3283
+ reason: "DID ownership verification failed",
3284
+ details: `DNS check: ${dnsReason ?? "failed"}. DID document check: ${didDocReason ?? "failed"}.`,
3285
+ subjectDid,
3286
+ connectedWalletDid
3287
+ };
3288
+ }
3289
+ async function verifyDidPkhOwnership(params) {
3290
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
3291
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
3292
+ if (!isEvmDidPkh(subjectDid)) {
3293
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be an EVM did:pkh DID", {
3294
+ subjectDid: params.subjectDid
3295
+ });
3296
+ }
3297
+ const subjectAddress = getAddressFromDidPkh(subjectDid);
3298
+ const connectedWalletAddress = getAddressFromDidPkh(connectedWalletDid);
3299
+ const chainIdRaw = getChainIdFromDidPkh(subjectDid);
3300
+ if (!subjectAddress || !connectedWalletAddress || !chainIdRaw) {
3301
+ throw new OmaTrustError("INVALID_INPUT", "Could not parse did:pkh ownership inputs", {
3302
+ subjectDid: params.subjectDid,
3303
+ connectedWalletDid: params.connectedWalletDid
3304
+ });
3305
+ }
3306
+ const chainId = Number(chainIdRaw);
3307
+ if (!Number.isFinite(chainId)) {
3308
+ throw new OmaTrustError("INVALID_INPUT", "Invalid chain id in subjectDid", {
3309
+ subjectDid: params.subjectDid
3310
+ });
3311
+ }
3312
+ const code = await params.provider.getCode(subjectAddress);
3313
+ const isContract = code !== "0x" && code !== "0x0";
3314
+ if (!isContract) {
3315
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress)) {
3316
+ return {
3317
+ valid: true,
3318
+ method: "wallet",
3319
+ details: "Verified direct wallet ownership from matching did:pkh subject and connected wallet",
3320
+ subjectDid,
3321
+ connectedWalletDid
3322
+ };
3323
+ }
3324
+ return {
3325
+ valid: false,
3326
+ reason: "EOA did:pkh subject does not match connected wallet",
3327
+ details: "For direct wallet did:pkh subjects, connectedWalletDid must match the subject DID.",
3328
+ subjectDid,
3329
+ connectedWalletDid
3330
+ };
3331
+ }
3332
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
3333
+ if (params.txHash) {
3334
+ if (!controllingWalletDid) {
3335
+ return {
3336
+ valid: false,
3337
+ reason: "Could not discover controlling wallet",
3338
+ details: "Contract does not expose owner/admin/getOwner or a readable EIP-1967 admin slot for transfer verification.",
3339
+ subjectDid,
3340
+ connectedWalletDid
3341
+ };
3342
+ }
3343
+ const tx = await params.provider.getTransaction(params.txHash);
3344
+ if (!tx) {
3345
+ return {
3346
+ valid: false,
3347
+ reason: "Transaction not found",
3348
+ details: `Transaction ${params.txHash} was not found on chain ${chainId}.`,
3349
+ subjectDid,
3350
+ connectedWalletDid,
3351
+ controllingWalletDid
3352
+ };
3353
+ }
3354
+ const receipt = await params.provider.getTransactionReceipt(params.txHash);
3355
+ if (!receipt) {
3356
+ return {
3357
+ valid: false,
3358
+ reason: "Transaction not confirmed",
3359
+ details: "Transaction exists but is not yet confirmed.",
3360
+ subjectDid,
3361
+ connectedWalletDid,
3362
+ controllingWalletDid
3363
+ };
3364
+ }
3365
+ const controllingWalletAddress = getAddressFromDidPkh(controllingWalletDid);
3366
+ if (!tx.from || !controllingWalletAddress || getAddress(tx.from) !== getAddress(controllingWalletAddress)) {
3367
+ return {
3368
+ valid: false,
3369
+ reason: "Wrong sender",
3370
+ details: `Transfer must originate from controlling wallet ${controllingWalletDid}.`,
3371
+ subjectDid,
3372
+ connectedWalletDid,
3373
+ controllingWalletDid
3374
+ };
3375
+ }
3376
+ if (!tx.to || getAddress(tx.to) !== getAddress(connectedWalletAddress)) {
3377
+ return {
3378
+ valid: false,
3379
+ reason: "Wrong recipient",
3380
+ details: `Transfer must be sent to connected wallet ${connectedWalletDid}.`,
3381
+ subjectDid,
3382
+ connectedWalletDid,
3383
+ controllingWalletDid
3384
+ };
3385
+ }
3386
+ const expectedAmount = calculateTransferAmount(
3387
+ subjectDid,
3388
+ connectedWalletDid,
3389
+ chainId,
3390
+ "shared-control"
3391
+ );
3392
+ const actualValue = BigInt(tx.value ?? 0n);
3393
+ if (actualValue !== expectedAmount) {
3394
+ return {
3395
+ valid: false,
3396
+ reason: "Wrong amount",
3397
+ details: `Transfer amount ${actualValue.toString()} does not match expected proof amount ${expectedAmount.toString()}.`,
3398
+ subjectDid,
3399
+ connectedWalletDid,
3400
+ controllingWalletDid
3401
+ };
3402
+ }
3403
+ await params.provider.getBlockNumber();
3404
+ await params.provider.getBlock(receipt.blockNumber);
3405
+ return {
3406
+ valid: true,
3407
+ method: "transfer",
3408
+ details: `Verified via transfer proof ${params.txHash}.`,
3409
+ subjectDid,
3410
+ connectedWalletDid,
3411
+ controllingWalletDid
3412
+ };
3413
+ }
3414
+ for (const pattern of OWNERSHIP_PATTERNS) {
3415
+ const ownerAddress = await readOwnerFromContract(
3416
+ params.provider,
3417
+ subjectAddress,
3418
+ pattern.signature,
3419
+ pattern.method
3420
+ );
3421
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(connectedWalletAddress)) {
3422
+ return {
3423
+ valid: true,
3424
+ method: "contract",
3425
+ details: `Verified via ${pattern.method}() ownership check.`,
3426
+ subjectDid,
3427
+ connectedWalletDid,
3428
+ controllingWalletDid: controllingWalletDid ?? void 0
3429
+ };
3430
+ }
3431
+ }
3432
+ try {
3433
+ const adminValue = await params.provider.getStorage(subjectAddress, EIP1967_ADMIN_SLOT);
3434
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
3435
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
3436
+ if (adminAddress === getAddress(connectedWalletAddress)) {
3437
+ return {
3438
+ valid: true,
3439
+ method: "contract",
3440
+ details: "Verified via EIP-1967 admin slot.",
3441
+ subjectDid,
3442
+ connectedWalletDid,
3443
+ controllingWalletDid: controllingWalletDid ?? buildEvmDidPkh(chainId, adminAddress)
3444
+ };
3445
+ }
3446
+ }
3447
+ } catch {
3448
+ }
3449
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress) && controllingWalletDid) {
3450
+ return {
3451
+ valid: true,
3452
+ method: "minting-wallet",
3453
+ details: `Verified because connected wallet matches the contract DID address. Controlling wallet is ${controllingWalletDid}.`,
3454
+ subjectDid,
3455
+ connectedWalletDid,
3456
+ controllingWalletDid
3457
+ };
3458
+ }
3459
+ return {
3460
+ valid: false,
3461
+ reason: "Contract ownership verification failed",
3462
+ details: controllingWalletDid ? `Connected wallet ${connectedWalletDid} does not match the controlling wallet ${controllingWalletDid} or the subject contract address ${subjectDid}.` : `Could not match connected wallet ${connectedWalletDid} to contract ownership for ${subjectDid}.`,
3463
+ subjectDid,
3464
+ connectedWalletDid,
3465
+ controllingWalletDid: controllingWalletDid ?? void 0
3466
+ };
3467
+ }
3468
+ async function verifySubjectOwnership(params) {
3469
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
3470
+ const method = extractDidMethod(subjectDid);
3471
+ if (method === "web") {
3472
+ return verifyDidWebOwnership(params);
3473
+ }
3474
+ if (method === "pkh") {
3475
+ const didPkhParams = params;
3476
+ if (!didPkhParams.provider) {
3477
+ throw new OmaTrustError(
3478
+ "INVALID_INPUT",
3479
+ "provider is required for did:pkh ownership verification",
3480
+ { subjectDid }
3481
+ );
3482
+ }
3483
+ return verifyDidPkhOwnership(didPkhParams);
3484
+ }
3485
+ throw new OmaTrustError("INVALID_INPUT", "Unsupported DID type for ownership verification", {
3486
+ subjectDid
3487
+ });
3488
+ }
1614
3489
 
1615
3490
  // src/app-registry/index.ts
1616
3491
  var app_registry_exports = {};