@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,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var jose = require('jose');
3
4
  var ethers = require('ethers');
4
5
  var easSdk = require('@ethereum-attestation-service/eas-sdk');
5
6
  var canonicalize = require('canonicalize');
@@ -41,6 +42,11 @@ function assertString(value, name, code = "INVALID_INPUT") {
41
42
  throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
42
43
  }
43
44
  }
45
+ function assertObject(value, name, code = "INVALID_INPUT") {
46
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
47
+ throw new OmaTrustError(code, `${name} must be an object`, { value });
48
+ }
49
+ }
44
50
  var init_assert = __esm({
45
51
  "src/shared/assert.ts"() {
46
52
  init_errors();
@@ -71,6 +77,152 @@ var init_caip = __esm({
71
77
  CAIP_10_REGEX = /^(?<namespace>[a-z0-9-]+):(?<reference>[a-zA-Z0-9-]+):(?<address>.+)$/;
72
78
  }
73
79
  });
80
+ function validatePublicJwk(jwk) {
81
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
82
+ return { valid: false, error: "JWK must be a non-null object" };
83
+ }
84
+ const obj = jwk;
85
+ const kty = obj.kty;
86
+ if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
87
+ return {
88
+ valid: false,
89
+ error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
90
+ };
91
+ }
92
+ for (const field of PRIVATE_KEY_FIELDS) {
93
+ if (field in obj) {
94
+ return {
95
+ valid: false,
96
+ error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
97
+ };
98
+ }
99
+ }
100
+ const required = REQUIRED_PUBLIC_FIELDS[kty];
101
+ if (required) {
102
+ for (const field of required) {
103
+ if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
104
+ return {
105
+ valid: false,
106
+ error: `Missing required public key field "${field}" for kty="${kty}"`
107
+ };
108
+ }
109
+ }
110
+ }
111
+ return { valid: true };
112
+ }
113
+ function canonicalizeJwkJson(jwk) {
114
+ const sorted = Object.keys(jwk).sort();
115
+ const obj = {};
116
+ for (const key of sorted) {
117
+ obj[key] = jwk[key];
118
+ }
119
+ return JSON.stringify(obj);
120
+ }
121
+ function jwkToDidJwk(jwk) {
122
+ assertObject(jwk, "jwk", "INVALID_JWK");
123
+ const validation = validatePublicJwk(jwk);
124
+ if (!validation.valid) {
125
+ throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
126
+ }
127
+ const canonical = canonicalizeJwkJson(jwk);
128
+ const encoded = jose.base64url.encode(new TextEncoder().encode(canonical));
129
+ return `did:jwk:${encoded}`;
130
+ }
131
+ function didJwkToJwk(didJwk) {
132
+ if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
133
+ throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
134
+ }
135
+ const parts = didJwk.split(":");
136
+ if (parts.length !== 3) {
137
+ throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
138
+ input: didJwk
139
+ });
140
+ }
141
+ const encoded = parts[2];
142
+ if (!encoded || encoded.length === 0) {
143
+ throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
144
+ input: didJwk
145
+ });
146
+ }
147
+ let decoded;
148
+ try {
149
+ const bytes = jose.base64url.decode(encoded);
150
+ decoded = new TextDecoder().decode(bytes);
151
+ } catch {
152
+ throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
153
+ input: didJwk
154
+ });
155
+ }
156
+ let jwk;
157
+ try {
158
+ jwk = JSON.parse(decoded);
159
+ } catch {
160
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
161
+ input: didJwk
162
+ });
163
+ }
164
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
165
+ throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
166
+ input: didJwk
167
+ });
168
+ }
169
+ const validation = validatePublicJwk(jwk);
170
+ if (!validation.valid) {
171
+ throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
172
+ input: didJwk
173
+ });
174
+ }
175
+ return jwk;
176
+ }
177
+ function extractPublicKeyFields(jwk) {
178
+ const result = {};
179
+ const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
180
+ for (const [key, value] of Object.entries(jwk)) {
181
+ if (PRIVATE_KEY_FIELDS.has(key)) continue;
182
+ if (metadataFields.has(key)) continue;
183
+ result[key] = value;
184
+ }
185
+ return result;
186
+ }
187
+ function publicJwkEquals(a, b) {
188
+ assertObject(a, "a", "INVALID_JWK");
189
+ assertObject(b, "b", "INVALID_JWK");
190
+ const aObj = a;
191
+ const bObj = b;
192
+ for (const field of PRIVATE_KEY_FIELDS) {
193
+ if (field in aObj) {
194
+ throw new OmaTrustError(
195
+ "INVALID_JWK",
196
+ `First JWK contains private key field "${field}"`,
197
+ { field }
198
+ );
199
+ }
200
+ if (field in bObj) {
201
+ throw new OmaTrustError(
202
+ "INVALID_JWK",
203
+ `Second JWK contains private key field "${field}"`,
204
+ { field }
205
+ );
206
+ }
207
+ }
208
+ const aPublic = extractPublicKeyFields(aObj);
209
+ const bPublic = extractPublicKeyFields(bObj);
210
+ return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
211
+ }
212
+ var VALID_KTY, PRIVATE_KEY_FIELDS, REQUIRED_PUBLIC_FIELDS;
213
+ var init_jwk = __esm({
214
+ "src/identity/jwk.ts"() {
215
+ init_errors();
216
+ init_assert();
217
+ VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
218
+ PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
219
+ REQUIRED_PUBLIC_FIELDS = {
220
+ EC: ["crv", "x", "y"],
221
+ OKP: ["crv", "x"],
222
+ RSA: ["n", "e"]
223
+ };
224
+ }
225
+ });
74
226
  function isValidDid(did) {
75
227
  return DID_REGEX.test(did);
76
228
  }
@@ -139,7 +291,7 @@ function normalizeDidKey(input) {
139
291
  }
