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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +21 -7
  2. package/dist/identity/index.cjs +544 -1
  3. package/dist/identity/index.cjs.map +1 -1
  4. package/dist/identity/index.d.cts +2 -1
  5. package/dist/identity/index.d.ts +2 -1
  6. package/dist/identity/index.js +528 -2
  7. package/dist/identity/index.js.map +1 -1
  8. package/dist/index-BOvk-7Ku.d.cts +235 -0
  9. package/dist/index-C2w5EvFH.d.ts +329 -0
  10. package/dist/index-QueRiudB.d.cts +329 -0
  11. package/dist/index-R78TpAhN.d.ts +235 -0
  12. package/dist/index.cjs +2020 -145
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +4 -2
  15. package/dist/index.d.ts +4 -2
  16. package/dist/index.js +2021 -146
  17. package/dist/index.js.map +1 -1
  18. package/dist/reputation/index.browser.cjs +3010 -0
  19. package/dist/reputation/index.browser.cjs.map +1 -0
  20. package/dist/reputation/index.browser.d.cts +14 -0
  21. package/dist/reputation/index.browser.d.ts +14 -0
  22. package/dist/reputation/index.browser.js +2946 -0
  23. package/dist/reputation/index.browser.js.map +1 -0
  24. package/dist/reputation/index.cjs +1884 -123
  25. package/dist/reputation/index.cjs.map +1 -1
  26. package/dist/reputation/index.d.cts +3 -1
  27. package/dist/reputation/index.d.ts +3 -1
  28. package/dist/reputation/index.js +1861 -123
  29. package/dist/reputation/index.js.map +1 -1
  30. package/dist/subject-ownership-B7cFlm8c.d.cts +664 -0
  31. package/dist/subject-ownership-B7cFlm8c.d.ts +664 -0
  32. package/dist/types-dpYxRq8N.d.cts +281 -0
  33. package/dist/types-dpYxRq8N.d.ts +281 -0
  34. package/dist/widgets/index.cjs +238 -0
  35. package/dist/widgets/index.cjs.map +1 -0
  36. package/dist/widgets/index.d.cts +158 -0
  37. package/dist/widgets/index.d.ts +158 -0
  38. package/dist/widgets/index.js +226 -0
  39. package/dist/widgets/index.js.map +1 -0
  40. package/package.json +35 -7
  41. package/dist/index-ChbJxwOA.d.cts +0 -415
  42. package/dist/index-ChbJxwOA.d.ts +0 -415
  43. package/dist/index-QZDExA4I.d.cts +0 -90
  44. package/dist/index-QZDExA4I.d.ts +0 -90
@@ -1,5 +1,6 @@
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, Interface, Contract, hexlify, randomBytes } from 'ethers';
3
+ import { decodeProtectedHeader, importJWK, compactVerify, base64url } from 'jose';
3
4
  import canonicalize from 'canonicalize';
4
5
  import { resolveTxt } from 'dns/promises';
5
6
 
@@ -52,6 +53,13 @@ function encodeAttestationData(schema, data) {
52
53
  if (!data || typeof data !== "object") {
53
54
  throw new OmaTrustError("INVALID_INPUT", "data must be an object");
54
55
  }
56
+ const validationErrors = validateAttestationData(schema, data);
57
+ if (validationErrors.length > 0) {
58
+ const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
59
+ throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
60
+ errors: validationErrors
61
+ });
62
+ }
55
63
  const fields = normalizeSchema(schema);
56
64
  const schemaString = schemaToString(fields);
57
65
  const encoder = new SchemaEncoder(schemaString);
@@ -64,6 +72,99 @@ function encodeAttestationData(schema, data) {
64
72
  );
65
73
  return encoded;
66
74
  }
75
+ function getActualType(value) {
76
+ if (value === null) {
77
+ return "null";
78
+ }
79
+ if (value === void 0) {
80
+ return "undefined";
81
+ }
82
+ if (Array.isArray(value)) {
83
+ return "array";
84
+ }
85
+ if (typeof value === "number") {
86
+ return Number.isFinite(value) ? "number" : "non-finite-number";
87
+ }
88
+ return typeof value;
89
+ }
90
+ function isNumericValue(value, allowNegative) {
91
+ if (typeof value === "bigint") {
92
+ return allowNegative || value >= 0n;
93
+ }
94
+ if (typeof value === "number") {
95
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
96
+ return false;
97
+ }
98
+ return allowNegative || value >= 0;
99
+ }
100
+ if (typeof value === "string") {
101
+ if (value.length === 0) {
102
+ return false;
103
+ }
104
+ const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
105
+ return pattern.test(value);
106
+ }
107
+ return false;
108
+ }
109
+ function isHex(value) {
110
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
111
+ }
112
+ function validateFieldValue(type, value) {
113
+ const normalizedType = type.trim().toLowerCase();
114
+ const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
115
+ if (/^uint\d*$/.test(normalizedType)) {
116
+ return isNumericValue(value, false);
117
+ }
118
+ if (/^int\d*$/.test(normalizedType)) {
119
+ return isNumericValue(value, true);
120
+ }
121
+ if (normalizedType === "string") {
122
+ return typeof value === "string";
123
+ }
124
+ if (normalizedType === "string[]") {
125
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
126
+ }
127
+ if (normalizedType === "bool") {
128
+ return typeof value === "boolean";
129
+ }
130
+ if (normalizedType === "address") {
131
+ return typeof value === "string" && isAddress(value);
132
+ }
133
+ if (normalizedType === "bytes") {
134
+ return isHex(value) && (value.length - 2) % 2 === 0;
135
+ }
136
+ if (bytesNMatch) {
137
+ const expectedBytes = Number(bytesNMatch[1]);
138
+ return isHex(value) && value.length === 2 + expectedBytes * 2;
139
+ }
140
+ return true;
141
+ }
142
+ function validateAttestationData(schema, data) {
143
+ if (!data || typeof data !== "object") {
144
+ return [{
145
+ schemaFieldName: "data",
146
+ expectedType: "object",
147
+ providedType: getActualType(data),
148
+ providedValue: data
149
+ }];
150
+ }
151
+ const fields = normalizeSchema(schema);
152
+ const errors = [];
153
+ for (const field of fields) {
154
+ const value = data[field.name];
155
+ const isValid = validateFieldValue(field.type, value);
156
+ if (isValid) {
157
+ continue;
158
+ }
159
+ errors.push({
160
+ schemaFieldName: field.name,
161
+ expectedType: field.type,
162
+ providedType: getActualType(value),
163
+ providedValue: value
164
+ });
165
+ }
166
+ return errors;
167
+ }
67
168
  function decodeAttestationData(schema, encodedData) {
68
169
  if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
69
170
  throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
@@ -84,22 +185,11 @@ function decodeAttestationData(schema, encodedData) {
84
185
  return result;
85
186
  }
86
187
  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
- }
188
+ const value = data["expiresAt"];
189
+ if (value === void 0 || value === null) return void 0;
190
+ if (typeof value === "bigint") return value;
191
+ if (typeof value === "number") return value;
192
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
103
193
  return void 0;
104
194
  }
105
195
 
@@ -109,6 +199,11 @@ function assertString(value, name, code = "INVALID_INPUT") {
109
199
  throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
110
200
  }
111
201
  }
202
+ function assertObject(value, name, code = "INVALID_INPUT") {
203
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
204
+ throw new OmaTrustError(code, `${name} must be an object`, { value });
205
+ }
206
+ }
112
207
 
113
208
  // src/identity/caip.ts
114
209
  var CAIP_10_REGEX = /^(?<namespace>[a-z0-9-]+):(?<reference>[a-zA-Z0-9-]+):(?<address>.+)$/;
@@ -139,7 +234,7 @@ function extractDidMethod(did) {
139
234
  }
140
235
  function normalizeDomain(domain) {
141
236
  assertString(domain, "domain", "INVALID_DID");
142
- return domain.trim().toLowerCase().replace(/\.$/, "");
237
+ return domain.trim().toLowerCase().replace(/\.$/, "").replace(/^www\./, "");
143
238
  }
144
239
  function normalizeDidWeb(input) {
145
240
  assertString(input, "input", "INVALID_DID");
@@ -215,6 +310,8 @@ function normalizeDid(input) {
215
310
  return normalizeDidHandle(trimmed);
216
311
  case "key":
217
312
  return normalizeDidKey(trimmed);
313
+ case "jwk":
314
+ return normalizeDidJwk(trimmed);
218
315
  default:
219
316
  return trimmed;
220
317
  }
@@ -258,6 +355,26 @@ function parseDidPkh(did) {
258
355
  }
259
356
  return { namespace, chainId, address };
260
357
  }
358
+ function getChainIdFromDidPkh(did) {
359
+ return parseDidPkh(did)?.chainId ?? null;
360
+ }
361
+ function getAddressFromDidPkh(did) {
362
+ return parseDidPkh(did)?.address ?? null;
363
+ }
364
+ function getNamespaceFromDidPkh(did) {
365
+ return parseDidPkh(did)?.namespace ?? null;
366
+ }
367
+ function isEvmDidPkh(did) {
368
+ return getNamespaceFromDidPkh(did) === "eip155";
369
+ }
370
+ function getDomainFromDidWeb(did) {
371
+ if (!did.startsWith("did:web:")) {
372
+ return null;
373
+ }
374
+ const identifier = did.slice("did:web:".length);
375
+ const [domain] = identifier.split("/");
376
+ return domain || null;
377
+ }
261
378
  function extractAddressFromDid(identifier) {
262
379
  assertString(identifier, "identifier", "INVALID_DID");
263
380
  if (identifier.startsWith("did:pkh:")) {
@@ -284,6 +401,73 @@ function extractAddressFromDid(identifier) {
284
401
  }
285
402
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
286
403
  }
404
+ var BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
405
+ var VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
406
+ function validateDidJwk(did) {
407
+ const parts = did.split(":");
408
+ if (parts.length !== 3) {
409
+ return {
410
+ valid: false,
411
+ method: "jwk",
412
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
413
+ };
414
+ }
415
+ const [, , encoded] = parts;
416
+ if (!encoded || encoded.length === 0) {
417
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
418
+ }
419
+ if (!BASE64URL_REGEX.test(encoded)) {
420
+ return {
421
+ valid: false,
422
+ method: "jwk",
423
+ error: "Identifier contains invalid base64url characters"
424
+ };
425
+ }
426
+ let decoded;
427
+ try {
428
+ const bytes = base64url.decode(encoded);
429
+ decoded = new TextDecoder().decode(bytes);
430
+ } catch {
431
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
432
+ }
433
+ let jwk;
434
+ try {
435
+ jwk = JSON.parse(decoded);
436
+ } catch {
437
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
438
+ }
439
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
440
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
441
+ }
442
+ const kty = jwk.kty;
443
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
444
+ return {
445
+ valid: false,
446
+ method: "jwk",
447
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
448
+ };
449
+ }
450
+ if ("d" in jwk) {
451
+ return {
452
+ valid: false,
453
+ method: "jwk",
454
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
455
+ };
456
+ }
457
+ return { valid: true, method: "jwk" };
458
+ }
459
+ function normalizeDidJwk(input) {
460
+ assertString(input, "input", "INVALID_DID");
461
+ const trimmed = input.trim();
462
+ if (!trimmed.startsWith("did:jwk:")) {
463
+ throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
464
+ }
465
+ const result = validateDidJwk(trimmed);
466
+ if (!result.valid) {
467
+ throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
468
+ }
469
+ return trimmed;
470
+ }
287
471
 
