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

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 (48) hide show
  1. package/README.md +3 -0
  2. package/dist/app-registry/index.cjs +53 -0
  3. package/dist/app-registry/index.cjs.map +1 -1
  4. package/dist/app-registry/index.js +53 -0
  5. package/dist/app-registry/index.js.map +1 -1
  6. package/dist/identity/index.cjs +1028 -6
  7. package/dist/identity/index.cjs.map +1 -1
  8. package/dist/identity/index.d.cts +2 -1
  9. package/dist/identity/index.d.ts +2 -1
  10. package/dist/identity/index.js +984 -7
  11. package/dist/identity/index.js.map +1 -1
  12. package/dist/index-B5OC9_8B.d.cts +480 -0
  13. package/dist/index-Bu-xxcv9.d.ts +407 -0
  14. package/dist/index-C6WNgPRx.d.ts +480 -0
  15. package/dist/index-C7odEbp6.d.cts +407 -0
  16. package/dist/index.cjs +2498 -373
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -3
  19. package/dist/index.d.ts +4 -3
  20. package/dist/index.js +2342 -236
  21. package/dist/index.js.map +1 -1
  22. package/dist/reputation/index.browser.cjs +949 -67
  23. package/dist/reputation/index.browser.cjs.map +1 -1
  24. package/dist/reputation/index.browser.d.cts +2 -2
  25. package/dist/reputation/index.browser.d.ts +2 -2
  26. package/dist/reputation/index.browser.js +947 -67
  27. package/dist/reputation/index.browser.js.map +1 -1
  28. package/dist/reputation/index.cjs +1745 -232
  29. package/dist/reputation/index.cjs.map +1 -1
  30. package/dist/reputation/index.d.cts +3 -2
  31. package/dist/reputation/index.d.ts +3 -2
  32. package/dist/reputation/index.js +1727 -232
  33. package/dist/reputation/index.js.map +1 -1
  34. package/dist/{subject-ownership-CXvzEjpH.d.cts → subject-ownership-B7cFlm8c.d.cts} +256 -26
  35. package/dist/{subject-ownership-CXvzEjpH.d.ts → subject-ownership-B7cFlm8c.d.ts} +256 -26
  36. package/dist/types-Dn0OwaNj.d.cts +307 -0
  37. package/dist/types-Dn0OwaNj.d.ts +307 -0
  38. package/dist/widgets/index.cjs +70 -27
  39. package/dist/widgets/index.cjs.map +1 -1
  40. package/dist/widgets/index.d.cts +45 -18
  41. package/dist/widgets/index.d.ts +45 -18
  42. package/dist/widgets/index.js +67 -26
  43. package/dist/widgets/index.js.map +1 -1
  44. package/package.json +5 -2
  45. package/dist/index-B9KW02US.d.cts +0 -119
  46. package/dist/index-DXrwBex9.d.ts +0 -119
  47. package/dist/index-QZDExA4I.d.cts +0 -90
  48. package/dist/index-QZDExA4I.d.ts +0 -90
@@ -1,3 +1,4 @@
1
+ import { base64url, decodeProtectedHeader, importJWK, compactVerify } from 'jose';
1
2
  import { ZeroAddress, Signature, toUtf8Bytes, sha256, keccak256, formatUnits, getAddress, isAddress, verifyTypedData, hexlify, randomBytes, Contract, Interface } from 'ethers';
2
3
  import { SchemaEncoder, EAS } from '@ethereum-attestation-service/eas-sdk';
3
4
  import canonicalize from 'canonicalize';
@@ -35,6 +36,11 @@ function assertString(value, name, code = "INVALID_INPUT") {
35
36
  throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
36
37
  }
37
38
  }