140
292
  function normalizeDid(input) {
141
293
  assertString(input, "input", "INVALID_DID");
142
- const trimmed = input.trim();
294
+ const trimmed = input.trim().split("#")[0];
143
295
  if (!trimmed.startsWith("did:")) {
144
296
  return normalizeDidWeb(trimmed);
145
297
  }
@@ -156,6 +308,8 @@ function normalizeDid(input) {
156
308
  return normalizeDidHandle(trimmed);
157
309
  case "key":
158
310
  return normalizeDidKey(trimmed);
311
+ case "jwk":
312
+ return normalizeDidJwk(trimmed);
159
313
  default:
160
314
  return trimmed;
161
315
  }
@@ -245,13 +399,85 @@ function extractAddressFromDid(identifier) {
245
399
  }
246
400
  throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
247
401
  }
248
- var DID_REGEX;
402
+ function validateDidJwk(did) {
403
+ const parts = did.split(":");
404
+ if (parts.length !== 3) {
405
+ return {
406
+ valid: false,
407
+ method: "jwk",
408
+ error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
409
+ };
410
+ }
411
+ const [, , encoded] = parts;
412
+ if (!encoded || encoded.length === 0) {
413
+ return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
414
+ }
415
+ if (!BASE64URL_REGEX.test(encoded)) {
416
+ return {
417
+ valid: false,
418
+ method: "jwk",
419
+ error: "Identifier contains invalid base64url characters"
420
+ };
421
+ }
422
+ let decoded;
423
+ try {
424
+ const bytes = jose.base64url.decode(encoded);
425
+ decoded = new TextDecoder().decode(bytes);
426
+ } catch {
427
+ return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
428
+ }
429
+ let jwk;
430
+ try {
431
+ jwk = JSON.parse(decoded);
432
+ } catch {
433
+ return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
434
+ }
435
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
436
+ return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
437
+ }
438
+ const kty = jwk.kty;
439
+ if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
440
+ return {
441
+ valid: false,
442
+ method: "jwk",
443
+ error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
444
+ };
445
+ }
446
+ if ("d" in jwk) {
447
+ return {
448
+ valid: false,
449
+ method: "jwk",
450
+ error: "DID must reference a public key \u2014 private key component (d) is not allowed"
451
+ };
452
+ }
453
+ const jwkValidation = validatePublicJwk(jwk);
454
+ if (!jwkValidation.valid) {
455
+ return { valid: false, method: "jwk", error: jwkValidation.error };
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
+ }
471
+ var DID_REGEX, BASE64URL_REGEX, VALID_JWK_KTY;
249
472
  var init_did = __esm({
250
473
  "src/identity/did.ts"() {
251
474
  init_errors();
252
475
  init_assert();
253
476
  init_caip();
477
+ init_jwk();
254
478
  DID_REGEX = /^did:[a-z0-9]+:.+$/i;
479
+ BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
480
+ VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
255
481
  }
256
482
  });
257
483
 
@@ -262,21 +488,23 @@ function parseDnsTxtRecord(record) {
262
488
  }
263
489
  const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
264
490
  const parsed = {};
491
+ const controllers = [];
265
492
  for (const entry of entries) {
266
- const [key, ...valueParts] = entry.split("=");
267
- if (!key) {
268
- continue;
269
- }
270
- const value = valueParts.join("=");
271
- if (!value) {
272
- continue;
493
+ const eqIndex = entry.indexOf("=");
494
+ if (eqIndex === -1) continue;
495
+ const key = entry.slice(0, eqIndex).trim();
496
+ const value = entry.slice(eqIndex + 1).trim();
497
+ if (!key || !value) continue;
498
+ if (key === "controller") {
499
+ controllers.push(value);
500
+ } else {
501
+ parsed[key] = value;
273
502
  }
274
- parsed[key.trim()] = value.trim();
275
503
  }
276
504
  return {
277
505
  version: parsed.v,
278
- controller: parsed.controller,
279
- ...parsed
506
+ controller: controllers[0],
507
+ controllers
280
508
  };
281
509
  }
282
510
  function buildDnsTxtRecord(controllerDid) {
@@ -290,6 +518,56 @@ var init_dns_txt_record = __esm({
290
518
  }
291
519
  });
292
520
 
521
+ // src/identity/controller-id.ts
522
+ function isSameControllerId(a, b) {
523
+ let normalizedA = null;
524
+ let normalizedB = null;
525
+ try {
526
+ normalizedA = normalizeDid(a);
527
+ } catch {
528
+ }
529
+ try {
530
+ normalizedB = normalizeDid(b);
531
+ } catch {
532
+ }
533
+ if (normalizedA && normalizedB && normalizedA === normalizedB) {
534
+ return true;
535
+ }
536
+ const evmA = extractControllerEvmAddress(a);
537
+ const evmB = extractControllerEvmAddress(b);
538
+ if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
539
+ return true;
540
+ }
541
+ const methodA = extractDidMethod(a);
542
+ const methodB = extractDidMethod(b);
543
+ if (methodA === "jwk" && methodB === "jwk") {
544
+ try {
545
+ const jwkA = didJwkToJwk(a);
546
+ const jwkB = didJwkToJwk(b);
547
+ return publicJwkEquals(jwkA, jwkB);
548
+ } catch {
549
+ return false;
550
+ }
551
+ }
552
+ return false;
553
+ }
554
+ function extractControllerEvmAddress(controllerDid) {
555
+ try {
556
+ const method = extractDidMethod(controllerDid);
557
+ if (method === "pkh" && controllerDid.includes("eip155")) {
558
+ return extractAddressFromDid(controllerDid);
559
+ }
560
+ } catch {
561
+ }
562
+ return null;
563
+ }
564
+ var init_controller_id = __esm({
565
+ "src/identity/controller-id.ts"() {
566
+ init_did();
567
+ init_jwk();
568
+ }
569
+ });
570
+
293
571
  // src/reputation/proof/dns-txt-shared.ts
294
572
  var dns_txt_shared_exports = {};
