@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
@@ -1,5 +1,5 @@
1
1
  import { SchemaEncoder, EAS } from '@ethereum-attestation-service/eas-sdk';
2
- import { ZeroAddress, Signature, Contract, toUtf8Bytes, sha256, keccak256, formatUnits, getAddress, isAddress, verifyTypedData, hexlify, randomBytes } from 'ethers';
2
+ import { ZeroAddress, Signature, toUtf8Bytes, sha256, keccak256, formatUnits, getAddress, isAddress, verifyTypedData, hexlify, randomBytes, Contract, Interface } from 'ethers';
3
3
  import canonicalize from 'canonicalize';
4
4
  import { resolveTxt } from 'dns/promises';
5
5
 
@@ -52,6 +52,13 @@ function encodeAttestationData(schema, data) {
52
52
  if (!data || typeof data !== "object") {
53
53
  throw new OmaTrustError("INVALID_INPUT", "data must be an object");
54
54
  }
55
+ const validationErrors = validateAttestationData(schema, data);
56
+ if (validationErrors.length > 0) {
57
+ const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
58
+ throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
59
+ errors: validationErrors
60
+ });
61
+ }
55
62
  const fields = normalizeSchema(schema);
56
63
  const schemaString = schemaToString(fields);
57
64
  const encoder = new SchemaEncoder(schemaString);
@@ -64,6 +71,99 @@ function encodeAttestationData(schema, data) {
64
71
  );
65
72
  return encoded;
66
73
  }
