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