295
573
  __export(dns_txt_shared_exports, {
@@ -302,7 +580,6 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
302
580
  if (!options.resolveTxt) {
303
581
  throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
304
582
  }
305
- const expected = normalizeDid(expectedControllerDid);
306
583
  const prefix = options.recordPrefix ?? "_controllers";
307
584
  const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
308
585
  let records;
@@ -314,15 +591,18 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
314
591
  for (const recordParts of records) {
315
592
  const record = recordParts.join("");
316
593
  const parsed = parseDnsTxtRecord(record);
317
- if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
318
- return { valid: true, record };
594
+ if (parsed.version !== "1") continue;
595
+ for (const recordController of parsed.controllers) {
596
+ if (isSameControllerId(recordController, expectedControllerDid)) {
597
+ return { valid: true, record };
598
+ }
319
599
  }
320
600
  }
321
- return { valid: false, reason: "No TXT record matched expected controller DID" };
601
+ return { valid: false, reason: "Controller DID not found in DNS TXT records" };
322
602
  }
323
603
  var init_dns_txt_shared = __esm({
324
604
  "src/reputation/proof/dns-txt-shared.ts"() {
325
- init_did();
605
+ init_controller_id();
326
606
  init_errors();
327
607
  init_dns_txt_record();
328
608
  }
@@ -793,19 +1073,32 @@ async function submitDelegatedAttestation(params) {
793
1073
  payload = {};
794
1074
  }
795
1075
  if (!response.ok) {
796
- throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
797
- status: response.status,
1076
+ const relayError = {
1077
+ httpStatus: response.status,
1078
+ code: payload.code,
1079
+ error: payload.error ?? payload.message,
798
1080
  payload
799
- });
1081
+ };
1082
+ throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
800
1083
  }
801
1084
  const uid = payload.uid ?? ZERO_UID;
802
1085
  const txHash = payload.txHash;
803
- const status = payload.status ?? "submitted";
804
- return {
805
- uid,
806
- txHash,
807
- status
808
- };
1086
+ const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
1087
+ const status = hasConfirmationSignals ? "confirmed" : "submitted";
1088
+ const relay = {};
1089
+ if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
1090
+ if (typeof payload.chain === "string") relay.chain = payload.chain;
1091
+ if (typeof payload.success === "boolean") relay.success = payload.success;
1092
+ for (const key of Object.keys(payload)) {
1093
+ if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
1094
+ relay[key] = payload[key];
1095
+ }
1096
+ }
1097
+ const result = { uid, txHash, status };
1098
+ if (Object.keys(relay).length > 0) {
1099
+ result.relay = relay;
1100
+ }
1101
+ return result;
809
1102
  }
810
1103
 
811
1104
  // src/reputation/query.ts
@@ -911,7 +1204,7 @@ async function getAttestationsForDid(params) {
911
1204
  provider,
912
1205
  fromBlock,
913
1206
  toBlock,
914
- { recipient: didToAddress(params.did), schemas: params.schemas }
1207
+ { recipient: didToAddress(params.subjectDid), schemas: params.schemas }
915
1208
  );
916
1209
  }
917
1210
  async function getAttestationsByAttester(params) {
@@ -1163,6 +1456,10 @@ function getExplorerAddressUrl(chainId, address) {
1163
1456
 
1164
1457
  // src/reputation/proof/did-json.ts
1165
1458
  init_did();
1459
+ init_jwk();
1460
+ init_errors();
1461
+
1462
+ // src/shared/did-document.ts
1166
1463
  init_errors();
1167
1464
  async function fetchDidDocument(domain) {
1168
1465
  const normalized = domain.toLowerCase().replace(/\.$/, "");
@@ -1174,15 +1471,16 @@ async function fetchDidDocument(domain) {
1174
1471
  throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
1175
1472
  }
1176
1473
  if (!response.ok) {
1177
- throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
1474
+ throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
1178
1475
  domain,
1179
1476
  status: response.status
1180
1477
  });
1181
1478
  }
1182
- const body = await response.json();
1183
- return body;
1479
+ return await response.json();
1184
1480
  }
1185
- function extractAddressesFromDidDocument(didDocument) {
1481
+
1482
+ // src/reputation/proof/did-json.ts
1483
+ function extractEvmAddressesFromDidDocument(didDocument) {
1186
1484
  const methods = didDocument.verificationMethod;
1187
1485
  if (!Array.isArray(methods)) {
1188
1486
  return [];
@@ -1206,14 +1504,52 @@ function extractAddressesFromDidDocument(didDocument) {
1206
1504
  }
1207
1505
  return [...addresses];
1208
1506
  }
1507
+ function extractJwksFromDidDocument(didDocument) {
1508
+ const methods = didDocument.verificationMethod;
1509
+ if (!Array.isArray(methods)) {
1510
+ return [];
1511
+ }
1512
+ const jwks = [];
1513
+ for (const method of methods) {
1514
+ const publicKeyJwk = method.publicKeyJwk;
1515
+ if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
1516
+ jwks.push(publicKeyJwk);
1517
+ }
1518
+ }
1519
+ return jwks;
1520
+ }
1209
1521
  function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
1522
+ const method = extractDidMethod(expectedControllerDid);
1523
+ if (method === "jwk") {
1524
+ try {
1525
+ const expectedJwk = didJwkToJwk(expectedControllerDid);
1526
+ const documentJwks = extractJwksFromDidDocument(didDocument);
1527
+ for (const docJwk of documentJwks) {
1528
+ try {
1529
+ if (publicJwkEquals(docJwk, expectedJwk)) {
1530
+ return { valid: true };
1531
+ }
1532
+ } catch {
1533
+ }
1534
+ }
1535
+ return {
1536
+ valid: false,
1537
+ reason: "No matching publicKeyJwk found in DID document verification methods"
1538
+ };
1539
+ } catch {
1540
+ return {
1541
+ valid: false,
1542
+ reason: "Failed to decode did:jwk controller DID"
1543
+ };
1544
+ }
1545
+ }
1210
1546
  let expectedAddress;
1211
1547
  try {
1212
1548
  expectedAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1213
1549
  } catch {
1214
- return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
1550
+ return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
1215
1551
  }
1216
- const addresses = extractAddressesFromDidDocument(didDocument);
1552
+ const addresses = extractEvmAddressesFromDidDocument(didDocument);
1217
1553
  if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
1218
1554
  return { valid: true };
1219
1555
  }
