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

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 (36) hide show
  1. package/README.md +21 -7
  2. package/dist/identity/index.cjs +1 -1
  3. package/dist/identity/index.cjs.map +1 -1
  4. package/dist/identity/index.js +1 -1
  5. package/dist/identity/index.js.map +1 -1
  6. package/dist/index-B9KW02US.d.cts +119 -0
  7. package/dist/index-DXrwBex9.d.ts +119 -0
  8. package/dist/index.cjs +656 -91
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +2 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +657 -92
  13. package/dist/index.js.map +1 -1
  14. package/dist/reputation/index.browser.cjs +2133 -0
  15. package/dist/reputation/index.browser.cjs.map +1 -0
  16. package/dist/reputation/index.browser.d.cts +14 -0
  17. package/dist/reputation/index.browser.d.ts +14 -0
  18. package/dist/reputation/index.browser.js +2071 -0
  19. package/dist/reputation/index.browser.js.map +1 -0
  20. package/dist/reputation/index.cjs +675 -90
  21. package/dist/reputation/index.cjs.map +1 -1
  22. package/dist/reputation/index.d.cts +2 -1
  23. package/dist/reputation/index.d.ts +2 -1
  24. package/dist/reputation/index.js +669 -92
  25. package/dist/reputation/index.js.map +1 -1
  26. package/dist/subject-ownership-CXvzEjpH.d.cts +434 -0
  27. package/dist/subject-ownership-CXvzEjpH.d.ts +434 -0
  28. package/dist/widgets/index.cjs +195 -0
  29. package/dist/widgets/index.cjs.map +1 -0
  30. package/dist/widgets/index.d.cts +134 -0
  31. package/dist/widgets/index.d.ts +134 -0
  32. package/dist/widgets/index.js +185 -0
  33. package/dist/widgets/index.js.map +1 -0
  34. package/package.json +33 -6
  35. package/dist/index-ChbJxwOA.d.cts +0 -415
  36. package/dist/index-ChbJxwOA.d.ts +0 -415
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
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, hexlify, randomBytes, id, Contract, Interface } from 'ethers';
2
2
  import canonicalize from 'canonicalize';
3
3
  import { SchemaEncoder, EAS } from '@ethereum-attestation-service/eas-sdk';
4
4
  import { resolveTxt } from 'dns/promises';
@@ -152,7 +152,7 @@ function extractDidIdentifier(did) {
152
152
  }
153
153
  function normalizeDomain(domain) {
154
154
  assertString(domain, "domain", "INVALID_DID");
155
- return domain.trim().toLowerCase().replace(/\.$/, "");
155
+ return domain.trim().toLowerCase().replace(/\.$/, "").replace(/^www\./, "");
156
156
  }
