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