288
472
  // src/reputation/internal.ts
289
473
  var ZERO_UID = "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -324,6 +508,53 @@ function resolveRecipientAddress(data) {
324
508
  return ZeroAddress;
325
509
  }
326
510
 
511
+ // src/reputation/eas-adapter.ts
512
+ function isRecord(value) {
513
+ return Boolean(value) && typeof value === "object";
514
+ }
515
+ function isHex32(value) {
516
+ return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
517
+ }
518
+ function getEasTransactionReceipt(tx) {
519
+ if (!isRecord(tx)) {
520
+ return void 0;
521
+ }
522
+ const candidates = [tx.receipt, tx.tx, tx.transaction];
523
+ for (const candidate of candidates) {
524
+ if (isRecord(candidate)) {
525
+ return candidate;
526
+ }
527
+ }
528
+ return void 0;
529
+ }
530
+ function getEasTransactionHash(tx) {
531
+ const receipt = getEasTransactionReceipt(tx);
532
+ if (receipt) {
533
+ const receiptHash = receipt.hash;
534
+ if (isHex32(receiptHash)) {
535
+ return receiptHash;
536
+ }
537
+ const transactionHash = receipt.transactionHash;
538
+ if (isHex32(transactionHash)) {
539
+ return transactionHash;
540
+ }
541
+ }
542
+ if (isRecord(tx) && isHex32(tx.hash)) {
543
+ return tx.hash;
544
+ }
545
+ return void 0;
546
+ }
547
+ function isEasSchemaNotFoundError(err) {
548
+ if (!isRecord(err)) {
549
+ return false;
550
+ }
551
+ const message = err.message;
552
+ if (typeof message !== "string" || message.length === 0) {
553
+ return false;
554
+ }
555
+ return /schema not found/i.test(message);
556
+ }
557
+
327
558
  // src/reputation/submit.ts
328
559
  async function submitAttestation(params) {
329
560
  if (!params || typeof params !== "object") {
@@ -354,25 +585,46 @@ async function submitAttestation(params) {
354
585
  }
355
586
  });
356
587
  const uid = await tx.wait();
357
- const txAny = tx;
358
- const txHash = txAny.tx?.hash ?? txAny.hash ?? txAny.transaction?.hash ?? ZERO_UID;
588
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
589
+ const receipt = getEasTransactionReceipt(tx);
359
590
  return {
360
591
  uid,
361
592
  txHash,
362
- receipt: txAny.tx
593
+ receipt
363
594
  };
364
595
  } catch (err) {
365
596
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
366
597
  }
367
598
  }
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
- );
599
+ async function revokeAttestation(params) {
600
+ if (!params || typeof params !== "object") {
601
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
602
+ }
603
+ if (!params.signer) {
604
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
605
+ }
606
+ const eas = new EAS(params.easContractAddress);
607
+ eas.connect(params.signer);
608
+ try {
609
+ const tx = await eas.revoke({
610
+ schema: params.schemaUid,
611
+ data: {
612
+ uid: params.uid,
613
+ value: toBigIntOrDefault(params.value, 0n)
614
+ }
615
+ });
616
+ await tx.wait();
617
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
618
+ const receipt = getEasTransactionReceipt(tx);
619
+ return {
620
+ txHash,
621
+ receipt
622
+ };
623
+ } catch (err) {
624
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
625
+ }
626
+ }
627
+ function buildDelegatedTypedData(params) {
376
628
  return {
377
629
  domain: {
378
630
  name: "EAS",
@@ -397,17 +649,52 @@ function buildDelegatedAttestationTypedData(params) {
397
649
  message: {
398
650
  attester: params.attester,
399
651
  schema: params.schemaUid,
400
- recipient,
401
- expirationTime: expiration,
652
+ recipient: params.recipient,
653
+ expirationTime: toBigIntOrDefault(params.expirationTime, 0n),
402
654
  revocable: params.revocable ?? true,
403
655
  refUID: params.refUid ?? ZERO_UID,
404
- data: encodedData,
656
+ data: params.encodedData,
405
657
  value: toBigIntOrDefault(params.value, 0n),
406
658
  nonce: toBigIntOrDefault(params.nonce, 0n),
407
659
  deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
408
660
  }
409
661
  };
410
662
  }
663
+ function buildDelegatedAttestationTypedData(params) {
664
+ const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
665
+ const encodedData = encodeAttestationData(params.schema, dataWithHash);
666
+ const recipient = resolveRecipientAddress(dataWithHash);
667
+ return buildDelegatedTypedData({
668
+ chainId: params.chainId,
669
+ easContractAddress: params.easContractAddress,
670
+ schemaUid: params.schemaUid,
671
+ encodedData,
672
+ recipient,
673
+ attester: params.attester,
674
+ nonce: params.nonce,
675
+ revocable: params.revocable,
676
+ expirationTime: params.expirationTime ?? extractExpirationTime(dataWithHash),
677
+ refUid: params.refUid,
678
+ value: params.value,
679
+ deadline: params.deadline
680
+ });
681
+ }
682
+ function buildDelegatedTypedDataFromEncoded(params) {
683
+ return buildDelegatedTypedData({
684
+ chainId: params.chainId,
685
+ easContractAddress: params.easContractAddress,
686
+ schemaUid: params.schemaUid,
687
+ encodedData: params.encodedData,
688
+ recipient: params.recipient,
689
+ attester: params.attester,
690
+ nonce: params.nonce,
691
+ revocable: params.revocable,
692
+ expirationTime: params.expirationTime,
693
+ refUid: params.refUid,
694
+ value: params.value,
695
+ deadline: params.deadline
696
+ });
697
+ }
411
698
  function splitSignature(signature) {
412
699
  try {
413
700
  const parsed = Signature.from(signature);
@@ -467,24 +754,44 @@ async function submitDelegatedAttestation(params) {
467
754
  payload = {};
468
755
  }
469
756
  if (!response.ok) {
470
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
471
- status: response.status,
757
+ const relayError = {
758
+ httpStatus: response.status,
759
+ code: payload.code,
760
+ error: payload.error ?? payload.message,
472
761
  payload
473
- });
762
+ };
763
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
474
764
  }
475
765
  const uid = payload.uid ?? ZERO_UID;
476
766
  const txHash = payload.txHash;
477
- const status = payload.status ?? "submitted";
478
- return {
479
- uid,
480
- txHash,
481
- status
482
- };
767
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
768
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
769
+ const relay = {};
770
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
771
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
772
+ if (typeof payload.success === "boolean") relay.success = payload.success;
773
+ for (const key of Object.keys(payload)) {
774
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
775
+ relay[key] = payload[key];
776
+ }
777
+ }
778
+ const result = { uid, txHash, status };
779
+ if (Object.keys(relay).length > 0) {
780
+ result.relay = relay;
781
+ }
782
+ return result;
483
783
  }
484
784
  var EAS_EVENT_ABI = [
485
785
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
486
786
  ];
487
- function parseAttestation(attestation, schema) {
787
+ function parseEventTxHash(event) {
788
+ const txHash = event.transactionHash;
789
+ if (typeof txHash !== "string") {
790
+ return void 0;
791
+ }
792
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
793
+ }
794
+ function parseAttestation(attestation, schema, txHash) {
488
795
  const rawData = attestation.data ?? "0x";
489
796
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
490
797
  const toBigIntSafe = (value) => {
@@ -504,6 +811,7 @@ function parseAttestation(attestation, schema) {
504
811
  schema: attestation.schema,
505
812
  attester: attestation.attester,
506
813
  recipient: attestation.recipient,
814
+ txHash,
507
815
  revocable: Boolean(attestation.revocable),
508
816
  revocationTime: toBigIntSafe(attestation.revocationTime),
509
817
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -529,21 +837,18 @@ async function getAttestation(params) {
529
837
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
530
838
  }
531
839
  }
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));
840
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
841
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI, provider);
842
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
538
843
  let events;
539
844
  try {
540
845
  events = await contract.queryFilter(filter, fromBlock, toBlock);
541
846
  } catch (err) {
542
847
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
543
848
  }
544
- const eas = new EAS(params.easContractAddress);
849
+ const eas = new EAS(easContractAddress);
545
850
  eas.connect(provider);
546
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
851
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
547
852
  const results = [];