157
157
  function normalizeDidWeb(input) {
158
158
  assertString(input, "input", "INVALID_DID");
@@ -355,6 +355,7 @@ function hashCanonicalizedJson(obj, algorithm) {
355
355
  var reputation_exports = {};
356
356
  __export(reputation_exports, {
357
357
  buildDelegatedAttestationTypedData: () => buildDelegatedAttestationTypedData,
358
+ buildDelegatedTypedDataFromEncoded: () => buildDelegatedTypedDataFromEncoded,
358
359
  buildDnsTxtRecord: () => buildDnsTxtRecord,
359
360
  buildEip712Domain: () => buildEip712Domain,
360
361
  calculateAverageUserReviewRating: () => calculateAverageUserReviewRating,
@@ -378,6 +379,7 @@ __export(reputation_exports, {
378
379
  formatSchemaUid: () => formatSchemaUid,
379
380
  formatTransferAmount: () => formatTransferAmount,
380
381
  getAttestation: () => getAttestation,
382
+ getAttestationsByAttester: () => getAttestationsByAttester,
381
383
  getAttestationsForDid: () => getAttestationsForDid,
382
384
  getChainConstants: () => getChainConstants,
383
385
  getExplorerAddressUrl: () => getExplorerAddressUrl,
@@ -393,16 +395,22 @@ __export(reputation_exports, {
393
395
  normalizeSchema: () => normalizeSchema,
394
396
  parseDnsTxtRecord: () => parseDnsTxtRecord,
395
397
  prepareDelegatedAttestation: () => prepareDelegatedAttestation,
398
+ revokeAttestation: () => revokeAttestation,
396
399
  schemaToString: () => schemaToString,
397
400
  splitSignature: () => splitSignature,
398
401
  submitAttestation: () => submitAttestation,
399
402
  submitDelegatedAttestation: () => submitDelegatedAttestation,
403
+ validateAttestationData: () => validateAttestationData,
400
404
  verifyAttestation: () => verifyAttestation,
401
405
  verifyDidDocumentControllerDid: () => verifyDidDocumentControllerDid,
406
+ verifyDidJsonControllerDid: () => verifyDidJsonControllerDid,
407
+ verifyDidPkhOwnership: () => verifyDidPkhOwnership,
408
+ verifyDidWebOwnership: () => verifyDidWebOwnership,
402
409
  verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid,
403
410
  verifyEip712Signature: () => verifyEip712Signature,
404
411
  verifyProof: () => verifyProof,
405
- verifySchemaExists: () => verifySchemaExists
412
+ verifySchemaExists: () => verifySchemaExists,
413
+ verifySubjectOwnership: () => verifySubjectOwnership
406
414
  });
407
415
  function normalizeSchema(schema) {
408
416
  if (Array.isArray(schema)) {
@@ -438,6 +446,13 @@ function encodeAttestationData(schema, data) {
438
446
  if (!data || typeof data !== "object") {
439
447
  throw new OmaTrustError("INVALID_INPUT", "data must be an object");
440
448
  }
449
+ const validationErrors = validateAttestationData(schema, data);
450
+ if (validationErrors.length > 0) {
451
+ const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
452
+ throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
453
+ errors: validationErrors
454
+ });
455
+ }
441
456
  const fields = normalizeSchema(schema);
442
457
  const schemaString = schemaToString(fields);
443
458
  const encoder = new SchemaEncoder(schemaString);
@@ -450,6 +465,99 @@ function encodeAttestationData(schema, data) {
450
465
  );
451
466
  return encoded;
452
467
  }
468
+ function getActualType(value) {
469
+ if (value === null) {
470
+ return "null";
471
+ }
472
+ if (value === void 0) {
473
+ return "undefined";
474
+ }
475
+ if (Array.isArray(value)) {
476
+ return "array";
477
+ }
478
+ if (typeof value === "number") {
479
+ return Number.isFinite(value) ? "number" : "non-finite-number";
480
+ }
481
+ return typeof value;
482
+ }
483
+ function isNumericValue(value, allowNegative) {
484
+ if (typeof value === "bigint") {
485
+ return allowNegative || value >= 0n;
486
+ }
487
+ if (typeof value === "number") {
488
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
489
+ return false;
490
+ }
491
+ return allowNegative || value >= 0;
492
+ }
493
+ if (typeof value === "string") {
494
+ if (value.length === 0) {
495
+ return false;
496
+ }
497
+ const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
498
+ return pattern.test(value);
499
+ }
500
+ return false;
501
+ }
502
+ function isHex(value) {
503
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
504
+ }
505
+ function validateFieldValue(type, value) {
506
+ const normalizedType = type.trim().toLowerCase();
507
+ const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
508
+ if (/^uint\d*$/.test(normalizedType)) {
509
+ return isNumericValue(value, false);
510
+ }
511
+ if (/^int\d*$/.test(normalizedType)) {
512
+ return isNumericValue(value, true);
513
+ }
514
+ if (normalizedType === "string") {
515
+ return typeof value === "string";
516
+ }
517
+ if (normalizedType === "string[]") {
518
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
519
+ }
520
+ if (normalizedType === "bool") {
521
+ return typeof value === "boolean";
522
+ }
523
+ if (normalizedType === "address") {
524
+ return typeof value === "string" && isAddress(value);
525
+ }
526
+ if (normalizedType === "bytes") {
527
+ return isHex(value) && (value.length - 2) % 2 === 0;
528
+ }
529
+ if (bytesNMatch) {
530
+ const expectedBytes = Number(bytesNMatch[1]);
531
+ return isHex(value) && value.length === 2 + expectedBytes * 2;
532
+ }
533
+ return true;
534
+ }
535
+ function validateAttestationData(schema, data) {
536
+ if (!data || typeof data !== "object") {
537
+ return [{
538
+ schemaFieldName: "data",
539
+ expectedType: "object",
540
+ providedType: getActualType(data),
541
+ providedValue: data
542
+ }];
543
+ }
544
+ const fields = normalizeSchema(schema);
545
+ const errors = [];
546
+ for (const field of fields) {
547
+ const value = data[field.name];
548
+ const isValid = validateFieldValue(field.type, value);
549
+ if (isValid) {
550
+ continue;
551
+ }
552
+ errors.push({
553
+ schemaFieldName: field.name,
554
+ expectedType: field.type,
555
+ providedType: getActualType(value),
556
+ providedValue: value
557
+ });
558
+ }
559
+ return errors;
560
+ }
453
561
  function decodeAttestationData(schema, encodedData) {
454
562
  if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
455
563
  throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
@@ -470,22 +578,11 @@ function decodeAttestationData(schema, encodedData) {
470
578
  return result;
471
579
  }
472
580
  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
- }
581
+ const value = data["expiresAt"];
582
+ if (value === void 0 || value === null) return void 0;
583
+ if (typeof value === "bigint") return value;
584
+ if (typeof value === "number") return value;
585
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
489
586
  return void 0;
490
587
  }
491
588
  var ZERO_UID = "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -526,6 +623,53 @@ function resolveRecipientAddress(data) {
526
623
  return ZeroAddress;
527
624
  }
528
625
 
626
+ // src/reputation/eas-adapter.ts
627
+ function isRecord(value) {
628
+ return Boolean(value) && typeof value === "object";
629
+ }
630
+ function isHex32(value) {
631
+ return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
632
+ }
633
+ function getEasTransactionReceipt(tx) {
634
+ if (!isRecord(tx)) {
635
+ return void 0;
636
+ }
637
+ const candidates = [tx.receipt, tx.tx, tx.transaction];
638
+ for (const candidate of candidates) {
639
+ if (isRecord(candidate)) {
640
+ return candidate;
641
+ }
642
+ }
643
+ return void 0;
644
+ }
645
+ function getEasTransactionHash(tx) {
646
+ const receipt = getEasTransactionReceipt(tx);
647
+ if (receipt) {
648
+ const receiptHash = receipt.hash;
649
+ if (isHex32(receiptHash)) {
650
+ return receiptHash;
651
+ }
652
+ const transactionHash = receipt.transactionHash;
653
+ if (isHex32(transactionHash)) {
654
+ return transactionHash;
655
+ }
656
+ }
657
+ if (isRecord(tx) && isHex32(tx.hash)) {
658
+ return tx.hash;
659
+ }
660
+ return void 0;
661
+ }
662
+ function isEasSchemaNotFoundError(err) {
663
+ if (!isRecord(err)) {
664
+ return false;
665
+ }
666
+ const message = err.message;
667
+ if (typeof message !== "string" || message.length === 0) {
668
+ return false;
669
+ }
670
+ return /schema not found/i.test(message);
671
+ }
672
+
529
673
  // src/reputation/submit.ts
530
674
  async function submitAttestation(params) {
531
675
  if (!params || typeof params !== "object") {
@@ -556,25 +700,46 @@ async function submitAttestation(params) {
556
700
  }
557
701
  });
558
702
  const uid = await tx.wait();
559
- const txAny = tx;
560
- const txHash = txAny.tx?.hash ?? txAny.hash ?? txAny.transaction?.hash ?? ZERO_UID;
703
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
704
+ const receipt = getEasTransactionReceipt(tx);
561
705
  return {
562
706
  uid,
563
707
  txHash,
564
- receipt: txAny.tx
708
+ receipt
565
709
  };
566
710
  } catch (err) {
567
711
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
568
712
  }
569
713
  }
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
- );
714
+ async function revokeAttestation(params) {
715
+ if (!params || typeof params !== "object") {
716
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
717
+ }
718
+ if (!params.signer) {
719
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
720
+ }
721
+ const eas = new EAS(params.easContractAddress);
722
+ eas.connect(params.signer);
723
+ try {
724
+ const tx = await eas.revoke({
725
+ schema: params.schemaUid,
726
+ data: {
727
+ uid: params.uid,
728
+ value: toBigIntOrDefault(params.value, 0n)
729
+ }
730
+ });
731
+ await tx.wait();
732
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
733
+ const receipt = getEasTransactionReceipt(tx);
734
+ return {
735
+ txHash,
736
+ receipt
737
+ };
738
+ } catch (err) {
739
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
740
+ }
741
+ }
742
+ function buildDelegatedTypedData(params) {
578
743
  return {
579
744
  domain: {
580
745
  name: "EAS",
@@ -599,17 +764,52 @@ function buildDelegatedAttestationTypedData(params) {
599
764
  message: {
600
765
  attester: params.attester,
601
766
  schema: params.schemaUid,
602
- recipient,
603
- expirationTime: expiration,
767
+ recipient: params.recipient,
768
+ expirationTime: toBigIntOrDefault(params.expirationTime, 0n),
604
769
  revocable: params.revocable ?? true,
605
770
  refUID: params.refUid ?? ZERO_UID,
606
- data: encodedData,
771
+ data: params.encodedData,
607
772
  value: toBigIntOrDefault(params.value, 0n),
608
773
  nonce: toBigIntOrDefault(params.nonce, 0n),
609
774
  deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
610
775
  }
611
776
  };
612
777
  }
778
+ function buildDelegatedAttestationTypedData(params) {
779
+ const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
780
+ const encodedData = encodeAttestationData(params.schema, dataWithHash);
781
+ const recipient = resolveRecipientAddress(dataWithHash);
782
+ return buildDelegatedTypedData({
783
+ chainId: params.chainId,
784
+ easContractAddress: params.easContractAddress,
785
+ schemaUid: params.schemaUid,
786
+ encodedData,
787
+ recipient,
788
+ attester: params.attester,
789
+ nonce: params.nonce,
790
+ revocable: params.revocable,
791
+ expirationTime: params.expirationTime ?? extractExpirationTime(dataWithHash),
792
+ refUid: params.refUid,
793
+ value: params.value,
794
+ deadline: params.deadline
795
+ });
796
+ }
797
+ function buildDelegatedTypedDataFromEncoded(params) {
798
+ return buildDelegatedTypedData({
799
+ chainId: params.chainId,
800
+ easContractAddress: params.easContractAddress,
801
+ schemaUid: params.schemaUid,
802
+ encodedData: params.encodedData,
803
+ recipient: params.recipient,
804
+ attester: params.attester,
805
+ nonce: params.nonce,
806
+ revocable: params.revocable,
807
+ expirationTime: params.expirationTime,
808
+ refUid: params.refUid,
809
+ value: params.value,
810
+ deadline: params.deadline
811
+ });
812
+ }
613
813
  function splitSignature(signature) {
614
814
  try {
615
815
  const parsed = Signature.from(signature);
@@ -686,7 +886,14 @@ async function submitDelegatedAttestation(params) {
686
886
  var EAS_EVENT_ABI = [
687
887
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
688
888
  ];
689
- function parseAttestation(attestation, schema) {
889
+ function parseEventTxHash(event) {
890
+ const txHash = event.transactionHash;
891
+ if (typeof txHash !== "string") {
892
+ return void 0;
893
+ }
894
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
895
+ }
896
+ function parseAttestation(attestation, schema, txHash) {
690
897
  const rawData = attestation.data ?? "0x";
691
898
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
692
899
  const toBigIntSafe = (value) => {
@@ -706,6 +913,7 @@ function parseAttestation(attestation, schema) {
706
913
  schema: attestation.schema,
707
914
  attester: attestation.attester,
708
915
  recipient: attestation.recipient,
916
+ txHash,
709
917
  revocable: Boolean(attestation.revocable),
710
918
  revocationTime: toBigIntSafe(attestation.revocationTime),
711
919
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -731,21 +939,18 @@ async function getAttestation(params) {
731
939
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
732
940
  }
733
941
  }
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));
942
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
943
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI, provider);
944
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
740
945
  let events;
741
946
  try {
742
947
  events = await contract.queryFilter(filter, fromBlock, toBlock);
743
948
  } catch (err) {
744
949
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
745
950
  }
746
- const eas = new EAS(params.easContractAddress);
951
+ const eas = new EAS(easContractAddress);
747
952
  eas.connect(provider);
748
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
953
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
749
954
  const results = [];
750
955
  for (const event of events) {
751
956
  if (!("args" in event && Array.isArray(event.args))) {
@@ -764,11 +969,39 @@ async function getAttestationsForDid(params) {
764
969
  if (!attestation || !attestation.uid) {
765
970
  continue;
766
971
  }
767
- results.push(parseAttestation(attestation));
972
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
768
973
  }
769
974
  results.sort((a, b) => Number(b.time - a.time));
770
975
  return results;
771
976
  }
977
+ async function getAttestationsForDid(params) {
978
+ const provider = params.provider;
979
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
980
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
981
+ return queryAttestationEvents(
982
+ params.easContractAddress,
983
+ provider,
984
+ fromBlock,
985
+ toBlock,
986
+ { recipient: didToAddress(params.did), schemas: params.schemas }
987
+ );
988
+ }
989
+ async function getAttestationsByAttester(params) {
990
+ const provider = params.provider;
991
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
992
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
993
+ if (params.limit !== void 0 && params.limit <= 0) {
994
+ return [];
995
+ }
996
+ const results = await queryAttestationEvents(
997
+ params.easContractAddress,
998
+ provider,
999
+ fromBlock,
1000
+ toBlock,
1001
+ { attester: params.attester, schemas: params.schemas }
1002
+ );
1003
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
1004
+ }
772
1005
  async function listAttestations(params) {
773
1006
  const limit = params.limit ?? 20;
774
1007
  if (limit <= 0) {
@@ -779,34 +1012,15 @@ async function listAttestations(params) {
779
1012
  }
780
1013
  async function getLatestAttestations(params) {
781
1014
  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));
1015
+ const toBlock = await provider.getBlockNumber();
1016
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
1017
+ const results = await queryAttestationEvents(
1018
+ params.easContractAddress,
1019
+ provider,
1020
+ fromBlock,
1021
+ toBlock,
1022
+ { schemas: params.schemas }
1023
+ );
810
1024
  return results.slice(0, params.limit ?? 20);
811
1025
  }
812
1026
  function deduplicateReviews(attestations) {
@@ -1068,6 +1282,13 @@ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1068
1282
  reason: `No matching address found in DID document (expected ${expectedAddress})`
1069
1283
  };
1070
1284
  }
1285
+ async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1286
+ if (!domain || typeof domain !== "string") {
1287
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1288
+ }
1289
+ const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
1290
+ return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
1291
+ }
1071
1292
  function buildEip712Domain(name, version, chainId, verifyingContract) {
1072
1293
  return { name, version, chainId, verifyingContract };
1073
1294
  }
@@ -1100,6 +1321,8 @@ function verifyEip712Signature(typedData, signature) {
1100
1321
  throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
1101
1322
  }
1102
1323
  }
1324
+
1325
+ // src/reputation/proof/dns-txt-record.ts
1103
1326
  function parseDnsTxtRecord(record) {
1104
1327
  if (!record || typeof record !== "string") {
1105
1328
  throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
@@ -1127,27 +1350,6 @@ function buildDnsTxtRecord(controllerDid) {
1127
1350
  const normalized = normalizeDid(controllerDid);
1128
1351
  return `v=1;controller=${normalized}`;
1129
1352
  }
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 });
1133
- }
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 });
1141
- }
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 };
1147
- }
1148
- }
1149
- return { valid: false, reason: "No TXT record matched expected controller DID" };
1150
- }
1151
1353
 
