@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
@@ -58,6 +58,13 @@ function encodeAttestationData(schema, data) {
58
58
  if (!data || typeof data !== "object") {
59
59
  throw new OmaTrustError("INVALID_INPUT", "data must be an object");
60
60
  }
61
+ const validationErrors = validateAttestationData(schema, data);
62
+ if (validationErrors.length > 0) {
63
+ const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
64
+ throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
65
+ errors: validationErrors
66
+ });
67
+ }
61
68
  const fields = normalizeSchema(schema);
62
69
  const schemaString = schemaToString(fields);
63
70
  const encoder = new easSdk.SchemaEncoder(schemaString);
@@ -70,6 +77,99 @@ function encodeAttestationData(schema, data) {
70
77
  );
71
78
  return encoded;
72
79
  }
80
+ function getActualType(value) {
81
+ if (value === null) {
82
+ return "null";
83
+ }
84
+ if (value === void 0) {
85
+ return "undefined";
86
+ }
87
+ if (Array.isArray(value)) {
88
+ return "array";
89
+ }
90
+ if (typeof value === "number") {
91
+ return Number.isFinite(value) ? "number" : "non-finite-number";
92
+ }
93
+ return typeof value;
94
+ }
95
+ function isNumericValue(value, allowNegative) {
96
+ if (typeof value === "bigint") {
97
+ return allowNegative || value >= 0n;
98
+ }
99
+ if (typeof value === "number") {
100
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
101
+ return false;
102
+ }
103
+ return allowNegative || value >= 0;
104
+ }
105
+ if (typeof value === "string") {
106
+ if (value.length === 0) {
107
+ return false;
108
+ }
109
+ const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
110
+ return pattern.test(value);
111
+ }
112
+ return false;
113
+ }
114
+ function isHex(value) {
115
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
116
+ }
117
+ function validateFieldValue(type, value) {
118
+ const normalizedType = type.trim().toLowerCase();
119
+ const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
120
+ if (/^uint\d*$/.test(normalizedType)) {
121
+ return isNumericValue(value, false);
122
+ }
123
+ if (/^int\d*$/.test(normalizedType)) {
124
+ return isNumericValue(value, true);
125
+ }
126
+ if (normalizedType === "string") {
127
+ return typeof value === "string";
128
+ }
129
+ if (normalizedType === "string[]") {
130
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
131
+ }
132
+ if (normalizedType === "bool") {
133
+ return typeof value === "boolean";
134
+ }
135
+ if (normalizedType === "address") {
136
+ return typeof value === "string" && ethers.isAddress(value);
137
+ }
138
+ if (normalizedType === "bytes") {
139
+ return isHex(value) && (value.length - 2) % 2 === 0;
140
+ }
141
+ if (bytesNMatch) {
142
+ const expectedBytes = Number(bytesNMatch[1]);
143
+ return isHex(value) && value.length === 2 + expectedBytes * 2;
144
+ }
145
+ return true;
146
+ }
147
+ function validateAttestationData(schema, data) {
148
+ if (!data || typeof data !== "object") {
149
+ return [{
150
+ schemaFieldName: "data",
151
+ expectedType: "object",
152
+ providedType: getActualType(data),
153
+ providedValue: data
154
+ }];
155
+ }
156
+ const fields = normalizeSchema(schema);
157
+ const errors = [];
158
+ for (const field of fields) {
159
+ const value = data[field.name];
160
+ const isValid = validateFieldValue(field.type, value);
161
+ if (isValid) {
162
+ continue;
163
+ }
164
+ errors.push({
165
+ schemaFieldName: field.name,
166
+ expectedType: field.type,
167
+ providedType: getActualType(value),
168
+ providedValue: value
169
+ });
170
+ }
171
+ return errors;
172
+ }
73
173
  function decodeAttestationData(schema, encodedData) {
74
174
  if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
75
175
  throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
@@ -90,22 +190,11 @@ function decodeAttestationData(schema, encodedData) {
90
190
  return result;
91
191
  }
92
192
  function extractExpirationTime(data) {
93
- const keys = ["expirationTime", "expiration", "validUntil", "expiresAt", "expires"];
94
- for (const key of keys) {
95
- const value = data[key];
96
- if (value === void 0 || value === null) {
97
- continue;
98
- }
99
- if (typeof value === "bigint") {
100
- return value;
101
- }
102
- if (typeof value === "number") {
103
- return value;
104
- }
105
- if (typeof value === "string" && /^\d+$/.test(value)) {
106
- return BigInt(value);
107
- }
108
- }
193
+ const value = data["expiresAt"];
194
+ if (value === void 0 || value === null) return void 0;
195
+ if (typeof value === "bigint") return value;
196
+ if (typeof value === "number") return value;
197
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
109
198
  return void 0;
110
199
  }
111
200
 
@@ -145,7 +234,7 @@ function extractDidMethod(did) {
145
234
  }
146
235
  function normalizeDomain(domain) {
147
236
  assertString(domain, "domain", "INVALID_DID");
148
- return domain.trim().toLowerCase().replace(/\.$/, "");
237
+ return domain.trim().toLowerCase().replace(/\.$/, "").replace(/^www\./, "");
149
238
  }
150
239
  function normalizeDidWeb(input) {
151
240
  assertString(input, "input", "INVALID_DID");
@@ -264,6 +353,26 @@ function parseDidPkh(did) {
264
353
  }
265
354
  return { namespace, chainId, address };
266
355
  }
356
+ function getChainIdFromDidPkh(did) {
357
+ return parseDidPkh(did)?.chainId ?? null;
358
+ }
359
+ function getAddressFromDidPkh(did) {
360
+ return parseDidPkh(did)?.address ?? null;
361
+ }
362
+ function getNamespaceFromDidPkh(did) {
363
+ return parseDidPkh(did)?.namespace ?? null;
364
+ }
365
+ function isEvmDidPkh(did) {
366
+ return getNamespaceFromDidPkh(did) === "eip155";
367
+ }
368
+ function getDomainFromDidWeb(did) {
369
+ if (!did.startsWith("did:web:")) {
370
+ return null;
371
+ }
372
+ const identifier = did.slice("did:web:".length);
373
+ const [domain] = identifier.split("/");
374
+ return domain || null;
375
+ }
267
376
  function extractAddressFromDid(identifier) {
268
377
  assertString(identifier, "identifier", "INVALID_DID");
269
378
  if (identifier.startsWith("did:pkh:")) {
@@ -330,6 +439,53 @@ function resolveRecipientAddress(data) {
330
439
  return ethers.ZeroAddress;
331
440
  }
332
441
 
442
+ // src/reputation/eas-adapter.ts
443
+ function isRecord(value) {
444
+ return Boolean(value) && typeof value === "object";
445
+ }
446
+ function isHex32(value) {
447
+ return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
448
+ }
449
+ function getEasTransactionReceipt(tx) {
450
+ if (!isRecord(tx)) {
451
+ return void 0;
452
+ }
453
+ const candidates = [tx.receipt, tx.tx, tx.transaction];
454
+ for (const candidate of candidates) {
455
+ if (isRecord(candidate)) {
456
+ return candidate;
457
+ }
458
+ }
459
+ return void 0;
460
+ }
461
+ function getEasTransactionHash(tx) {
462
+ const receipt = getEasTransactionReceipt(tx);
463
+ if (receipt) {
464
+ const receiptHash = receipt.hash;
465
+ if (isHex32(receiptHash)) {
466
+ return receiptHash;
467
+ }
468
+ const transactionHash = receipt.transactionHash;
469
+ if (isHex32(transactionHash)) {
470
+ return transactionHash;
471
+ }
472
+ }
473
+ if (isRecord(tx) && isHex32(tx.hash)) {
474
+ return tx.hash;
475
+ }
476
+ return void 0;
477
+ }
478
+ function isEasSchemaNotFoundError(err) {
479
+ if (!isRecord(err)) {
480
+ return false;
481
+ }
482
+ const message = err.message;
483
+ if (typeof message !== "string" || message.length === 0) {
484
+ return false;
485
+ }
486
+ return /schema not found/i.test(message);
487
+ }
488
+
333
489
  // src/reputation/submit.ts
334
490
  async function submitAttestation(params) {
335
491
  if (!params || typeof params !== "object") {
@@ -360,25 +516,46 @@ async function submitAttestation(params) {
360
516
  }
361
517
  });
362
518
  const uid = await tx.wait();
363
- const txAny = tx;
364
- const txHash = txAny.tx?.hash ?? txAny.hash ?? txAny.transaction?.hash ?? ZERO_UID;
519
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
520
+ const receipt = getEasTransactionReceipt(tx);
365
521
  return {
366
522
  uid,
367
523
  txHash,
368
- receipt: txAny.tx
524
+ receipt
369
525
  };
370
526
  } catch (err) {
371
527
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
372
528
  }
373
529
  }
374
- function buildDelegatedAttestationTypedData(params) {
375
- const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
376
- const encodedData = encodeAttestationData(params.schema, dataWithHash);
377
- const recipient = resolveRecipientAddress(dataWithHash);
378
- const expiration = toBigIntOrDefault(
379
- params.expirationTime ?? extractExpirationTime(dataWithHash),
380
- 0n
381
- );
530
+ async function revokeAttestation(params) {
531
+ if (!params || typeof params !== "object") {
532
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
533
+ }
534
+ if (!params.signer) {
535
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
536
+ }
537
+ const eas = new easSdk.EAS(params.easContractAddress);
538
+ eas.connect(params.signer);
539
+ try {
540
+ const tx = await eas.revoke({
541
+ schema: params.schemaUid,
542
+ data: {
543
+ uid: params.uid,
544
+ value: toBigIntOrDefault(params.value, 0n)
545
+ }
546
+ });
547
+ await tx.wait();
548
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
549
+ const receipt = getEasTransactionReceipt(tx);
550
+ return {
551
+ txHash,
552
+ receipt
553
+ };
554
+ } catch (err) {
555
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
556
+ }
557
+ }
558
+ function buildDelegatedTypedData(params) {
382
559
  return {
383
560
  domain: {
384
561
  name: "EAS",
@@ -403,17 +580,52 @@ function buildDelegatedAttestationTypedData(params) {
403
580
  message: {
404
581
  attester: params.attester,
405
582
  schema: params.schemaUid,
406
- recipient,
407
- expirationTime: expiration,
583
+ recipient: params.recipient,
584
+ expirationTime: toBigIntOrDefault(params.expirationTime, 0n),
408
585
  revocable: params.revocable ?? true,
409
586
  refUID: params.refUid ?? ZERO_UID,
410
- data: encodedData,
587
+ data: params.encodedData,
411
588
  value: toBigIntOrDefault(params.value, 0n),
412
589
  nonce: toBigIntOrDefault(params.nonce, 0n),
413
590
  deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
414
591
  }
415
592
  };
416
593
  }
594
+ function buildDelegatedAttestationTypedData(params) {
595
+ const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
596
+ const encodedData = encodeAttestationData(params.schema, dataWithHash);
597
+ const recipient = resolveRecipientAddress(dataWithHash);
598
+ return buildDelegatedTypedData({
599
+ chainId: params.chainId,
600
+ easContractAddress: params.easContractAddress,
601
+ schemaUid: params.schemaUid,
602
+ encodedData,
603
+ recipient,
604
+ attester: params.attester,
605
+ nonce: params.nonce,
606
+ revocable: params.revocable,
607
+ expirationTime: params.expirationTime ?? extractExpirationTime(dataWithHash),
608
+ refUid: params.refUid,
609
+ value: params.value,
610
+ deadline: params.deadline
611
+ });
612
+ }
613
+ function buildDelegatedTypedDataFromEncoded(params) {
614
+ return buildDelegatedTypedData({
615
+ chainId: params.chainId,
616
+ easContractAddress: params.easContractAddress,
617
+ schemaUid: params.schemaUid,
618
+ encodedData: params.encodedData,
619
+ recipient: params.recipient,
620
+ attester: params.attester,
621
+ nonce: params.nonce,
622
+ revocable: params.revocable,
623
+ expirationTime: params.expirationTime,
624
+ refUid: params.refUid,
625
+ value: params.value,
626
+ deadline: params.deadline
627
+ });
628
+ }
417
629
  function splitSignature(signature) {
418
630
  try {
419
631
  const parsed = ethers.Signature.from(signature);
@@ -490,7 +702,14 @@ async function submitDelegatedAttestation(params) {
490
702
  var EAS_EVENT_ABI = [
491
703
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
492
704
  ];
493
- function parseAttestation(attestation, schema) {
705
+ function parseEventTxHash(event) {
706
+ const txHash = event.transactionHash;
707
+ if (typeof txHash !== "string") {
708
+ return void 0;
709
+ }
710
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
711
+ }
712
+ function parseAttestation(attestation, schema, txHash) {
494
713
  const rawData = attestation.data ?? "0x";
495
714
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
496
715
  const toBigIntSafe = (value) => {
@@ -510,6 +729,7 @@ function parseAttestation(attestation, schema) {
510
729
  schema: attestation.schema,
511
730
  attester: attestation.attester,
512
731
  recipient: attestation.recipient,
732
+ txHash,
513
733
  revocable: Boolean(attestation.revocable),
514
734
  revocationTime: toBigIntSafe(attestation.revocationTime),
515
735
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -535,21 +755,18 @@ async function getAttestation(params) {
535
755
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
536
756
  }
537
757
  }
538
- async function getAttestationsForDid(params) {
539
- const provider = params.provider;
540
- const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
541
- const toBlock = params.toBlock ?? await provider.getBlockNumber();
542
- const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
543
- const filter = contract.filters.Attested(didToAddress(params.did));
758
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
759
+ const contract = new ethers.Contract(easContractAddress, EAS_EVENT_ABI, provider);
760
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
544
761
  let events;
545
762
  try {
546
763
  events = await contract.queryFilter(filter, fromBlock, toBlock);
547
764
  } catch (err) {
548
765
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
549
766
  }
550
- const eas = new easSdk.EAS(params.easContractAddress);
767
+ const eas = new easSdk.EAS(easContractAddress);
551
768
  eas.connect(provider);
552
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
769
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
553
770
  const results = [];
554
771
  for (const event of events) {
555
772
  if (!("args" in event && Array.isArray(event.args))) {
@@ -568,11 +785,39 @@ async function getAttestationsForDid(params) {
568
785
  if (!attestation || !attestation.uid) {
569
786
  continue;
570
787
  }
571
- results.push(parseAttestation(attestation));
788
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
572
789
  }
573
790
  results.sort((a, b) => Number(b.time - a.time));
574
791
  return results;
575
792
  }
793
+ async function getAttestationsForDid(params) {
794
+ const provider = params.provider;
795
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
796
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
797
+ return queryAttestationEvents(
798
+ params.easContractAddress,
799
+ provider,
800
+ fromBlock,
801
+ toBlock,
802
+ { recipient: didToAddress(params.did), schemas: params.schemas }
803
+ );
804
+ }
805
+ async function getAttestationsByAttester(params) {
806
+ const provider = params.provider;
807
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
808
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
809
+ if (params.limit !== void 0 && params.limit <= 0) {
810
+ return [];
811
+ }
812
+ const results = await queryAttestationEvents(
813
+ params.easContractAddress,
814
+ provider,
815
+ fromBlock,
816
+ toBlock,
817
+ { attester: params.attester, schemas: params.schemas }
818
+ );
819
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
820
+ }
576
821
  async function listAttestations(params) {
577
822
  const limit = params.limit ?? 20;
578
823
  if (limit <= 0) {
@@ -583,34 +828,15 @@ async function listAttestations(params) {
583
828
  }
584
829
  async function getLatestAttestations(params) {
585
830
  const provider = params.provider;
586
- const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
587
- const currentBlock = await provider.getBlockNumber();
588
- const fromBlock = params.fromBlock ?? Math.max(0, currentBlock - 5e4);
589
- const events = await contract.queryFilter(contract.filters.Attested(), fromBlock, currentBlock);
590
- const eas = new easSdk.EAS(params.easContractAddress);
591
- eas.connect(provider);
592
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
593
- const results = [];
594
- for (const event of events) {
595
- if (!("args" in event && Array.isArray(event.args))) {
596
- continue;
597
- }
598
- const args = event.args;
599
- const uid = args?.[2];
600
- const schemaUid = args?.[3];
601
- if (!uid || !schemaUid) {
602
- continue;
603
- }
604
- if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
605
- continue;
606
- }
607
- const attestation = await eas.getAttestation(uid);
608
- if (!attestation || !attestation.uid) {
609
- continue;
610
- }
611
- results.push(parseAttestation(attestation));
612
- }
613
- results.sort((a, b) => Number(b.time - a.time));
831
+ const toBlock = await provider.getBlockNumber();
832
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
833
+ const results = await queryAttestationEvents(
834
+ params.easContractAddress,
835
+ provider,
836
+ fromBlock,
837
+ toBlock,
838
+ { schemas: params.schemas }
839
+ );
614
840
  return results.slice(0, params.limit ?? 20);
615
841
  }
616
842
  function deduplicateReviews(attestations) {
@@ -872,6 +1098,13 @@ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
872
1098
  reason: `No matching address found in DID document (expected ${expectedAddress})`
873
1099
  };
874
1100
  }
1101
+ async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1102
+ if (!domain || typeof domain !== "string") {
1103
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1104
+ }
1105
+ const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
1106
+ return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
1107
+ }
875
1108
  function buildEip712Domain(name, version, chainId, verifyingContract) {
876
1109
  return { name, version, chainId, verifyingContract };
877
1110
  }
@@ -904,6 +1137,8 @@ function verifyEip712Signature(typedData, signature) {
904
1137
  throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
905
1138
  }
906
1139
  }
1140
+
1141
+ // src/reputation/proof/dns-txt-record.ts
907
1142
  function parseDnsTxtRecord(record) {
908
1143
  if (!record || typeof record !== "string") {
909
1144
  throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
@@ -931,27 +1166,6 @@ function buildDnsTxtRecord(controllerDid) {
931
1166
  const normalized = normalizeDid(controllerDid);
932
1167
  return `v=1;controller=${normalized}`;
933
1168
  }
934
- async function verifyDnsTxtControllerDid(domain, expectedControllerDid) {
935
- if (!domain || typeof domain !== "string") {
936
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
937
- }
938
- const expected = normalizeDid(expectedControllerDid);
939
- const host = `_omatrust.${domain.toLowerCase().replace(/\.$/, "")}`;
940
- let records;
941
- try {
942
- records = await promises.resolveTxt(host);
943
- } catch (err) {
944
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
945
- }
946
- for (const recordParts of records) {
947
- const record = recordParts.join("");
948
- const parsed = parseDnsTxtRecord(record);
949
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
950
- return { valid: true, record };
951
- }
952
- }
953
- return { valid: false, reason: "No TXT record matched expected controller DID" };
954
- }
955
1169
 
956
1170
  // src/reputation/verify.ts
957
1171
  function parseChainId(input) {
@@ -1232,6 +1446,9 @@ async function getSchemaDetails(schemaRegistry, schemaUid) {
1232
1446
  revocable: Boolean(details.revocable)
1233
1447
  };
1234
1448
  } catch (err) {
1449
+ if (isEasSchemaNotFoundError(err)) {
1450
+ throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
1451
+ }
1235
1452
  if (err instanceof OmaTrustError) {
1236
1453
  throw err;
1237
1454
  }
@@ -1415,8 +1632,369 @@ function createEvidencePointerProof(url) {
1415
1632
  issuedAt: Math.floor(Date.now() / 1e3)
1416
1633
  };
1417
1634
  }
1635
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
1636
+ if (!domain || typeof domain !== "string") {
1637
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1638
+ }
1639
+ const expected = normalizeDid(expectedControllerDid);
1640
+ const prefix = options.recordPrefix ?? "_controllers";
1641
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1642
+ let records;
1643
+ try {
1644
+ records = await (options.resolveTxt ?? promises.resolveTxt)(host);
1645
+ } catch (err) {
1646
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1647
+ }
1648
+ for (const recordParts of records) {
1649
+ const record = recordParts.join("");
1650
+ const parsed = parseDnsTxtRecord(record);
1651
+ if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1652
+ return { valid: true, record };
1653
+ }
1654
+ }
1655
+ return { valid: false, reason: "No TXT record matched expected controller DID" };
1656
+ }
1657
+
1658
+ // src/reputation/proof/dns-txt-shared.ts
1659
+ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
1660
+ if (!domain || typeof domain !== "string") {
1661
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1662
+ }
1663
+ if (!options.resolveTxt) {
1664
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
1665
+ }
1666
+ const expected = normalizeDid(expectedControllerDid);
1667
+ const prefix = options.recordPrefix ?? "_controllers";
1668
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1669
+ let records;
1670
+ try {
1671
+ records = await options.resolveTxt(host);
1672
+ } catch (err) {
1673
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1674
+ }
1675
+ for (const recordParts of records) {
1676
+ const record = recordParts.join("");
1677
+ const parsed = parseDnsTxtRecord(record);
1678
+ if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1679
+ return { valid: true, record };
1680
+ }
1681
+ }
1682
+ return { valid: false, reason: "No TXT record matched expected controller DID" };
1683
+ }
1684
+
1685
+ // src/reputation/proof/subject-ownership.ts
1686
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1687
+ var OWNERSHIP_PATTERNS = [
1688
+ { method: "owner", signature: "function owner() view returns (address)" },
1689
+ { method: "admin", signature: "function admin() view returns (address)" },
1690
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
1691
+ ];
1692
+ function assertConnectedWalletDid(input) {
1693
+ const normalized = normalizeDid(input);
1694
+ if (extractDidMethod(normalized) !== "pkh") {
1695
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
1696
+ connectedWalletDid: input
1697
+ });
1698
+ }
1699
+ return normalized;
1700
+ }
1701
+ function normalizeSubjectDid(input) {
1702
+ return normalizeDid(input);
1703
+ }
1704
+ async function readAddressFromContract(provider, contractAddress, signature, method) {
1705
+ try {
1706
+ const iface = new ethers.Interface([signature]);
1707
+ const data = iface.encodeFunctionData(method, []);
1708
+ const result = await provider.call({ to: contractAddress, data });
1709
+ const [value] = iface.decodeFunctionResult(method, result);
1710
+ if (typeof value === "string" && ethers.isAddress(value) && value !== ethers.ZeroAddress) {
1711
+ return ethers.getAddress(value);
1712
+ }
1713
+ } catch {
1714
+ }
1715
+ return null;
1716
+ }
1717
+ async function discoverControllingWallet(provider, contractAddress, chainId) {
1718
+ for (const pattern of OWNERSHIP_PATTERNS) {
1719
+ const address = await readAddressFromContract(
1720
+ provider,
1721
+ contractAddress,
1722
+ pattern.signature,
1723
+ pattern.method
1724
+ );
1725
+ if (address) {
1726
+ return buildEvmDidPkh(chainId, address);
1727
+ }
1728
+ }
1729
+ try {
1730
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
1731
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1732
+ const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
1733
+ if (adminAddress !== ethers.ZeroAddress) {
1734
+ return buildEvmDidPkh(chainId, adminAddress);
1735
+ }
1736
+ }
1737
+ } catch {
1738
+ }
1739
+ return null;
1740
+ }
1741
+ async function verifyDidWebOwnership(params) {
1742
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1743
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
1744
+ const domain = getDomainFromDidWeb(subjectDid);
1745
+ if (!domain) {
1746
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be a did:web DID", {
1747
+ subjectDid: params.subjectDid
1748
+ });
1749
+ }
1750
+ let dnsReason;
1751
+ try {
1752
+ const dnsResult = await verifyDnsTxtControllerDid2(domain, connectedWalletDid, {
1753
+ resolveTxt: params.resolveTxt,
1754
+ recordPrefix: params.recordPrefix
1755
+ });
1756
+ if (dnsResult.valid) {
1757
+ return {
1758
+ valid: true,
1759
+ method: "dns",
1760
+ details: `Verified via DNS TXT record at ${params.recordPrefix ?? "_controllers"}.${domain}`,
1761
+ subjectDid,
1762
+ connectedWalletDid
1763
+ };
1764
+ }
1765
+ dnsReason = dnsResult.reason;
1766
+ } catch (error) {
1767
+ dnsReason = error instanceof Error ? error.message : "DNS TXT verification failed";
1768
+ }
1769
+ let didDocReason;
1770
+ try {
1771
+ const didDocumentResult = await verifyDidJsonControllerDid(domain, connectedWalletDid, {
1772
+ fetchDidDocument: params.fetchDidDocument
1773
+ });
1774
+ if (didDocumentResult.valid) {
1775
+ return {
1776
+ valid: true,
1777
+ method: "did-document",
1778
+ details: `Verified via DID document at https://${domain}/.well-known/did.json`,
1779
+ subjectDid,
1780
+ connectedWalletDid
1781
+ };
1782
+ }
1783
+ didDocReason = didDocumentResult.reason;
1784
+ } catch (error) {
1785
+ didDocReason = error instanceof Error ? error.message : "DID document verification failed";
1786
+ }
1787
+ return {
1788
+ valid: false,
1789
+ reason: "DID ownership verification failed",
1790
+ details: `DNS check: ${dnsReason ?? "failed"}. DID document check: ${didDocReason ?? "failed"}.`,
1791
+ subjectDid,
1792
+ connectedWalletDid
1793
+ };
1794
+ }
1795
+ async function verifyDidPkhOwnership(params) {
1796
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1797
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
1798
+ if (!isEvmDidPkh(subjectDid)) {
1799
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be an EVM did:pkh DID", {
1800
+ subjectDid: params.subjectDid
1801
+ });
1802
+ }
1803
+ const subjectAddress = getAddressFromDidPkh(subjectDid);
1804
+ const connectedWalletAddress = getAddressFromDidPkh(connectedWalletDid);
1805
+ const chainIdRaw = getChainIdFromDidPkh(subjectDid);
1806
+ if (!subjectAddress || !connectedWalletAddress || !chainIdRaw) {
1807
+ throw new OmaTrustError("INVALID_INPUT", "Could not parse did:pkh ownership inputs", {
1808
+ subjectDid: params.subjectDid,
1809
+ connectedWalletDid: params.connectedWalletDid
1810
+ });
1811
+ }
1812
+ const chainId = Number(chainIdRaw);
1813
+ if (!Number.isFinite(chainId)) {
1814
+ throw new OmaTrustError("INVALID_INPUT", "Invalid chain id in subjectDid", {
1815
+ subjectDid: params.subjectDid
1816
+ });
1817
+ }
1818
+ const code = await params.provider.getCode(subjectAddress);
1819
+ const isContract = code !== "0x" && code !== "0x0";
1820
+ if (!isContract) {
1821
+ if (ethers.getAddress(subjectAddress) === ethers.getAddress(connectedWalletAddress)) {
1822
+ return {
1823
+ valid: true,
1824
+ method: "wallet",
1825
+ details: "Verified direct wallet ownership from matching did:pkh subject and connected wallet",
1826
+ subjectDid,
1827
+ connectedWalletDid
1828
+ };
1829
+ }
1830
+ return {
1831
+ valid: false,
1832
+ reason: "EOA did:pkh subject does not match connected wallet",
1833
+ details: "For direct wallet did:pkh subjects, connectedWalletDid must match the subject DID.",
1834
+ subjectDid,
1835
+ connectedWalletDid
1836
+ };
1837
+ }
1838
+ const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
1839
+ if (params.txHash) {
1840
+ if (!controllingWalletDid) {
1841
+ return {
1842
+ valid: false,
1843
+ reason: "Could not discover controlling wallet",
1844
+ details: "Contract does not expose owner/admin/getOwner or a readable EIP-1967 admin slot for transfer verification.",
1845
+ subjectDid,
1846
+ connectedWalletDid
1847
+ };
1848
+ }
1849
+ const tx = await params.provider.getTransaction(params.txHash);
1850
+ if (!tx) {
1851
+ return {
1852
+ valid: false,
1853
+ reason: "Transaction not found",
1854
+ details: `Transaction ${params.txHash} was not found on chain ${chainId}.`,
1855
+ subjectDid,
1856
+ connectedWalletDid,
1857
+ controllingWalletDid
1858
+ };
1859
+ }
1860
+ const receipt = await params.provider.getTransactionReceipt(params.txHash);
1861
+ if (!receipt) {
1862
+ return {
1863
+ valid: false,
1864
+ reason: "Transaction not confirmed",
1865
+ details: "Transaction exists but is not yet confirmed.",
1866
+ subjectDid,
1867
+ connectedWalletDid,
1868
+ controllingWalletDid
1869
+ };
1870
+ }
1871
+ const controllingWalletAddress = getAddressFromDidPkh(controllingWalletDid);
1872
+ if (!tx.from || !controllingWalletAddress || ethers.getAddress(tx.from) !== ethers.getAddress(controllingWalletAddress)) {
1873
+ return {
1874
+ valid: false,
1875
+ reason: "Wrong sender",
1876
+ details: `Transfer must originate from controlling wallet ${controllingWalletDid}.`,
1877
+ subjectDid,
1878
+ connectedWalletDid,
1879
+ controllingWalletDid
1880
+ };
1881
+ }
1882
+ if (!tx.to || ethers.getAddress(tx.to) !== ethers.getAddress(connectedWalletAddress)) {
1883
+ return {
1884
+ valid: false,
1885
+ reason: "Wrong recipient",
1886
+ details: `Transfer must be sent to connected wallet ${connectedWalletDid}.`,
1887
+ subjectDid,
1888
+ connectedWalletDid,
1889
+ controllingWalletDid
1890
+ };
1891
+ }
1892
+ const expectedAmount = calculateTransferAmount(
1893
+ subjectDid,
1894
+ connectedWalletDid,
1895
+ chainId,
1896
+ "shared-control"
1897
+ );
1898
+ const actualValue = BigInt(tx.value ?? 0n);
1899
+ if (actualValue !== expectedAmount) {
1900
+ return {
1901
+ valid: false,
1902
+ reason: "Wrong amount",
1903
+ details: `Transfer amount ${actualValue.toString()} does not match expected proof amount ${expectedAmount.toString()}.`,
1904
+ subjectDid,
1905
+ connectedWalletDid,
1906
+ controllingWalletDid
1907
+ };
1908
+ }
1909
+ await params.provider.getBlockNumber();
1910
+ await params.provider.getBlock(receipt.blockNumber);
1911
+ return {
1912
+ valid: true,
1913
+ method: "transfer",
1914
+ details: `Verified via transfer proof ${params.txHash}.`,
1915
+ subjectDid,
1916
+ connectedWalletDid,
1917
+ controllingWalletDid
1918
+ };
1919
+ }
1920
+ for (const pattern of OWNERSHIP_PATTERNS) {
1921
+ const ownerAddress = await readAddressFromContract(
1922
+ params.provider,
1923
+ subjectAddress,
1924
+ pattern.signature,
1925
+ pattern.method
1926
+ );
1927
+ if (ownerAddress && ethers.getAddress(ownerAddress) === ethers.getAddress(connectedWalletAddress)) {
1928
+ return {
1929
+ valid: true,
1930
+ method: "contract",
1931
+ details: `Verified via ${pattern.method}() ownership check.`,
1932
+ subjectDid,
1933
+ connectedWalletDid,
1934
+ controllingWalletDid: controllingWalletDid ?? void 0
1935
+ };
1936
+ }
1937
+ }
1938
+ try {
1939
+ const adminValue = await params.provider.getStorage(subjectAddress, EIP1967_ADMIN_SLOT);
1940
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1941
+ const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
1942
+ if (adminAddress === ethers.getAddress(connectedWalletAddress)) {
1943
+ return {
1944
+ valid: true,
1945
+ method: "contract",
1946
+ details: "Verified via EIP-1967 admin slot.",
1947
+ subjectDid,
1948
+ connectedWalletDid,
1949
+ controllingWalletDid: controllingWalletDid ?? buildEvmDidPkh(chainId, adminAddress)
1950
+ };
1951
+ }
1952
+ }
1953
+ } catch {
1954
+ }
1955
+ if (ethers.getAddress(subjectAddress) === ethers.getAddress(connectedWalletAddress) && controllingWalletDid) {
1956
+ return {
1957
+ valid: true,
1958
+ method: "minting-wallet",
1959
+ details: `Verified because connected wallet matches the contract DID address. Controlling wallet is ${controllingWalletDid}.`,
1960
+ subjectDid,
1961
+ connectedWalletDid,
1962
+ controllingWalletDid
1963
+ };
1964
+ }
1965
+ return {
1966
+ valid: false,
1967
+ reason: "Contract ownership verification failed",
1968
+ 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}.`,
1969
+ subjectDid,
1970
+ connectedWalletDid,
1971
+ controllingWalletDid: controllingWalletDid ?? void 0
1972
+ };
1973
+ }
1974
+ async function verifySubjectOwnership(params) {
1975
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1976
+ const method = extractDidMethod(subjectDid);
1977
+ if (method === "web") {
1978
+ return verifyDidWebOwnership(params);
1979
+ }
1980
+ if (method === "pkh") {
1981
+ const didPkhParams = params;
1982
+ if (!didPkhParams.provider) {
1983
+ throw new OmaTrustError(
1984
+ "INVALID_INPUT",
1985
+ "provider is required for did:pkh ownership verification",
1986
+ { subjectDid }
1987
+ );
1988
+ }
1989
+ return verifyDidPkhOwnership(didPkhParams);
1990
+ }
1991
+ throw new OmaTrustError("INVALID_INPUT", "Unsupported DID type for ownership verification", {
1992
+ subjectDid
1993
+ });
1994
+ }
1418
1995
 
1419
1996
  exports.buildDelegatedAttestationTypedData = buildDelegatedAttestationTypedData;
1997
+ exports.buildDelegatedTypedDataFromEncoded = buildDelegatedTypedDataFromEncoded;
1420
1998
  exports.buildDnsTxtRecord = buildDnsTxtRecord;
1421
1999
  exports.buildEip712Domain = buildEip712Domain;
1422
2000
  exports.calculateAverageUserReviewRating = calculateAverageUserReviewRating;
@@ -1440,6 +2018,7 @@ exports.fetchDidDocument = fetchDidDocument;
1440
2018
  exports.formatSchemaUid = formatSchemaUid;
1441
2019
  exports.formatTransferAmount = formatTransferAmount;
1442
2020
  exports.getAttestation = getAttestation;
2021
+ exports.getAttestationsByAttester = getAttestationsByAttester;
1443
2022
  exports.getAttestationsForDid = getAttestationsForDid;
1444
2023
  exports.getChainConstants = getChainConstants;
1445
2024
  exports.getExplorerAddressUrl = getExplorerAddressUrl;
@@ -1455,15 +2034,21 @@ exports.listAttestations = listAttestations;
1455
2034
  exports.normalizeSchema = normalizeSchema;
1456
2035
  exports.parseDnsTxtRecord = parseDnsTxtRecord;
1457
2036
  exports.prepareDelegatedAttestation = prepareDelegatedAttestation;
2037
+ exports.revokeAttestation = revokeAttestation;
1458
2038
  exports.schemaToString = schemaToString;
1459
2039
  exports.splitSignature = splitSignature;
1460
2040
  exports.submitAttestation = submitAttestation;
1461
2041
  exports.submitDelegatedAttestation = submitDelegatedAttestation;
2042
+ exports.validateAttestationData = validateAttestationData;
1462
2043
  exports.verifyAttestation = verifyAttestation;
1463
2044
  exports.verifyDidDocumentControllerDid = verifyDidDocumentControllerDid;
2045
+ exports.verifyDidJsonControllerDid = verifyDidJsonControllerDid;
2046
+ exports.verifyDidPkhOwnership = verifyDidPkhOwnership;
2047
+ exports.verifyDidWebOwnership = verifyDidWebOwnership;
1464
2048
  exports.verifyDnsTxtControllerDid = verifyDnsTxtControllerDid;
1465
2049
  exports.verifyEip712Signature = verifyEip712Signature;
1466
2050
  exports.verifyProof = verifyProof;
1467
2051
  exports.verifySchemaExists = verifySchemaExists;
2052
+ exports.verifySubjectOwnership = verifySubjectOwnership;
1468
2053
  //# sourceMappingURL=index.cjs.map
1469
2054
  //# sourceMappingURL=index.cjs.map