548
853
  for (const event of events) {
549
854
  if (!("args" in event && Array.isArray(event.args))) {
@@ -562,11 +867,39 @@ async function getAttestationsForDid(params) {
562
867
  if (!attestation || !attestation.uid) {
563
868
  continue;
564
869
  }
565
- results.push(parseAttestation(attestation));
870
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
566
871
  }
567
872
  results.sort((a, b) => Number(b.time - a.time));
568
873
  return results;
569
874
  }
875
+ async function getAttestationsForDid(params) {
876
+ const provider = params.provider;
877
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
878
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
879
+ return queryAttestationEvents(
880
+ params.easContractAddress,
881
+ provider,
882
+ fromBlock,
883
+ toBlock,
884
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
885
+ );
886
+ }
887
+ async function getAttestationsByAttester(params) {
888
+ const provider = params.provider;
889
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
890
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
891
+ if (params.limit !== void 0 && params.limit <= 0) {
892
+ return [];
893
+ }
894
+ const results = await queryAttestationEvents(
895
+ params.easContractAddress,
896
+ provider,
897
+ fromBlock,
898
+ toBlock,
899
+ { attester: params.attester, schemas: params.schemas }
900
+ );
901
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
902
+ }
570
903
  async function listAttestations(params) {
571
904
  const limit = params.limit ?? 20;
572
905
  if (limit <= 0) {
@@ -577,34 +910,15 @@ async function listAttestations(params) {
577
910
  }
578
911
  async function getLatestAttestations(params) {
579
912
  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));
913
+ const toBlock = await provider.getBlockNumber();
914
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
915
+ const results = await queryAttestationEvents(
916
+ params.easContractAddress,
917
+ provider,
918
+ fromBlock,
919
+ toBlock,
920
+ { schemas: params.schemas }
921
+ );
608
922
  return results.slice(0, params.limit ?? 20);
609
923
  }
610
924
  function deduplicateReviews(attestations) {
@@ -808,6 +1122,147 @@ function getExplorerTxUrl(chainId, txHash) {
808
1122
  function getExplorerAddressUrl(chainId, address) {
809
1123
  return `${getConfig(chainId).explorer}/address/${address}`;
810
1124
  }
1125
+ var VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
1126
+ var PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
1127
+ var REQUIRED_PUBLIC_FIELDS = {
1128
+ EC: ["crv", "x", "y"],
1129
+ OKP: ["crv", "x"],
1130
+ RSA: ["n", "e"]
1131
+ };
1132
+ function validatePublicJwk(jwk) {
1133
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
1134
+ return { valid: false, error: "JWK must be a non-null object" };
1135
+ }
1136
+ const obj = jwk;
1137
+ const kty = obj.kty;
1138
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
1139
+ return {
1140
+ valid: false,
1141
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
1142
+ };
1143
+ }
1144
+ for (const field of PRIVATE_KEY_FIELDS) {
1145
+ if (field in obj) {
1146
+ return {
1147
+ valid: false,
1148
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
1149
+ };
1150
+ }
1151
+ }
1152
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
1153
+ if (required) {
1154
+ for (const field of required) {
1155
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
1156
+ return {
1157
+ valid: false,
1158
+ error: `Missing required public key field "${field}" for kty="${kty}"`
1159
+ };
1160
+ }
1161
+ }
1162
+ }
1163
+ return { valid: true };
1164
+ }
1165
+ function canonicalizeJwkJson(jwk) {
1166
+ const sorted = Object.keys(jwk).sort();
1167
+ const obj = {};
1168
+ for (const key of sorted) {
1169
+ obj[key] = jwk[key];
1170
+ }
1171
+ return JSON.stringify(obj);
1172
+ }
1173
+ function jwkToDidJwk(jwk) {
1174
+ assertObject(jwk, "jwk", "INVALID_JWK");
1175
+ const validation = validatePublicJwk(jwk);
1176
+ if (!validation.valid) {
1177
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
1178
+ }
1179
+ const canonical = canonicalizeJwkJson(jwk);
1180
+ const encoded = base64url.encode(new TextEncoder().encode(canonical));
1181
+ return `did:jwk:${encoded}`;
1182
+ }
1183
+ function didJwkToJwk(didJwk) {
1184
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
1185
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
1186
+ }
1187
+ const parts = didJwk.split(":");
1188
+ if (parts.length !== 3) {
1189
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
1190
+ input: didJwk
1191
+ });
1192
+ }
1193
+ const encoded = parts[2];
1194
+ if (!encoded || encoded.length === 0) {
1195
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
1196
+ input: didJwk
1197
+ });
1198
+ }
1199
+ let decoded;
1200
+ try {
1201
+ const bytes = base64url.decode(encoded);
1202
+ decoded = new TextDecoder().decode(bytes);
1203
+ } catch {
1204
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
1205
+ input: didJwk
1206
+ });
1207
+ }
1208
+ let jwk;
1209
+ try {
1210
+ jwk = JSON.parse(decoded);
1211
+ } catch {
1212
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
1213
+ input: didJwk
1214
+ });
1215
+ }
1216
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
1217
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
1218
+ input: didJwk
1219
+ });
1220
+ }
1221
+ const validation = validatePublicJwk(jwk);
1222
+ if (!validation.valid) {
1223
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
1224
+ input: didJwk
1225
+ });
1226
+ }
1227
+ return jwk;
1228
+ }
1229
+ function extractPublicKeyFields(jwk) {
1230
+ const result = {};
1231
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
1232
+ for (const [key, value] of Object.entries(jwk)) {
1233
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
1234
+ if (metadataFields.has(key)) continue;
1235
+ result[key] = value;
1236
+ }
1237
+ return result;
1238
+ }
1239
+ function publicJwkEquals(a, b) {
1240
+ assertObject(a, "a", "INVALID_JWK");
1241
+ assertObject(b, "b", "INVALID_JWK");
1242
+ const aObj = a;
1243
+ const bObj = b;
1244
+ for (const field of PRIVATE_KEY_FIELDS) {
1245
+ if (field in aObj) {
1246
+ throw new OmaTrustError(
1247
+ "INVALID_JWK",
1248
+ `First JWK contains private key field "${field}"`,
1249
+ { field }
1250
+ );
1251
+ }
1252
+ if (field in bObj) {
1253
+ throw new OmaTrustError(
1254
+ "INVALID_JWK",
1255
+ `Second JWK contains private key field "${field}"`,
1256
+ { field }
1257
+ );
1258
+ }
1259
+ }
1260
+ const aPublic = extractPublicKeyFields(aObj);
1261
+ const bPublic = extractPublicKeyFields(bObj);
1262
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
1263
+ }
1264
+
1265
+ // src/shared/did-document.ts
811
1266
  async function fetchDidDocument(domain) {
812
1267
  const normalized = domain.toLowerCase().replace(/\.$/, "");
813
1268
  const url = `https://${normalized}/.well-known/did.json`;
@@ -818,15 +1273,16 @@ async function fetchDidDocument(domain) {
818
1273
  throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
819
1274
  }
820
1275
  if (!response.ok) {
821
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1276
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
822
1277
  domain,
823
1278
  status: response.status
824
1279
  });
825
1280
  }
826
- const body = await response.json();
827
- return body;
1281
+ return await response.json();
828
1282
  }
829
- function extractAddressesFromDidDocument(didDocument) {
1283
+
1284
+ // src/reputation/proof/did-json.ts
1285
+ function extractEvmAddressesFromDidDocument(didDocument) {
830
1286
  const methods = didDocument.verificationMethod;
831
1287
  if (!Array.isArray(methods)) {
832
1288
  return [];
@@ -850,14 +1306,52 @@ function extractAddressesFromDidDocument(didDocument) {
850
1306
  }
851
1307
  return [...addresses];
852
1308
  }
1309
+ function extractJwksFromDidDocument(didDocument) {
1310
+ const methods = didDocument.verificationMethod;
1311
+ if (!Array.isArray(methods)) {
1312
+ return [];
1313
+ }
1314
+ const jwks = [];
1315
+ for (const method of methods) {
1316
+ const publicKeyJwk = method.publicKeyJwk;
1317
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
1318
+ jwks.push(publicKeyJwk);
1319
+ }
1320
+ }
1321
+ return jwks;
1322
+ }
853
1323
  function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1324
+ const method = extractDidMethod(expectedControllerDid);
1325
+ if (method === "jwk") {
1326
+ try {
1327
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
1328
+ const documentJwks = extractJwksFromDidDocument(didDocument);
1329
+ for (const docJwk of documentJwks) {
1330
+ try {
1331
+ if (publicJwkEquals(docJwk, expectedJwk)) {
1332
+ return { valid: true };
1333
+ }
1334
+ } catch {
1335
+ }
1336
+ }
1337
+ return {
1338
+ valid: false,
1339
+ reason: "No matching publicKeyJwk found in DID document verification methods"
1340
+ };
1341
+ } catch {
1342
+ return {
1343
+ valid: false,
1344
+ reason: "Failed to decode did:jwk controller DID"
1345
+ };
1346
+ }
1347
+ }
854
1348
  let expectedAddress;
855
1349
  try {
856
1350
  expectedAddress = getAddress(extractAddressFromDid(expectedControllerDid));
857
1351
  } catch {
858
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
1352
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
859
1353
  }
860
- const addresses = extractAddressesFromDidDocument(didDocument);
1354
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
861
1355
  if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
862
1356
  return { valid: true };
863
1357
  }
@@ -866,6 +1360,13 @@ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
866
1360
  reason: `No matching address found in DID document (expected ${expectedAddress})`
867
1361
  };
868
1362
  }
1363
+ async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
1364
+ if (!domain || typeof domain !== "string") {
1365
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
1366
+ }
1367
+ const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
1368
+ return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
1369
+ }
869
1370
  function buildEip712Domain(name, version, chainId, verifyingContract) {
870
1371
  return { name, version, chainId, verifyingContract };
871
1372
  }
@@ -898,53 +1399,485 @@ function verifyEip712Signature(typedData, signature) {
898
1399
  throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
899
1400
  }
900
1401
  }
