@oma3/omatrust 0.1.0-alpha.1 → 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 +21 -7
- package/dist/identity/index.cjs +1 -1
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.js +1 -1
- 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 +656 -91
- 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 +657 -92
- 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 +675 -90
- 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 +669 -92
- 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/index.cjs
CHANGED
|
@@ -158,7 +158,7 @@ function extractDidIdentifier(did) {
|
|
|
158
158
|
}
|
|
159
159
|
function normalizeDomain(domain) {
|
|
160
160
|
assertString(domain, "domain", "INVALID_DID");
|
|
161
|
-
return domain.trim().toLowerCase().replace(/\.$/, "");
|
|
161
|
+
return domain.trim().toLowerCase().replace(/\.$/, "").replace(/^www\./, "");
|
|
162
162
|
}
|
|
163
163
|
function normalizeDidWeb(input) {
|
|
164
164
|
assertString(input, "input", "INVALID_DID");
|
|
@@ -361,6 +361,7 @@ function hashCanonicalizedJson(obj, algorithm) {
|
|
|
361
361
|
var reputation_exports = {};
|
|
362
362
|
__export(reputation_exports, {
|
|
363
363
|
buildDelegatedAttestationTypedData: () => buildDelegatedAttestationTypedData,
|
|
364
|
+
buildDelegatedTypedDataFromEncoded: () => buildDelegatedTypedDataFromEncoded,
|
|
364
365
|
buildDnsTxtRecord: () => buildDnsTxtRecord,
|
|
365
366
|
buildEip712Domain: () => buildEip712Domain,
|
|
366
367
|
calculateAverageUserReviewRating: () => calculateAverageUserReviewRating,
|
|
@@ -384,6 +385,7 @@ __export(reputation_exports, {
|
|
|
384
385
|
formatSchemaUid: () => formatSchemaUid,
|
|
385
386
|
formatTransferAmount: () => formatTransferAmount,
|
|
386
387
|
getAttestation: () => getAttestation,
|
|
388
|
+
getAttestationsByAttester: () => getAttestationsByAttester,
|
|
387
389
|
getAttestationsForDid: () => getAttestationsForDid,
|
|
388
390
|
getChainConstants: () => getChainConstants,
|
|
389
391
|
getExplorerAddressUrl: () => getExplorerAddressUrl,
|
|
@@ -399,16 +401,22 @@ __export(reputation_exports, {
|
|
|
399
401
|
normalizeSchema: () => normalizeSchema,
|
|
400
402
|
parseDnsTxtRecord: () => parseDnsTxtRecord,
|
|
401
403
|
prepareDelegatedAttestation: () => prepareDelegatedAttestation,
|
|
404
|
+
revokeAttestation: () => revokeAttestation,
|
|
402
405
|
schemaToString: () => schemaToString,
|
|
403
406
|
splitSignature: () => splitSignature,
|
|
404
407
|
submitAttestation: () => submitAttestation,
|
|
405
408
|
submitDelegatedAttestation: () => submitDelegatedAttestation,
|
|
409
|
+
validateAttestationData: () => validateAttestationData,
|
|
406
410
|
verifyAttestation: () => verifyAttestation,
|
|
407
411
|
verifyDidDocumentControllerDid: () => verifyDidDocumentControllerDid,
|
|
412
|
+
verifyDidJsonControllerDid: () => verifyDidJsonControllerDid,
|
|
413
|
+
verifyDidPkhOwnership: () => verifyDidPkhOwnership,
|
|
414
|
+
verifyDidWebOwnership: () => verifyDidWebOwnership,
|
|
408
415
|
verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid,
|
|
409
416
|
verifyEip712Signature: () => verifyEip712Signature,
|
|
410
417
|
verifyProof: () => verifyProof,
|
|
411
|
-
verifySchemaExists: () => verifySchemaExists
|
|
418
|
+
verifySchemaExists: () => verifySchemaExists,
|
|
419
|
+
verifySubjectOwnership: () => verifySubjectOwnership
|
|
412
420
|
});
|
|
413
421
|
function normalizeSchema(schema) {
|
|
414
422
|
if (Array.isArray(schema)) {
|
|
@@ -444,6 +452,13 @@ function encodeAttestationData(schema, data) {
|
|
|
444
452
|
if (!data || typeof data !== "object") {
|
|
445
453
|
throw new OmaTrustError("INVALID_INPUT", "data must be an object");
|
|
446
454
|
}
|
|
455
|
+
const validationErrors = validateAttestationData(schema, data);
|
|
456
|
+
if (validationErrors.length > 0) {
|
|
457
|
+
const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
|
|
458
|
+
throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
|
|
459
|
+
errors: validationErrors
|
|
460
|
+
});
|
|
461
|
+
}
|
|
447
462
|
const fields = normalizeSchema(schema);
|
|
448
463
|
const schemaString = schemaToString(fields);
|
|
449
464
|
const encoder = new easSdk.SchemaEncoder(schemaString);
|
|
@@ -456,6 +471,99 @@ function encodeAttestationData(schema, data) {
|
|
|
456
471
|
);
|
|
457
472
|
return encoded;
|
|
458
473
|
}
|
|
474
|
+
function getActualType(value) {
|
|
475
|
+
if (value === null) {
|
|
476
|
+
return "null";
|
|
477
|
+
}
|
|
478
|
+
if (value === void 0) {
|
|
479
|
+
return "undefined";
|
|
480
|
+
}
|
|
481
|
+
if (Array.isArray(value)) {
|
|
482
|
+
return "array";
|
|
483
|
+
}
|
|
484
|
+
if (typeof value === "number") {
|
|
485
|
+
return Number.isFinite(value) ? "number" : "non-finite-number";
|
|
486
|
+
}
|
|
487
|
+
return typeof value;
|
|
488
|
+
}
|
|
489
|
+
function isNumericValue(value, allowNegative) {
|
|
490
|
+
if (typeof value === "bigint") {
|
|
491
|
+
return allowNegative || value >= 0n;
|
|
492
|
+
}
|
|
493
|
+
if (typeof value === "number") {
|
|
494
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
return allowNegative || value >= 0;
|
|
498
|
+
}
|
|
499
|
+
if (typeof value === "string") {
|
|
500
|
+
if (value.length === 0) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
|
|
504
|
+
return pattern.test(value);
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
function isHex(value) {
|
|
509
|
+
return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
|
|
510
|
+
}
|
|
511
|
+
function validateFieldValue(type, value) {
|
|
512
|
+
const normalizedType = type.trim().toLowerCase();
|
|
513
|
+
const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
|
|
514
|
+
if (/^uint\d*$/.test(normalizedType)) {
|
|
515
|
+
return isNumericValue(value, false);
|
|
516
|
+
}
|
|
517
|
+
if (/^int\d*$/.test(normalizedType)) {
|
|
518
|
+
return isNumericValue(value, true);
|
|
519
|
+
}
|
|
520
|
+
if (normalizedType === "string") {
|
|
521
|
+
return typeof value === "string";
|
|
522
|
+
}
|
|
523
|
+
if (normalizedType === "string[]") {
|
|
524
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
525
|
+
}
|
|
526
|
+
if (normalizedType === "bool") {
|
|
527
|
+
return typeof value === "boolean";
|
|
528
|
+
}
|
|
529
|
+
if (normalizedType === "address") {
|
|
530
|
+
return typeof value === "string" && ethers.isAddress(value);
|
|
531
|
+
}
|
|
532
|
+
if (normalizedType === "bytes") {
|
|
533
|
+
return isHex(value) && (value.length - 2) % 2 === 0;
|
|
534
|
+
}
|
|
535
|
+
if (bytesNMatch) {
|
|
536
|
+
const expectedBytes = Number(bytesNMatch[1]);
|
|
537
|
+
return isHex(value) && value.length === 2 + expectedBytes * 2;
|
|
538
|
+
}
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
function validateAttestationData(schema, data) {
|
|
542
|
+
if (!data || typeof data !== "object") {
|
|
543
|
+
return [{
|
|
544
|
+
schemaFieldName: "data",
|
|
545
|
+
expectedType: "object",
|
|
546
|
+
providedType: getActualType(data),
|
|
547
|
+
providedValue: data
|
|
548
|
+
}];
|
|
549
|
+
}
|
|
550
|
+
const fields = normalizeSchema(schema);
|
|
551
|
+
const errors = [];
|
|
552
|
+
for (const field of fields) {
|
|
553
|
+
const value = data[field.name];
|
|
554
|
+
const isValid = validateFieldValue(field.type, value);
|
|
555
|
+
if (isValid) {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
errors.push({
|
|
559
|
+
schemaFieldName: field.name,
|
|
560
|
+
expectedType: field.type,
|
|
561
|
+
providedType: getActualType(value),
|
|
562
|
+
providedValue: value
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
return errors;
|
|
566
|
+
}
|
|
459
567
|
function decodeAttestationData(schema, encodedData) {
|
|
460
568
|
if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
|
|
461
569
|
throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
|
|
@@ -476,22 +584,11 @@ function decodeAttestationData(schema, encodedData) {
|
|
|
476
584
|
return result;
|
|
477
585
|
}
|
|
478
586
|
function extractExpirationTime(data) {
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
if (typeof value === "bigint") {
|
|
486
|
-
return value;
|
|
487
|
-
}
|
|
488
|
-
if (typeof value === "number") {
|
|
489
|
-
return value;
|
|
490
|
-
}
|
|
491
|
-
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
492
|
-
return BigInt(value);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
587
|
+
const value = data["expiresAt"];
|
|
588
|
+
if (value === void 0 || value === null) return void 0;
|
|
589
|
+
if (typeof value === "bigint") return value;
|
|
590
|
+
if (typeof value === "number") return value;
|
|
591
|
+
if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
|
|
495
592
|
return void 0;
|
|
496
593
|
}
|
|
497
594
|
var ZERO_UID = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
@@ -532,6 +629,53 @@ function resolveRecipientAddress(data) {
|
|
|
532
629
|
return ethers.ZeroAddress;
|
|
533
630
|
}
|
|
534
631
|
|
|
632
|
+
// src/reputation/eas-adapter.ts
|
|
633
|
+
function isRecord(value) {
|
|
634
|
+
return Boolean(value) && typeof value === "object";
|
|
635
|
+
}
|
|
636
|
+
function isHex32(value) {
|
|
637
|
+
return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
|
|
638
|
+
}
|
|
639
|
+
function getEasTransactionReceipt(tx) {
|
|
640
|
+
if (!isRecord(tx)) {
|
|
641
|
+
return void 0;
|
|
642
|
+
}
|
|
643
|
+
const candidates = [tx.receipt, tx.tx, tx.transaction];
|
|
644
|
+
for (const candidate of candidates) {
|
|
645
|
+
if (isRecord(candidate)) {
|
|
646
|
+
return candidate;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return void 0;
|
|
650
|
+
}
|
|
651
|
+
function getEasTransactionHash(tx) {
|
|
652
|
+
const receipt = getEasTransactionReceipt(tx);
|
|
653
|
+
if (receipt) {
|
|
654
|
+
const receiptHash = receipt.hash;
|
|
655
|
+
if (isHex32(receiptHash)) {
|
|
656
|
+
return receiptHash;
|
|
657
|
+
}
|
|
658
|
+
const transactionHash = receipt.transactionHash;
|
|
659
|
+
if (isHex32(transactionHash)) {
|
|
660
|
+
return transactionHash;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (isRecord(tx) && isHex32(tx.hash)) {
|
|
664
|
+
return tx.hash;
|
|
665
|
+
}
|
|
666
|
+
return void 0;
|
|
667
|
+
}
|
|
668
|
+
function isEasSchemaNotFoundError(err) {
|
|
669
|
+
if (!isRecord(err)) {
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
const message = err.message;
|
|
673
|
+
if (typeof message !== "string" || message.length === 0) {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
return /schema not found/i.test(message);
|
|
677
|
+
}
|
|
678
|
+
|
|
535
679
|
// src/reputation/submit.ts
|
|
536
680
|
async function submitAttestation(params) {
|
|
537
681
|
if (!params || typeof params !== "object") {
|
|
@@ -562,25 +706,46 @@ async function submitAttestation(params) {
|
|
|
562
706
|
}
|
|
563
707
|
});
|
|
564
708
|
const uid = await tx.wait();
|
|
565
|
-
const
|
|
566
|
-
const
|
|
709
|
+
const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
|
|
710
|
+
const receipt = getEasTransactionReceipt(tx);
|
|
567
711
|
return {
|
|
568
712
|
uid,
|
|
569
713
|
txHash,
|
|
570
|
-
receipt
|
|
714
|
+
receipt
|
|
571
715
|
};
|
|
572
716
|
} catch (err) {
|
|
573
717
|
throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
|
|
574
718
|
}
|
|
575
719
|
}
|
|
576
|
-
function
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
);
|
|
720
|
+
async function revokeAttestation(params) {
|
|
721
|
+
if (!params || typeof params !== "object") {
|
|
722
|
+
throw new OmaTrustError("INVALID_INPUT", "params must be provided");
|
|
723
|
+
}
|
|
724
|
+
if (!params.signer) {
|
|
725
|
+
throw new OmaTrustError("INVALID_INPUT", "signer is required");
|
|
726
|
+
}
|
|
727
|
+
const eas = new easSdk.EAS(params.easContractAddress);
|
|
728
|
+
eas.connect(params.signer);
|
|
729
|
+
try {
|
|
730
|
+
const tx = await eas.revoke({
|
|
731
|
+
schema: params.schemaUid,
|
|
732
|
+
data: {
|
|
733
|
+
uid: params.uid,
|
|
734
|
+
value: toBigIntOrDefault(params.value, 0n)
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
await tx.wait();
|
|
738
|
+
const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
|
|
739
|
+
const receipt = getEasTransactionReceipt(tx);
|
|
740
|
+
return {
|
|
741
|
+
txHash,
|
|
742
|
+
receipt
|
|
743
|
+
};
|
|
744
|
+
} catch (err) {
|
|
745
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function buildDelegatedTypedData(params) {
|
|
584
749
|
return {
|
|
585
750
|
domain: {
|
|
586
751
|
name: "EAS",
|
|
@@ -605,17 +770,52 @@ function buildDelegatedAttestationTypedData(params) {
|
|
|
605
770
|
message: {
|
|
606
771
|
attester: params.attester,
|
|
607
772
|
schema: params.schemaUid,
|
|
608
|
-
recipient,
|
|
609
|
-
expirationTime:
|
|
773
|
+
recipient: params.recipient,
|
|
774
|
+
expirationTime: toBigIntOrDefault(params.expirationTime, 0n),
|
|
610
775
|
revocable: params.revocable ?? true,
|
|
611
776
|
refUID: params.refUid ?? ZERO_UID,
|
|
612
|
-
data: encodedData,
|
|
777
|
+
data: params.encodedData,
|
|
613
778
|
value: toBigIntOrDefault(params.value, 0n),
|
|
614
779
|
nonce: toBigIntOrDefault(params.nonce, 0n),
|
|
615
780
|
deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
|
|
616
781
|
}
|
|
617
782
|
};
|
|
618
783
|
}
|
|
784
|
+
function buildDelegatedAttestationTypedData(params) {
|
|
785
|
+
const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
|
|
786
|
+
const encodedData = encodeAttestationData(params.schema, dataWithHash);
|
|
787
|
+
const recipient = resolveRecipientAddress(dataWithHash);
|
|
788
|
+
return buildDelegatedTypedData({
|
|
789
|
+
chainId: params.chainId,
|
|
790
|
+
easContractAddress: params.easContractAddress,
|
|
791
|
+
schemaUid: params.schemaUid,
|
|
792
|
+
encodedData,
|
|
793
|
+
recipient,
|
|
794
|
+
attester: params.attester,
|
|
795
|
+
nonce: params.nonce,
|
|
796
|
+
revocable: params.revocable,
|
|
797
|
+
expirationTime: params.expirationTime ?? extractExpirationTime(dataWithHash),
|
|
798
|
+
refUid: params.refUid,
|
|
799
|
+
value: params.value,
|
|
800
|
+
deadline: params.deadline
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
function buildDelegatedTypedDataFromEncoded(params) {
|
|
804
|
+
return buildDelegatedTypedData({
|
|
805
|
+
chainId: params.chainId,
|
|
806
|
+
easContractAddress: params.easContractAddress,
|
|
807
|
+
schemaUid: params.schemaUid,
|
|
808
|
+
encodedData: params.encodedData,
|
|
809
|
+
recipient: params.recipient,
|
|
810
|
+
attester: params.attester,
|
|
811
|
+
nonce: params.nonce,
|
|
812
|
+
revocable: params.revocable,
|
|
813
|
+
expirationTime: params.expirationTime,
|
|
814
|
+
refUid: params.refUid,
|
|
815
|
+
value: params.value,
|
|
816
|
+
deadline: params.deadline
|
|
817
|
+
});
|
|
818
|
+
}
|
|
619
819
|
function splitSignature(signature) {
|
|
620
820
|
try {
|
|
621
821
|
const parsed = ethers.Signature.from(signature);
|
|
@@ -692,7 +892,14 @@ async function submitDelegatedAttestation(params) {
|
|
|
692
892
|
var EAS_EVENT_ABI = [
|
|
693
893
|
"event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
|
|
694
894
|
];
|
|
695
|
-
function
|
|
895
|
+
function parseEventTxHash(event) {
|
|
896
|
+
const txHash = event.transactionHash;
|
|
897
|
+
if (typeof txHash !== "string") {
|
|
898
|
+
return void 0;
|
|
899
|
+
}
|
|
900
|
+
return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
|
|
901
|
+
}
|
|
902
|
+
function parseAttestation(attestation, schema, txHash) {
|
|
696
903
|
const rawData = attestation.data ?? "0x";
|
|
697
904
|
const decoded = schema ? decodeAttestationData(schema, rawData) : {};
|
|
698
905
|
const toBigIntSafe = (value) => {
|
|
@@ -712,6 +919,7 @@ function parseAttestation(attestation, schema) {
|
|
|
712
919
|
schema: attestation.schema,
|
|
713
920
|
attester: attestation.attester,
|
|
714
921
|
recipient: attestation.recipient,
|
|
922
|
+
txHash,
|
|
715
923
|
revocable: Boolean(attestation.revocable),
|
|
716
924
|
revocationTime: toBigIntSafe(attestation.revocationTime),
|
|
717
925
|
expirationTime: toBigIntSafe(attestation.expirationTime),
|
|
@@ -737,21 +945,18 @@ async function getAttestation(params) {
|
|
|
737
945
|
throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
|
|
738
946
|
}
|
|
739
947
|
}
|
|
740
|
-
async function
|
|
741
|
-
const
|
|
742
|
-
const
|
|
743
|
-
const toBlock = params.toBlock ?? await provider.getBlockNumber();
|
|
744
|
-
const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
|
|
745
|
-
const filter = contract.filters.Attested(didToAddress(params.did));
|
|
948
|
+
async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
|
|
949
|
+
const contract = new ethers.Contract(easContractAddress, EAS_EVENT_ABI, provider);
|
|
950
|
+
const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
|
|
746
951
|
let events;
|
|
747
952
|
try {
|
|
748
953
|
events = await contract.queryFilter(filter, fromBlock, toBlock);
|
|
749
954
|
} catch (err) {
|
|
750
955
|
throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
|
|
751
956
|
}
|
|
752
|
-
const eas = new easSdk.EAS(
|
|
957
|
+
const eas = new easSdk.EAS(easContractAddress);
|
|
753
958
|
eas.connect(provider);
|
|
754
|
-
const schemaFilter =
|
|
959
|
+
const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
|
|
755
960
|
const results = [];
|
|
756
961
|
for (const event of events) {
|
|
757
962
|
if (!("args" in event && Array.isArray(event.args))) {
|
|
@@ -770,11 +975,39 @@ async function getAttestationsForDid(params) {
|
|
|
770
975
|
if (!attestation || !attestation.uid) {
|
|
771
976
|
continue;
|
|
772
977
|
}
|
|
773
|
-
results.push(parseAttestation(attestation));
|
|
978
|
+
results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
|
|
774
979
|
}
|
|
775
980
|
results.sort((a, b) => Number(b.time - a.time));
|
|
776
981
|
return results;
|
|
777
982
|
}
|
|
983
|
+
async function getAttestationsForDid(params) {
|
|
984
|
+
const provider = params.provider;
|
|
985
|
+
const toBlock = params.toBlock ?? await provider.getBlockNumber();
|
|
986
|
+
const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
|
|
987
|
+
return queryAttestationEvents(
|
|
988
|
+
params.easContractAddress,
|
|
989
|
+
provider,
|
|
990
|
+
fromBlock,
|
|
991
|
+
toBlock,
|
|
992
|
+
{ recipient: didToAddress(params.did), schemas: params.schemas }
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
async function getAttestationsByAttester(params) {
|
|
996
|
+
const provider = params.provider;
|
|
997
|
+
const toBlock = params.toBlock ?? await provider.getBlockNumber();
|
|
998
|
+
const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
|
|
999
|
+
if (params.limit !== void 0 && params.limit <= 0) {
|
|
1000
|
+
return [];
|
|
1001
|
+
}
|
|
1002
|
+
const results = await queryAttestationEvents(
|
|
1003
|
+
params.easContractAddress,
|
|
1004
|
+
provider,
|
|
1005
|
+
fromBlock,
|
|
1006
|
+
toBlock,
|
|
1007
|
+
{ attester: params.attester, schemas: params.schemas }
|
|
1008
|
+
);
|
|
1009
|
+
return params.limit !== void 0 ? results.slice(0, params.limit) : results;
|
|
1010
|
+
}
|
|
778
1011
|
async function listAttestations(params) {
|
|
779
1012
|
const limit = params.limit ?? 20;
|
|
780
1013
|
if (limit <= 0) {
|
|
@@ -785,34 +1018,15 @@ async function listAttestations(params) {
|
|
|
785
1018
|
}
|
|
786
1019
|
async function getLatestAttestations(params) {
|
|
787
1020
|
const provider = params.provider;
|
|
788
|
-
const
|
|
789
|
-
const
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (!("args" in event && Array.isArray(event.args))) {
|
|
798
|
-
continue;
|
|
799
|
-
}
|
|
800
|
-
const args = event.args;
|
|
801
|
-
const uid = args?.[2];
|
|
802
|
-
const schemaUid = args?.[3];
|
|
803
|
-
if (!uid || !schemaUid) {
|
|
804
|
-
continue;
|
|
805
|
-
}
|
|
806
|
-
if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
|
|
807
|
-
continue;
|
|
808
|
-
}
|
|
809
|
-
const attestation = await eas.getAttestation(uid);
|
|
810
|
-
if (!attestation || !attestation.uid) {
|
|
811
|
-
continue;
|
|
812
|
-
}
|
|
813
|
-
results.push(parseAttestation(attestation));
|
|
814
|
-
}
|
|
815
|
-
results.sort((a, b) => Number(b.time - a.time));
|
|
1021
|
+
const toBlock = await provider.getBlockNumber();
|
|
1022
|
+
const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
|
|
1023
|
+
const results = await queryAttestationEvents(
|
|
1024
|
+
params.easContractAddress,
|
|
1025
|
+
provider,
|
|
1026
|
+
fromBlock,
|
|
1027
|
+
toBlock,
|
|
1028
|
+
{ schemas: params.schemas }
|
|
1029
|
+
);
|
|
816
1030
|
return results.slice(0, params.limit ?? 20);
|
|
817
1031
|
}
|
|
818
1032
|
function deduplicateReviews(attestations) {
|
|
@@ -1074,6 +1288,13 @@ function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
|
|
|
1074
1288
|
reason: `No matching address found in DID document (expected ${expectedAddress})`
|
|
1075
1289
|
};
|
|
1076
1290
|
}
|
|
1291
|
+
async function verifyDidJsonControllerDid(domain, expectedControllerDid, options = {}) {
|
|
1292
|
+
if (!domain || typeof domain !== "string") {
|
|
1293
|
+
throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
|
|
1294
|
+
}
|
|
1295
|
+
const didDocument = options.fetchDidDocument ? await options.fetchDidDocument(domain) : await fetchDidDocument(domain);
|
|
1296
|
+
return verifyDidDocumentControllerDid(didDocument, expectedControllerDid);
|
|
1297
|
+
}
|
|
1077
1298
|
function buildEip712Domain(name, version, chainId, verifyingContract) {
|
|
1078
1299
|
return { name, version, chainId, verifyingContract };
|
|
1079
1300
|
}
|
|
@@ -1106,6 +1327,8 @@ function verifyEip712Signature(typedData, signature) {
|
|
|
1106
1327
|
throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
|
|
1107
1328
|
}
|
|
1108
1329
|
}
|
|
1330
|
+
|
|
1331
|
+
// src/reputation/proof/dns-txt-record.ts
|
|
1109
1332
|
function parseDnsTxtRecord(record) {
|
|
1110
1333
|
if (!record || typeof record !== "string") {
|
|
1111
1334
|
throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
|
|
@@ -1133,27 +1356,6 @@ function buildDnsTxtRecord(controllerDid) {
|
|
|
1133
1356
|
const normalized = normalizeDid(controllerDid);
|
|
1134
1357
|
return `v=1;controller=${normalized}`;
|
|
1135
1358
|
}
|
|
1136
|
-
async function verifyDnsTxtControllerDid(domain, expectedControllerDid) {
|
|
1137
|
-
if (!domain || typeof domain !== "string") {
|
|
1138
|
-
throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
|
|
1139
|
-
}
|
|
1140
|
-
const expected = normalizeDid(expectedControllerDid);
|
|
1141
|
-
const host = `_omatrust.${domain.toLowerCase().replace(/\.$/, "")}`;
|
|
1142
|
-
let records;
|
|
1143
|
-
try {
|
|
1144
|
-
records = await promises.resolveTxt(host);
|
|
1145
|
-
} catch (err) {
|
|
1146
|
-
throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
|
|
1147
|
-
}
|
|
1148
|
-
for (const recordParts of records) {
|
|
1149
|
-
const record = recordParts.join("");
|
|
1150
|
-
const parsed = parseDnsTxtRecord(record);
|
|
1151
|
-
if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
|
|
1152
|
-
return { valid: true, record };
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
return { valid: false, reason: "No TXT record matched expected controller DID" };
|
|
1156
|
-
}
|
|
1157
1359
|
|
|
1158
1360
|
// src/reputation/verify.ts
|
|
1159
1361
|
function parseChainId(input) {
|
|
@@ -1434,6 +1636,9 @@ async function getSchemaDetails(schemaRegistry, schemaUid) {
|
|
|
1434
1636
|
revocable: Boolean(details.revocable)
|
|
1435
1637
|
};
|
|
1436
1638
|
} catch (err) {
|
|
1639
|
+
if (isEasSchemaNotFoundError(err)) {
|
|
1640
|
+
throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
|
|
1641
|
+
}
|
|
1437
1642
|
if (err instanceof OmaTrustError) {
|
|
1438
1643
|
throw err;
|
|
1439
1644
|
}
|
|
@@ -1617,6 +1822,366 @@ function createEvidencePointerProof(url) {
|
|
|
1617
1822
|
issuedAt: Math.floor(Date.now() / 1e3)
|
|
1618
1823
|
};
|
|
1619
1824
|
}
|
|
1825
|
+
async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options = {}) {
|
|
1826
|
+
if (!domain || typeof domain !== "string") {
|
|
1827
|
+
throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
|
|
1828
|
+
}
|
|
1829
|
+
const expected = normalizeDid(expectedControllerDid);
|
|
1830
|
+
const prefix = options.recordPrefix ?? "_controllers";
|
|
1831
|
+
const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
|
|
1832
|
+
let records;
|
|
1833
|
+
try {
|
|
1834
|
+
records = await (options.resolveTxt ?? promises.resolveTxt)(host);
|
|
1835
|
+
} catch (err) {
|
|
1836
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
|
|
1837
|
+
}
|
|
1838
|
+
for (const recordParts of records) {
|
|
1839
|
+
const record = recordParts.join("");
|
|
1840
|
+
const parsed = parseDnsTxtRecord(record);
|
|
1841
|
+
if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
|
|
1842
|
+
return { valid: true, record };
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return { valid: false, reason: "No TXT record matched expected controller DID" };
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// src/reputation/proof/dns-txt-shared.ts
|
|
1849
|
+
async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options = {}) {
|
|
1850
|
+
if (!domain || typeof domain !== "string") {
|
|
1851
|
+
throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
|
|
1852
|
+
}
|
|
1853
|
+
if (!options.resolveTxt) {
|
|
1854
|
+
throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
|
|
1855
|
+
}
|
|
1856
|
+
const expected = normalizeDid(expectedControllerDid);
|
|
1857
|
+
const prefix = options.recordPrefix ?? "_controllers";
|
|
1858
|
+
const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
|
|
1859
|
+
let records;
|
|
1860
|
+
try {
|
|
1861
|
+
records = await options.resolveTxt(host);
|
|
1862
|
+
} catch (err) {
|
|
1863
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
|
|
1864
|
+
}
|
|
1865
|
+
for (const recordParts of records) {
|
|
1866
|
+
const record = recordParts.join("");
|
|
1867
|
+
const parsed = parseDnsTxtRecord(record);
|
|
1868
|
+
if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
|
|
1869
|
+
return { valid: true, record };
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
return { valid: false, reason: "No TXT record matched expected controller DID" };
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// src/reputation/proof/subject-ownership.ts
|
|
1876
|
+
var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
|
|
1877
|
+
var OWNERSHIP_PATTERNS = [
|
|
1878
|
+
{ method: "owner", signature: "function owner() view returns (address)" },
|
|
1879
|
+
{ method: "admin", signature: "function admin() view returns (address)" },
|
|
1880
|
+
{ method: "getOwner", signature: "function getOwner() view returns (address)" }
|
|
1881
|
+
];
|
|
1882
|
+
function assertConnectedWalletDid(input) {
|
|
1883
|
+
const normalized = normalizeDid(input);
|
|
1884
|
+
if (extractDidMethod(normalized) !== "pkh") {
|
|
1885
|
+
throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
|
|
1886
|
+
connectedWalletDid: input
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
return normalized;
|
|
1890
|
+
}
|
|
1891
|
+
function normalizeSubjectDid(input) {
|
|
1892
|
+
return normalizeDid(input);
|
|
1893
|
+
}
|
|
1894
|
+
async function readAddressFromContract(provider, contractAddress, signature, method) {
|
|
1895
|
+
try {
|
|
1896
|
+
const iface = new ethers.Interface([signature]);
|
|
1897
|
+
const data = iface.encodeFunctionData(method, []);
|
|
1898
|
+
const result = await provider.call({ to: contractAddress, data });
|
|
1899
|
+
const [value] = iface.decodeFunctionResult(method, result);
|
|
1900
|
+
if (typeof value === "string" && ethers.isAddress(value) && value !== ethers.ZeroAddress) {
|
|
1901
|
+
return ethers.getAddress(value);
|
|
1902
|
+
}
|
|
1903
|
+
} catch {
|
|
1904
|
+
}
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
async function discoverControllingWallet(provider, contractAddress, chainId) {
|
|
1908
|
+
for (const pattern of OWNERSHIP_PATTERNS) {
|
|
1909
|
+
const address = await readAddressFromContract(
|
|
1910
|
+
provider,
|
|
1911
|
+
contractAddress,
|
|
1912
|
+
pattern.signature,
|
|
1913
|
+
pattern.method
|
|
1914
|
+
);
|
|
1915
|
+
if (address) {
|
|
1916
|
+
return buildEvmDidPkh(chainId, address);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
try {
|
|
1920
|
+
const adminValue = await provider.getStorage(contractAddress, EIP1967_ADMIN_SLOT);
|
|
1921
|
+
if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
1922
|
+
const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
|
|
1923
|
+
if (adminAddress !== ethers.ZeroAddress) {
|
|
1924
|
+
return buildEvmDidPkh(chainId, adminAddress);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
} catch {
|
|
1928
|
+
}
|
|
1929
|
+
return null;
|
|
1930
|
+
}
|
|
1931
|
+
async function verifyDidWebOwnership(params) {
|
|
1932
|
+
const subjectDid = normalizeSubjectDid(params.subjectDid);
|
|
1933
|
+
const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
|
|
1934
|
+
const domain = getDomainFromDidWeb(subjectDid);
|
|
1935
|
+
if (!domain) {
|
|
1936
|
+
throw new OmaTrustError("INVALID_INPUT", "subjectDid must be a did:web DID", {
|
|
1937
|
+
subjectDid: params.subjectDid
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
let dnsReason;
|
|
1941
|
+
try {
|
|
1942
|
+
const dnsResult = await verifyDnsTxtControllerDid2(domain, connectedWalletDid, {
|
|
1943
|
+
resolveTxt: params.resolveTxt,
|
|
1944
|
+
recordPrefix: params.recordPrefix
|
|
1945
|
+
});
|
|
1946
|
+
if (dnsResult.valid) {
|
|
1947
|
+
return {
|
|
1948
|
+
valid: true,
|
|
1949
|
+
method: "dns",
|
|
1950
|
+
details: `Verified via DNS TXT record at ${params.recordPrefix ?? "_controllers"}.${domain}`,
|
|
1951
|
+
subjectDid,
|
|
1952
|
+
connectedWalletDid
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
dnsReason = dnsResult.reason;
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
dnsReason = error instanceof Error ? error.message : "DNS TXT verification failed";
|
|
1958
|
+
}
|
|
1959
|
+
let didDocReason;
|
|
1960
|
+
try {
|
|
1961
|
+
const didDocumentResult = await verifyDidJsonControllerDid(domain, connectedWalletDid, {
|
|
1962
|
+
fetchDidDocument: params.fetchDidDocument
|
|
1963
|
+
});
|
|
1964
|
+
if (didDocumentResult.valid) {
|
|
1965
|
+
return {
|
|
1966
|
+
valid: true,
|
|
1967
|
+
method: "did-document",
|
|
1968
|
+
details: `Verified via DID document at https://${domain}/.well-known/did.json`,
|
|
1969
|
+
subjectDid,
|
|
1970
|
+
connectedWalletDid
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
didDocReason = didDocumentResult.reason;
|
|
1974
|
+
} catch (error) {
|
|
1975
|
+
didDocReason = error instanceof Error ? error.message : "DID document verification failed";
|
|
1976
|
+
}
|
|
1977
|
+
return {
|
|
1978
|
+
valid: false,
|
|
1979
|
+
reason: "DID ownership verification failed",
|
|
1980
|
+
details: `DNS check: ${dnsReason ?? "failed"}. DID document check: ${didDocReason ?? "failed"}.`,
|
|
1981
|
+
subjectDid,
|
|
1982
|
+
connectedWalletDid
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
async function verifyDidPkhOwnership(params) {
|
|
1986
|
+
const subjectDid = normalizeSubjectDid(params.subjectDid);
|
|
1987
|
+
const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
|
|
1988
|
+
if (!isEvmDidPkh(subjectDid)) {
|
|
1989
|
+
throw new OmaTrustError("INVALID_INPUT", "subjectDid must be an EVM did:pkh DID", {
|
|
1990
|
+
subjectDid: params.subjectDid
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
const subjectAddress = getAddressFromDidPkh(subjectDid);
|
|
1994
|
+
const connectedWalletAddress = getAddressFromDidPkh(connectedWalletDid);
|
|
1995
|
+
const chainIdRaw = getChainIdFromDidPkh(subjectDid);
|
|
1996
|
+
if (!subjectAddress || !connectedWalletAddress || !chainIdRaw) {
|
|
1997
|
+
throw new OmaTrustError("INVALID_INPUT", "Could not parse did:pkh ownership inputs", {
|
|
1998
|
+
subjectDid: params.subjectDid,
|
|
1999
|
+
connectedWalletDid: params.connectedWalletDid
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
const chainId = Number(chainIdRaw);
|
|
2003
|
+
if (!Number.isFinite(chainId)) {
|
|
2004
|
+
throw new OmaTrustError("INVALID_INPUT", "Invalid chain id in subjectDid", {
|
|
2005
|
+
subjectDid: params.subjectDid
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
const code = await params.provider.getCode(subjectAddress);
|
|
2009
|
+
const isContract = code !== "0x" && code !== "0x0";
|
|
2010
|
+
if (!isContract) {
|
|
2011
|
+
if (ethers.getAddress(subjectAddress) === ethers.getAddress(connectedWalletAddress)) {
|
|
2012
|
+
return {
|
|
2013
|
+
valid: true,
|
|
2014
|
+
method: "wallet",
|
|
2015
|
+
details: "Verified direct wallet ownership from matching did:pkh subject and connected wallet",
|
|
2016
|
+
subjectDid,
|
|
2017
|
+
connectedWalletDid
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
return {
|
|
2021
|
+
valid: false,
|
|
2022
|
+
reason: "EOA did:pkh subject does not match connected wallet",
|
|
2023
|
+
details: "For direct wallet did:pkh subjects, connectedWalletDid must match the subject DID.",
|
|
2024
|
+
subjectDid,
|
|
2025
|
+
connectedWalletDid
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
const controllingWalletDid = await discoverControllingWallet(params.provider, subjectAddress, chainId);
|
|
2029
|
+
if (params.txHash) {
|
|
2030
|
+
if (!controllingWalletDid) {
|
|
2031
|
+
return {
|
|
2032
|
+
valid: false,
|
|
2033
|
+
reason: "Could not discover controlling wallet",
|
|
2034
|
+
details: "Contract does not expose owner/admin/getOwner or a readable EIP-1967 admin slot for transfer verification.",
|
|
2035
|
+
subjectDid,
|
|
2036
|
+
connectedWalletDid
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
const tx = await params.provider.getTransaction(params.txHash);
|
|
2040
|
+
if (!tx) {
|
|
2041
|
+
return {
|
|
2042
|
+
valid: false,
|
|
2043
|
+
reason: "Transaction not found",
|
|
2044
|
+
details: `Transaction ${params.txHash} was not found on chain ${chainId}.`,
|
|
2045
|
+
subjectDid,
|
|
2046
|
+
connectedWalletDid,
|
|
2047
|
+
controllingWalletDid
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
const receipt = await params.provider.getTransactionReceipt(params.txHash);
|
|
2051
|
+
if (!receipt) {
|
|
2052
|
+
return {
|
|
2053
|
+
valid: false,
|
|
2054
|
+
reason: "Transaction not confirmed",
|
|
2055
|
+
details: "Transaction exists but is not yet confirmed.",
|
|
2056
|
+
subjectDid,
|
|
2057
|
+
connectedWalletDid,
|
|
2058
|
+
controllingWalletDid
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
const controllingWalletAddress = getAddressFromDidPkh(controllingWalletDid);
|
|
2062
|
+
if (!tx.from || !controllingWalletAddress || ethers.getAddress(tx.from) !== ethers.getAddress(controllingWalletAddress)) {
|
|
2063
|
+
return {
|
|
2064
|
+
valid: false,
|
|
2065
|
+
reason: "Wrong sender",
|
|
2066
|
+
details: `Transfer must originate from controlling wallet ${controllingWalletDid}.`,
|
|
2067
|
+
subjectDid,
|
|
2068
|
+
connectedWalletDid,
|
|
2069
|
+
controllingWalletDid
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
if (!tx.to || ethers.getAddress(tx.to) !== ethers.getAddress(connectedWalletAddress)) {
|
|
2073
|
+
return {
|
|
2074
|
+
valid: false,
|
|
2075
|
+
reason: "Wrong recipient",
|
|
2076
|
+
details: `Transfer must be sent to connected wallet ${connectedWalletDid}.`,
|
|
2077
|
+
subjectDid,
|
|
2078
|
+
connectedWalletDid,
|
|
2079
|
+
controllingWalletDid
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
const expectedAmount = calculateTransferAmount(
|
|
2083
|
+
subjectDid,
|
|
2084
|
+
connectedWalletDid,
|
|
2085
|
+
chainId,
|
|
2086
|
+
"shared-control"
|
|
2087
|
+
);
|
|
2088
|
+
const actualValue = BigInt(tx.value ?? 0n);
|
|
2089
|
+
if (actualValue !== expectedAmount) {
|
|
2090
|
+
return {
|
|
2091
|
+
valid: false,
|
|
2092
|
+
reason: "Wrong amount",
|
|
2093
|
+
details: `Transfer amount ${actualValue.toString()} does not match expected proof amount ${expectedAmount.toString()}.`,
|
|
2094
|
+
subjectDid,
|
|
2095
|
+
connectedWalletDid,
|
|
2096
|
+
controllingWalletDid
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
await params.provider.getBlockNumber();
|
|
2100
|
+
await params.provider.getBlock(receipt.blockNumber);
|
|
2101
|
+
return {
|
|
2102
|
+
valid: true,
|
|
2103
|
+
method: "transfer",
|
|
2104
|
+
details: `Verified via transfer proof ${params.txHash}.`,
|
|
2105
|
+
subjectDid,
|
|
2106
|
+
connectedWalletDid,
|
|
2107
|
+
controllingWalletDid
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
for (const pattern of OWNERSHIP_PATTERNS) {
|
|
2111
|
+
const ownerAddress = await readAddressFromContract(
|
|
2112
|
+
params.provider,
|
|
2113
|
+
subjectAddress,
|
|
2114
|
+
pattern.signature,
|
|
2115
|
+
pattern.method
|
|
2116
|
+
);
|
|
2117
|
+
if (ownerAddress && ethers.getAddress(ownerAddress) === ethers.getAddress(connectedWalletAddress)) {
|
|
2118
|
+
return {
|
|
2119
|
+
valid: true,
|
|
2120
|
+
method: "contract",
|
|
2121
|
+
details: `Verified via ${pattern.method}() ownership check.`,
|
|
2122
|
+
subjectDid,
|
|
2123
|
+
connectedWalletDid,
|
|
2124
|
+
controllingWalletDid: controllingWalletDid ?? void 0
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
try {
|
|
2129
|
+
const adminValue = await params.provider.getStorage(subjectAddress, EIP1967_ADMIN_SLOT);
|
|
2130
|
+
if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
2131
|
+
const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
|
|
2132
|
+
if (adminAddress === ethers.getAddress(connectedWalletAddress)) {
|
|
2133
|
+
return {
|
|
2134
|
+
valid: true,
|
|
2135
|
+
method: "contract",
|
|
2136
|
+
details: "Verified via EIP-1967 admin slot.",
|
|
2137
|
+
subjectDid,
|
|
2138
|
+
connectedWalletDid,
|
|
2139
|
+
controllingWalletDid: controllingWalletDid ?? buildEvmDidPkh(chainId, adminAddress)
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
} catch {
|
|
2144
|
+
}
|
|
2145
|
+
if (ethers.getAddress(subjectAddress) === ethers.getAddress(connectedWalletAddress) && controllingWalletDid) {
|
|
2146
|
+
return {
|
|
2147
|
+
valid: true,
|
|
2148
|
+
method: "minting-wallet",
|
|
2149
|
+
details: `Verified because connected wallet matches the contract DID address. Controlling wallet is ${controllingWalletDid}.`,
|
|
2150
|
+
subjectDid,
|
|
2151
|
+
connectedWalletDid,
|
|
2152
|
+
controllingWalletDid
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
return {
|
|
2156
|
+
valid: false,
|
|
2157
|
+
reason: "Contract ownership verification failed",
|
|
2158
|
+
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}.`,
|
|
2159
|
+
subjectDid,
|
|
2160
|
+
connectedWalletDid,
|
|
2161
|
+
controllingWalletDid: controllingWalletDid ?? void 0
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
async function verifySubjectOwnership(params) {
|
|
2165
|
+
const subjectDid = normalizeSubjectDid(params.subjectDid);
|
|
2166
|
+
const method = extractDidMethod(subjectDid);
|
|
2167
|
+
if (method === "web") {
|
|
2168
|
+
return verifyDidWebOwnership(params);
|
|
2169
|
+
}
|
|
2170
|
+
if (method === "pkh") {
|
|
2171
|
+
const didPkhParams = params;
|
|
2172
|
+
if (!didPkhParams.provider) {
|
|
2173
|
+
throw new OmaTrustError(
|
|
2174
|
+
"INVALID_INPUT",
|
|
2175
|
+
"provider is required for did:pkh ownership verification",
|
|
2176
|
+
{ subjectDid }
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2179
|
+
return verifyDidPkhOwnership(didPkhParams);
|
|
2180
|
+
}
|
|
2181
|
+
throw new OmaTrustError("INVALID_INPUT", "Unsupported DID type for ownership verification", {
|
|
2182
|
+
subjectDid
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
1620
2185
|
|
|
1621
2186
|
// src/app-registry/index.ts
|
|
1622
2187
|
var app_registry_exports = {};
|