39
+ function assertObject(value, name, code = "INVALID_INPUT") {
40
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
41
+ throw new OmaTrustError(code, `${name} must be an object`, { value });
42
+ }
43
+ }
38
44
  var init_assert = __esm({
39
45
  "src/shared/assert.ts"() {
40
46
  init_errors();
@@ -65,6 +71,152 @@ var init_caip = __esm({
65
71
  CAIP_10_REGEX = /^(?<namespace>[a-z0-9-]+):(?<reference>[a-zA-Z0-9-]+):(?<address>.+)$/;
66
72
  }
67
73
  });
74
+ function validatePublicJwk(jwk) {
75
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
76
+ return { valid: false, error: "JWK must be a non-null object" };
77
+ }
78
+ const obj = jwk;
79
+ const kty = obj.kty;
80
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
81
+ return {
82
+ valid: false,
83
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
84
+ };
85
+ }
86
+ for (const field of PRIVATE_KEY_FIELDS) {
87
+ if (field in obj) {
88
+ return {
89
+ valid: false,
90
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
91
+ };
92
+ }
93
+ }
94
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
95
+ if (required) {
96
+ for (const field of required) {
97
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
98
+ return {
99
+ valid: false,
100
+ error: `Missing required public key field "${field}" for kty="${kty}"`
101
+ };
102
+ }
103
+ }
104
+ }
105
+ return { valid: true };
106
+ }
107
+ function canonicalizeJwkJson(jwk) {
108
+ const sorted = Object.keys(jwk).sort();
109
+ const obj = {};
110
+ for (const key of sorted) {
111
+ obj[key] = jwk[key];
112
+ }
113
+ return JSON.stringify(obj);
114
+ }
115
+ function jwkToDidJwk(jwk) {
116
+ assertObject(jwk, "jwk", "INVALID_JWK");
117
+ const validation = validatePublicJwk(jwk);
118
+ if (!validation.valid) {
119
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
120
+ }
121
+ const canonical = canonicalizeJwkJson(jwk);
122
+ const encoded = base64url.encode(new TextEncoder().encode(canonical));
123
+ return `did:jwk:${encoded}`;
124
+ }
125
+ function didJwkToJwk(didJwk) {
126
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
127
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
128
+ }
129
+ const parts = didJwk.split(":");
130
+ if (parts.length !== 3) {
131
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
132
+ input: didJwk
133
+ });
134
+ }
135
+ const encoded = parts[2];
136
+ if (!encoded || encoded.length === 0) {
137
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
138
+ input: didJwk
139
+ });
140
+ }
141
+ let decoded;
142
+ try {
143
+ const bytes = base64url.decode(encoded);
144
+ decoded = new TextDecoder().decode(bytes);
145
+ } catch {
146
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
147
+ input: didJwk
148
+ });
149
+ }
150
+ let jwk;
151
+ try {
152
+ jwk = JSON.parse(decoded);
153
+ } catch {
154
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
155
+ input: didJwk
156
+ });
157
+ }
158
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
159
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
160
+ input: didJwk
161
+ });
162
+ }
163
+ const validation = validatePublicJwk(jwk);
164
+ if (!validation.valid) {
165
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
166
+ input: didJwk
167
+ });
168
+ }
169
+ return jwk;
170
+ }
171
+ function extractPublicKeyFields(jwk) {
172
+ const result = {};
173
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
174
+ for (const [key, value] of Object.entries(jwk)) {
175
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
176
+ if (metadataFields.has(key)) continue;
177
+ result[key] = value;
178
+ }
179
+ return result;
180
+ }
181
+ function publicJwkEquals(a, b) {
182
+ assertObject(a, "a", "INVALID_JWK");
183
+ assertObject(b, "b", "INVALID_JWK");
184
+ const aObj = a;
185
+ const bObj = b;
186
+ for (const field of PRIVATE_KEY_FIELDS) {
187
+ if (field in aObj) {
188
+ throw new OmaTrustError(
189
+ "INVALID_JWK",
190
+ `First JWK contains private key field "${field}"`,
191
+ { field }
192
+ );
193
+ }
194
+ if (field in bObj) {
195
+ throw new OmaTrustError(
196
+ "INVALID_JWK",
197
+ `Second JWK contains private key field "${field}"`,
198
+ { field }
199
+ );
200
+ }
201
+ }
202
+ const aPublic = extractPublicKeyFields(aObj);
203
+ const bPublic = extractPublicKeyFields(bObj);
204
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
205
+ }
206
+ var VALID_KTY, PRIVATE_KEY_FIELDS, REQUIRED_PUBLIC_FIELDS;
207
+ var init_jwk = __esm({
208
+ "src/identity/jwk.ts"() {
209
+ init_errors();
210
+ init_assert();
211
+ VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
212
+ PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
213
+ REQUIRED_PUBLIC_FIELDS = {
214
+ EC: ["crv", "x", "y"],
215
+ OKP: ["crv", "x"],
216
+ RSA: ["n", "e"]
217
+ };
218
+ }
219
+ });
68
220
  function isValidDid(did) {
69
221
  return DID_REGEX.test(did);
70
222
  }
@@ -133,7 +285,7 @@ function normalizeDidKey(input) {
133
285
  }
134
286
  function normalizeDid(input) {
135
287
  assertString(input, "input", "INVALID_DID");
136
- const trimmed = input.trim();
288
+ const trimmed = input.trim().split("#")[0];
137
289
  if (!trimmed.startsWith("did:")) {
138
290
  return normalizeDidWeb(trimmed);
139
291
  }
@@ -150,6 +302,8 @@ function normalizeDid(input) {
150
302
  return normalizeDidHandle(trimmed);
151
303
  case "key":
152
304
  return normalizeDidKey(trimmed);
305
+ case "jwk":
306
+ return normalizeDidJwk(trimmed);
153
307
  default:
154
308
  return trimmed;
155
309
  }
@@ -239,13 +393,85 @@ function extractAddressFromDid(identifier) {
239
393
  }
240
394
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
241
395
  }
242
- var DID_REGEX;
396
+ function validateDidJwk(did) {
397
+ const parts = did.split(":");
398
+ if (parts.length !== 3) {
399
+ return {
400
+ valid: false,
401
+ method: "jwk",
402
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
403
+ };
404
+ }
405
+ const [, , encoded] = parts;
406
+ if (!encoded || encoded.length === 0) {
407
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
408
+ }
409
+ if (!BASE64URL_REGEX.test(encoded)) {
410
+ return {
411
+ valid: false,
412
+ method: "jwk",
413
+ error: "Identifier contains invalid base64url characters"
414
+ };
415
+ }
416
+ let decoded;
417
+ try {
418
+ const bytes = base64url.decode(encoded);
419
+ decoded = new TextDecoder().decode(bytes);
420
+ } catch {
421
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
422
+ }
423
+ let jwk;
424
+ try {
425
+ jwk = JSON.parse(decoded);
426
+ } catch {
427
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
428
+ }
429
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
430
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
431
+ }
432
+ const kty = jwk.kty;
433
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
434
+ return {
435
+ valid: false,
436
+ method: "jwk",
437
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
438
+ };
439
+ }
440
+ if ("d" in jwk) {
441
+ return {
442
+ valid: false,
443
+ method: "jwk",
444
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
445
+ };
446
+ }
447
+ const jwkValidation = validatePublicJwk(jwk);
448
+ if (!jwkValidation.valid) {
449
+ return { valid: false, method: "jwk", error: jwkValidation.error };
450
+ }
451
+ return { valid: true, method: "jwk" };
452
+ }
453
+ function normalizeDidJwk(input) {
454
+ assertString(input, "input", "INVALID_DID");
455
+ const trimmed = input.trim();
456
+ if (!trimmed.startsWith("did:jwk:")) {
457
+ throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
458
+ }
459
+ const result = validateDidJwk(trimmed);
460
+ if (!result.valid) {
461
+ throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
462
+ }
463
+ return trimmed;
464
+ }
465
+ var DID_REGEX, BASE64URL_REGEX, VALID_JWK_KTY;
243
466
  var init_did = __esm({
244
467
  "src/identity/did.ts"() {
245
468
  init_errors();
246
469
  init_assert();
247
470
  init_caip();
471
+ init_jwk();
248
472
  DID_REGEX = /^did:[a-z0-9]+:.+$/i;
473
+ BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
474
+ VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
249
475
  }
250
476
  });
251
477
 
@@ -256,21 +482,23 @@ function parseDnsTxtRecord(record) {
256
482
  }
257
483
  const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
258
484
  const parsed = {};