1402
+
1403
+ // src/reputation/proof/dns-txt-record.ts
901
1404
  function parseDnsTxtRecord(record) {
902
1405
  if (!record || typeof record !== "string") {
903
1406
  throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
904
1407
  }
905
1408
  const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
906
1409
  const parsed = {};
1410
+ const controllers = [];
907
1411
  for (const entry of entries) {
908
- const [key, ...valueParts] = entry.split("=");
909
- if (!key) {
910
- continue;
911
- }
912
- const value = valueParts.join("=");
913
- if (!value) {
914
- continue;
1412
+ const eqIndex = entry.indexOf("=");
1413
+ if (eqIndex === -1) continue;
1414
+ const key = entry.slice(0, eqIndex).trim();
1415
+ const value = entry.slice(eqIndex + 1).trim();
1416
+ if (!key || !value) continue;
1417
+ if (key === "controller") {
1418
+ controllers.push(value);
1419
+ } else {
1420
+ parsed[key] = value;
915
1421
  }
916
- parsed[key.trim()] = value.trim();
917
1422
  }
918
1423
  return {
919
1424
  version: parsed.v,
920
- controller: parsed.controller,
921
- ...parsed
1425
+ controller: controllers[0],
1426
+ controllers
922
1427
  };
923
1428
  }
924
1429
  function buildDnsTxtRecord(controllerDid) {
925
1430
  const normalized = normalizeDid(controllerDid);
926
1431
  return `v=1;controller=${normalized}`;
927
1432
  }
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 });
1433
+
1434
+ // src/identity/did-url.ts
1435
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
1436
+ function parseDidUrl(input) {
1437
+ assertString(input, "input", "INVALID_DID_URL");
1438
+ const trimmed = input.trim();
1439
+ const hashIndex = trimmed.indexOf("#");
1440
+ let did;
1441
+ let fragment;
1442
+ if (hashIndex === -1) {
1443
+ did = trimmed;
1444
+ fragment = null;
1445
+ } else {
1446
+ did = trimmed.slice(0, hashIndex);
1447
+ const rawFragment = trimmed.slice(hashIndex + 1);
1448
+ if (rawFragment.length === 0) {
1449
+ throw new OmaTrustError(
1450
+ "INVALID_DID_URL",
1451
+ "DID URL has empty fragment (trailing '#' with no identifier)",
1452
+ { input }
1453
+ );
1454
+ }
1455
+ fragment = rawFragment;
931
1456
  }
932
- const expected = normalizeDid(expectedControllerDid);
933
- const host = `_omatrust.${domain.toLowerCase().replace(/\.$/, "")}`;
934
- let records;
1457
+ if (!DID_URL_REGEX.test(did)) {
1458
+ throw new OmaTrustError(
1459
+ "INVALID_DID_URL",
1460
+ "Invalid DID URL: base DID portion is malformed",
1461
+ { input, did }
1462
+ );
1463
+ }
1464
+ return {
1465
+ didUrl: trimmed,
1466
+ did,
1467
+ fragment
1468
+ };
1469
+ }
1470
+
1471
+ // src/identity/resolve-key.ts
1472
+ function findVerificationMethod(didDocument, didUrl, fragment) {
1473
+ const methods = didDocument.verificationMethod;
1474
+ if (!Array.isArray(methods)) {
1475
+ return null;
1476
+ }
1477
+ for (const method of methods) {
1478
+ if (!method || typeof method !== "object") continue;
1479
+ const id = method.id;
1480
+ if (typeof id !== "string") continue;
1481
+ if (id === didUrl) return method;
1482
+ if (fragment && (id === `#${fragment}` || id.endsWith(`#${fragment}`))) {
1483
+ return method;
1484
+ }
1485
+ }
1486
+ return null;
1487
+ }
1488
+ function extractMethodIdsFromDidDocument(didDocument) {
1489
+ const methods = didDocument.verificationMethod;
1490
+ if (!Array.isArray(methods)) return [];
1491
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
1492
+ }
1493
+ async function resolveDidUrlToPublicKey(didUrl, options) {
1494
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
1495
+ const parsed = parseDidUrl(didUrl);
1496
+ const method = extractDidMethod(parsed.did);
1497
+ if (!method) {
1498
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
1499
+ didUrl,
1500
+ did: parsed.did
1501
+ });
1502
+ }
1503
+ switch (method) {
1504
+ case "web":
1505
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment, options);
1506
+ default:
1507
+ throw new OmaTrustError(
1508
+ "UNSUPPORTED_DID_METHOD",
1509
+ `DID URL key resolution is not supported for method "${method}"`,
1510
+ { didUrl, method }
1511
+ );
1512
+ }
1513
+ }
1514
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
1515
+ const domain = getDomainFromDidWeb(did);
1516
+ if (!domain) {
1517
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
1518
+ didUrl,
1519
+ did
1520
+ });
1521
+ }
1522
+ const fetchFn = options?.fetchDidDocument ?? fetchDidDocument;
1523
+ const didDocument = await fetchFn(domain);
1524
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
1525
+ if (!method) {
1526
+ throw new OmaTrustError(
1527
+ "KEY_NOT_FOUND",
1528
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
1529
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
1530
+ );
1531
+ }
1532
+ const publicKeyJwk = method.publicKeyJwk;
1533
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
1534
+ throw new OmaTrustError(
1535
+ "KEY_NOT_FOUND",
1536
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
1537
+ { didUrl, verificationMethodId: method.id }
1538
+ );
1539
+ }
1540
+ const validation = validatePublicJwk(publicKeyJwk);
1541
+ if (!validation.valid) {
1542
+ throw new OmaTrustError(
1543
+ "INVALID_JWK",
1544
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
1545
+ { didUrl, verificationMethodId: method.id }
1546
+ );
1547
+ }
1548
+ return {
1549
+ didUrl,
1550
+ did,
1551
+ fragment,
1552
+ publicKeyJwk,
1553
+ verificationMethodId: method.id
1554
+ };
1555
+ }
1556
+
1557
+ // src/reputation/proof/x402-jws.ts
1558
+ var REQUIRED_OFFER_FIELDS = [
1559
+ "version",
1560
+ "resourceUrl",
1561
+ "scheme",
1562
+ "network",
1563
+ "asset",
1564
+ "payTo",
1565
+ "amount"
1566
+ ];
1567
+ var REQUIRED_RECEIPT_FIELDS = [
1568
+ "version",
1569
+ "network",
1570
+ "resourceUrl",
1571
+ "payer",
1572
+ "issuedAt"
1573
+ ];
1574
+ function validateOfferPayload(payload) {
1575
+ for (const field of REQUIRED_OFFER_FIELDS) {
1576
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1577
+ return { valid: false, field };
1578
+ }
1579
+ }
1580
+ return { valid: true };
1581
+ }
1582
+ function validateReceiptPayload(payload) {
1583
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
1584
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1585
+ return { valid: false, field };
1586
+ }
1587
+ }
1588
+ return { valid: true };
1589
+ }
1590
+ function failure(code, message, partial) {
1591
+ return {
1592
+ valid: false,
1593
+ error: { code, message },
1594
+ ...partial
1595
+ };
1596
+ }
1597
+ async function verifyX402JwsArtifact(artifact, options) {
1598
+ if (!artifact || typeof artifact !== "object") {
1599
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
1600
+ }
1601
+ if (artifact.format !== "jws") {
1602
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
1603
+ }
1604
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1605
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
1606
+ }
1607
+ const compactJws = artifact.signature;
1608
+ const parts = compactJws.split(".");
1609
+ if (parts.length !== 3) {
1610
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
1611
+ }
1612
+ let header;
935
1613
  try {
936
- records = await resolveTxt(host);
1614
+ header = decodeProtectedHeader(compactJws);
1615
+ } catch {
1616
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
1617
+ }
1618
+ if (!header.alg || typeof header.alg !== "string") {
1619
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
1620
+ }
1621
+ const kid = typeof header.kid === "string" ? header.kid : null;
1622
+ const embeddedJwk = header.jwk;
1623
+ if (!kid && !embeddedJwk) {
1624
+ return failure(
1625
+ "MISSING_KEY_MATERIAL",
1626
+ "JWS header must include at least one of kid or jwk",
1627
+ { header }
1628
+ );
1629
+ }
1630
+ let publicKeyJwk;
1631
+ let publicKeySource;
1632
+ if (embeddedJwk) {
1633
+ const validation = validatePublicJwk(embeddedJwk);
1634
+ if (!validation.valid) {
1635
+ return failure(
1636
+ "INVALID_EMBEDDED_JWK",
1637
+ `Embedded JWK is invalid: ${validation.error}`,
1638
+ { header }
1639
+ );
1640
+ }
1641
+ publicKeyJwk = embeddedJwk;
1642
+ publicKeySource = "embedded-jwk";
1643
+ if (kid) {
1644
+ try {
1645
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1646
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
1647
+ return failure(
1648
+ "KEY_CONFLICT",
1649
+ "Embedded jwk conflicts with public key resolved from kid",
1650
+ { header, kid }
1651
+ );
1652
+ }
1653
+ } catch (err) {
1654
+ }
1655
+ }
1656
+ } else {
1657
+ publicKeySource = "kid-resolution";
1658
+ try {
1659
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1660
+ publicKeyJwk = resolved.publicKeyJwk;
1661
+ } catch (err) {
1662
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
1663
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
1664
+ }
1665
+ }
1666
+ let payload;
1667
+ try {
1668
+ const key = await importJWK(publicKeyJwk, header.alg);
1669
+ const result = await compactVerify(compactJws, key);
1670
+ const payloadText = new TextDecoder().decode(result.payload);
1671
+ payload = JSON.parse(payloadText);
937
1672
  } catch (err) {
938
- throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
1673
+ const message = err instanceof Error ? err.message : "Signature verification failed";
1674
+ return failure("SIGNATURE_INVALID", message, { header, kid });
939
1675
  }
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 };
1676
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
1677
+ return {
1678
+ valid: true,
1679
+ header,
1680
+ payload,
1681
+ kid,
1682
+ publicKeyJwk,
1683
+ publicKeySource,
1684
+ publicKeyDid
1685
+ };
1686
+ }
1687
+ async function verifyX402JwsOffer(artifact, options) {
1688
+ const result = await verifyX402JwsArtifact(artifact, options);
1689
+ if (!result.valid) return result;
1690
+ const payloadCheck = validateOfferPayload(result.payload);
1691
+ if (!payloadCheck.valid) {
1692
+ return failure(
1693
+ "INVALID_OFFER_PAYLOAD",
1694
+ `Missing required offer field: ${payloadCheck.field}`,
1695
+ {
1696
+ header: result.header,
1697
+ payload: result.payload,
1698
+ kid: result.kid,
1699
+ publicKeyJwk: result.publicKeyJwk,
1700
+ publicKeySource: result.publicKeySource,
1701
+ publicKeyDid: result.publicKeyDid
1702
+ }
1703
+ );
1704
+ }
1705
+ return result;
1706
+ }
1707
+ async function verifyX402JwsReceipt(artifact, options) {
1708
+ const result = await verifyX402JwsArtifact(artifact, options);
1709
+ if (!result.valid) return result;
1710
+ const payloadCheck = validateReceiptPayload(result.payload);
1711
+ if (!payloadCheck.valid) {
1712
+ return failure(
1713
+ "INVALID_RECEIPT_PAYLOAD",
1714
+ `Missing required receipt field: ${payloadCheck.field}`,
1715
+ {
1716
+ header: result.header,
1717
+ payload: result.payload,
1718
+ kid: result.kid,
1719
+ publicKeyJwk: result.publicKeyJwk,
1720
+ publicKeySource: result.publicKeySource,
1721
+ publicKeyDid: result.publicKeyDid
1722
+ }
1723
+ );
1724
+ }
1725
+ return result;
1726
+ }
1727
+ var OFFER_DOMAIN = {
1728
+ name: "x402 offer",
1729
+ version: "1",
1730
+ chainId: 1
1731
+ };
1732
+ var RECEIPT_DOMAIN = {
1733
+ name: "x402 receipt",
1734
+ version: "1",
1735
+ chainId: 1
1736
+ };
1737
+ var OFFER_TYPES = {
1738
+ Offer: [
1739
+ { name: "version", type: "uint256" },
1740
+ { name: "resourceUrl", type: "string" },
1741
+ { name: "scheme", type: "string" },
1742
+ { name: "network", type: "string" },
1743
+ { name: "asset", type: "string" },
1744
+ { name: "payTo", type: "string" },
1745
+ { name: "amount", type: "string" },
1746
+ { name: "validUntil", type: "uint256" }
1747
+ ]
1748
+ };
1749
+ var RECEIPT_TYPES = {
1750
+ Receipt: [
1751
+ { name: "version", type: "uint256" },
1752
+ { name: "network", type: "string" },
1753
+ { name: "resourceUrl", type: "string" },
1754
+ { name: "payer", type: "string" },
1755
+ { name: "issuedAt", type: "uint256" },
1756
+ { name: "transaction", type: "string" }
1757
+ ]
1758
+ };
1759
+ var REQUIRED_OFFER_FIELDS2 = [
1760
+ "version",
1761
+ "resourceUrl",
1762
+ "scheme",
1763
+ "network",
1764
+ "asset",
1765
+ "payTo",
1766
+ "amount"
1767
+ ];
1768
+ var REQUIRED_RECEIPT_FIELDS2 = [
1769
+ "version",
1770
+ "network",
1771
+ "resourceUrl",
1772
+ "payer",
1773
+ "issuedAt"
1774
+ ];
1775
+ function validateOfferPayload2(payload) {
1776
+ for (const field of REQUIRED_OFFER_FIELDS2) {
1777
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1778
+ return { valid: false, field };
1779
+ }
1780
+ }
1781
+ return { valid: true };
1782
+ }
1783
+ function validateReceiptPayload2(payload) {
1784
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
1785
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1786
+ return { valid: false, field };
945
1787
  }
