@oma3/omatrust 0.1.0-alpha.4 → 0.1.0-alpha.6

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.
@@ -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 keys = ["expirationTime", "expiration", "validUntil", "expiresAt", "expires"];
94
- for (const key of keys) {
95
- const value = data[key];
96
- if (value === void 0 || value === null) {
97
- continue;
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
 
@@ -418,6 +507,34 @@ async function submitAttestation(params) {
418
507
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
419
508
  }
420
509
  }
510
+ async function revokeAttestation(params) {
511
+ if (!params || typeof params !== "object") {
512
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
513
+ }
514
+ if (!params.signer) {
515
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
516
+ }
517
+ const eas = new easSdk.EAS(params.easContractAddress);
518
+ eas.connect(params.signer);
519
+ try {
520
+ const tx = await eas.revoke({
521
+ schema: params.schemaUid,
522
+ data: {
523
+ uid: params.uid,
524
+ value: toBigIntOrDefault(params.value, 0n)
525
+ }
526
+ });
527
+ await tx.wait();
528
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
529
+ const receipt = getEasTransactionReceipt(tx);
530
+ return {
531
+ txHash,
532
+ receipt
533
+ };
534
+ } catch (err) {
535
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
536
+ }
537
+ }
421
538
  function buildDelegatedTypedData(params) {
422
539
  return {
423
540
  domain: {
@@ -565,7 +682,14 @@ async function submitDelegatedAttestation(params) {
565
682
  var EAS_EVENT_ABI = [
566
683
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
567
684
  ];
568
- function parseAttestation(attestation, schema) {
685
+ function parseEventTxHash(event) {
686
+ const txHash = event.transactionHash;
687
+ if (typeof txHash !== "string") {
688
+ return void 0;
689
+ }
690
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
691
+ }
692
+ function parseAttestation(attestation, schema, txHash) {
569
693
  const rawData = attestation.data ?? "0x";
570
694
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
571
695
  const toBigIntSafe = (value) => {
@@ -585,6 +709,7 @@ function parseAttestation(attestation, schema) {
585
709
  schema: attestation.schema,
586
710
  attester: attestation.attester,
587
711
  recipient: attestation.recipient,
712
+ txHash,
588
713
  revocable: Boolean(attestation.revocable),
589
714
  revocationTime: toBigIntSafe(attestation.revocationTime),
590
715
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -610,21 +735,18 @@ async function getAttestation(params) {
610
735
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
611
736
  }
612
737
  }
613
- async function getAttestationsForDid(params) {
614
- const provider = params.provider;
615
- const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
616
- const toBlock = params.toBlock ?? await provider.getBlockNumber();
617
- const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
618
- const filter = contract.filters.Attested(didToAddress(params.did));
738
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
739
+ const contract = new ethers.Contract(easContractAddress, EAS_EVENT_ABI, provider);
740
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
619
741
  let events;
620
742
  try {
621
743
  events = await contract.queryFilter(filter, fromBlock, toBlock);
622
744
  } catch (err) {
623
745
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
624
746
  }
625
- const eas = new easSdk.EAS(params.easContractAddress);
747
+ const eas = new easSdk.EAS(easContractAddress);
626
748
  eas.connect(provider);
627
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
749
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
628
750
  const results = [];
629
751
  for (const event of events) {
630
752
  if (!("args" in event && Array.isArray(event.args))) {
@@ -643,11 +765,39 @@ async function getAttestationsForDid(params) {
643
765
  if (!attestation || !attestation.uid) {
644
766
  continue;
645
767
  }
646
- results.push(parseAttestation(attestation));
768
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
647
769
  }
648
770
  results.sort((a, b) => Number(b.time - a.time));
649
771
  return results;
650
772
  }
773
+ async function getAttestationsForDid(params) {
774
+ const provider = params.provider;
775
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
776
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
777
+ return queryAttestationEvents(
778
+ params.easContractAddress,
779
+ provider,
780
+ fromBlock,
781
+ toBlock,
782
+ { recipient: didToAddress(params.did), schemas: params.schemas }
783
+ );
784
+ }
785
+ async function getAttestationsByAttester(params) {
786
+ const provider = params.provider;
787
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
788
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
789
+ if (params.limit !== void 0 && params.limit <= 0) {
790
+ return [];
791
+ }
792
+ const results = await queryAttestationEvents(
793
+ params.easContractAddress,
794
+ provider,
795
+ fromBlock,
796
+ toBlock,
797
+ { attester: params.attester, schemas: params.schemas }
798
+ );
799
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
800
+ }
651
801
  async function listAttestations(params) {
652
802
  const limit = params.limit ?? 20;
653
803
  if (limit <= 0) {
@@ -658,34 +808,15 @@ async function listAttestations(params) {
658
808
  }
659
809
  async function getLatestAttestations(params) {
660
810
  const provider = params.provider;
661
- const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
662
- const currentBlock = await provider.getBlockNumber();
663
- const fromBlock = params.fromBlock ?? Math.max(0, currentBlock - 5e4);
664
- const events = await contract.queryFilter(contract.filters.Attested(), fromBlock, currentBlock);
665
- const eas = new easSdk.EAS(params.easContractAddress);
666
- eas.connect(provider);
667
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
668
- const results = [];
669
- for (const event of events) {
670
- if (!("args" in event && Array.isArray(event.args))) {
671
- continue;
672
- }
673
- const args = event.args;
674
- const uid = args?.[2];
675
- const schemaUid = args?.[3];
676
- if (!uid || !schemaUid) {
677
- continue;
678
- }
679
- if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
680
- continue;
681
- }
682
- const attestation = await eas.getAttestation(uid);
683
- if (!attestation || !attestation.uid) {
684
- continue;
685
- }
686
- results.push(parseAttestation(attestation));
687
- }
688
- results.sort((a, b) => Number(b.time - a.time));
811
+ const toBlock = await provider.getBlockNumber();
812
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
813
+ const results = await queryAttestationEvents(
814
+ params.easContractAddress,
815
+ provider,
816
+ fromBlock,
817
+ toBlock,
818
+ { schemas: params.schemas }
819
+ );
689
820
  return results.slice(0, params.limit ?? 20);
690
821
  }
691
822
  function deduplicateReviews(attestations) {
@@ -1521,6 +1652,7 @@ exports.fetchDidDocument = fetchDidDocument;
1521
1652
  exports.formatSchemaUid = formatSchemaUid;
1522
1653
  exports.formatTransferAmount = formatTransferAmount;
1523
1654
  exports.getAttestation = getAttestation;
1655
+ exports.getAttestationsByAttester = getAttestationsByAttester;
1524
1656
  exports.getAttestationsForDid = getAttestationsForDid;
1525
1657
  exports.getChainConstants = getChainConstants;
1526
1658
  exports.getExplorerAddressUrl = getExplorerAddressUrl;
@@ -1536,10 +1668,12 @@ exports.listAttestations = listAttestations;
1536
1668
  exports.normalizeSchema = normalizeSchema;
1537
1669
  exports.parseDnsTxtRecord = parseDnsTxtRecord;
1538
1670
  exports.prepareDelegatedAttestation = prepareDelegatedAttestation;
1671
+ exports.revokeAttestation = revokeAttestation;
1539
1672
  exports.schemaToString = schemaToString;
1540
1673
  exports.splitSignature = splitSignature;
1541
1674
  exports.submitAttestation = submitAttestation;
1542
1675
  exports.submitDelegatedAttestation = submitDelegatedAttestation;
1676
+ exports.validateAttestationData = validateAttestationData;
1543
1677
  exports.verifyAttestation = verifyAttestation;
1544
1678
  exports.verifyDidDocumentControllerDid = verifyDidDocumentControllerDid;
1545
1679
  exports.verifyDnsTxtControllerDid = verifyDnsTxtControllerDid;