1152
1354
  // src/reputation/verify.ts
1153
1355
  function parseChainId(input) {
@@ -1428,6 +1630,9 @@ async function getSchemaDetails(schemaRegistry, schemaUid) {
1428
1630
  revocable: Boolean(details.revocable)
1429
1631
  };
1430
1632
  } catch (err) {
1633
+ if (isEasSchemaNotFoundError(err)) {
1634
+ throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
1635
+ }
1431
1636
  if (err instanceof OmaTrustError) {
1432
1637
  throw err;
1433
1638
  }
@@ -1611,6 +1816,366 @@ function createEvidencePointerProof(url) {
1611
1816
  issuedAt: Math.floor(Date.now() / 1e3)
1612
1817
  };
1613
1818
  }
1819
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
1820
+ if (!domain || typeof domain !== "string") {
1821
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1822
+ }
1823
+ const expected = normalizeDid(expectedControllerDid);
1824
+ const prefix = options.recordPrefix ?? "_controllers";
1825
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1826
+ let records;
1827
+ try {
1828
+ records = await (options.resolveTxt ?? resolveTxt)(host);
1829
+ } catch (err) {
1830
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1831
+ }
1832
+ for (const recordParts of records) {
1833
+ const record = recordParts.join("");
1834
+ const parsed = parseDnsTxtRecord(record);
1835
+ if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1836
+ return { valid: true, record };
1837
+ }
1838
+ }
1839
+ return { valid: false, reason: "No TXT record matched expected controller DID" };
1840
+ }
1841
+
1842
+ // src/reputation/proof/dns-txt-shared.ts
1843
+ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
1844
+ if (!domain || typeof domain !== "string") {
1845
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1846
+ }
1847
+ if (!options.resolveTxt) {
1848
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
1849
+ }
1850
+ const expected = normalizeDid(expectedControllerDid);
1851
+ const prefix = options.recordPrefix ?? "_controllers";
1852
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1853
+ let records;
1854
+ try {
1855
+ records = await options.resolveTxt(host);
1856
+ } catch (err) {
1857
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1858
+ }
1859
+ for (const recordParts of records) {
1860
+ const record = recordParts.join("");
1861
+ const parsed = parseDnsTxtRecord(record);
1862
+ if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1863
+ return { valid: true, record };
1864
+ }
1865
+ }
1866
+ return { valid: false, reason: "No TXT record matched expected controller DID" };
1867
+ }
1868
+
1869
+ // src/reputation/proof/subject-ownership.ts
1870
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1871
+ var OWNERSHIP_PATTERNS = [
1872
+ { method: "owner", signature: "function owner() view returns (address)" },
1873
+ { method: "admin", signature: "function admin() view returns (address)" },
1874
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
1875
+ ];
1876
+ function assertConnectedWalletDid(input) {
1877
+ const normalized = normalizeDid(input);
1878
+ if (extractDidMethod(normalized) !== "pkh") {
1879
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
1880
+ connectedWalletDid: input
1881
+ });
1882
+ }
1883
+ return normalized;
1884
+ }
1885
+ function normalizeSubjectDid(input) {
1886
+ return normalizeDid(input);
1887
+ }
1888
+ async function readAddressFromContract(provider, contractAddress, signature, method) {
1889
+ try {
1890
+ const iface = new Interface([signature]);
1891
+ const data = iface.encodeFunctionData(method, []);
1892
+ const result = await provider.call({ to: contractAddress, data });
1893
+ const [value] = iface.decodeFunctionResult(method, result);
1894
+ if (typeof value === "string" && isAddress(value) && value !== ZeroAddress) {
1895
+ return getAddress(value);
1896
+ }
1897
+ } catch {
1898
+ }
1899
+ return null;
1900
+ }
1901
+ async function discoverControllingWallet(provider, contractAddress, chainId) {
1902
+ for (const pattern of OWNERSHIP_PATTERNS) {
1903
+ const address = await readAddressFromContract(
1904
+ provider,
1905
+ contractAddress,
1906
+ pattern.signature,
1907
+ pattern.method
1908
+ );
1909
+ if (address) {
1910
+ return buildEvmDidPkh(chainId, address);
1911
+ }
1912
+ }
1913
+ try {
1914
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
1915
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1916
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
1917
+ if (adminAddress !== ZeroAddress) {
1918
+ return buildEvmDidPkh(chainId, adminAddress);
1919
+ }
1920
+ }
1921
+ } catch {
1922
+ }
1923
+ return null;
1924
+ }
1925
+ async function verifyDidWebOwnership(params) {
1926
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1927
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
1928
+ const domain = getDomainFromDidWeb(subjectDid);
1929
+ if (!domain) {
1930
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be a did:web DID", {
1931
+ subjectDid: params.subjectDid
1932
+ });
1933
+ }
1934
+ let dnsReason;
1935
+ try {
1936
+ const dnsResult = await verifyDnsTxtControllerDid2(domain, connectedWalletDid, {
1937
+ resolveTxt: params.resolveTxt,
1938
+ recordPrefix: params.recordPrefix
1939
+ });
1940
+ if (dnsResult.valid) {
1941
+ return {
1942
+ valid: true,
1943
+ method: "dns",
1944
+ details: `Verified via DNS TXT record at ${params.recordPrefix ?? "_controllers"}.${domain}`,
1945
+ subjectDid,
1946
+ connectedWalletDid
1947
+ };
1948
+ }
1949
+ dnsReason = dnsResult.reason;
1950
+ } catch (error) {
1951
+ dnsReason = error instanceof Error ? error.message : "DNS TXT verification failed";
1952
+ }
1953
+ let didDocReason;
1954
+ try {
1955
+ const didDocumentResult = await verifyDidJsonControllerDid(domain, connectedWalletDid, {
1956
+ fetchDidDocument: params.fetchDidDocument
1957
+ });
1958
+ if (didDocumentResult.valid) {
1959
+ return {
1960
+ valid: true,
1961
+ method: "did-document",
1962
+ details: `Verified via DID document at https://${domain}/.well-known/did.json`,
1963
+ subjectDid,
1964
+ connectedWalletDid
1965
+ };
1966
+ }
1967
+ didDocReason = didDocumentResult.reason;
1968
+ } catch (error) {
1969
+ didDocReason = error instanceof Error ? error.message : "DID document verification failed";
1970
+ }
1971
+ return {
1972
+ valid: false,
1973
+ reason: "DID ownership verification failed",
1974
+ details: `DNS check: ${dnsReason ?? "failed"}. DID document check: ${didDocReason ?? "failed"}.`,
1975
+ subjectDid,
1976
+ connectedWalletDid
1977
+ };
1978
+ }
1979
+ async function verifyDidPkhOwnership(params) {
1980
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1981
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
1982
+ if (!isEvmDidPkh(subjectDid)) {
1983
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be an EVM did:pkh DID", {
1984
+ subjectDid: params.subjectDid
1985
+ });
1986
+ }
1987
+ const subjectAddress = getAddressFromDidPkh(subjectDid);
1988
+ const connectedWalletAddress = getAddressFromDidPkh(connectedWalletDid);
1989
+ const chainIdRaw = getChainIdFromDidPkh(subjectDid);
1990
+ if (!subjectAddress || !connectedWalletAddress || !chainIdRaw) {
1991
+ throw new OmaTrustError("INVALID_INPUT", "Could not parse did:pkh ownership inputs", {
1992
+ subjectDid: params.subjectDid,
1993
+ connectedWalletDid: params.connectedWalletDid
1994
+ });
1995
+ }
1996
+ const chainId = Number(chainIdRaw);
1997
+ if (!Number.isFinite(chainId)) {
1998
+ throw new OmaTrustError("INVALID_INPUT", "Invalid chain id in subjectDid", {
1999
+ subjectDid: params.subjectDid
2000
+ });
2001
+ }
2002
+ const code = await params.provider.getCode(subjectAddress);
2003
+ const isContract = code !== "0x" && code !== "0x0";
2004
+ if (!isContract) {
2005
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress)) {
2006
+ return {
2007
+ valid: true,
2008
+ method: "wallet",
2009
+ details: "Verified direct wallet ownership from matching did:pkh subject and connected wallet",
2010
+ subjectDid,
2011
+ connectedWalletDid
2012
+ };
2013
+ }
2014
+ return {
2015
+ valid: false,
2016
+ reason: "EOA did:pkh subject does not match connected wallet",
2017
+ details: "For direct wallet did:pkh subjects, connectedWalletDid must match the subject DID.",
2018
+ subjectDid,
2019
+ connectedWalletDid
2020
+ };
2021
+ }
2022
+ const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
2023
+ if (params.txHash) {
2024
+ if (!controllingWalletDid) {
2025
+ return {
2026
+ valid: false,
2027
+ reason: "Could not discover controlling wallet",
2028
+ details: "Contract does not expose owner/admin/getOwner or a readable EIP-1967 admin slot for transfer verification.",
2029
+ subjectDid,
2030
+ connectedWalletDid
2031
+ };
2032
+ }
2033
+ const tx = await params.provider.getTransaction(params.txHash);
2034
+ if (!tx) {
2035
+ return {
2036
+ valid: false,
2037
+ reason: "Transaction not found",
2038
+ details: `Transaction ${params.txHash} was not found on chain ${chainId}.`,
2039
+ subjectDid,
2040
+ connectedWalletDid,
2041
+ controllingWalletDid
2042
+ };
2043
+ }
2044
+ const receipt = await params.provider.getTransactionReceipt(params.txHash);
2045
+ if (!receipt) {
2046
+ return {
2047
+ valid: false,
2048
+ reason: "Transaction not confirmed",
2049
+ details: "Transaction exists but is not yet confirmed.",
2050
+ subjectDid,
2051
+ connectedWalletDid,
2052
+ controllingWalletDid
2053
+ };
2054
+ }
2055
+ const controllingWalletAddress = getAddressFromDidPkh(controllingWalletDid);
2056
+ if (!tx.from || !controllingWalletAddress || getAddress(tx.from) !== getAddress(controllingWalletAddress)) {
2057
+ return {
2058
+ valid: false,
2059
+ reason: "Wrong sender",
2060
+ details: `Transfer must originate from controlling wallet ${controllingWalletDid}.`,
2061
+ subjectDid,
2062
+ connectedWalletDid,
2063
+ controllingWalletDid
2064
+ };
2065
+ }
2066
+ if (!tx.to || getAddress(tx.to) !== getAddress(connectedWalletAddress)) {
2067
+ return {
2068
+ valid: false,
2069
+ reason: "Wrong recipient",
2070
+ details: `Transfer must be sent to connected wallet ${connectedWalletDid}.`,
2071
+ subjectDid,
2072
+ connectedWalletDid,
2073
+ controllingWalletDid
2074
+ };
2075
+ }
2076
+ const expectedAmount = calculateTransferAmount(
2077
+ subjectDid,
2078
+ connectedWalletDid,
2079
+ chainId,
2080
+ "shared-control"
2081
+ );
2082
+ const actualValue = BigInt(tx.value ?? 0n);
2083
+ if (actualValue !== expectedAmount) {
2084
+ return {
2085
+ valid: false,
2086
+ reason: "Wrong amount",
2087
+ details: `Transfer amount ${actualValue.toString()} does not match expected proof amount ${expectedAmount.toString()}.`,
2088
+ subjectDid,
2089
+ connectedWalletDid,
2090
+ controllingWalletDid
2091
+ };
2092
+ }
2093
+ await params.provider.getBlockNumber();
2094
+ await params.provider.getBlock(receipt.blockNumber);
2095
+ return {
2096
+ valid: true,
2097
+ method: "transfer",
2098
+ details: `Verified via transfer proof ${params.txHash}.`,
2099
+ subjectDid,
2100
+ connectedWalletDid,
2101
+ controllingWalletDid
2102
+ };
2103
+ }
2104
+ for (const pattern of OWNERSHIP_PATTERNS) {
2105
+ const ownerAddress = await readAddressFromContract(
2106
+ params.provider,
2107
+ subjectAddress,
2108
+ pattern.signature,
2109
+ pattern.method
2110
+ );
2111
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(connectedWalletAddress)) {
2112
+ return {
2113
+ valid: true,
2114
+ method: "contract",
2115
+ details: `Verified via ${pattern.method}() ownership check.`,
2116
+ subjectDid,
2117
+ connectedWalletDid,
2118
+ controllingWalletDid: controllingWalletDid ?? void 0
2119
+ };
2120
+ }
2121
+ }
2122
+ try {
2123
+ const adminValue = await params.provider.getStorage(subjectAddress, EIP1967_ADMIN_SLOT);
2124
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
2125
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
2126
+ if (adminAddress === getAddress(connectedWalletAddress)) {
2127
+ return {
2128
+ valid: true,
2129
+ method: "contract",
2130
+ details: "Verified via EIP-1967 admin slot.",
2131
+ subjectDid,
2132
+ connectedWalletDid,
2133
+ controllingWalletDid: controllingWalletDid ?? buildEvmDidPkh(chainId, adminAddress)
2134
+ };
2135
+ }
2136
+ }
2137
+ } catch {
2138
+ }
2139
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress) && controllingWalletDid) {
2140
+ return {
2141
+ valid: true,
2142
+ method: "minting-wallet",
2143
+ details: `Verified because connected wallet matches the contract DID address. Controlling wallet is ${controllingWalletDid}.`,
2144
+ subjectDid,
2145
+ connectedWalletDid,
2146
+ controllingWalletDid
2147
+ };
2148
+ }
2149
+ return {
2150
+ valid: false,
2151
+ reason: "Contract ownership verification failed",
2152
+ 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}.`,
2153
+ subjectDid,
2154
+ connectedWalletDid,
2155
+ controllingWalletDid: controllingWalletDid ?? void 0
2156
+ };
2157
+ }
2158
+ async function verifySubjectOwnership(params) {
2159
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
2160
+ const method = extractDidMethod(subjectDid);
2161
+ if (method === "web") {
2162
+ return verifyDidWebOwnership(params);
2163
+ }
2164
+ if (method === "pkh") {
2165
+ const didPkhParams = params;
2166
+ if (!didPkhParams.provider) {
2167
+ throw new OmaTrustError(
2168
+ "INVALID_INPUT",
2169
+ "provider is required for did:pkh ownership verification",
2170
+ { subjectDid }
2171
+ );
2172
+ }
2173
+ return verifyDidPkhOwnership(didPkhParams);
2174
+ }
2175
+ throw new OmaTrustError("INVALID_INPUT", "Unsupported DID type for ownership verification", {
2176
+ subjectDid
2177
+ });
2178
+ }
1614
2179
 
1615
2180
  // src/app-registry/index.ts
1616
2181
  var app_registry_exports = {};