485
+ const controllers = [];
259
486
  for (const entry of entries) {
260
- const [key, ...valueParts] = entry.split("=");
261
- if (!key) {
262
- continue;
263
- }
264
- const value = valueParts.join("=");
265
- if (!value) {
266
- continue;
487
+ const eqIndex = entry.indexOf("=");
488
+ if (eqIndex === -1) continue;
489
+ const key = entry.slice(0, eqIndex).trim();
490
+ const value = entry.slice(eqIndex + 1).trim();
491
+ if (!key || !value) continue;
492
+ if (key === "controller") {
493
+ controllers.push(value);
494
+ } else {
495
+ parsed[key] = value;
267
496
  }
268
- parsed[key.trim()] = value.trim();
269
497
  }
270
498
  return {
271
499
  version: parsed.v,
272
- controller: parsed.controller,
273
- ...parsed
500
+ controller: controllers[0],
501
+ controllers
274
502
  };
275
503
  }
276
504
  function buildDnsTxtRecord(controllerDid) {
@@ -284,6 +512,56 @@ var init_dns_txt_record = __esm({
284
512
  }
285
513
  });
286
514
 
515
+ // src/identity/controller-id.ts
516
+ function isSameControllerId(a, b) {
517
+ let normalizedA = null;
518
+ let normalizedB = null;
519
+ try {
520
+ normalizedA = normalizeDid(a);
521
+ } catch {
522
+ }
523
+ try {
524
+ normalizedB = normalizeDid(b);
525
+ } catch {
526
+ }
527
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
528
+ return true;
529
+ }
530
+ const evmA = extractControllerEvmAddress(a);
531
+ const evmB = extractControllerEvmAddress(b);
532
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
533
+ return true;
534
+ }
535
+ const methodA = extractDidMethod(a);
536
+ const methodB = extractDidMethod(b);
537
+ if (methodA === "jwk" && methodB === "jwk") {
538
+ try {
539
+ const jwkA = didJwkToJwk(a);
540
+ const jwkB = didJwkToJwk(b);
541
+ return publicJwkEquals(jwkA, jwkB);
542
+ } catch {
543
+ return false;
544
+ }
545
+ }
546
+ return false;
547
+ }
548
+ function extractControllerEvmAddress(controllerDid) {
549
+ try {
550
+ const method = extractDidMethod(controllerDid);
551
+ if (method === "pkh" && controllerDid.includes("eip155")) {
552
+ return extractAddressFromDid(controllerDid);
553
+ }
554
+ } catch {
555
+ }
556
+ return null;
557
+ }
558
+ var init_controller_id = __esm({
559
+ "src/identity/controller-id.ts"() {
560
+ init_did();
561
+ init_jwk();
562
+ }
563
+ });
564
+
287
565
  // src/reputation/proof/dns-txt-shared.ts
288
566
  var dns_txt_shared_exports = {};
289
567
  __export(dns_txt_shared_exports, {
@@ -296,7 +574,6 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
296
574
  if (!options.resolveTxt) {
297
575
  throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
298
576
  }
299
- const expected = normalizeDid(expectedControllerDid);
300
577
  const prefix = options.recordPrefix ?? "_controllers";
301
578
  const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
302
579
  let records;
@@ -308,15 +585,18 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
308
585
  for (const recordParts of records) {
309
586
  const record = recordParts.join("");
310
587
  const parsed = parseDnsTxtRecord(record);
311
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
312
- return { valid: true, record };
588
+ if (parsed.version !== "1") continue;
589
+ for (const recordController of parsed.controllers) {
590
+ if (isSameControllerId(recordController, expectedControllerDid)) {
591
+ return { valid: true, record };
592
+ }
313
593
  }
314
594
  }
315
- return { valid: false, reason: "No TXT record matched expected controller DID" };
595
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
316
596
  }
317
597
  var init_dns_txt_shared = __esm({
318
598
  "src/reputation/proof/dns-txt-shared.ts"() {
319
- init_did();
599
+ init_controller_id();
320
600
  init_errors();
321
601
  init_dns_txt_record();
322
602
  }
@@ -787,19 +1067,32 @@ async function submitDelegatedAttestation(params) {
787
1067
  payload = {};
788
1068
  }
789
1069
  if (!response.ok) {
790
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
791
- status: response.status,
1070
+ const relayError = {
1071
+ httpStatus: response.status,
1072
+ code: payload.code,
1073
+ error: payload.error ?? payload.message,
792
1074
  payload
793
- });
1075
+ };
1076
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
794
1077
  }
795
1078
  const uid = payload.uid ?? ZERO_UID;
796
1079
  const txHash = payload.txHash;
797
- const status = payload.status ?? "submitted";
798
- return {
799
- uid,
800
- txHash,
801
- status
802
- };
1080
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
1081
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
1082
+ const relay = {};
1083
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
1084
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
1085
+ if (typeof payload.success === "boolean") relay.success = payload.success;
1086
+ for (const key of Object.keys(payload)) {
1087
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
1088
+ relay[key] = payload[key];
1089
+ }
1090
+ }
1091
+ const result = { uid, txHash, status };
1092
+ if (Object.keys(relay).length > 0) {
1093
+ result.relay = relay;
1094
+ }
1095
+ return result;
803
1096
  }
804
1097
 
805
1098
  // src/reputation/query.ts
@@ -905,7 +1198,7 @@ async function getAttestationsForDid(params) {
905
1198
  provider,
906
1199
  fromBlock,
907
1200
  toBlock,
908
- { recipient: didToAddress(params.did), schemas: params.schemas }
1201
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
909
1202
  );
910
1203
  }
911
1204
  async function getAttestationsByAttester(params) {
@@ -1157,6 +1450,10 @@ function getExplorerAddressUrl(chainId, address) {
1157
1450
 
1158
1451
  // src/reputation/proof/did-json.ts
1159
1452
  init_did();
1453
+ init_jwk();
1454
+ init_errors();
1455
+
1456
+ // src/shared/did-document.ts
1160
1457
  init_errors();
1161
1458
  async function fetchDidDocument(domain) {
1162
1459
  const normalized = domain.toLowerCase().replace(/\.$/, "");
@@ -1168,15 +1465,16 @@ async function fetchDidDocument(domain) {
1168
1465
  throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
1169
1466
  }
1170
1467
  if (!response.ok) {
1171
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1468
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
1172
1469
  domain,
1173
1470
  status: response.status
1174
1471
  });
1175
1472
  }
1176
- const body = await response.json();
1177
- return body;
1473
+ return await response.json();
1178
1474
  }