946
1788
  }
947
- return { valid: false, reason: "No TXT record matched expected controller DID" };
1789
+ return { valid: true };
1790
+ }
1791
+ function failure2(code, message, partial) {
1792
+ return {
1793
+ valid: false,
1794
+ error: { code, message },
1795
+ ...partial
1796
+ };
1797
+ }
1798
+ function prepareOfferMessage(payload) {
1799
+ return {
1800
+ version: payload.version,
1801
+ resourceUrl: payload.resourceUrl,
1802
+ scheme: payload.scheme,
1803
+ network: payload.network,
1804
+ asset: payload.asset,
1805
+ payTo: payload.payTo,
1806
+ amount: payload.amount,
1807
+ validUntil: payload.validUntil ?? 0
1808
+ };
1809
+ }
1810
+ function prepareReceiptMessage(payload) {
1811
+ return {
1812
+ version: payload.version,
1813
+ network: payload.network,
1814
+ resourceUrl: payload.resourceUrl,
1815
+ payer: payload.payer,
1816
+ issuedAt: payload.issuedAt,
1817
+ transaction: payload.transaction ?? ""
1818
+ };
1819
+ }
1820
+ function verifyX402Eip712Artifact(artifact, artifactType) {
1821
+ if (!artifact || typeof artifact !== "object") {
1822
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
1823
+ }
1824
+ if (artifact.format !== "eip712") {
1825
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
1826
+ }
1827
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
1828
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
1829
+ }
1830
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1831
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
1832
+ }
1833
+ const payload = artifact.payload;
1834
+ if (artifactType === "offer") {
1835
+ const check = validateOfferPayload2(payload);
1836
+ if (!check.valid) {
1837
+ return failure2(
1838
+ "INVALID_OFFER_PAYLOAD",
1839
+ `Missing required offer field: ${check.field}`,
1840
+ { payload }
1841
+ );
1842
+ }
1843
+ } else {
1844
+ const check = validateReceiptPayload2(payload);
1845
+ if (!check.valid) {
1846
+ return failure2(
1847
+ "INVALID_RECEIPT_PAYLOAD",
1848
+ `Missing required receipt field: ${check.field}`,
1849
+ { payload }
1850
+ );
1851
+ }
1852
+ }
1853
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
1854
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
1855
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
1856
+ let signer;
1857
+ try {
1858
+ signer = verifyTypedData(
1859
+ domain,
1860
+ types,
1861
+ message,
1862
+ artifact.signature
1863
+ );
1864
+ signer = getAddress(signer);
1865
+ } catch (err) {
1866
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
1867
+ return failure2("SIGNATURE_INVALID", message2, { payload });
1868
+ }
1869
+ return {
1870
+ valid: true,
1871
+ payload,
1872
+ signer,
1873
+ artifactType
1874
+ };
1875
+ }
1876
+ function verifyX402Eip712Offer(artifact) {
1877
+ return verifyX402Eip712Artifact(artifact, "offer");
1878
+ }
1879
+ function verifyX402Eip712Receipt(artifact) {
1880
+ return verifyX402Eip712Artifact(artifact, "receipt");
948
1881
  }
949
1882
 
950
1883
  // src/reputation/verify.ts
@@ -1004,18 +1937,18 @@ function decodeJwtPayload(compactJws) {
1004
1937
  return JSON.parse(json);
1005
1938
  }