74
+ function getActualType(value) {
75
+ if (value === null) {
76
+ return "null";
77
+ }
78
+ if (value === void 0) {
79
+ return "undefined";
80
+ }
81
+ if (Array.isArray(value)) {
82
+ return "array";
83
+ }
84
+ if (typeof value === "number") {
85
+ return Number.isFinite(value) ? "number" : "non-finite-number";
86
+ }
87
+ return typeof value;
88
+ }
89
+ function isNumericValue(value, allowNegative) {
90
+ if (typeof value === "bigint") {
91
+ return allowNegative || value >= 0n;
92
+ }
93
+ if (typeof value === "number") {
94
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
95
+ return false;
96
+ }
97
+ return allowNegative || value >= 0;
98
+ }
99
+ if (typeof value === "string") {
100
+ if (value.length === 0) {
101
+ return false;
102
+ }
103
+ const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
104
+ return pattern.test(value);
105
+ }
106
+ return false;
107
+ }
108
+ function isHex(value) {
109
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
110
+ }
111
+ function validateFieldValue(type, value) {
112
+ const normalizedType = type.trim().toLowerCase();
113
+ const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
114
+ if (/^uint\d*$/.test(normalizedType)) {
115
+ return isNumericValue(value, false);
116
+ }
117
+ if (/^int\d*$/.test(normalizedType)) {
118
+ return isNumericValue(value, true);
119
+ }
120
+ if (normalizedType === "string") {
121
+ return typeof value === "string";
122
+ }
123
+ if (normalizedType === "string[]") {
124
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
125
+ }
126
+ if (normalizedType === "bool") {
127
+ return typeof value === "boolean";
128
+ }
129
+ if (normalizedType === "address") {
130
+ return typeof value === "string" && isAddress(value);
131
+ }
132
+ if (normalizedType === "bytes") {
133
+ return isHex(value) && (value.length - 2) % 2 === 0;
134
+ }
135
+ if (bytesNMatch) {
136
+ const expectedBytes = Number(bytesNMatch[1]);
137
+ return isHex(value) && value.length === 2 + expectedBytes * 2;
138
+ }
139
+ return true;
140
+ }
141
+ function validateAttestationData(schema, data) {
142
+ if (!data || typeof data !== "object") {
143
+ return [{
144
+ schemaFieldName: "data",
145
+ expectedType: "object",
146
+ providedType: getActualType(data),
147
+ providedValue: data
148
+ }];
149
+ }
150
+ const fields = normalizeSchema(schema);
151
+ const errors = [];
152
+ for (const field of fields) {
153
+ const value = data[field.name];
154
+ const isValid = validateFieldValue(field.type, value);
155
+ if (isValid) {
156
+ continue;
157
+ }
158
+ errors.push({
159
+ schemaFieldName: field.name,
160
+ expectedType: field.type,
161
+ providedType: getActualType(value),
162
+ providedValue: value
163
+ });
164
+ }
165
+ return errors;
166
+ }
67
167
  function decodeAttestationData(schema, encodedData) {
68
168
  if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
69
169
  throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
@@ -84,22 +184,11 @@ function decodeAttestationData(schema, encodedData) {
84
184
  return result;
85
185
  }
86
186
  function extractExpirationTime(data) {
87
- const keys = ["expirationTime", "expiration", "validUntil", "expiresAt", "expires"];
88
- for (const key of keys) {
89
- const value = data[key];
90
- if (value === void 0 || value === null) {
91
- continue;
92
- }
93
- if (typeof value === "bigint") {
94
- return value;
95
- }
96
- if (typeof value === "number") {
97
- return value;
98
- }
99
- if (typeof value === "string" && /^\d+$/.test(value)) {
100
- return BigInt(value);
101
- }
102
- }
187
+ const value = data["expiresAt"];
188
+ if (value === void 0 || value === null) return void 0;
189
+ if (typeof value === "bigint") return value;
190
+ if (typeof value === "number") return value;
191
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
103
192
  return void 0;
104
193
  }
105
194
 
@@ -139,7 +228,7 @@ function extractDidMethod(did) {
139
228
  }
140
229
  function normalizeDomain(domain) {
141
230
  assertString(domain, "domain", "INVALID_DID");
142
- return domain.trim().toLowerCase().replace(/\.$/, "");
231
+ return domain.trim().toLowerCase().replace(/\.$/, "").replace(/^www\./, "");
143
232
  }
144
233
  function normalizeDidWeb(input) {
145
234
  assertString(input, "input", "INVALID_DID");
@@ -258,6 +347,26 @@ function parseDidPkh(did) {
258
347
  }
259
348
  return { namespace, chainId, address };
260
349
  }
350
+ function getChainIdFromDidPkh(did) {
351
+ return parseDidPkh(did)?.chainId ?? null;
352
+ }
353
+ function getAddressFromDidPkh(did) {
354
+ return parseDidPkh(did)?.address ?? null;
355
+ }
356
+ function getNamespaceFromDidPkh(did) {
357
+ return parseDidPkh(did)?.namespace ?? null;
358
+ }
359
+ function isEvmDidPkh(did) {
360
+ return getNamespaceFromDidPkh(did) === "eip155";
361
+ }
362
+ function getDomainFromDidWeb(did) {
363
+ if (!did.startsWith("did:web:")) {
364
+ return null;
365
+ }
366
+ const identifier = did.slice("did:web:".length);
367
+ const [domain] = identifier.split("/");
368
+ return domain || null;
369
+ }
261
370
  function extractAddressFromDid(identifier) {
262
371
  assertString(identifier, "identifier", "INVALID_DID");
263
372
  if (identifier.startsWith("did:pkh:")) {
@@ -324,6 +433,53 @@ function resolveRecipientAddress(data) {
324
433
  return ZeroAddress;
325
434
  }
326
435
 
436
+ // src/reputation/eas-adapter.ts
437
+ function isRecord(value) {
438
+ return Boolean(value) && typeof value === "object";
439
+ }
440
+ function isHex32(value) {
441
+ return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
442
+ }
443
+ function getEasTransactionReceipt(tx) {
444
+ if (!isRecord(tx)) {
445
+ return void 0;
446
+ }
447
+ const candidates = [tx.receipt, tx.tx, tx.transaction];
448
+ for (const candidate of candidates) {
449
+ if (isRecord(candidate)) {
450
+ return candidate;
451
+ }
452
+ }
453
+ return void 0;
454
+ }
455
+ function getEasTransactionHash(tx) {
456
+ const receipt = getEasTransactionReceipt(tx);
457
+ if (receipt) {
458
+ const receiptHash = receipt.hash;
459
+ if (isHex32(receiptHash)) {
460
+ return receiptHash;
461
+ }
462
+ const transactionHash = receipt.transactionHash;
463
+ if (isHex32(transactionHash)) {
464
+ return transactionHash;
465
+ }
466
+ }
467
+ if (isRecord(tx) && isHex32(tx.hash)) {
468
+ return tx.hash;
469
+ }
470
+ return void 0;
471
+ }
472
+ function isEasSchemaNotFoundError(err) {
473
+ if (!isRecord(err)) {
474
+ return false;
475
+ }
476
+ const message = err.message;
477
+ if (typeof message !== "string" || message.length === 0) {
478
+ return false;
479
+ }
480
+ return /schema not found/i.test(message);
481
+ }
482
+
327
483
  // src/reputation/submit.ts
328
484
  async function submitAttestation(params) {
329
485
  if (!params || typeof params !== "object") {
@@ -354,25 +510,46 @@ async function submitAttestation(params) {
354
510
  }
355
511
  });
356
512
  const uid = await tx.wait();
357
- const txAny = tx;
358
- const txHash = txAny.tx?.hash ?? txAny.hash ?? txAny.transaction?.hash ?? ZERO_UID;
513
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
514
+ const receipt = getEasTransactionReceipt(tx);
359
515
  return {
360
516
  uid,
361
517
  txHash,
362
- receipt: txAny.tx
518
+ receipt
363
519
  };
364
520
  } catch (err) {
365
521
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
366
522
  }
367
523
  }
368
- function buildDelegatedAttestationTypedData(params) {
369
- const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
370
- const encodedData = encodeAttestationData(params.schema, dataWithHash);
371
- const recipient = resolveRecipientAddress(dataWithHash);
372
- const expiration = toBigIntOrDefault(
373
- params.expirationTime ?? extractExpirationTime(dataWithHash),
374
- 0n
375
- );
524
+ async function revokeAttestation(params) {
525
+ if (!params || typeof params !== "object") {
526
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
527
+ }
528
+ if (!params.signer) {
529
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
530
+ }
531
+ const eas = new EAS(params.easContractAddress);
532
+ eas.connect(params.signer);
533
+ try {
534
+ const tx = await eas.revoke({
535
+ schema: params.schemaUid,
536
+ data: {
537
+ uid: params.uid,
538
+ value: toBigIntOrDefault(params.value, 0n)
539
+ }
540
+ });
541
+ await tx.wait();
542
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
543
+ const receipt = getEasTransactionReceipt(tx);
544
+ return {
545
+ txHash,
546
+ receipt
547
+ };
548
+ } catch (err) {
549
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
550
+ }
551
+ }
552
+ function buildDelegatedTypedData(params) {
376
553
  return {
377
554
  domain: {
378
555
  name: "EAS",
@@ -397,17 +574,52 @@ function buildDelegatedAttestationTypedData(params) {
397
574
  message: {
398
575
  attester: params.attester,
399
576
  schema: params.schemaUid,
400
- recipient,
401
- expirationTime: expiration,
577
+ recipient: params.recipient,
578
+ expirationTime: toBigIntOrDefault(params.expirationTime, 0n),
402
579
  revocable: params.revocable ?? true,
403
580
  refUID: params.refUid ?? ZERO_UID,
404
- data: encodedData,
581
+ data: params.encodedData,
405
582
  value: toBigIntOrDefault(params.value, 0n),
406
583
  nonce: toBigIntOrDefault(params.nonce, 0n),
407
584
  deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
408
585
  }
409
586
  };
410
587
  }
588
+ function buildDelegatedAttestationTypedData(params) {
589
+ const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
590
+ const encodedData = encodeAttestationData(params.schema, dataWithHash);
591
+ const recipient = resolveRecipientAddress(dataWithHash);
592
+ return buildDelegatedTypedData({
593
+ chainId: params.chainId,
594
+ easContractAddress: params.easContractAddress,
595
+ schemaUid: params.schemaUid,
596
+ encodedData,
597
+ recipient,
598
+ attester: params.attester,
599
+ nonce: params.nonce,
600
+ revocable: params.revocable,
601
+ expirationTime: params.expirationTime ?? extractExpirationTime(dataWithHash),
602
+ refUid: params.refUid,
603
+ value: params.value,
604
+ deadline: params.deadline
605
+ });
606
+ }
607
+ function buildDelegatedTypedDataFromEncoded(params) {
608
+ return buildDelegatedTypedData({
609
+ chainId: params.chainId,
610
+ easContractAddress: params.easContractAddress,
611
+ schemaUid: params.schemaUid,
612
+ encodedData: params.encodedData,
613
+ recipient: params.recipient,
614
+ attester: params.attester,
615
+ nonce: params.nonce,
616
+ revocable: params.revocable,
617
+ expirationTime: params.expirationTime,
618
+ refUid: params.refUid,
619
+ value: params.value,
620
+ deadline: params.deadline
621
+ });
622
+ }
411
623
  function splitSignature(signature) {
412
624
  try {
413
625
  const parsed = Signature.from(signature);
@@ -484,7 +696,14 @@ async function submitDelegatedAttestation(params) {
484
696
  var EAS_EVENT_ABI = [
485
697
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
486
698
  ];
487
- function parseAttestation(attestation, schema) {
699
+ function parseEventTxHash(event) {
700
+ const txHash = event.transactionHash;
701
+ if (typeof txHash !== "string") {
702
+ return void 0;
703
+ }
704
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
705
+ }
706
+ function parseAttestation(attestation, schema, txHash) {
488
707
  const rawData = attestation.data ?? "0x";
489
708
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
490
709
  const toBigIntSafe = (value) => {
@@ -504,6 +723,7 @@ function parseAttestation(attestation, schema) {
504
723
  schema: attestation.schema,
505
724
  attester: attestation.attester,
506
725
  recipient: attestation.recipient,
726
+ txHash,
507
727
  revocable: Boolean(attestation.revocable),
508
728
  revocationTime: toBigIntSafe(attestation.revocationTime),
509
729
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -529,21 +749,18 @@ async function getAttestation(params) {
529
749
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
530
750
  }
531
751
  }
532
- async function getAttestationsForDid(params) {
533
- const provider = params.provider;
534
- const contract = new Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
535
- const toBlock = params.toBlock ?? await provider.getBlockNumber();
536
- const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
537
- const filter = contract.filters.Attested(didToAddress(params.did));
752
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
753
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI, provider);
754
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
538
755
  let events;
539
756
  try {
540
757
  events = await contract.queryFilter(filter, fromBlock, toBlock);
541
758
  } catch (err) {
542
759
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
543
760
  }
544
- const eas = new EAS(params.easContractAddress);
761
+ const eas = new EAS(easContractAddress);
545
762
  eas.connect(provider);
546
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
763
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
547
764
  const results = [];
548
765
  for (const event of events) {
549
766
  if (!("args" in event && Array.isArray(event.args))) {
@@ -562,11 +779,39 @@ async function getAttestationsForDid(params) {
562
779
  if (!attestation || !attestation.uid) {
563
780
  continue;
564
781
  }
565
- results.push(parseAttestation(attestation));
782
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
566
783
  }
567
784
  results.sort((a, b) => Number(b.time - a.time));
568
785
  return results;
569
786
  }
787
+ async function getAttestationsForDid(params) {
788
+ const provider = params.provider;
789
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
790
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
791
+ return queryAttestationEvents(
792
+ params.easContractAddress,
793
+ provider,
794
+ fromBlock,
795
+ toBlock,
796
+ { recipient: didToAddress(params.did), schemas: params.schemas }
797
+ );
798
+ }
799
+ async function getAttestationsByAttester(params) {
800
+ const provider = params.provider;
801
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
802
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
803
+ if (params.limit !== void 0 && params.limit <= 0) {
804
+ return [];
805
+ }
806
+ const results = await queryAttestationEvents(
807
+ params.easContractAddress,
808
+ provider,
809
+ fromBlock,
810
+ toBlock,
811
+ { attester: params.attester, schemas: params.schemas }
812
+ );
813
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
814
+ }
570
815
  async function listAttestations(params) {
571
816
  const limit = params.limit ?? 20;
572
817
  if (limit <= 0) {
@@ -577,34 +822,15 @@ async function listAttestations(params) {
577
822
  }
578
823
  async function getLatestAttestations(params) {
579
824
  const provider = params.provider;
580
- const contract = new Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
581
- const currentBlock = await provider.getBlockNumber();
582
- const fromBlock = params.fromBlock ?? Math.max(0, currentBlock - 5e4);
583
- const events = await contract.queryFilter(contract.filters.Attested(), fromBlock, currentBlock);
584
- const eas = new EAS(params.easContractAddress);
585
- eas.connect(provider);
586
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
587
- const results = [];
588
- for (const event of events) {
589
- if (!("args" in event && Array.isArray(event.args))) {
590
- continue;
591
- }
592
- const args = event.args;
593
- const uid = args?.[2];
594
- const schemaUid = args?.[3];
595
- if (!uid || !schemaUid) {
596
- continue;
597
- }
598
- if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
599
- continue;
600
- }
601
- const attestation = await eas.getAttestation(uid);
602
- if (!attestation || !attestation.uid) {
603
- continue;
604
- }
605
- results.push(parseAttestation(attestation));
606
- }
607
- results.sort((a, b) => Number(b.time - a.time));
825
+ const toBlock = await provider.getBlockNumber();
826
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
827
+ const results = await queryAttestationEvents(
828
+ params.easContractAddress,
829
+ provider,
830
+ fromBlock,
831
+ toBlock,
832
+ { schemas: params.schemas }
833
+ );
608
834
  return results.slice(0, params.limit ?? 20);
609
835
  }
610
836
  function deduplicateReviews(attestations) {
@@ -866,6 +1092,13 @@ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
866
1092
  reason: `No matching address found in DID document (expected ${expectedAddress})`
867
1093
  };
868
1094
  }
1095
+ async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1096
+ if (!domain || typeof domain !== "string") {
1097
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1098
+ }
1099
+ const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
1100
+ return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
1101
+ }
869
1102
  function buildEip712Domain(name, version, chainId, verifyingContract) {
870
1103
  return { name, version, chainId, verifyingContract };
871
1104
  }
@@ -898,6 +1131,8 @@ function verifyEip712Signature(typedData, signature) {
898
1131
  throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
899
1132
  }
900
1133
  }
1134
+
1135
+ // src/reputation/proof/dns-txt-record.ts
901
1136
  function parseDnsTxtRecord(record) {
902
1137
  if (!record || typeof record !== "string") {
903
1138
  throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
@@ -925,27 +1160,6 @@ function buildDnsTxtRecord(controllerDid) {
925
1160
  const normalized = normalizeDid(controllerDid);
926
1161
  return `v=1;controller=${normalized}`;
927
1162
  }
928
- async function verifyDnsTxtControllerDid(domain, expectedControllerDid) {
929
- if (!domain || typeof domain !== "string") {
930
- throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
931
- }
932
- const expected = normalizeDid(expectedControllerDid);
933
- const host = `_omatrust.${domain.toLowerCase().replace(/\.$/, "")}`;
934
- let records;
935
- try {
936
- records = await resolveTxt(host);
937
- } catch (err) {
938
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
939
- }
940
- for (const recordParts of records) {
941
- const record = recordParts.join("");
942
- const parsed = parseDnsTxtRecord(record);
943
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
944
- return { valid: true, record };
945
- }
946
- }
947
- return { valid: false, reason: "No TXT record matched expected controller DID" };
948
- }
949
1163
 
950
1164
  // src/reputation/verify.ts
951
1165
  function parseChainId(input) {
@@ -1226,6 +1440,9 @@ async function getSchemaDetails(schemaRegistry, schemaUid) {
1226
1440
  revocable: Boolean(details.revocable)
1227
1441
  };
1228
1442
  } catch (err) {
1443
+ if (isEasSchemaNotFoundError(err)) {
1444
+ throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
1445
+ }
1229
1446
  if (err instanceof OmaTrustError) {
1230
1447
  throw err;
1231
1448
  }
@@ -1409,7 +1626,367 @@ function createEvidencePointerProof(url) {
1409
1626
  issuedAt: Math.floor(Date.now() / 1e3)
1410
1627
  };
1411
1628
  }
1629
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
1630
+ if (!domain || typeof domain !== "string") {
1631
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1632
+ }
1633
+ const expected = normalizeDid(expectedControllerDid);
1634
+ const prefix = options.recordPrefix ?? "_controllers";
1635
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1636
+ let records;
1637
+ try {
1638
+ records = await (options.resolveTxt ?? resolveTxt)(host);
1639
+ } catch (err) {
1640
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1641
+ }
1642
+ for (const recordParts of records) {
1643
+ const record = recordParts.join("");
1644
+ const parsed = parseDnsTxtRecord(record);
1645
+ if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1646
+ return { valid: true, record };
1647
+ }
1648
+ }
1649
+ return { valid: false, reason: "No TXT record matched expected controller DID" };
1650
+ }
1651
+
1652
+ // src/reputation/proof/dns-txt-shared.ts
1653
+ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
1654
+ if (!domain || typeof domain !== "string") {
1655
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1656
+ }
1657
+ if (!options.resolveTxt) {
1658
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
1659
+ }
1660
+ const expected = normalizeDid(expectedControllerDid);
1661
+ const prefix = options.recordPrefix ?? "_controllers";
1662
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
1663
+ let records;
1664
+ try {
1665
+ records = await options.resolveTxt(host);
1666
+ } catch (err) {
1667
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1668
+ }
1669
+ for (const recordParts of records) {
1670
+ const record = recordParts.join("");
1671
+ const parsed = parseDnsTxtRecord(record);
1672
+ if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
1673
+ return { valid: true, record };
1674
+ }
1675
+ }
1676
+ return { valid: false, reason: "No TXT record matched expected controller DID" };
1677
+ }
1678
+
1679
+ // src/reputation/proof/subject-ownership.ts
1680
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1681
+ var OWNERSHIP_PATTERNS = [
1682
+ { method: "owner", signature: "function owner() view returns (address)" },
1683
+ { method: "admin", signature: "function admin() view returns (address)" },
1684
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
1685
+ ];
1686
+ function assertConnectedWalletDid(input) {
1687
+ const normalized = normalizeDid(input);
1688
+ if (extractDidMethod(normalized) !== "pkh") {
1689
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
1690
+ connectedWalletDid: input
1691
+ });
1692
+ }
1693
+ return normalized;
1694
+ }
1695
+ function normalizeSubjectDid(input) {
1696
+ return normalizeDid(input);
1697
+ }
1698
+ async function readAddressFromContract(provider, contractAddress, signature, method) {
1699
+ try {
1700
+ const iface = new Interface([signature]);
1701
+ const data = iface.encodeFunctionData(method, []);
1702
+ const result = await provider.call({ to: contractAddress, data });
1703
+ const [value] = iface.decodeFunctionResult(method, result);
1704
+ if (typeof value === "string" && isAddress(value) && value !== ZeroAddress) {
1705
+ return getAddress(value);
1706
+ }
1707
+ } catch {
1708
+ }
1709
+ return null;
1710
+ }
1711
+ async function discoverControllingWallet(provider, contractAddress, chainId) {
1712
+ for (const pattern of OWNERSHIP_PATTERNS) {
1713
+ const address = await readAddressFromContract(
1714
+ provider,
1715
+ contractAddress,
1716
+ pattern.signature,
1717
+ pattern.method
1718
+ );
1719
+ if (address) {
1720
+ return buildEvmDidPkh(chainId, address);
1721
+ }
1722
+ }
1723
+ try {
1724
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
1725
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1726
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
1727
+ if (adminAddress !== ZeroAddress) {
1728
+ return buildEvmDidPkh(chainId, adminAddress);
1729
+ }
1730
+ }
1731
+ } catch {
1732
+ }
1733
+ return null;
1734
+ }
1735
+ async function verifyDidWebOwnership(params) {
1736
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1737
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
1738
+ const domain = getDomainFromDidWeb(subjectDid);
1739
+ if (!domain) {
1740
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be a did:web DID", {
1741
+ subjectDid: params.subjectDid
1742
+ });
1743
+ }
1744
+ let dnsReason;
1745
+ try {
1746
+ const dnsResult = await verifyDnsTxtControllerDid2(domain, connectedWalletDid, {
1747
+ resolveTxt: params.resolveTxt,
1748
+ recordPrefix: params.recordPrefix
1749
+ });
1750
+ if (dnsResult.valid) {
1751
+ return {
1752
+ valid: true,
1753
+ method: "dns",
1754
+ details: `Verified via DNS TXT record at ${params.recordPrefix ?? "_controllers"}.${domain}`,
1755
+ subjectDid,
1756
+ connectedWalletDid
1757
+ };
1758
+ }
1759
+ dnsReason = dnsResult.reason;
1760
+ } catch (error) {
1761
+ dnsReason = error instanceof Error ? error.message : "DNS TXT verification failed";
1762
+ }
1763
+ let didDocReason;
1764
+ try {
1765
+ const didDocumentResult = await verifyDidJsonControllerDid(domain, connectedWalletDid, {
1766
+ fetchDidDocument: params.fetchDidDocument
1767
+ });
1768
+ if (didDocumentResult.valid) {
1769
+ return {
1770
+ valid: true,
1771
+ method: "did-document",
1772
+ details: `Verified via DID document at https://${domain}/.well-known/did.json`,
1773
+ subjectDid,
1774
+ connectedWalletDid
1775
+ };
1776
+ }
1777
+ didDocReason = didDocumentResult.reason;
1778
+ } catch (error) {
1779
+ didDocReason = error instanceof Error ? error.message : "DID document verification failed";
1780
+ }
1781
+ return {
1782
+ valid: false,
1783
+ reason: "DID ownership verification failed",
1784
+ details: `DNS check: ${dnsReason ?? "failed"}. DID document check: ${didDocReason ?? "failed"}.`,
1785
+ subjectDid,
1786
+ connectedWalletDid
1787
+ };
1788
+ }
1789
+ async function verifyDidPkhOwnership(params) {
1790
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1791
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
1792
+ if (!isEvmDidPkh(subjectDid)) {
1793
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be an EVM did:pkh DID", {
1794
+ subjectDid: params.subjectDid
1795
+ });
1796
+ }
1797
+ const subjectAddress = getAddressFromDidPkh(subjectDid);
1798
+ const connectedWalletAddress = getAddressFromDidPkh(connectedWalletDid);
1799
+ const chainIdRaw = getChainIdFromDidPkh(subjectDid);
1800
+ if (!subjectAddress || !connectedWalletAddress || !chainIdRaw) {
1801
+ throw new OmaTrustError("INVALID_INPUT", "Could not parse did:pkh ownership inputs", {
1802
+ subjectDid: params.subjectDid,
1803
+ connectedWalletDid: params.connectedWalletDid
1804
+ });
1805
+ }
1806
+ const chainId = Number(chainIdRaw);
1807
+ if (!Number.isFinite(chainId)) {
1808
+ throw new OmaTrustError("INVALID_INPUT", "Invalid chain id in subjectDid", {
1809
+ subjectDid: params.subjectDid
1810
+ });
1811
+ }
1812
+ const code = await params.provider.getCode(subjectAddress);
1813
+ const isContract = code !== "0x" && code !== "0x0";
1814
+ if (!isContract) {
1815
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress)) {
1816
+ return {
1817
+ valid: true,
1818
+ method: "wallet",
1819
+ details: "Verified direct wallet ownership from matching did:pkh subject and connected wallet",
1820
+ subjectDid,
1821
+ connectedWalletDid
1822
+ };
1823
+ }
1824
+ return {
1825
+ valid: false,
1826
+ reason: "EOA did:pkh subject does not match connected wallet",
1827
+ details: "For direct wallet did:pkh subjects, connectedWalletDid must match the subject DID.",
1828
+ subjectDid,
1829
+ connectedWalletDid
1830
+ };
1831
+ }
1832
+ const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
1833
+ if (params.txHash) {
1834
+ if (!controllingWalletDid) {
1835
+ return {
1836
+ valid: false,
1837
+ reason: "Could not discover controlling wallet",
1838
+ details: "Contract does not expose owner/admin/getOwner or a readable EIP-1967 admin slot for transfer verification.",
1839
+ subjectDid,
1840
+ connectedWalletDid
1841
+ };
1842
+ }
1843
+ const tx = await params.provider.getTransaction(params.txHash);
1844
+ if (!tx) {
1845
+ return {
1846
+ valid: false,
1847
+ reason: "Transaction not found",
1848
+ details: `Transaction ${params.txHash} was not found on chain ${chainId}.`,
1849
+ subjectDid,
1850
+ connectedWalletDid,
1851
+ controllingWalletDid
1852
+ };
1853
+ }
1854
+ const receipt = await params.provider.getTransactionReceipt(params.txHash);
1855
+ if (!receipt) {
1856
+ return {
1857
+ valid: false,
1858
+ reason: "Transaction not confirmed",
1859
+ details: "Transaction exists but is not yet confirmed.",
1860
+ subjectDid,
1861
+ connectedWalletDid,
1862
+ controllingWalletDid
1863
+ };
1864
+ }
1865
+ const controllingWalletAddress = getAddressFromDidPkh(controllingWalletDid);
1866
+ if (!tx.from || !controllingWalletAddress || getAddress(tx.from) !== getAddress(controllingWalletAddress)) {
1867
+ return {
1868
+ valid: false,
1869
+ reason: "Wrong sender",
1870
+ details: `Transfer must originate from controlling wallet ${controllingWalletDid}.`,
1871
+ subjectDid,
1872
+ connectedWalletDid,
1873
+ controllingWalletDid
1874
+ };
1875
+ }
1876
+ if (!tx.to || getAddress(tx.to) !== getAddress(connectedWalletAddress)) {
1877
+ return {
1878
+ valid: false,
1879
+ reason: "Wrong recipient",
1880
+ details: `Transfer must be sent to connected wallet ${connectedWalletDid}.`,
1881
+ subjectDid,
1882
+ connectedWalletDid,
1883
+ controllingWalletDid
1884
+ };
1885
+ }
1886
+ const expectedAmount = calculateTransferAmount(
1887
+ subjectDid,
1888
+ connectedWalletDid,
1889
+ chainId,
1890
+ "shared-control"
1891
+ );
1892
+ const actualValue = BigInt(tx.value ?? 0n);
1893
+ if (actualValue !== expectedAmount) {
1894
+ return {
1895
+ valid: false,
1896
+ reason: "Wrong amount",
1897
+ details: `Transfer amount ${actualValue.toString()} does not match expected proof amount ${expectedAmount.toString()}.`,
1898
+ subjectDid,
1899
+ connectedWalletDid,
1900
+ controllingWalletDid
1901
+ };
1902
+ }
1903
+ await params.provider.getBlockNumber();
1904
+ await params.provider.getBlock(receipt.blockNumber);
1905
+ return {
1906
+ valid: true,
1907
+ method: "transfer",
1908
+ details: `Verified via transfer proof ${params.txHash}.`,
1909
+ subjectDid,
1910
+ connectedWalletDid,
1911
+ controllingWalletDid
1912
+ };
1913
+ }
1914
+ for (const pattern of OWNERSHIP_PATTERNS) {
1915
+ const ownerAddress = await readAddressFromContract(
1916
+ params.provider,
1917
+ subjectAddress,
1918
+ pattern.signature,
1919
+ pattern.method
1920
+ );
1921
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(connectedWalletAddress)) {
1922
+ return {
1923
+ valid: true,
1924
+ method: "contract",
1925
+ details: `Verified via ${pattern.method}() ownership check.`,
1926
+ subjectDid,
1927
+ connectedWalletDid,
1928
+ controllingWalletDid: controllingWalletDid ?? void 0
1929
+ };
1930
+ }
1931
+ }
1932
+ try {
1933
+ const adminValue = await params.provider.getStorage(subjectAddress, EIP1967_ADMIN_SLOT);
1934
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1935
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
1936
+ if (adminAddress === getAddress(connectedWalletAddress)) {
1937
+ return {
1938
+ valid: true,
1939
+ method: "contract",
1940
+ details: "Verified via EIP-1967 admin slot.",
1941
+ subjectDid,
1942
+ connectedWalletDid,
1943
+ controllingWalletDid: controllingWalletDid ?? buildEvmDidPkh(chainId, adminAddress)
1944
+ };
1945
+ }
1946
+ }
1947
+ } catch {
1948
+ }
1949
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress) && controllingWalletDid) {
1950
+ return {
1951
+ valid: true,
1952
+ method: "minting-wallet",
1953
+ details: `Verified because connected wallet matches the contract DID address. Controlling wallet is ${controllingWalletDid}.`,
1954
+ subjectDid,
1955
+ connectedWalletDid,
1956
+ controllingWalletDid
1957
+ };
1958
+ }
1959
+ return {
1960
+ valid: false,
1961
+ reason: "Contract ownership verification failed",
1962
+ 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}.`,
1963
+ subjectDid,
1964
+ connectedWalletDid,
1965
+ controllingWalletDid: controllingWalletDid ?? void 0
1966
+ };
1967
+ }
1968
+ async function verifySubjectOwnership(params) {
1969
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
1970
+ const method = extractDidMethod(subjectDid);
1971
+ if (method === "web") {
1972
+ return verifyDidWebOwnership(params);
1973
+ }
1974
+ if (method === "pkh") {
1975
+ const didPkhParams = params;
1976
+ if (!didPkhParams.provider) {
1977
+ throw new OmaTrustError(
1978
+ "INVALID_INPUT",
1979
+ "provider is required for did:pkh ownership verification",
1980
+ { subjectDid }
1981
+ );
1982
+ }
1983
+ return verifyDidPkhOwnership(didPkhParams);
1984
+ }
1985
+ throw new OmaTrustError("INVALID_INPUT", "Unsupported DID type for ownership verification", {
1986
+ subjectDid
1987
+ });
1988
+ }
1412
1989
 
1413
- export { buildDelegatedAttestationTypedData, buildDnsTxtRecord, buildEip712Domain, calculateAverageUserReviewRating, calculateTransferAmount, calculateTransferAmountFromAddresses, callControllerWitness, constructSeed, createEvidencePointerProof, createPopEip712Proof, createPopJwsProof, createTxEncodedValueProof, createTxInteractionProof, createX402OfferProof, createX402ReceiptProof, decodeAttestationData, deduplicateReviews, encodeAttestationData, extractAddressesFromDidDocument, extractExpirationTime, fetchDidDocument, formatSchemaUid, formatTransferAmount, getAttestation, getAttestationsForDid, getChainConstants, getExplorerAddressUrl, getExplorerTxUrl, getLatestAttestations, getMajorVersion, getOmaTrustProofEip712Types, getSchemaDetails, getSupportedChainIds, hashSeed, isChainSupported, listAttestations, normalizeSchema, parseDnsTxtRecord, prepareDelegatedAttestation, schemaToString, splitSignature, submitAttestation, submitDelegatedAttestation, verifyAttestation, verifyDidDocumentControllerDid, verifyDnsTxtControllerDid, verifyEip712Signature, verifyProof, verifySchemaExists };
1990
+ export { buildDelegatedAttestationTypedData, buildDelegatedTypedDataFromEncoded, buildDnsTxtRecord, buildEip712Domain, calculateAverageUserReviewRating, calculateTransferAmount, calculateTransferAmountFromAddresses, callControllerWitness, constructSeed, createEvidencePointerProof, createPopEip712Proof, createPopJwsProof, createTxEncodedValueProof, createTxInteractionProof, createX402OfferProof, createX402ReceiptProof, decodeAttestationData, deduplicateReviews, encodeAttestationData, extractAddressesFromDidDocument, extractExpirationTime, fetchDidDocument, formatSchemaUid, formatTransferAmount, getAttestation, getAttestationsByAttester, getAttestationsForDid, getChainConstants, getExplorerAddressUrl, getExplorerTxUrl, getLatestAttestations, getMajorVersion, getOmaTrustProofEip712Types, getSchemaDetails, getSupportedChainIds, hashSeed, isChainSupported, listAttestations, normalizeSchema, parseDnsTxtRecord, prepareDelegatedAttestation, revokeAttestation, schemaToString, splitSignature, submitAttestation, submitDelegatedAttestation, validateAttestationData, verifyAttestation, verifyDidDocumentControllerDid, verifyDidJsonControllerDid, verifyDidPkhOwnership, verifyDidWebOwnership, verifyDnsTxtControllerDid, verifyEip712Signature, verifyProof, verifySchemaExists, verifySubjectOwnership };
1414
1991
  //# sourceMappingURL=index.js.map
1415
1992
  //# sourceMappingURL=index.js.map