1179
- function extractAddressesFromDidDocument(didDocument) {
1475
+
1476
+ // src/reputation/proof/did-json.ts
1477
+ function extractEvmAddressesFromDidDocument(didDocument) {
1180
1478
  const methods = didDocument.verificationMethod;
1181
1479
  if (!Array.isArray(methods)) {
1182
1480
  return [];
@@ -1200,14 +1498,52 @@ function extractAddressesFromDidDocument(didDocument) {
1200
1498
  }
1201
1499
  return [...addresses];
1202
1500
  }
1501
+ function extractJwksFromDidDocument(didDocument) {
1502
+ const methods = didDocument.verificationMethod;
1503
+ if (!Array.isArray(methods)) {
1504
+ return [];
1505
+ }
1506
+ const jwks = [];
1507
+ for (const method of methods) {
1508
+ const publicKeyJwk = method.publicKeyJwk;
1509
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
1510
+ jwks.push(publicKeyJwk);
1511
+ }
1512
+ }
1513
+ return jwks;
1514
+ }
1203
1515
  function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1516
+ const method = extractDidMethod(expectedControllerDid);
1517
+ if (method === "jwk") {
1518
+ try {
1519
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
1520
+ const documentJwks = extractJwksFromDidDocument(didDocument);
1521
+ for (const docJwk of documentJwks) {
1522
+ try {
1523
+ if (publicJwkEquals(docJwk, expectedJwk)) {
1524
+ return { valid: true };
1525
+ }
1526
+ } catch {
1527
+ }
1528
+ }
1529
+ return {
1530
+ valid: false,
1531
+ reason: "No matching publicKeyJwk found in DID document verification methods"
1532
+ };
1533
+ } catch {
1534
+ return {
1535
+ valid: false,
1536
+ reason: "Failed to decode did:jwk controller DID"
1537
+ };
1538
+ }
1539
+ }
1204
1540
  let expectedAddress;
1205
1541
  try {
1206
1542
  expectedAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1207
1543
  } catch {
1208
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
1544
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
1209
1545
  }
1210
- const addresses = extractAddressesFromDidDocument(didDocument);
1546
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
1211
1547
  if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1212
1548
  return { valid: true };
1213
1549
  }