1006
1939
  async function verifyProof(params) {
1007
- const { proof, provider, expectedSubject, expectedController } = params;
1940
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1008
1941
  try {
1009
1942
  switch (proof.proofType) {
1010
1943
  case "tx-encoded-value": {
1011
1944
  if (!provider) {
1012
1945
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1013
1946
  }
1014
- if (!expectedSubject || !expectedController) {
1947
+ if (!expectedSubjectDid || !expectedControllerDid) {
1015
1948
  return {
1016
1949
  valid: false,
1017
1950
  proofType: proof.proofType,
1018
- reason: "expectedSubject and expectedController are required"
1951
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1019
1952
  };
1020
1953
  }
1021
1954
  const proofObject = proof.proofObject;
@@ -1027,13 +1960,13 @@ async function verifyProof(params) {
1027
1960
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1028
1961
  }
1029
1962
  const expectedAmount = calculateTransferAmount(
1030
- expectedSubject,
1031
- expectedController,
1963
+ expectedSubjectDid,
1964
+ expectedControllerDid,
1032
1965
  chainId,
1033
1966
  getProofPurpose(proof)
1034
1967
  );
1035
- const subjectAddress = getAddress(extractAddressFromDid(expectedSubject));
1036
- const controllerAddress = getAddress(extractAddressFromDid(expectedController));
1968
+ const subjectAddress = getAddress(extractAddressFromDid(expectedSubjectDid));
1969
+ const controllerAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1037
1970
  if (tx.from && getAddress(tx.from) !== subjectAddress) {
1038
1971
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1039
1972
  }
@@ -1107,6 +2040,38 @@ async function verifyProof(params) {
1107
2040
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1108
2041
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1109
2042
  }
2043
+ const proofObj = proof.proofObject;
2044
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2045
+ const artifact = {
2046
+ format: "jws",
2047
+ signature: proofObj.signature
2048
+ };
2049
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2050
+ if (!jwsResult.valid) {
2051
+ return {
2052
+ valid: false,
2053
+ proofType: proof.proofType,
2054
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2055
+ };
2056
+ }
2057
+ return { valid: true, proofType: proof.proofType };
2058
+ }
2059
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2060
+ const artifact = {
2061
+ format: "eip712",
2062
+ payload: proofObj.payload,
2063
+ signature: proofObj.signature
2064
+ };
2065
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2066
+ if (!eip712Result.valid) {
2067
+ return {
2068
+ valid: false,
2069
+ proofType: proof.proofType,
2070
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2071
+ };
2072
+ }
2073
+ return { valid: true, proofType: proof.proofType };
2074
+ }
1110
2075
  return { valid: true, proofType: proof.proofType };
1111
2076
  }
1112
2077
  case "evidence-pointer": {
@@ -1118,9 +2083,9 @@ async function verifyProof(params) {
1118
2083
  if (!response.ok) {
1119
2084
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1120
2085
  }
1121
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2086
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1122
2087
  const didDoc = await response.json();
1123
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2088
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1124
2089
  return {
1125
2090
  valid: didCheck.valid,
1126
2091
  proofType: proof.proofType,
@@ -1128,10 +2093,10 @@ async function verifyProof(params) {
1128
2093
  };
1129
2094
  }
1130
2095
  const body = await response.text();
1131
- if (expectedController && !body.includes(expectedController)) {
2096
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1132
2097
  try {
1133
2098
  const parsed = parseDnsTxtRecord(body);
1134
- if (parsed.controller !== expectedController) {
2099
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1135
2100
  return {
1136
2101
  valid: false,
1137
2102
  proofType: proof.proofType,
@@ -1187,8 +2152,8 @@ async function verifyAttestation(params) {
1187
2152
  const result = await verifyProof({
1188
2153
  proof,
1189
2154
  provider: params.provider,
1190
- expectedSubject: params.context?.subject,
1191
- expectedController: params.context?.controller
2155
+ expectedSubjectDid: params.context?.subjectDid,
2156
+ expectedControllerDid: params.context?.controllerDid
1192
2157
  });
1193
2158
  checks[proof.proofType] = result.valid;
1194
2159
  if (!result.valid) {
@@ -1226,6 +2191,9 @@ async function getSchemaDetails(schemaRegistry, schemaUid) {
1226
2191
  revocable: Boolean(details.revocable)
1227
2192
  };
1228
2193
  } catch (err) {
2194
+ if (isEasSchemaNotFoundError(err)) {
2195
+ throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
2196
+ }
1229
2197
  if (err instanceof OmaTrustError) {
1230
2198
  throw err;
1231
2199
  }
@@ -1288,6 +2256,504 @@ async function callControllerWitness(params) {
1288
2256
  method: "did-json"
1289
2257
  };
1290
2258
  }
2259
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
2260
+ async function requestControllerWitness(params) {
2261
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
2262
+ const body = {
2263
+ subjectDid: params.subjectDid,
2264
+ controllerDid: params.controllerDid
2265
+ };
2266
+ if (params.chainId !== void 0) {
2267
+ body.chainId = params.chainId;
2268
+ }
2269
+ let response;
2270
+ try {
2271
+ response = await fetch(url, {
2272
+ method: "POST",
2273
+ headers: { "Content-Type": "application/json" },
2274
+ credentials: "include",
2275
+ body: JSON.stringify(body),
2276
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
2277
+ });
2278
+ } catch (err) {
2279
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
2280
+ }
2281
+ if (!response.ok) {
2282
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
2283
+ throw new OmaTrustError(
2284
+ "API_ERROR",
2285
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
2286
+ { status: response.status, ...errorBody }
2287
+ );
2288
+ }
2289
+ return await response.json();
2290
+ }
2291
+ var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
2292
+ var OWNERSHIP_PATTERNS = [
2293
+ { method: "owner", signature: "function owner() view returns (address)" },
2294
+ { method: "admin", signature: "function admin() view returns (address)" },
2295
+ { method: "getOwner", signature: "function getOwner() view returns (address)" }
2296
+ ];
2297
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
2298
+ try {
2299
+ const iface = new Interface([signature]);
2300
+ const data = iface.encodeFunctionData(method, []);
2301
+ const result = await provider.call({ to: contractAddress, data });
2302
+ const [value] = iface.decodeFunctionResult(method, result);
2303
+ if (typeof value === "string" && isAddress(value) && value !== ZeroAddress) {
2304
+ return getAddress(value);
2305
+ }
2306
+ } catch {
2307
+ }
2308
+ return null;
2309
+ }
2310
+ async function discoverContractOwner(provider, contractAddress) {
2311
+ try {
2312
+ const code = await provider.getCode(contractAddress);
2313
+ if (code === "0x" || code === "0x0") {
2314
+ return null;
2315
+ }
2316
+ } catch {
2317
+ return null;
2318
+ }
2319
+ for (const pattern of OWNERSHIP_PATTERNS) {
2320
+ const address = await readOwnerFromContract(
2321
+ provider,
2322
+ contractAddress,
2323
+ pattern.signature,
2324
+ pattern.method
2325
+ );
2326
+ if (address) {
2327
+ return address;
2328
+ }
2329
+ }
2330
+ try {
2331
+ const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
2332
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
2333
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
2334
+ if (adminAddress !== ZeroAddress) {
2335
+ return adminAddress;
2336
+ }
2337
+ }
2338
+ } catch {
2339
+ }
2340
+ return null;
2341
+ }
2342
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
2343
+ const owner = await discoverContractOwner(provider, contractAddress);
2344
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
2345
+ }
2346
+ async function verifyTransferProof(provider, txHash, subjectDid, attesterAddress, chainId) {
2347
+ try {
2348
+ const tx = await provider.getTransaction(txHash);
2349
+ if (!tx || !tx.from || !tx.to) {
2350
+ return false;
2351
+ }
2352
+ if (getAddress(tx.to) !== getAddress(attesterAddress)) {
2353
+ return false;
2354
+ }
2355
+ const attesterDid = buildEvmDidPkh(chainId, attesterAddress);
2356
+ const expectedAmount = calculateTransferAmount(
2357
+ subjectDid,
2358
+ attesterDid,
2359
+ chainId,
2360
+ "shared-control"
2361
+ );
2362
+ const actualValue = BigInt(tx.value ?? 0n);
2363
+ return actualValue === expectedAmount;
2364
+ } catch {
2365
+ return false;
2366
+ }
2367
+ }
2368
+
2369
+ // src/identity/controller-id.ts
2370
+ function isSameControllerId(a, b) {
2371
+ let normalizedA = null;
2372
+ let normalizedB = null;
2373
+ try {
2374
+ normalizedA = normalizeDid(a);
2375
+ } catch {
2376
+ }
2377
+ try {
2378
+ normalizedB = normalizeDid(b);
2379
+ } catch {
2380
+ }
2381
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
2382
+ return true;
2383
+ }
2384
+ const evmA = extractControllerEvmAddress(a);
2385
+ const evmB = extractControllerEvmAddress(b);
2386
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
2387
+ return true;
2388
+ }
2389
+ const methodA = extractDidMethod(a);
2390
+ const methodB = extractDidMethod(b);
2391
+ if (methodA === "jwk" && methodB === "jwk") {
2392
+ try {
2393
+ const jwkA = didJwkToJwk(a);
2394
+ const jwkB = didJwkToJwk(b);
2395
+ return publicJwkEquals(jwkA, jwkB);
2396
+ } catch {
2397
+ return false;
2398
+ }
2399
+ }
2400
+ return false;
2401
+ }
2402
+ function extractControllerEvmAddress(controllerDid) {
2403
+ try {
2404
+ const method = extractDidMethod(controllerDid);
2405
+ if (method === "pkh" && controllerDid.includes("eip155")) {
2406
+ return extractAddressFromDid(controllerDid);
2407
+ }
2408
+ } catch {
2409
+ }
2410
+ return null;
2411
+ }
2412
+
2413
+ // src/shared/trust-anchors.ts
2414
+ var DEFAULT_TRUST_ANCHORS_URL = "https://api.omatrust.org/v1/trust-anchors";
2415
+ var TRUST_ANCHORS_URL = DEFAULT_TRUST_ANCHORS_URL;
2416
+ var cachedAnchors = null;
2417
+ var cacheTimestamp = 0;
2418
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
2419
+ async function fetchTrustAnchors(url) {
2420
+ const now = Date.now();
2421
+ if (cachedAnchors && now - cacheTimestamp < CACHE_TTL_MS) {
2422
+ return cachedAnchors;
2423
+ }
2424
+ let res;
2425
+ try {
2426
+ res = await fetch(url ?? TRUST_ANCHORS_URL);
2427
+ } catch (err) {
2428
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch trust anchors", { err });
2429
+ }
2430
+ if (!res.ok) {
2431
+ throw new OmaTrustError("NETWORK_ERROR", `Failed to fetch trust anchors: ${res.status} ${res.statusText}`);
2432
+ }
2433
+ const anchors = await res.json();
2434
+ if (!anchors.version || !anchors.chains || typeof anchors.chains !== "object") {
2435
+ throw new OmaTrustError("INVALID_INPUT", "Invalid trust anchors format");
2436
+ }
2437
+ cachedAnchors = anchors;
2438
+ cacheTimestamp = now;
2439
+ return anchors;
2440
+ }
2441
+ function getChainAnchors(anchors, caip2) {
2442
+ const chain = anchors.chains[caip2];
2443
+ if (!chain) {
2444
+ throw new OmaTrustError("UNSUPPORTED_CHAIN", `Chain ${caip2} is not in the trust anchors`, {
2445
+ caip2,
2446
+ supportedChains: Object.keys(anchors.chains)
2447
+ });
2448
+ }
2449
+ return chain;
2450
+ }
2451
+
2452
+ // src/reputation/proof/dns-txt-shared.ts
2453
+ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
2454
+ if (!domain || typeof domain !== "string") {
2455
+ throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
2456
+ }
2457
+ if (!options.resolveTxt) {
2458
+ throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
2459
+ }
2460
+ const prefix = options.recordPrefix ?? "_controllers";
2461
+ const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
2462
+ let records;
2463
+ try {
2464
+ records = await options.resolveTxt(host);
2465
+ } catch (err) {
2466
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
2467
+ }
2468
+ for (const recordParts of records) {
2469
+ const record = recordParts.join("");
2470
+ const parsed = parseDnsTxtRecord(record);
2471
+ if (parsed.version !== "1") continue;
2472
+ for (const recordController of parsed.controllers) {
2473
+ if (isSameControllerId(recordController, expectedControllerDid)) {
2474
+ return { valid: true, record };
2475
+ }
2476
+ }
2477
+ }
2478
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
2479
+ }
2480
+
2481
+ // src/reputation/attester-authorization.ts
2482
+ var DEFAULT_CHAIN = "eip155:6623";
2483
+ var DEFAULT_PURPOSES = ["authentication", "assertionMethod"];
2484
+ async function getControllerAuthorization(params) {
2485
+ const chain = params.chain ?? DEFAULT_CHAIN;
2486
+ const purposes = params.purpose && params.purpose.length > 0 ? params.purpose : DEFAULT_PURPOSES;
2487
+ const controllerDid = normalizeDid(params.controllerDid);
2488
+ const controllerMethod = extractDidMethod(controllerDid);
2489
+ const controllerEvmAddress = extractControllerEvmAddress(controllerDid);
2490
+ const anchors = await fetchTrustAnchors();
2491
+ const chainAnchors = getChainAnchors(anchors, chain);
2492
+ const easContractAddress = params.easContractAddress ?? chainAnchors.easContract;
2493
+ const provider = params.provider;
2494
+ const controllerWitnessSchemaUid = chainAnchors.schemas["controller-witness"];
2495
+ const keyBindingSchemaUid = chainAnchors.schemas["key-binding"];
2496
+ const subjectAddress = didToAddress(params.subjectDid);
2497
+ let relevantWitnesses = [];
2498
+ if (controllerWitnessSchemaUid) {
2499
+ const rawWitnesses = await queryByRecipientAndSchema(
2500
+ easContractAddress,
2501
+ provider,
2502
+ subjectAddress,
2503
+ [controllerWitnessSchemaUid],
2504
+ params.fromBlock ?? 0
2505
+ );
2506
+ relevantWitnesses = rawWitnesses.filter((att) => {
2507
+ const witnessController = att.data?.controller;
2508
+ if (typeof witnessController !== "string") return false;
2509
+ return isSameControllerId(witnessController, controllerDid);
2510
+ });
2511
+ relevantWitnesses.sort((a, b) => Number(a.time - b.time));
2512
+ }
2513
+ const controllerWitnesses = relevantWitnesses.map((att) => ({
2514
+ uid: att.uid,
2515
+ issuedAt: att.time,
2516
+ attester: att.attester,
2517
+ method: resolveWitnessMethod(att.data?.method)
2518
+ }));
2519
+ let keyBindingUid = null;
2520
+ let until = null;
2521
+ let keyPurposeStatus = "not-required";
2522
+ if (keyBindingSchemaUid) {
2523
+ const keyBindings = await queryByRecipientAndSchema(
2524
+ easContractAddress,
2525
+ provider,
2526
+ subjectAddress,
2527
+ [keyBindingSchemaUid],
2528
+ params.fromBlock ?? 0
2529
+ );
2530
+ const relevantKeyBindings = keyBindings.filter((att) => controllerMatchesKeyBinding(att, controllerDid, controllerMethod)).sort((a, b) => Number(b.time - a.time));
2531
+ const relevantKeyBinding = relevantKeyBindings[0] ?? null;
2532
+ if (relevantKeyBinding) {
2533
+ keyBindingUid = relevantKeyBinding.uid;
2534
+ if (relevantKeyBinding.revocationTime > 0n) {
2535
+ until = relevantKeyBinding.revocationTime;
2536
+ }
2537
+ const keyPurposes = relevantKeyBinding.data?.keyPurpose;
2538
+ if (!Array.isArray(keyPurposes) || keyPurposes.length === 0) {
2539
+ keyPurposeStatus = "unknown";
2540
+ } else {
2541
+ const allMatched = purposes.every((p) => keyPurposes.includes(p));
2542
+ keyPurposeStatus = allMatched ? "matched" : "mismatch";
2543
+ }
2544
+ }
2545
+ }
2546
+ let currentlyVerified = false;
2547
+ let liveMethod = null;
2548
+ let transferProofVerified = false;
2549
+ let transferProofAnchor = null;
2550
+ const subjectMethod = extractDidMethod(params.subjectDid);
2551
+ if (subjectMethod === "web") {
2552
+ const domain = getDomainFromDidWeb(params.subjectDid);
2553
+ if (domain) {
2554
+ try {
2555
+ const dnsResult = await verifyDnsTxtControllerDid(domain, controllerDid, {
2556
+ resolveTxt: params.resolveTxt
2557
+ });
2558
+ if (dnsResult.valid) {
2559
+ currentlyVerified = true;
2560
+ liveMethod = "dns";
2561
+ }
2562
+ } catch {
2563
+ }
2564
+ if (!currentlyVerified) {
2565
+ try {
2566
+ const fetchDoc = params.fetchDidDocument ?? fetchDidDocument;
2567
+ const didDoc = await fetchDoc(domain);
2568
+ const didJsonResult = verifyDidDocumentControllerDid(didDoc, controllerDid);
2569
+ if (didJsonResult.valid) {
2570
+ currentlyVerified = true;
2571
+ liveMethod = "did-document";
2572
+ }
2573
+ } catch {
2574
+ }
2575
+ }
2576
+ }
2577
+ } else if (subjectMethod === "pkh") {
2578
+ if (isEvmDidPkh(params.subjectDid) && controllerEvmAddress) {
2579
+ const subjectContractAddress = getAddressFromDidPkh(params.subjectDid);
2580
+ const subjectChainId = getChainIdFromDidPkh(params.subjectDid);
2581
+ const chainIdNum = subjectChainId ? Number(subjectChainId) : null;
2582
+ if (subjectContractAddress && chainIdNum) {
2583
+ const ownerAddress = await discoverContractOwner(
2584
+ provider,
2585
+ subjectContractAddress
2586
+ );
2587
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(controllerEvmAddress)) {
2588
+ currentlyVerified = true;
2589
+ liveMethod = "contract-ownership";
2590
+ }
2591
+ if (!currentlyVerified && keyBindingUid) {
2592
+ const relevantKeyBinding = await findKeyBindingWithTransferProof(
2593
+ keyBindingSchemaUid,
2594
+ easContractAddress,
2595
+ provider,
2596
+ subjectAddress,
2597
+ controllerDid,
2598
+ controllerMethod,
2599
+ params.fromBlock ?? 0
2600
+ );
2601
+ if (relevantKeyBinding) {
2602
+ const proofTxHash = relevantKeyBinding.data?.proofTxHash;
2603
+ if (proofTxHash && /^0x[0-9a-fA-F]{64}$/.test(proofTxHash)) {
2604
+ const verified = await verifyTransferProof(
2605
+ provider,
2606
+ proofTxHash,
2607
+ params.subjectDid,
2608
+ controllerEvmAddress,
2609
+ chainIdNum
2610
+ );
2611
+ if (verified) {
2612
+ transferProofVerified = true;
2613
+ transferProofAnchor = relevantKeyBinding.time;
2614
+ }
2615
+ }
2616
+ }
2617
+ }
2618
+ }
2619
+ }
2620
+ }
2621
+ const witnessAnchor = relevantWitnesses.length > 0 ? relevantWitnesses[0].time : null;
2622
+ const anchoredFrom = witnessAnchor ?? transferProofAnchor;
2623
+ const purposeBlocks = keyPurposeStatus === "mismatch";
2624
+ const hasOpenWindow = relevantWitnesses.length > 0 && until === null;
2625
+ const hasKeyBindingWithProof = keyBindingUid !== null && transferProofVerified && until === null;
2626
+ const authorized = !purposeBlocks && (hasOpenWindow || currentlyVerified && until === null || hasKeyBindingWithProof);
2627
+ return {
2628
+ authorized,
2629
+ anchoredFrom,
2630
+ until,
2631
+ currentlyVerified,
2632
+ liveMethod,
2633
+ controllerWitnesses,
2634
+ keyBindingUid,
2635
+ keyPurposeStatus,
2636
+ transferProofVerified: transferProofVerified || void 0
2637
+ };
2638
+ }
2639
+ function controllerMatchesKeyBinding(att, controllerDid, controllerMethod) {
2640
+ const keyId = att.data?.keyId;
2641
+ if (typeof keyId === "string") {
2642
+ if (isSameControllerId(keyId, controllerDid)) {
2643
+ return true;
2644
+ }
2645
+ }
2646
+ if (controllerMethod === "jwk") {
2647
+ const publicKeyJwkRaw = att.data?.publicKeyJwk;
2648
+ if (publicKeyJwkRaw) {
2649
+ try {
2650
+ const onChainJwk = typeof publicKeyJwkRaw === "string" ? JSON.parse(publicKeyJwkRaw) : publicKeyJwkRaw;
2651
+ const controllerJwk = didJwkToJwk(controllerDid);
2652
+ return publicJwkEquals(onChainJwk, controllerJwk);
2653
+ } catch {
2654
+ }
2655
+ }
2656
+ }
2657
+ return false;
2658
+ }
2659
+ var EAS_EVENT_ABI2 = [
2660
+ "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
2661
+ ];
2662
+ function resolveWitnessMethod(value) {
2663
+ if (typeof value !== "string") return void 0;
2664
+ switch (value) {
2665
+ case "dns":
2666
+ case "dns-txt":
2667
+ return "dns";
2668
+ case "did-document":
2669
+ case "did-json":
2670
+ return "did-document";
2671
+ case "manual":
2672
+ return "manual";
2673
+ default:
2674
+ return "other";
2675
+ }
2676
+ }
2677
+ async function queryByRecipientAndSchema(easContractAddress, provider, recipient, schemas, fromBlock) {
2678
+ const contract = new Contract(easContractAddress, EAS_EVENT_ABI2, provider);
2679
+ const filter = contract.filters.Attested(recipient, null);
2680
+ const toBlock = await provider.getBlockNumber();
2681
+ let events;
2682
+ try {
2683
+ events = await contract.queryFilter(filter, fromBlock, toBlock);
2684
+ } catch (err) {
2685
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
2686
+ }
2687
+ const eas = new EAS(easContractAddress);
2688
+ eas.connect(provider);
2689
+ const schemaFilter = schemas.map((s) => s.toLowerCase());
2690
+ const results = [];
2691
+ for (const event of events) {
2692
+ if (!("args" in event) || !Array.isArray(event.args)) continue;
2693
+ const args = event.args;
2694
+ const uid = args?.[2];
2695
+ const schemaUid = args?.[3];
2696
+ if (!uid || !schemaUid) continue;
2697
+ if (!schemaFilter.includes(schemaUid.toLowerCase())) continue;
2698
+ const attestation = await eas.getAttestation(uid);
2699
+ if (!attestation || !attestation.uid) continue;
2700
+ let data = {};
2701
+ const rawData = attestation.data;
2702
+ if (rawData && rawData !== "0x") {
2703
+ try {
2704
+ data = decodeAttestationData(
2705
+ "string subject, string controller, string method",
2706
+ rawData
2707
+ );
2708
+ } catch {
2709
+ try {
2710
+ data = decodeAttestationData(
2711
+ "string subject, string keyId, string publicKeyJwk, string[] keyPurpose, string[] proofs, uint256 issuedAt, uint256 effectiveAt, uint256 expiresAt",
2712
+ rawData
2713
+ );
2714
+ } catch {
2715
+ }
2716
+ }
2717
+ }
2718
+ const toBigIntSafe = (value) => {
2719
+ if (typeof value === "bigint") return value;
2720
+ if (typeof value === "number") return BigInt(value);
2721
+ if (typeof value === "string" && value.length > 0) return BigInt(value);
2722
+ return 0n;
2723
+ };
2724
+ results.push({
2725
+ uid: attestation.uid,
2726
+ schema: attestation.schema,
2727
+ attester: attestation.attester,
2728
+ recipient: attestation.recipient,
2729
+ revocable: Boolean(attestation.revocable),
2730
+ revocationTime: toBigIntSafe(attestation.revocationTime),
2731
+ expirationTime: toBigIntSafe(attestation.expirationTime),
2732
+ time: toBigIntSafe(attestation.time),
2733
+ refUID: attestation.refUID,
2734
+ data
2735
+ });
2736
+ }
2737
+ return results;
2738
+ }
2739
+ async function findKeyBindingWithTransferProof(keyBindingSchemaUid, easContractAddress, provider, subjectAddress, controllerDid, controllerMethod, fromBlock) {
2740
+ const keyBindings = await queryByRecipientAndSchema(
2741
+ easContractAddress,
2742
+ provider,
2743
+ subjectAddress,
2744
+ [keyBindingSchemaUid],
2745
+ fromBlock
2746
+ );
2747
+ const withProof = keyBindings.filter((att) => {
2748
+ if (!controllerMatchesKeyBinding(att, controllerDid, controllerMethod)) return false;
2749
+ const proofs = att.data?.proofs;
2750
+ if (Array.isArray(proofs)) {
2751
+ return proofs.some((p) => typeof p === "string" && /^0x[0-9a-fA-F]{64}$/.test(p));
2752
+ }
2753
+ return typeof att.data?.proofTxHash === "string";
2754
+ }).sort((a, b) => Number(b.time - a.time));
2755
+ return withProof[0] ?? null;
2756
+ }
1291
2757
 
1292
2758
  // src/reputation/proof/tx-interaction.ts
1293
2759
  function createTxInteractionProof(chainId, txHash) {
@@ -1409,7 +2875,279 @@ function createEvidencePointerProof(url) {
1409
2875
  issuedAt: Math.floor(Date.now() / 1e3)
1410
2876
  };
1411
2877
  }
2878
+ function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
2879
+ return verifyDnsTxtControllerDid(domain, expectedControllerDid, {
2880
+ ...options,
2881
+ resolveTxt: options.resolveTxt ?? resolveTxt
2882
+ });
2883
+ }
2884
+ function assertConnectedWalletDid(input) {
2885
+ const normalized = normalizeDid(input);
2886
+ if (extractDidMethod(normalized) !== "pkh") {
2887
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
2888
+ connectedWalletDid: input
2889
+ });
2890
+ }
2891
+ return normalized;
2892
+ }
2893
+ function normalizeSubjectDid(input) {
2894
+ return normalizeDid(input);
2895
+ }
2896
+ async function verifyDidWebOwnership(params) {
2897
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
2898
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
2899
+ const domain = getDomainFromDidWeb(subjectDid);
2900
+ if (!domain) {
2901
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be a did:web DID", {
2902
+ subjectDid: params.subjectDid
2903
+ });
2904
+ }
2905
+ let dnsReason;
2906
+ try {
2907
+ const dnsResult = await verifyDnsTxtControllerDid(domain, connectedWalletDid, {
2908
+ resolveTxt: params.resolveTxt,
2909
+ recordPrefix: params.recordPrefix
2910
+ });
2911
+ if (dnsResult.valid) {
2912
+ return {
2913
+ valid: true,
2914
+ method: "dns",
2915
+ details: `Verified via DNS TXT record at ${params.recordPrefix ?? "_controllers"}.${domain}`,
2916
+ subjectDid,
2917
+ connectedWalletDid
2918
+ };
2919
+ }
2920
+ dnsReason = dnsResult.reason;
2921
+ } catch (error) {
2922
+ dnsReason = error instanceof Error ? error.message : "DNS TXT verification failed";
2923
+ }
2924
+ let didDocReason;
2925
+ try {
2926
+ const didDocumentResult = await verifyDidJsonControllerDid(domain, connectedWalletDid, {
2927
+ fetchDidDocument: params.fetchDidDocument
2928
+ });
2929
+ if (didDocumentResult.valid) {
2930
+ return {
2931
+ valid: true,
2932
+ method: "did-document",
2933
+ details: `Verified via DID document at https://${domain}/.well-known/did.json`,
2934
+ subjectDid,
2935
+ connectedWalletDid
2936
+ };
2937
+ }
2938
+ didDocReason = didDocumentResult.reason;
2939
+ } catch (error) {
2940
+ didDocReason = error instanceof Error ? error.message : "DID document verification failed";
2941
+ }
2942
+ return {
2943
+ valid: false,
2944
+ reason: "DID ownership verification failed",
2945
+ details: `DNS check: ${dnsReason ?? "failed"}. DID document check: ${didDocReason ?? "failed"}.`,
2946
+ subjectDid,
2947
+ connectedWalletDid
2948
+ };
2949
+ }
2950
+ async function verifyDidPkhOwnership(params) {
2951
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
2952
+ const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
2953
+ if (!isEvmDidPkh(subjectDid)) {
2954
+ throw new OmaTrustError("INVALID_INPUT", "subjectDid must be an EVM did:pkh DID", {
2955
+ subjectDid: params.subjectDid
2956
+ });
2957
+ }
2958
+ const subjectAddress = getAddressFromDidPkh(subjectDid);
2959
+ const connectedWalletAddress = getAddressFromDidPkh(connectedWalletDid);
2960
+ const chainIdRaw = getChainIdFromDidPkh(subjectDid);
2961
+ if (!subjectAddress || !connectedWalletAddress || !chainIdRaw) {
2962
+ throw new OmaTrustError("INVALID_INPUT", "Could not parse did:pkh ownership inputs", {
2963
+ subjectDid: params.subjectDid,
2964
+ connectedWalletDid: params.connectedWalletDid
2965
+ });
2966
+ }
2967
+ const chainId = Number(chainIdRaw);
2968
+ if (!Number.isFinite(chainId)) {
2969
+ throw new OmaTrustError("INVALID_INPUT", "Invalid chain id in subjectDid", {
2970
+ subjectDid: params.subjectDid
2971
+ });
2972
+ }
2973
+ const code = await params.provider.getCode(subjectAddress);
2974
+ const isContract = code !== "0x" && code !== "0x0";
2975
+ if (!isContract) {
2976
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress)) {
2977
+ return {
2978
+ valid: true,
2979
+ method: "wallet",
2980
+ details: "Verified direct wallet ownership from matching did:pkh subject and connected wallet",
2981
+ subjectDid,
2982
+ connectedWalletDid
2983
+ };
2984
+ }
2985
+ return {
2986
+ valid: false,
2987
+ reason: "EOA did:pkh subject does not match connected wallet",
2988
+ details: "For direct wallet did:pkh subjects, connectedWalletDid must match the subject DID.",
2989
+ subjectDid,
2990
+ connectedWalletDid
2991
+ };
2992
+ }
2993
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
2994
+ if (params.txHash) {
2995
+ if (!controllingWalletDid) {
2996
+ return {
2997
+ valid: false,
2998
+ reason: "Could not discover controlling wallet",
2999
+ details: "Contract does not expose owner/admin/getOwner or a readable EIP-1967 admin slot for transfer verification.",
3000
+ subjectDid,
3001
+ connectedWalletDid
3002
+ };
3003
+ }
3004
+ const tx = await params.provider.getTransaction(params.txHash);
3005
+ if (!tx) {
3006
+ return {
3007
+ valid: false,
3008
+ reason: "Transaction not found",
3009
+ details: `Transaction ${params.txHash} was not found on chain ${chainId}.`,
3010
+ subjectDid,
3011
+ connectedWalletDid,
3012
+ controllingWalletDid
3013
+ };
3014
+ }
3015
+ const receipt = await params.provider.getTransactionReceipt(params.txHash);
3016
+ if (!receipt) {
3017
+ return {
3018
+ valid: false,
3019
+ reason: "Transaction not confirmed",
3020
+ details: "Transaction exists but is not yet confirmed.",
3021
+ subjectDid,
3022
+ connectedWalletDid,
3023
+ controllingWalletDid
3024
+ };
3025
+ }
3026
+ const controllingWalletAddress = getAddressFromDidPkh(controllingWalletDid);
3027
+ if (!tx.from || !controllingWalletAddress || getAddress(tx.from) !== getAddress(controllingWalletAddress)) {
3028
+ return {
3029
+ valid: false,
3030
+ reason: "Wrong sender",
3031
+ details: `Transfer must originate from controlling wallet ${controllingWalletDid}.`,
3032
+ subjectDid,
3033
+ connectedWalletDid,
3034
+ controllingWalletDid
3035
+ };
3036
+ }
3037
+ if (!tx.to || getAddress(tx.to) !== getAddress(connectedWalletAddress)) {
3038
+ return {
3039
+ valid: false,
3040
+ reason: "Wrong recipient",
3041
+ details: `Transfer must be sent to connected wallet ${connectedWalletDid}.`,
3042
+ subjectDid,
3043
+ connectedWalletDid,
3044
+ controllingWalletDid
3045
+ };
3046
+ }
3047
+ const expectedAmount = calculateTransferAmount(
3048
+ subjectDid,
3049
+ connectedWalletDid,
3050
+ chainId,
3051
+ "shared-control"
3052
+ );
3053
+ const actualValue = BigInt(tx.value ?? 0n);
3054
+ if (actualValue !== expectedAmount) {
3055
+ return {
3056
+ valid: false,
3057
+ reason: "Wrong amount",
3058
+ details: `Transfer amount ${actualValue.toString()} does not match expected proof amount ${expectedAmount.toString()}.`,
3059
+ subjectDid,
3060
+ connectedWalletDid,
3061
+ controllingWalletDid
3062
+ };
3063
+ }
3064
+ await params.provider.getBlockNumber();
3065
+ await params.provider.getBlock(receipt.blockNumber);
3066
+ return {
3067
+ valid: true,
3068
+ method: "transfer",
3069
+ details: `Verified via transfer proof ${params.txHash}.`,
3070
+ subjectDid,
3071
+ connectedWalletDid,
3072
+ controllingWalletDid
3073
+ };
3074
+ }
3075
+ for (const pattern of OWNERSHIP_PATTERNS) {
3076
+ const ownerAddress = await readOwnerFromContract(
3077
+ params.provider,
3078
+ subjectAddress,
3079
+ pattern.signature,
3080
+ pattern.method
3081
+ );
3082
+ if (ownerAddress && getAddress(ownerAddress) === getAddress(connectedWalletAddress)) {
3083
+ return {
3084
+ valid: true,
3085
+ method: "contract",
3086
+ details: `Verified via ${pattern.method}() ownership check.`,
3087
+ subjectDid,
3088
+ connectedWalletDid,
3089
+ controllingWalletDid: controllingWalletDid ?? void 0
3090
+ };
3091
+ }
3092
+ }
3093
+ try {
3094
+ const adminValue = await params.provider.getStorage(subjectAddress, EIP1967_ADMIN_SLOT);
3095
+ if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
3096
+ const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
3097
+ if (adminAddress === getAddress(connectedWalletAddress)) {
3098
+ return {
3099
+ valid: true,
3100
+ method: "contract",
3101
+ details: "Verified via EIP-1967 admin slot.",
3102
+ subjectDid,
3103
+ connectedWalletDid,
3104
+ controllingWalletDid: controllingWalletDid ?? buildEvmDidPkh(chainId, adminAddress)
3105
+ };
3106
+ }
3107
+ }
3108
+ } catch {
3109
+ }
3110
+ if (getAddress(subjectAddress) === getAddress(connectedWalletAddress) && controllingWalletDid) {
3111
+ return {
3112
+ valid: true,
3113
+ method: "minting-wallet",
3114
+ details: `Verified because connected wallet matches the contract DID address. Controlling wallet is ${controllingWalletDid}.`,
3115
+ subjectDid,
3116
+ connectedWalletDid,
3117
+ controllingWalletDid
3118
+ };
3119
+ }
3120
+ return {
3121
+ valid: false,
3122
+ reason: "Contract ownership verification failed",
3123
+ 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}.`,
3124
+ subjectDid,
3125
+ connectedWalletDid,
3126
+ controllingWalletDid: controllingWalletDid ?? void 0
3127
+ };
3128
+ }
3129
+ async function verifySubjectOwnership(params) {
3130
+ const subjectDid = normalizeSubjectDid(params.subjectDid);
3131
+ const method = extractDidMethod(subjectDid);
3132
+ if (method === "web") {
3133
+ return verifyDidWebOwnership(params);
3134
+ }
3135
+ if (method === "pkh") {
3136
+ const didPkhParams = params;
3137
+ if (!didPkhParams.provider) {
3138
+ throw new OmaTrustError(
3139
+ "INVALID_INPUT",
3140
+ "provider is required for did:pkh ownership verification",
3141
+ { subjectDid }
3142
+ );
3143
+ }
3144
+ return verifyDidPkhOwnership(didPkhParams);
3145
+ }
3146
+ throw new OmaTrustError("INVALID_INPUT", "Unsupported DID type for ownership verification", {
3147
+ subjectDid
3148
+ });
3149
+ }
1412
3150
 
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 };
3151
+ export { EIP1967_ADMIN_SLOT, OWNERSHIP_PATTERNS, buildDelegatedAttestationTypedData, buildDelegatedTypedDataFromEncoded, buildDnsTxtRecord, buildEip712Domain, calculateAverageUserReviewRating, calculateTransferAmount, calculateTransferAmountFromAddresses, callControllerWitness, constructSeed, createEvidencePointerProof, createPopEip712Proof, createPopJwsProof, createTxEncodedValueProof, createTxInteractionProof, createX402OfferProof, createX402ReceiptProof, decodeAttestationData, deduplicateReviews, discoverContractOwner, discoverControllingWalletDid, encodeAttestationData, extractEvmAddressesFromDidDocument, extractExpirationTime, extractJwksFromDidDocument, fetchDidDocument, formatSchemaUid, formatTransferAmount, getAttestation, getAttestationsByAttester, getAttestationsForDid, getChainConstants, getControllerAuthorization, getExplorerAddressUrl, getExplorerTxUrl, getLatestAttestations, getMajorVersion, getOmaTrustProofEip712Types, getSchemaDetails, getSupportedChainIds, hashSeed, isChainSupported, listAttestations, normalizeSchema, parseDnsTxtRecord, prepareDelegatedAttestation, readOwnerFromContract, requestControllerWitness, revokeAttestation, schemaToString, splitSignature, submitAttestation, submitDelegatedAttestation, validateAttestationData, verifyAttestation, verifyDidDocumentControllerDid, verifyDidJsonControllerDid, verifyDidPkhOwnership, verifyDidWebOwnership, verifyDnsTxtControllerDid2 as verifyDnsTxtControllerDid, verifyEip712Signature, verifyProof, verifySchemaExists, verifySubjectOwnership, verifyTransferProof, verifyX402Eip712Artifact, verifyX402Eip712Offer, verifyX402Eip712Receipt, verifyX402JwsArtifact, verifyX402JwsOffer, verifyX402JwsReceipt };
1414
3152
  //# sourceMappingURL=index.js.map
1415
3153
  //# sourceMappingURL=index.js.map