@@ -1267,6 +1603,469 @@ function verifyEip712Signature(typedData, signature) {
1267
1603
 
1268
1604
  // src/reputation/verify.ts
1269
1605
  init_dns_txt_record();
1606
+
1607
+ // src/reputation/proof/x402-jws.ts
1608
+ init_errors();
1609
+ init_jwk();
1610
+
1611
+ // src/identity/resolve-key.ts
1612
+ init_errors();
1613
+ init_assert();
1614
+
1615
+ // src/identity/did-url.ts
1616
+ init_errors();
1617
+ init_assert();
1618
+ var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
1619
+ function parseDidUrl(input) {
1620
+ assertString(input, "input", "INVALID_DID_URL");
1621
+ const trimmed = input.trim();
1622
+ const hashIndex = trimmed.indexOf("#");
1623
+ let did;
1624
+ let fragment;
1625
+ if (hashIndex === -1) {
1626
+ did = trimmed;
1627
+ fragment = null;
1628
+ } else {
1629
+ did = trimmed.slice(0, hashIndex);
1630
+ const rawFragment = trimmed.slice(hashIndex + 1);
1631
+ if (rawFragment.length === 0) {
1632
+ throw new OmaTrustError(
1633
+ "INVALID_DID_URL",
1634
+ "DID URL has empty fragment (trailing '#' with no identifier)",
1635
+ { input }
1636
+ );
1637
+ }
1638
+ fragment = rawFragment;
1639
+ }
1640
+ if (!DID_URL_REGEX.test(did)) {
1641
+ throw new OmaTrustError(
1642
+ "INVALID_DID_URL",
1643
+ "Invalid DID URL: base DID portion is malformed",
1644
+ { input, did }
1645
+ );
1646
+ }
1647
+ return {
1648
+ didUrl: trimmed,
1649
+ did,
1650
+ fragment
1651
+ };
1652
+ }
1653
+
1654
+ // src/identity/resolve-key.ts
1655
+ init_did();
1656
+ init_jwk();
1657
+ function findVerificationMethod(didDocument, didUrl, fragment) {
1658
+ const methods = didDocument.verificationMethod;
1659
+ if (!Array.isArray(methods)) {
1660
+ return null;
1661
+ }
1662
+ for (const method of methods) {
1663
+ if (!method || typeof method !== "object") continue;
1664
+ const id = method.id;
1665
+ if (typeof id !== "string") continue;
1666
+ if (id === didUrl) return method;
1667
+ if (fragment && (id === `#${fragment}` || id.endsWith(`#${fragment}`))) {
1668
+ return method;
1669
+ }
1670
+ }
1671
+ return null;
1672
+ }
1673
+ function extractMethodIdsFromDidDocument(didDocument) {
1674
+ const methods = didDocument.verificationMethod;
1675
+ if (!Array.isArray(methods)) return [];
1676
+ return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
1677
+ }
1678
+ async function resolveDidUrlToPublicKey(didUrl, options) {
1679
+ assertString(didUrl, "didUrl", "INVALID_DID_URL");
1680
+ const parsed = parseDidUrl(didUrl);
1681
+ const method = extractDidMethod(parsed.did);
1682
+ if (!method) {
1683
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
1684
+ didUrl,
1685
+ did: parsed.did
1686
+ });
1687
+ }
1688
+ switch (method) {
1689
+ case "web":
1690
+ return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment);
1691
+ default:
1692
+ throw new OmaTrustError(
1693
+ "UNSUPPORTED_DID_METHOD",
1694
+ `DID URL key resolution is not supported for method "${method}"`,
1695
+ { didUrl, method }
1696
+ );
1697
+ }
1698
+ }
1699
+ async function resolveDidUrlKey(didUrl, did, fragment, options) {
1700
+ const domain = getDomainFromDidWeb(did);
1701
+ if (!domain) {
1702
+ throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
1703
+ didUrl,
1704
+ did
1705
+ });
1706
+ }
1707
+ const fetchFn = fetchDidDocument;
1708
+ const didDocument = await fetchFn(domain);
1709
+ const method = findVerificationMethod(didDocument, didUrl, fragment);
1710
+ if (!method) {
1711
+ throw new OmaTrustError(
1712
+ "KEY_NOT_FOUND",
1713
+ `No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
1714
+ { didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
1715
+ );
1716
+ }
1717
+ const publicKeyJwk = method.publicKeyJwk;
1718
+ if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
1719
+ throw new OmaTrustError(
1720
+ "KEY_NOT_FOUND",
1721
+ `Verification method "${method.id}" does not contain publicKeyJwk`,
1722
+ { didUrl, verificationMethodId: method.id }
1723
+ );
1724
+ }
1725
+ const validation = validatePublicJwk(publicKeyJwk);
1726
+ if (!validation.valid) {
1727
+ throw new OmaTrustError(
1728
+ "INVALID_JWK",
1729
+ `publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
1730
+ { didUrl, verificationMethodId: method.id }
1731
+ );
1732
+ }
1733
+ return {
1734
+ didUrl,
1735
+ did,
1736
+ fragment,
1737
+ publicKeyJwk,
1738
+ verificationMethodId: method.id
1739
+ };
1740
+ }
1741
+
1742
+ // src/reputation/proof/x402-jws.ts
1743
+ var REQUIRED_OFFER_FIELDS = [
1744
+ "version",
1745
+ "resourceUrl",
1746
+ "scheme",
1747
+ "network",
1748
+ "asset",
1749
+ "payTo",
1750
+ "amount"
1751
+ ];
1752
+ var REQUIRED_RECEIPT_FIELDS = [
1753
+ "version",
1754
+ "network",
1755
+ "resourceUrl",
1756
+ "payer",
1757
+ "issuedAt"
1758
+ ];
1759
+ function validateOfferPayload(payload) {
1760
+ for (const field of REQUIRED_OFFER_FIELDS) {
1761
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1762
+ return { valid: false, field };
1763
+ }
1764
+ }
1765
+ return { valid: true };
1766
+ }
1767
+ function validateReceiptPayload(payload) {
1768
+ for (const field of REQUIRED_RECEIPT_FIELDS) {
1769
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1770
+ return { valid: false, field };
1771
+ }
1772
+ }
1773
+ return { valid: true };
1774
+ }
1775
+ function failure(code, message, partial) {
1776
+ return {
1777
+ valid: false,
1778
+ error: { code, message },
1779
+ ...partial
1780
+ };
1781
+ }
1782
+ async function verifyX402JwsArtifact(artifact, options) {
1783
+ if (!artifact || typeof artifact !== "object") {
1784
+ return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
1785
+ }
1786
+ if (artifact.format !== "jws") {
1787
+ return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
1788
+ }
1789
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
1790
+ return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
1791
+ }
1792
+ const compactJws = artifact.signature;
1793
+ const parts = compactJws.split(".");
1794
+ if (parts.length !== 3) {
1795
+ return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
1796
+ }
1797
+ let header;
1798
+ try {
1799
+ header = jose.decodeProtectedHeader(compactJws);
1800
+ } catch {
1801
+ return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
1802
+ }
1803
+ if (!header.alg || typeof header.alg !== "string") {
1804
+ return failure("MISSING_ALG", "JWS header must include alg", { header });
1805
+ }
1806
+ const kid = typeof header.kid === "string" ? header.kid : null;
1807
+ const embeddedJwk = header.jwk;
1808
+ if (!kid && !embeddedJwk) {
1809
+ return failure(
1810
+ "MISSING_KEY_MATERIAL",
1811
+ "JWS header must include at least one of kid or jwk",
1812
+ { header }
1813
+ );
1814
+ }
1815
+ let publicKeyJwk;
1816
+ let publicKeySource;
1817
+ if (embeddedJwk) {
1818
+ const validation = validatePublicJwk(embeddedJwk);
1819
+ if (!validation.valid) {
1820
+ return failure(
1821
+ "INVALID_EMBEDDED_JWK",
1822
+ `Embedded JWK is invalid: ${validation.error}`,
1823
+ { header }
1824
+ );
1825
+ }
1826
+ publicKeyJwk = embeddedJwk;
1827
+ publicKeySource = "embedded-jwk";
1828
+ if (kid) {
1829
+ try {
1830
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1831
+ if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
1832
+ return failure(
1833
+ "KEY_CONFLICT",
1834
+ "Embedded jwk conflicts with public key resolved from kid",
1835
+ { header, kid }
1836
+ );
1837
+ }
1838
+ } catch (err) {
1839
+ }
1840
+ }
1841
+ } else {
1842
+ publicKeySource = "kid-resolution";
1843
+ try {
1844
+ const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
1845
+ publicKeyJwk = resolved.publicKeyJwk;
1846
+ } catch (err) {
1847
+ const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
1848
+ return failure("KID_RESOLUTION_FAILED", message, { header, kid });
1849
+ }
1850
+ }
1851
+ let payload;
1852
+ try {
1853
+ const key = await jose.importJWK(publicKeyJwk, header.alg);
1854
+ const result = await jose.compactVerify(compactJws, key);
1855
+ const payloadText = new TextDecoder().decode(result.payload);
1856
+ payload = JSON.parse(payloadText);
1857
+ } catch (err) {
1858
+ const message = err instanceof Error ? err.message : "Signature verification failed";
1859
+ return failure("SIGNATURE_INVALID", message, { header, kid });
1860
+ }
1861
+ const publicKeyDid = jwkToDidJwk(publicKeyJwk);
1862
+ return {
1863
+ valid: true,
1864
+ header,
1865
+ payload,
1866
+ kid,
1867
+ publicKeyJwk,
1868
+ publicKeySource,
1869
+ publicKeyDid
1870
+ };
1871
+ }
1872
+ async function verifyX402JwsOffer(artifact, options) {
1873
+ const result = await verifyX402JwsArtifact(artifact, options);
1874
+ if (!result.valid) return result;
1875
+ const payloadCheck = validateOfferPayload(result.payload);
1876
+ if (!payloadCheck.valid) {
1877
+ return failure(
1878
+ "INVALID_OFFER_PAYLOAD",
1879
+ `Missing required offer field: ${payloadCheck.field}`,
1880
+ {
1881
+ header: result.header,
1882
+ payload: result.payload,
1883
+ kid: result.kid,
1884
+ publicKeyJwk: result.publicKeyJwk,
1885
+ publicKeySource: result.publicKeySource,
1886
+ publicKeyDid: result.publicKeyDid
1887
+ }
1888
+ );
1889
+ }
1890
+ return result;
1891
+ }
1892
+ async function verifyX402JwsReceipt(artifact, options) {
1893
+ const result = await verifyX402JwsArtifact(artifact, options);
1894
+ if (!result.valid) return result;
1895
+ const payloadCheck = validateReceiptPayload(result.payload);
1896
+ if (!payloadCheck.valid) {
1897
+ return failure(
1898
+ "INVALID_RECEIPT_PAYLOAD",
1899
+ `Missing required receipt field: ${payloadCheck.field}`,
1900
+ {
1901
+ header: result.header,
1902
+ payload: result.payload,
1903
+ kid: result.kid,
1904
+ publicKeyJwk: result.publicKeyJwk,
1905
+ publicKeySource: result.publicKeySource,
1906
+ publicKeyDid: result.publicKeyDid
1907
+ }
1908
+ );
1909
+ }
1910
+ return result;
1911
+ }
1912
+ var OFFER_DOMAIN = {
1913
+ name: "x402 offer",
1914
+ version: "1",
1915
+ chainId: 1
1916
+ };
1917
+ var RECEIPT_DOMAIN = {
1918
+ name: "x402 receipt",
1919
+ version: "1",
1920
+ chainId: 1
1921
+ };
1922
+ var OFFER_TYPES = {
1923
+ Offer: [
1924
+ { name: "version", type: "uint256" },
1925
+ { name: "resourceUrl", type: "string" },
1926
+ { name: "scheme", type: "string" },
1927
+ { name: "network", type: "string" },
1928
+ { name: "asset", type: "string" },
1929
+ { name: "payTo", type: "string" },
1930
+ { name: "amount", type: "string" },
1931
+ { name: "validUntil", type: "uint256" }
1932
+ ]
1933
+ };
1934
+ var RECEIPT_TYPES = {
1935
+ Receipt: [
1936
+ { name: "version", type: "uint256" },
1937
+ { name: "network", type: "string" },
1938
+ { name: "resourceUrl", type: "string" },
1939
+ { name: "payer", type: "string" },
1940
+ { name: "issuedAt", type: "uint256" },
1941
+ { name: "transaction", type: "string" }
1942
+ ]
1943
+ };
1944
+ var REQUIRED_OFFER_FIELDS2 = [
1945
+ "version",
1946
+ "resourceUrl",
1947
+ "scheme",
1948
+ "network",
1949
+ "asset",
1950
+ "payTo",
1951
+ "amount"
1952
+ ];
1953
+ var REQUIRED_RECEIPT_FIELDS2 = [
1954
+ "version",
1955
+ "network",
1956
+ "resourceUrl",
1957
+ "payer",
1958
+ "issuedAt"
1959
+ ];
1960
+ function validateOfferPayload2(payload) {
1961
+ for (const field of REQUIRED_OFFER_FIELDS2) {
1962
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1963
+ return { valid: false, field };
1964
+ }
1965
+ }
1966
+ return { valid: true };
1967
+ }
1968
+ function validateReceiptPayload2(payload) {
1969
+ for (const field of REQUIRED_RECEIPT_FIELDS2) {
1970
+ if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
1971
+ return { valid: false, field };
1972
+ }
1973
+ }
1974
+ return { valid: true };
1975
+ }
1976
+ function failure2(code, message, partial) {
1977
+ return {
1978
+ valid: false,
1979
+ error: { code, message },
1980
+ ...partial
1981
+ };
1982
+ }
1983
+ function prepareOfferMessage(payload) {
1984
+ return {
1985
+ version: payload.version,
1986
+ resourceUrl: payload.resourceUrl,
1987
+ scheme: payload.scheme,
1988
+ network: payload.network,
1989
+ asset: payload.asset,
1990
+ payTo: payload.payTo,
1991
+ amount: payload.amount,
1992
+ validUntil: payload.validUntil ?? 0
1993
+ };
1994
+ }
1995
+ function prepareReceiptMessage(payload) {
1996
+ return {
1997
+ version: payload.version,
1998
+ network: payload.network,
1999
+ resourceUrl: payload.resourceUrl,
2000
+ payer: payload.payer,
2001
+ issuedAt: payload.issuedAt,
2002
+ transaction: payload.transaction ?? ""
2003
+ };
2004
+ }
2005
+ function verifyX402Eip712Artifact(artifact, artifactType) {
2006
+ if (!artifact || typeof artifact !== "object") {
2007
+ return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
2008
+ }
2009
+ if (artifact.format !== "eip712") {
2010
+ return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
2011
+ }
2012
+ if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
2013
+ return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
2014
+ }
2015
+ if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
2016
+ return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
2017
+ }
2018
+ const payload = artifact.payload;
2019
+ if (artifactType === "offer") {
2020
+ const check = validateOfferPayload2(payload);
2021
+ if (!check.valid) {
2022
+ return failure2(
2023
+ "INVALID_OFFER_PAYLOAD",
2024
+ `Missing required offer field: ${check.field}`,
2025
+ { payload }
2026
+ );
2027
+ }
2028
+ } else {
2029
+ const check = validateReceiptPayload2(payload);
2030
+ if (!check.valid) {
2031
+ return failure2(
2032
+ "INVALID_RECEIPT_PAYLOAD",
2033
+ `Missing required receipt field: ${check.field}`,
2034
+ { payload }
2035
+ );
2036
+ }
2037
+ }
2038
+ const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
2039
+ const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
2040
+ const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
2041
+ let signer;
2042
+ try {
2043
+ signer = ethers.verifyTypedData(
2044
+ domain,
2045
+ types,
2046
+ message,
2047
+ artifact.signature
2048
+ );
2049
+ signer = ethers.getAddress(signer);
2050
+ } catch (err) {
2051
+ const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
2052
+ return failure2("SIGNATURE_INVALID", message2, { payload });
2053
+ }
2054
+ return {
2055
+ valid: true,
2056
+ payload,
2057
+ signer,
2058
+ artifactType
2059
+ };
2060
+ }
2061
+ function verifyX402Eip712Offer(artifact) {
2062
+ return verifyX402Eip712Artifact(artifact, "offer");
2063
+ }
2064
+ function verifyX402Eip712Receipt(artifact) {
2065
+ return verifyX402Eip712Artifact(artifact, "receipt");
2066
+ }
2067
+
2068
+ // src/reputation/verify.ts
1270
2069
  function parseChainId(input) {
1271
2070
  if (typeof input === "number") {
1272
2071
  return input;
@@ -1323,18 +2122,18 @@ function decodeJwtPayload(compactJws) {
1323
2122
  return JSON.parse(json);
1324
2123
  }
1325
2124
  async function verifyProof(params) {
1326
- const { proof, provider, expectedSubject, expectedController } = params;
2125
+ const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
1327
2126
  try {
1328
2127
  switch (proof.proofType) {
1329
2128
  case "tx-encoded-value": {
1330
2129
  if (!provider) {
1331
2130
  return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
1332
2131
  }
1333
- if (!expectedSubject || !expectedController) {
2132
+ if (!expectedSubjectDid || !expectedControllerDid) {
1334
2133
  return {
1335
2134
  valid: false,
1336
2135
  proofType: proof.proofType,
1337
- reason: "expectedSubject and expectedController are required"
2136
+ reason: "expectedSubjectDid and expectedControllerDid are required"
1338
2137
  };
1339
2138
  }
1340
2139
  const proofObject = proof.proofObject;
@@ -1346,13 +2145,13 @@ async function verifyProof(params) {
1346
2145
  return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
1347
2146
  }
1348
2147
  const expectedAmount = calculateTransferAmount(
1349
- expectedSubject,
1350
- expectedController,
2148
+ expectedSubjectDid,
2149
+ expectedControllerDid,
1351
2150
  chainId,
1352
2151
  getProofPurpose(proof)
1353
2152
  );
1354
- const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubject));
1355
- const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedController));
2153
+ const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubjectDid));
2154
+ const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
1356
2155
  if (tx.from && ethers.getAddress(tx.from) !== subjectAddress) {
1357
2156
  return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
1358
2157
  }
@@ -1426,6 +2225,38 @@ async function verifyProof(params) {
1426
2225
  if (!proof.proofObject || typeof proof.proofObject !== "object") {
1427
2226
  return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
1428
2227
  }
2228
+ const proofObj = proof.proofObject;
2229
+ if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
2230
+ const artifact = {
2231
+ format: "jws",
2232
+ signature: proofObj.signature
2233
+ };
2234
+ const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
2235
+ if (!jwsResult.valid) {
2236
+ return {
2237
+ valid: false,
2238
+ proofType: proof.proofType,
2239
+ reason: jwsResult.error?.message ?? "JWS verification failed"
2240
+ };
2241
+ }
2242
+ return { valid: true, proofType: proof.proofType };
2243
+ }
2244
+ if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
2245
+ const artifact = {
2246
+ format: "eip712",
2247
+ payload: proofObj.payload,
2248
+ signature: proofObj.signature
2249
+ };
2250
+ const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
2251
+ if (!eip712Result.valid) {
2252
+ return {
2253
+ valid: false,
2254
+ proofType: proof.proofType,
2255
+ reason: eip712Result.error?.message ?? "EIP-712 verification failed"
2256
+ };
2257
+ }
2258
+ return { valid: true, proofType: proof.proofType };
2259
+ }
1429
2260
  return { valid: true, proofType: proof.proofType };