@@ -1261,6 +1597,469 @@ function verifyEip712Signature(typedData, signature) {
1261
1597
 
1262
1598
  // src/reputation/verify.ts
1263
1599
  init_dns_txt_record();
1600
+
1601
+ // src/reputation/proof/x402-jws.ts
1602
+ init_errors();
1603
+ init_jwk();
1604
+
1605
+ // src/identity/resolve-key.ts
1606
+ init_errors();
1607
+ init_assert();
1608
+
1609
+ // src/identity/did-url.ts
1610
+ init_errors();
1611
+ init_assert();
1612
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
1613
+ function parseDidUrl(input) {
1614
+ assertString(input, "input", "INVALID_DID_URL");
1615
+ const trimmed = input.trim();
1616
+ const hashIndex = trimmed.indexOf("#");
1617
+ let did;
1618
+ let fragment;
1619
+ if (hashIndex === -1) {
1620
+ did = trimmed;
1621
+ fragment = null;
1622
+ } else {
1623
+ did = trimmed.slice(0, hashIndex);
1624
+ const rawFragment = trimmed.slice(hashIndex + 1);
1625
+ if (rawFragment.length === 0) {
1626
+ throw new OmaTrustError(
1627
+ "INVALID_DID_URL",
1628
+ "DID URL has empty fragment (trailing '#' with no identifier)",
1629
+ { input }
1630
+ );
1631
+ }
1632
+ fragment = rawFragment;
1633
+ }
1634
+ if (!DID_URL_REGEX.test(did)) {
1635
+ throw new OmaTrustError(
1636
+ "INVALID_DID_URL",
1637
+ "Invalid DID URL: base DID portion is malformed",
1638
+ { input, did }
1639
+ );
1640
+ }
1641
+ return {
1642
+ didUrl: trimmed,
1643
+ did,
1644
+ fragment
1645
+ };
1646
+ }
1647
+
1648
+ // src/identity/resolve-key.ts
1649
+ init_did();
1650
+ init_jwk();
1651
+ function findVerificationMethod(didDocument, didUrl, fragment) {
1652
+ const methods = didDocument.verificationMethod;
1653
+ if (!Array.isArray(methods)) {
1654
+ return null;
1655
+ }
1656
+ for (const method of methods) {
1657
+ if (!method || typeof method !== "object") continue;
1658
+ const id = method.id;
1659
+ if (typeof id !== "string") continue;
1660
+ if (id === didUrl) return method;
1661
+ if (fragment && (id === `#${fragment}` || id.endsWith(`#${fragment}`))) {
1662
+ return method;
1663
+ }
1664
+ }
1665
+ return null;
1666
+ }
1667
+ function extractMethodIdsFromDidDocument(didDocument) {
1668
+ const methods = didDocument.verificationMethod;
1669
+ if (!Array.isArray(methods)) return [];
1670
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
1671
+ }
1672
+ async function resolveDidUrlToPublicKey(didUrl, options) {
1673
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
1674
+ const parsed = parseDidUrl(didUrl);
1675
+ const method = extractDidMethod(parsed.did);
1676
+ if (!method) {
1677
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
1678
+ didUrl,
1679
+ did: parsed.did
1680
+ });
1681
+ }
1682
+ switch (method) {
1683
+ case "web":
1684
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment);
1685
+ default:
1686
+ throw new OmaTrustError(
1687
+ "UNSUPPORTED_DID_METHOD",
1688
+ `DID URL key resolution is not supported for method "${method}"`,
1689
+ { didUrl, method }
1690
+ );
1691
+ }
1692
+ }
1693
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
1694
+ const domain = getDomainFromDidWeb(did);
1695
+ if (!domain) {
1696
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
1697
+ didUrl,
1698
+ did
1699
+ });
1700
+ }
1701
+ const fetchFn = fetchDidDocument;
1702
+ const didDocument = await fetchFn(domain);
1703
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
1704
+ if (!method) {
1705
+ throw new OmaTrustError(
1706
+ "KEY_NOT_FOUND",
1707
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
1708
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
1709
+ );
1710
+ }
1711
+ const publicKeyJwk = method.publicKeyJwk;
1712
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
1713
+ throw new OmaTrustError(
1714
+ "KEY_NOT_FOUND",
1715
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
1716
+ { didUrl, verificationMethodId: method.id }
1717
+ );
1718
+ }
1719
+ const validation = validatePublicJwk(publicKeyJwk);
1720
+ if (!validation.valid) {
1721
+ throw new OmaTrustError(
1722
+ "INVALID_JWK",
1723
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
1724
+ { didUrl, verificationMethodId: method.id }
1725
+ );
1726
+ }
1727
+ return {
1728
+ didUrl,
1729
+ did,
1730
+ fragment,
1731
+ publicKeyJwk,
1732
+ verificationMethodId: method.id
1733
+ };
1734
+ }
1735
+
1736
+ // src/reputation/proof/x402-jws.ts
1737
+ var REQUIRED_OFFER_FIELDS = [
1738
+ "version",
1739
+ "resourceUrl",
1740
+ "scheme",
1741
+ "network",
1742
+ "asset",
1743
+ "payTo",
1744
+ "amount"
1745
+ ];
1746
+ var REQUIRED_RECEIPT_FIELDS = [
1747
+ "version",
1748
+ "network",
1749
+ "resourceUrl",
1750
+ "payer",
1751
+ "issuedAt"
1752
+ ];
1753
+ function validateOfferPayload(payload) {
1754
+ for (const field of REQUIRED_OFFER_FIELDS) {
1755
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1756
+ return { valid: false, field };
1757
+ }
1758
+ }
1759
+ return { valid: true };
1760
+ }
1761
+ function validateReceiptPayload(payload) {
1762
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
1763
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1764
+ return { valid: false, field };
1765
+ }
1766
+ }
1767
+ return { valid: true };
1768
+ }
1769
+ function failure(code, message, partial) {
1770
+ return {
1771
+ valid: false,
1772
+ error: { code, message },
1773
+ ...partial
1774
+ };
1775
+ }
1776
+ async function verifyX402JwsArtifact(artifact, options) {
1777
+ if (!artifact || typeof artifact !== "object") {
1778
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
1779
+ }
1780
+ if (artifact.format !== "jws") {
1781
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
1782
+ }
1783
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1784
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
1785
+ }
1786
+ const compactJws = artifact.signature;
1787
+ const parts = compactJws.split(".");
1788
+ if (parts.length !== 3) {
1789
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
1790
+ }
1791
+ let header;
1792
+ try {
1793
+ header = decodeProtectedHeader(compactJws);
1794
+ } catch {
1795
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
1796
+ }
1797
+ if (!header.alg || typeof header.alg !== "string") {
1798
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
1799
+ }
1800
+ const kid = typeof header.kid === "string" ? header.kid : null;
1801
+ const embeddedJwk = header.jwk;
1802
+ if (!kid && !embeddedJwk) {
1803
+ return failure(
1804
+ "MISSING_KEY_MATERIAL",
1805
+ "JWS header must include at least one of kid or jwk",
1806
+ { header }
1807
+ );
1808
+ }
1809
+ let publicKeyJwk;
1810
+ let publicKeySource;
1811
+ if (embeddedJwk) {
1812
+ const validation = validatePublicJwk(embeddedJwk);
1813
+ if (!validation.valid) {
1814
+ return failure(
1815
+ "INVALID_EMBEDDED_JWK",
1816
+ `Embedded JWK is invalid: ${validation.error}`,
1817
+ { header }
1818
+ );
1819
+ }
1820
+ publicKeyJwk = embeddedJwk;
1821
+ publicKeySource = "embedded-jwk";
1822
+ if (kid) {
1823
+ try {
1824
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1825
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
1826
+ return failure(
1827
+ "KEY_CONFLICT",
1828
+ "Embedded jwk conflicts with public key resolved from kid",
1829
+ { header, kid }
1830
+ );
1831
+ }
1832
+ } catch (err) {
1833
+ }
1834
+ }
1835
+ } else {
1836
+ publicKeySource = "kid-resolution";
1837
+ try {
1838
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1839
+ publicKeyJwk = resolved.publicKeyJwk;
1840
+ } catch (err) {
1841
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
1842
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
1843
+ }
1844
+ }
1845
+ let payload;
1846
+ try {
1847
+ const key = await importJWK(publicKeyJwk, header.alg);
1848
+ const result = await compactVerify(compactJws, key);
1849
+ const payloadText = new TextDecoder().decode(result.payload);
1850
+ payload = JSON.parse(payloadText);
1851
+ } catch (err) {
1852
+ const message = err instanceof Error ? err.message : "Signature verification failed";
1853
+ return failure("SIGNATURE_INVALID", message, { header, kid });
1854
+ }
1855
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
1856
+ return {
1857
+ valid: true,
1858
+ header,
1859
+ payload,
1860
+ kid,
1861
+ publicKeyJwk,
1862
+ publicKeySource,
1863
+ publicKeyDid
1864
+ };
1865
+ }
1866
+ async function verifyX402JwsOffer(artifact, options) {
1867
+ const result = await verifyX402JwsArtifact(artifact, options);
1868
+ if (!result.valid) return result;
1869
+ const payloadCheck = validateOfferPayload(result.payload);
1870
+ if (!payloadCheck.valid) {
1871
+ return failure(
1872
+ "INVALID_OFFER_PAYLOAD",
1873
+ `Missing required offer field: ${payloadCheck.field}`,
1874
+ {
1875
+ header: result.header,
1876
+ payload: result.payload,
1877
+ kid: result.kid,
1878
+ publicKeyJwk: result.publicKeyJwk,
1879
+ publicKeySource: result.publicKeySource,
1880
+ publicKeyDid: result.publicKeyDid
1881
+ }
1882
+ );
1883
+ }
1884
+ return result;
1885
+ }
1886
+ async function verifyX402JwsReceipt(artifact, options) {
1887
+ const result = await verifyX402JwsArtifact(artifact, options);
1888
+ if (!result.valid) return result;
1889
+ const payloadCheck = validateReceiptPayload(result.payload);
1890
+ if (!payloadCheck.valid) {
1891
+ return failure(
1892
+ "INVALID_RECEIPT_PAYLOAD",
1893
+ `Missing required receipt field: ${payloadCheck.field}`,
1894
+ {
1895
+ header: result.header,
1896
+ payload: result.payload,
1897
+ kid: result.kid,
1898
+ publicKeyJwk: result.publicKeyJwk,
1899
+ publicKeySource: result.publicKeySource,
1900
+ publicKeyDid: result.publicKeyDid
1901
+ }
1902
+ );
1903
+ }
1904
+ return result;
1905
+ }
1906
+ var OFFER_DOMAIN = {
1907
+ name: "x402 offer",
1908
+ version: "1",
1909
+ chainId: 1
1910
+ };
1911
+ var RECEIPT_DOMAIN = {
1912
+ name: "x402 receipt",
1913
+ version: "1",
1914
+ chainId: 1
1915
+ };
1916
+ var OFFER_TYPES = {
1917
+ Offer: [
1918
+ { name: "version", type: "uint256" },
1919
+ { name: "resourceUrl", type: "string" },
1920
+ { name: "scheme", type: "string" },
1921
+ { name: "network", type: "string" },
1922
+ { name: "asset", type: "string" },
1923
+ { name: "payTo", type: "string" },
1924
+ { name: "amount", type: "string" },
1925
+ { name: "validUntil", type: "uint256" }
1926
+ ]
1927
+ };
1928
+ var RECEIPT_TYPES = {
1929
+ Receipt: [
1930
+ { name: "version", type: "uint256" },
1931
+ { name: "network", type: "string" },
1932
+ { name: "resourceUrl", type: "string" },
1933
+ { name: "payer", type: "string" },
1934
+ { name: "issuedAt", type: "uint256" },
1935
+ { name: "transaction", type: "string" }
1936
+ ]
1937
+ };
1938
+ var REQUIRED_OFFER_FIELDS2 = [
1939
+ "version",
1940
+ "resourceUrl",
1941
+ "scheme",
1942
+ "network",
1943
+ "asset",
1944
+ "payTo",
1945
+ "amount"
1946
+ ];
1947
+ var REQUIRED_RECEIPT_FIELDS2 = [
1948
+ "version",
1949
+ "network",
1950
+ "resourceUrl",
1951
+ "payer",
1952
+ "issuedAt"
1953
+ ];
1954
+ function validateOfferPayload2(payload) {
1955
+ for (const field of REQUIRED_OFFER_FIELDS2) {
1956
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1957
+ return { valid: false, field };
1958
+ }
1959
+ }
1960
+ return { valid: true };
1961
+ }
1962
+ function validateReceiptPayload2(payload) {
1963
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
1964
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1965
+ return { valid: false, field };
1966
+ }
1967
+ }
1968
+ return { valid: true };
1969
+ }
1970
+ function failure2(code, message, partial) {
1971
+ return {
1972
+ valid: false,
1973
+ error: { code, message },
1974
+ ...partial
1975
+ };
1976
+ }
1977
+ function prepareOfferMessage(payload) {
1978
+ return {
1979
+ version: payload.version,
1980
+ resourceUrl: payload.resourceUrl,
1981
+ scheme: payload.scheme,
1982
+ network: payload.network,
1983
+ asset: payload.asset,
1984
+ payTo: payload.payTo,
1985
+ amount: payload.amount,
1986
+ validUntil: payload.validUntil ?? 0
1987
+ };
1988
+ }
1989
+ function prepareReceiptMessage(payload) {
1990
+ return {
1991
+ version: payload.version,
1992
+ network: payload.network,
1993
+ resourceUrl: payload.resourceUrl,
1994
+ payer: payload.payer,
1995
+ issuedAt: payload.issuedAt,
1996
+ transaction: payload.transaction ?? ""
1997
+ };
1998
+ }
1999
+ function verifyX402Eip712Artifact(artifact, artifactType) {
2000
+ if (!artifact || typeof artifact !== "object") {
2001
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
2002
+ }
2003
+ if (artifact.format !== "eip712") {
2004
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
2005
+ }
2006
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
2007
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
2008
+ }
2009
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2010
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
2011
+ }
2012
+ const payload = artifact.payload;
2013
+ if (artifactType === "offer") {
2014
+ const check = validateOfferPayload2(payload);
2015
+ if (!check.valid) {
2016
+ return failure2(
2017
+ "INVALID_OFFER_PAYLOAD",
2018
+ `Missing required offer field: ${check.field}`,
2019
+ { payload }
2020
+ );
2021
+ }
2022
+ } else {
2023
+ const check = validateReceiptPayload2(payload);
2024
+ if (!check.valid) {
2025
+ return failure2(
2026
+ "INVALID_RECEIPT_PAYLOAD",
2027
+ `Missing required receipt field: ${check.field}`,
2028
+ { payload }
2029
+ );
2030
+ }
2031
+ }
2032
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
2033
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
2034
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
2035
+ let signer;
2036
+ try {
2037
+ signer = verifyTypedData(
2038
+ domain,
2039
+ types,
2040
+ message,
2041
+ artifact.signature
2042
+ );
2043
+ signer = getAddress(signer);
2044
+ } catch (err) {
2045
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
2046
+ return failure2("SIGNATURE_INVALID", message2, { payload });
2047
+ }
2048
+ return {
2049
+ valid: true,
2050
+ payload,
2051
+ signer,
2052
+ artifactType
2053
+ };
2054
+ }
2055
+ function verifyX402Eip712Offer(artifact) {
2056
+ return verifyX402Eip712Artifact(artifact, "offer");
2057
+ }
2058
+ function verifyX402Eip712Receipt(artifact) {
2059
+ return verifyX402Eip712Artifact(artifact, "receipt");
2060
+ }
2061
+
2062
+ // src/reputation/verify.ts
1264
2063
  function parseChainId(input) {
1265
2064
  if (typeof input === "number") {
1266
2065
  return input;
@@ -1317,18 +2116,18 @@ function decodeJwtPayload(compactJws) {
1317
2116
  return JSON.parse(json);
1318
2117
  }
1319
2118
  async function verifyProof(params) {
1320
- const { proof, provider, expectedSubject, expectedController } = params;
2119
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1321
2120
  try {
1322
2121
  switch (proof.proofType) {
1323
2122
  case "tx-encoded-value": {
1324
2123
  if (!provider) {
1325
2124
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1326
2125
  }
1327
- if (!expectedSubject || !expectedController) {
2126
+ if (!expectedSubjectDid || !expectedControllerDid) {
1328
2127
  return {
1329
2128
  valid: false,
1330
2129
  proofType: proof.proofType,
1331
- reason: "expectedSubject and expectedController are required"
2130
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1332
2131
  };
1333
2132
  }
1334
2133
  const proofObject = proof.proofObject;
@@ -1340,13 +2139,13 @@ async function verifyProof(params) {
1340
2139
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1341
2140
  }
1342
2141
  const expectedAmount = calculateTransferAmount(
1343
- expectedSubject,
1344
- expectedController,
2142
+ expectedSubjectDid,
2143
+ expectedControllerDid,
1345
2144
  chainId,
1346
2145
  getProofPurpose(proof)
1347
2146
  );
1348
- const subjectAddress = getAddress(extractAddressFromDid(expectedSubject));
1349
- const controllerAddress = getAddress(extractAddressFromDid(expectedController));
2147
+ const subjectAddress = getAddress(extractAddressFromDid(expectedSubjectDid));
2148
+ const controllerAddress = getAddress(extractAddressFromDid(expectedControllerDid));
1350
2149
  if (tx.from && getAddress(tx.from) !== subjectAddress) {
1351
2150
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1352
2151
  }
@@ -1420,6 +2219,38 @@ async function verifyProof(params) {
1420
2219
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1421
2220
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1422
2221
  }
2222
+ const proofObj = proof.proofObject;
2223
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2224
+ const artifact = {
2225
+ format: "jws",
2226
+ signature: proofObj.signature
2227
+ };
2228
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2229
+ if (!jwsResult.valid) {
2230
+ return {
2231
+ valid: false,
2232
+ proofType: proof.proofType,
2233
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2234
+ };
2235
+ }
2236
+ return { valid: true, proofType: proof.proofType };
2237
+ }
2238
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2239
+ const artifact = {
2240
+ format: "eip712",
2241
+ payload: proofObj.payload,
2242
+ signature: proofObj.signature
2243
+ };
2244
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2245
+ if (!eip712Result.valid) {
2246
+ return {
2247
+ valid: false,
2248
+ proofType: proof.proofType,
2249
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2250
+ };
2251
+ }
2252
+ return { valid: true, proofType: proof.proofType };
2253
+ }
1423
2254
  return { valid: true, proofType: proof.proofType };
1424
2255
  }
1425
2256
  case "evidence-pointer": {
@@ -1431,9 +2262,9 @@ async function verifyProof(params) {
1431
2262
  if (!response.ok) {
1432
2263
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1433
2264
  }
1434
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2265
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1435
2266
  const didDoc = await response.json();
1436
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2267
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1437
2268
  return {
1438
2269
  valid: didCheck.valid,
1439
2270
  proofType: proof.proofType,
@@ -1441,10 +2272,10 @@ async function verifyProof(params) {
1441
2272
  };
1442
2273
  }
1443
2274
  const body = await response.text();
1444
- if (expectedController && !body.includes(expectedController)) {
2275
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1445
2276
  try {
1446
2277
  const parsed = parseDnsTxtRecord(body);
1447
- if (parsed.controller !== expectedController) {
2278
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1448
2279
  return {
1449
2280
  valid: false,
1450
2281
  proofType: proof.proofType,
@@ -1500,8 +2331,8 @@ async function verifyAttestation(params) {
1500
2331
  const result = await verifyProof({
1501
2332
  proof,
1502
2333
  provider: params.provider,
1503
- expectedSubject: params.context?.subject,
1504
- expectedController: params.context?.controller
2334
+ expectedSubjectDid: params.context?.subjectDid,
2335
+ expectedControllerDid: params.context?.controllerDid
1505
2336
  });
1506
2337
  checks[proof.proofType] = result.valid;
1507
2338
  if (!result.valid) {
@@ -1606,6 +2437,38 @@ async function callControllerWitness(params) {
1606
2437
  method: "did-json"
1607
2438
  };
1608
2439
  }
2440
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
2441
+ async function requestControllerWitness(params) {
2442
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
2443
+ const body = {
2444
+ subjectDid: params.subjectDid,
2445
+ controllerDid: params.controllerDid
2446
+ };
2447
+ if (params.chainId !== void 0) {
2448
+ body.chainId = params.chainId;
2449
+ }
2450
+ let response;
2451
+ try {
2452
+ response = await fetch(url, {
2453
+ method: "POST",
2454
+ headers: { "Content-Type": "application/json" },
2455
+ credentials: "include",
2456
+ body: JSON.stringify(body),
2457
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
2458
+ });
2459
+ } catch (err) {
2460
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
2461
+ }
2462
+ if (!response.ok) {
2463
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
2464
+ throw new OmaTrustError(
2465
+ "API_ERROR",
2466
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
2467
+ { status: response.status, ...errorBody }
2468
+ );
2469
+ }
2470
+ return await response.json();
2471
+ }
1609
2472
 
1610
2473
  // src/reputation/proof/tx-interaction.ts
1611
2474
  init_errors();
@@ -1752,29 +2615,20 @@ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options
1752
2615
  );
1753
2616
  }
1754
2617
 
1755
- // src/reputation/proof/subject-ownership.ts
2618
+ // src/reputation/subject-ownership.ts
1756
2619
  init_did();
1757
2620
  init_errors();
1758
2621
  init_dns_txt_shared();
2622
+
2623
+ // src/reputation/contract-ownership.ts
2624
+ init_did();
1759
2625
  var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1760
2626
  var OWNERSHIP_PATTERNS = [
1761
2627
  { method: "owner", signature: "function owner() view returns (address)" },
1762
2628
  { method: "admin", signature: "function admin() view returns (address)" },
1763
2629
  { method: "getOwner", signature: "function getOwner() view returns (address)" }
1764
2630
  ];
1765
- function assertConnectedWalletDid(input) {
1766
- const normalized = normalizeDid(input);
1767
- if (extractDidMethod(normalized) !== "pkh") {
1768
- throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
1769
- connectedWalletDid: input
1770
- });
1771
- }
1772
- return normalized;
1773
- }
1774
- function normalizeSubjectDid(input) {
1775
- return normalizeDid(input);
1776
- }
1777
- async function readAddressFromContract(provider, contractAddress, signature, method) {
2631
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
1778
2632
  try {
1779
2633
  const iface = new Interface([signature]);
1780
2634
  const data = iface.encodeFunctionData(method, []);
@@ -1787,16 +2641,24 @@ async function readAddressFromContract(provider, contractAddress, signature, met
1787
2641
  }
1788
2642
  return null;
1789
2643
  }
1790
- async function discoverControllingWallet(provider, contractAddress, chainId) {
2644
+ async function discoverContractOwner(provider, contractAddress) {
2645
+ try {
2646
+ const code = await provider.getCode(contractAddress);
2647
+ if (code === "0x" || code === "0x0") {
2648
+ return null;
2649
+ }
2650
+ } catch {
2651
+ return null;
2652
+ }
1791
2653
  for (const pattern of OWNERSHIP_PATTERNS) {
1792
- const address = await readAddressFromContract(
2654
+ const address = await readOwnerFromContract(
1793
2655
  provider,
1794
2656
  contractAddress,
1795
2657
  pattern.signature,
1796
2658
  pattern.method
1797
2659
  );
1798
2660
  if (address) {
1799
- return buildEvmDidPkh(chainId, address);
2661
+ return address;
1800
2662
  }
1801
2663
  }
1802
2664
  try {
@@ -1804,13 +2666,31 @@ async function discoverControllingWallet(provider, contractAddress, chainId) {
1804
2666
  if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1805
2667
  const adminAddress = getAddress(`0x${adminValue.slice(-40)}`);
1806
2668
  if (adminAddress !== ZeroAddress) {
1807
- return buildEvmDidPkh(chainId, adminAddress);
2669
+ return adminAddress;
1808
2670
  }
1809
2671
  }
1810
2672
  } catch {
1811
2673
  }
1812
2674
  return null;
1813
2675
  }
2676
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
2677
+ const owner = await discoverContractOwner(provider, contractAddress);
2678
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
2679
+ }
2680
+
2681
+ // src/reputation/subject-ownership.ts
2682
+ function assertConnectedWalletDid(input) {
2683
+ const normalized = normalizeDid(input);
2684
+ if (extractDidMethod(normalized) !== "pkh") {
2685
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
2686
+ connectedWalletDid: input
2687
+ });
2688
+ }
2689
+ return normalized;
2690
+ }
2691
+ function normalizeSubjectDid(input) {
2692
+ return normalizeDid(input);
2693
+ }
1814
2694
  async function verifyDidWebOwnership(params) {
1815
2695
  const subjectDid = normalizeSubjectDid(params.subjectDid);
1816
2696
  const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
@@ -1908,7 +2788,7 @@ async function verifyDidPkhOwnership(params) {
1908
2788
  connectedWalletDid
1909
2789
  };
1910
2790
  }
1911
- const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
2791
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
1912
2792
  if (params.txHash) {
1913
2793
  if (!controllingWalletDid) {
1914
2794
  return {
@@ -1991,7 +2871,7 @@ async function verifyDidPkhOwnership(params) {
1991
2871
  };
1992
2872
  }
1993
2873
  for (const pattern of OWNERSHIP_PATTERNS) {
1994
- const ownerAddress = await readAddressFromContract(
2874
+ const ownerAddress = await readOwnerFromContract(
1995
2875
  params.provider,
1996
2876
  subjectAddress,
1997
2877
  pattern.signature,
@@ -2066,6 +2946,6 @@ async function verifySubjectOwnership(params) {
2066
2946
  });
2067
2947
  }
2068
2948
 
2069
- 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, verifyDnsTxtControllerDid2 as verifyDnsTxtControllerDid, verifyEip712Signature, verifyProof, verifySchemaExists, verifySubjectOwnership };
2949
+ export { buildDelegatedAttestationTypedData, buildDelegatedTypedDataFromEncoded, buildDnsTxtRecord, buildEip712Domain, calculateAverageUserReviewRating, calculateTransferAmount, calculateTransferAmountFromAddresses, callControllerWitness, constructSeed, createEvidencePointerProof, createPopEip712Proof, createPopJwsProof, createTxEncodedValueProof, createTxInteractionProof, createX402OfferProof, createX402ReceiptProof, decodeAttestationData, deduplicateReviews, encodeAttestationData, extractEvmAddressesFromDidDocument, extractExpirationTime, extractJwksFromDidDocument, fetchDidDocument, formatSchemaUid, formatTransferAmount, getAttestation, getAttestationsByAttester, getAttestationsForDid, getChainConstants, getExplorerAddressUrl, getExplorerTxUrl, getLatestAttestations, getMajorVersion, getOmaTrustProofEip712Types, getSchemaDetails, getSupportedChainIds, hashSeed, isChainSupported, listAttestations, normalizeSchema, parseDnsTxtRecord, prepareDelegatedAttestation, requestControllerWitness, revokeAttestation, schemaToString, splitSignature, submitAttestation, submitDelegatedAttestation, validateAttestationData, verifyAttestation, verifyDidDocumentControllerDid, verifyDidJsonControllerDid, verifyDidPkhOwnership, verifyDidWebOwnership, verifyDnsTxtControllerDid2 as verifyDnsTxtControllerDid, verifyEip712Signature, verifyProof, verifySchemaExists, verifySubjectOwnership };
2070
2950
  //# sourceMappingURL=index.browser.js.map
2071
2951
  //# sourceMappingURL=index.browser.js.map