1430
2261
  }
1431
2262
  case "evidence-pointer": {
@@ -1437,9 +2268,9 @@ async function verifyProof(params) {
1437
2268
  if (!response.ok) {
1438
2269
  return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
1439
2270
  }
1440
- if (object.url.endsWith("/.well-known/did.json") && expectedController) {
2271
+ if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
1441
2272
  const didDoc = await response.json();
1442
- const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
2273
+ const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
1443
2274
  return {
1444
2275
  valid: didCheck.valid,
1445
2276
  proofType: proof.proofType,
@@ -1447,10 +2278,10 @@ async function verifyProof(params) {
1447
2278
  };
1448
2279
  }
1449
2280
  const body = await response.text();
1450
- if (expectedController && !body.includes(expectedController)) {
2281
+ if (expectedControllerDid && !body.includes(expectedControllerDid)) {
1451
2282
  try {
1452
2283
  const parsed = parseDnsTxtRecord(body);
1453
- if (parsed.controller !== expectedController) {
2284
+ if (!parsed.controllers.includes(expectedControllerDid)) {
1454
2285
  return {
1455
2286
  valid: false,
1456
2287
  proofType: proof.proofType,
@@ -1506,8 +2337,8 @@ async function verifyAttestation(params) {
1506
2337
  const result = await verifyProof({
1507
2338
  proof,
1508
2339
  provider: params.provider,
1509
- expectedSubject: params.context?.subject,
1510
- expectedController: params.context?.controller
2340
+ expectedSubjectDid: params.context?.subjectDid,
2341
+ expectedControllerDid: params.context?.controllerDid
1511
2342
  });
1512
2343
  checks[proof.proofType] = result.valid;
1513
2344
  if (!result.valid) {
@@ -1612,6 +2443,38 @@ async function callControllerWitness(params) {
1612
2443
  method: "did-json"
1613
2444
  };
1614
2445
  }
2446
+ var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
2447
+ async function requestControllerWitness(params) {
2448
+ const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
2449
+ const body = {
2450
+ subjectDid: params.subjectDid,
2451
+ controllerDid: params.controllerDid
2452
+ };
2453
+ if (params.chainId !== void 0) {
2454
+ body.chainId = params.chainId;
2455
+ }
2456
+ let response;
2457
+ try {
2458
+ response = await fetch(url, {
2459
+ method: "POST",
2460
+ headers: { "Content-Type": "application/json" },
2461
+ credentials: "include",
2462
+ body: JSON.stringify(body),
2463
+ signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
2464
+ });
2465
+ } catch (err) {
2466
+ throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
2467
+ }
2468
+ if (!response.ok) {
2469
+ const errorBody = await response.json().catch(() => ({ error: response.statusText }));
2470
+ throw new OmaTrustError(
2471
+ "API_ERROR",
2472
+ errorBody.error ?? `Controller witness failed: ${response.status}`,
2473
+ { status: response.status, ...errorBody }
2474
+ );
2475
+ }
2476
+ return await response.json();
2477
+ }
1615
2478
 
1616
2479
  // src/reputation/proof/tx-interaction.ts
1617
2480
  init_errors();
@@ -1758,29 +2621,20 @@ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options
1758
2621
  );
1759
2622
  }
1760
2623
 
1761
- // src/reputation/proof/subject-ownership.ts
2624
+ // src/reputation/subject-ownership.ts
1762
2625
  init_did();
1763
2626
  init_errors();
1764
2627
  init_dns_txt_shared();
2628
+
2629
+ // src/reputation/contract-ownership.ts
2630
+ init_did();
1765
2631
  var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
1766
2632
  var OWNERSHIP_PATTERNS = [
1767
2633
  { method: "owner", signature: "function owner() view returns (address)" },
1768
2634
  { method: "admin", signature: "function admin() view returns (address)" },
1769
2635
  { method: "getOwner", signature: "function getOwner() view returns (address)" }
1770
2636
  ];
1771
- function assertConnectedWalletDid(input) {
1772
- const normalized = normalizeDid(input);
1773
- if (extractDidMethod(normalized) !== "pkh") {
1774
- throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
1775
- connectedWalletDid: input
1776
- });
1777
- }
1778
- return normalized;
1779
- }
1780
- function normalizeSubjectDid(input) {
1781
- return normalizeDid(input);
1782
- }
1783
- async function readAddressFromContract(provider, contractAddress, signature, method) {
2637
+ async function readOwnerFromContract(provider, contractAddress, signature, method) {
1784
2638
  try {
1785
2639
  const iface = new ethers.Interface([signature]);
1786
2640
  const data = iface.encodeFunctionData(method, []);
@@ -1793,16 +2647,24 @@ async function readAddressFromContract(provider, contractAddress, signature, met
1793
2647
  }
1794
2648
  return null;
1795
2649
  }
1796
- async function discoverControllingWallet(provider, contractAddress, chainId) {
2650
+ async function discoverContractOwner(provider, contractAddress) {
2651
+ try {
2652
+ const code = await provider.getCode(contractAddress);
2653
+ if (code === "0x" || code === "0x0") {
2654
+ return null;
2655
+ }
2656
+ } catch {
2657
+ return null;
2658
+ }
1797
2659
  for (const pattern of OWNERSHIP_PATTERNS) {
1798
- const address = await readAddressFromContract(
2660
+ const address = await readOwnerFromContract(
1799
2661
  provider,
1800
2662
  contractAddress,
1801
2663
  pattern.signature,
1802
2664
  pattern.method
1803
2665
  );
1804
2666
  if (address) {
1805
- return buildEvmDidPkh(chainId, address);
2667
+ return address;
1806
2668
  }
1807
2669
  }
1808
2670
  try {
@@ -1810,13 +2672,31 @@ async function discoverControllingWallet(provider, contractAddress, chainId) {
1810
2672
  if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1811
2673
  const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
1812
2674
  if (adminAddress !== ethers.ZeroAddress) {
1813
- return buildEvmDidPkh(chainId, adminAddress);
2675
+ return adminAddress;
1814
2676
  }
1815
2677
  }
1816
2678
  } catch {
1817
2679
  }
1818
2680
  return null;
1819
2681
  }
2682
+ async function discoverControllingWalletDid(provider, contractAddress, chainId) {
2683
+ const owner = await discoverContractOwner(provider, contractAddress);
2684
+ return owner ? buildEvmDidPkh(chainId, owner) : null;
2685
+ }
2686
+
2687
+ // src/reputation/subject-ownership.ts
2688
+ function assertConnectedWalletDid(input) {
2689
+ const normalized = normalizeDid(input);
2690
+ if (extractDidMethod(normalized) !== "pkh") {
2691
+ throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
2692
+ connectedWalletDid: input
2693
+ });
2694
+ }
2695
+ return normalized;
2696
+ }
2697
+ function normalizeSubjectDid(input) {
2698
+ return normalizeDid(input);
2699
+ }
1820
2700
  async function verifyDidWebOwnership(params) {
1821
2701
  const subjectDid = normalizeSubjectDid(params.subjectDid);
1822
2702
  const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
@@ -1914,7 +2794,7 @@ async function verifyDidPkhOwnership(params) {
1914
2794
  connectedWalletDid
1915
2795
  };
1916
2796
  }
1917
- const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
2797
+ const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
1918
2798
  if (params.txHash) {
1919
2799
  if (!controllingWalletDid) {
1920
2800
  return {
@@ -1997,7 +2877,7 @@ async function verifyDidPkhOwnership(params) {
1997
2877
  };
1998
2878
  }
1999
2879
  for (const pattern of OWNERSHIP_PATTERNS) {
2000
- const ownerAddress = await readAddressFromContract(
2880
+ const ownerAddress = await readOwnerFromContract(
2001
2881
  params.provider,
2002
2882
  subjectAddress,
2003
2883
  pattern.signature,
@@ -2091,8 +2971,9 @@ exports.createX402ReceiptProof = createX402ReceiptProof;
2091
2971
  exports.decodeAttestationData = decodeAttestationData;
2092
2972
  exports.deduplicateReviews = deduplicateReviews;
2093
2973
  exports.encodeAttestationData = encodeAttestationData;
2094
- exports.extractAddressesFromDidDocument = extractAddressesFromDidDocument;
2974
+ exports.extractEvmAddressesFromDidDocument = extractEvmAddressesFromDidDocument;
2095
2975
  exports.extractExpirationTime = extractExpirationTime;
2976
+ exports.extractJwksFromDidDocument = extractJwksFromDidDocument;
2096
2977
  exports.fetchDidDocument = fetchDidDocument;
2097
2978
  exports.formatSchemaUid = formatSchemaUid;
2098
2979
  exports.formatTransferAmount = formatTransferAmount;
@@ -2113,6 +2994,7 @@ exports.listAttestations = listAttestations;
2113
2994
  exports.normalizeSchema = normalizeSchema;
2114
2995
  exports.parseDnsTxtRecord = parseDnsTxtRecord;
2115
2996
  exports.prepareDelegatedAttestation = prepareDelegatedAttestation;
2997
+ exports.requestControllerWitness = requestControllerWitness;
2116
2998
  exports.revokeAttestation = revokeAttestation;
2117
2999
  exports.schemaToString = schemaToString;
2118
3000
  exports.splitSignature = splitSignature;