@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.
package/dist/index.cjs CHANGED
@@ -385,6 +385,7 @@ __export(reputation_exports, {
385
385
  formatSchemaUid: () => formatSchemaUid,
386
386
  formatTransferAmount: () => formatTransferAmount,
387
387
  getAttestation: () => getAttestation,
388
+ getAttestationsByAttester: () => getAttestationsByAttester,
388
389
  getAttestationsForDid: () => getAttestationsForDid,
389
390
  getChainConstants: () => getChainConstants,
390
391
  getExplorerAddressUrl: () => getExplorerAddressUrl,
@@ -400,10 +401,12 @@ __export(reputation_exports, {
400
401
  normalizeSchema: () => normalizeSchema,
401
402
  parseDnsTxtRecord: () => parseDnsTxtRecord,
402
403
  prepareDelegatedAttestation: () => prepareDelegatedAttestation,
404
+ revokeAttestation: () => revokeAttestation,
403
405
  schemaToString: () => schemaToString,
404
406
  splitSignature: () => splitSignature,
405
407
  submitAttestation: () => submitAttestation,
406
408
  submitDelegatedAttestation: () => submitDelegatedAttestation,
409
+ validateAttestationData: () => validateAttestationData,
407
410
  verifyAttestation: () => verifyAttestation,
408
411
  verifyDidDocumentControllerDid: () => verifyDidDocumentControllerDid,
409
412
  verifyDnsTxtControllerDid: () => verifyDnsTxtControllerDid,
@@ -445,6 +448,13 @@ function encodeAttestationData(schema, data) {
445
448
  if (!data || typeof data !== "object") {
446
449
  throw new OmaTrustError("INVALID_INPUT", "data must be an object");
447
450
  }
451
+ const validationErrors = validateAttestationData(schema, data);
452
+ if (validationErrors.length > 0) {
453
+ const summary = validationErrors.map((error) => `Field "${error.schemaFieldName}" (${error.expectedType}) got ${error.providedType}`).join("; ");
454
+ throw new OmaTrustError("INVALID_INPUT", `Attestation data validation failed: ${summary}`, {
455
+ errors: validationErrors
456
+ });
457
+ }
448
458
  const fields = normalizeSchema(schema);
449
459
  const schemaString = schemaToString(fields);
450
460
  const encoder = new easSdk.SchemaEncoder(schemaString);
@@ -457,6 +467,99 @@ function encodeAttestationData(schema, data) {
457
467
  );
458
468
  return encoded;
459
469
  }
470
+ function getActualType(value) {
471
+ if (value === null) {
472
+ return "null";
473
+ }
474
+ if (value === void 0) {
475
+ return "undefined";
476
+ }
477
+ if (Array.isArray(value)) {
478
+ return "array";
479
+ }
480
+ if (typeof value === "number") {
481
+ return Number.isFinite(value) ? "number" : "non-finite-number";
482
+ }
483
+ return typeof value;
484
+ }
485
+ function isNumericValue(value, allowNegative) {
486
+ if (typeof value === "bigint") {
487
+ return allowNegative || value >= 0n;
488
+ }
489
+ if (typeof value === "number") {
490
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
491
+ return false;
492
+ }
493
+ return allowNegative || value >= 0;
494
+ }
495
+ if (typeof value === "string") {
496
+ if (value.length === 0) {
497
+ return false;
498
+ }
499
+ const pattern = allowNegative ? /^-?\d+$/ : /^\d+$/;
500
+ return pattern.test(value);
501
+ }
502
+ return false;
503
+ }
504
+ function isHex(value) {
505
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
506
+ }
507
+ function validateFieldValue(type, value) {
508
+ const normalizedType = type.trim().toLowerCase();
509
+ const bytesNMatch = normalizedType.match(/^bytes([1-9]|[12]\d|3[0-2])$/);
510
+ if (/^uint\d*$/.test(normalizedType)) {
511
+ return isNumericValue(value, false);
512
+ }
513
+ if (/^int\d*$/.test(normalizedType)) {
514
+ return isNumericValue(value, true);
515
+ }
516
+ if (normalizedType === "string") {
517
+ return typeof value === "string";
518
+ }
519
+ if (normalizedType === "string[]") {
520
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
521
+ }
522
+ if (normalizedType === "bool") {
523
+ return typeof value === "boolean";
524
+ }
525
+ if (normalizedType === "address") {
526
+ return typeof value === "string" && ethers.isAddress(value);
527
+ }
528
+ if (normalizedType === "bytes") {
529
+ return isHex(value) && (value.length - 2) % 2 === 0;
530
+ }
531
+ if (bytesNMatch) {
532
+ const expectedBytes = Number(bytesNMatch[1]);
533
+ return isHex(value) && value.length === 2 + expectedBytes * 2;
534
+ }
535
+ return true;
536
+ }
537
+ function validateAttestationData(schema, data) {
538
+ if (!data || typeof data !== "object") {
539
+ return [{
540
+ schemaFieldName: "data",
541
+ expectedType: "object",
542
+ providedType: getActualType(data),
543
+ providedValue: data
544
+ }];
545
+ }
546
+ const fields = normalizeSchema(schema);
547
+ const errors = [];
548
+ for (const field of fields) {
549
+ const value = data[field.name];
550
+ const isValid = validateFieldValue(field.type, value);
551
+ if (isValid) {
552
+ continue;
553
+ }
554
+ errors.push({
555
+ schemaFieldName: field.name,
556
+ expectedType: field.type,
557
+ providedType: getActualType(value),
558
+ providedValue: value
559
+ });
560
+ }
561
+ return errors;
562
+ }
460
563
  function decodeAttestationData(schema, encodedData) {
461
564
  if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
462
565
  throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
@@ -477,22 +580,11 @@ function decodeAttestationData(schema, encodedData) {
477
580
  return result;
478
581
  }
479
582
  function extractExpirationTime(data) {
480
- const keys = ["expirationTime", "expiration", "validUntil", "expiresAt", "expires"];
481
- for (const key of keys) {
482
- const value = data[key];
483
- if (value === void 0 || value === null) {
484
- continue;
485
- }
486
- if (typeof value === "bigint") {
487
- return value;
488
- }
489
- if (typeof value === "number") {
490
- return value;
491
- }
492
- if (typeof value === "string" && /^\d+$/.test(value)) {
493
- return BigInt(value);
494
- }
495
- }
583
+ const value = data["expiresAt"];
584
+ if (value === void 0 || value === null) return void 0;
585
+ if (typeof value === "bigint") return value;
586
+ if (typeof value === "number") return value;
587
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
496
588
  return void 0;
497
589
  }
498
590
  var ZERO_UID = "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -621,6 +713,34 @@ async function submitAttestation(params) {
621
713
  throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
622
714
  }
623
715
  }
716
+ async function revokeAttestation(params) {
717
+ if (!params || typeof params !== "object") {
718
+ throw new OmaTrustError("INVALID_INPUT", "params must be provided");
719
+ }
720
+ if (!params.signer) {
721
+ throw new OmaTrustError("INVALID_INPUT", "signer is required");
722
+ }
723
+ const eas = new easSdk.EAS(params.easContractAddress);
724
+ eas.connect(params.signer);
725
+ try {
726
+ const tx = await eas.revoke({
727
+ schema: params.schemaUid,
728
+ data: {
729
+ uid: params.uid,
730
+ value: toBigIntOrDefault(params.value, 0n)
731
+ }
732
+ });
733
+ await tx.wait();
734
+ const txHash = getEasTransactionHash(tx) ?? ZERO_UID;
735
+ const receipt = getEasTransactionReceipt(tx);
736
+ return {
737
+ txHash,
738
+ receipt
739
+ };
740
+ } catch (err) {
741
+ throw new OmaTrustError("NETWORK_ERROR", "Failed to revoke attestation", { err });
742
+ }
743
+ }
624
744
  function buildDelegatedTypedData(params) {
625
745
  return {
626
746
  domain: {
@@ -768,7 +888,14 @@ async function submitDelegatedAttestation(params) {
768
888
  var EAS_EVENT_ABI = [
769
889
  "event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
770
890
  ];
771
- function parseAttestation(attestation, schema) {
891
+ function parseEventTxHash(event) {
892
+ const txHash = event.transactionHash;
893
+ if (typeof txHash !== "string") {
894
+ return void 0;
895
+ }
896
+ return /^0x[0-9a-fA-F]{64}$/.test(txHash) ? txHash : void 0;
897
+ }
898
+ function parseAttestation(attestation, schema, txHash) {
772
899
  const rawData = attestation.data ?? "0x";
773
900
  const decoded = schema ? decodeAttestationData(schema, rawData) : {};
774
901
  const toBigIntSafe = (value) => {
@@ -788,6 +915,7 @@ function parseAttestation(attestation, schema) {
788
915
  schema: attestation.schema,
789
916
  attester: attestation.attester,
790
917
  recipient: attestation.recipient,
918
+ txHash,
791
919
  revocable: Boolean(attestation.revocable),
792
920
  revocationTime: toBigIntSafe(attestation.revocationTime),
793
921
  expirationTime: toBigIntSafe(attestation.expirationTime),
@@ -813,21 +941,18 @@ async function getAttestation(params) {
813
941
  throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
814
942
  }
815
943
  }
816
- async function getAttestationsForDid(params) {
817
- const provider = params.provider;
818
- const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
819
- const toBlock = params.toBlock ?? await provider.getBlockNumber();
820
- const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
821
- const filter = contract.filters.Attested(didToAddress(params.did));
944
+ async function queryAttestationEvents(easContractAddress, provider, fromBlock, toBlock, options) {
945
+ const contract = new ethers.Contract(easContractAddress, EAS_EVENT_ABI, provider);
946
+ const filter = contract.filters.Attested(options?.recipient ?? null, options?.attester ?? null);
822
947
  let events;
823
948
  try {
824
949
  events = await contract.queryFilter(filter, fromBlock, toBlock);
825
950
  } catch (err) {
826
951
  throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
827
952
  }
828
- const eas = new easSdk.EAS(params.easContractAddress);
953
+ const eas = new easSdk.EAS(easContractAddress);
829
954
  eas.connect(provider);
830
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
955
+ const schemaFilter = options?.schemas?.map((s) => s.toLowerCase());
831
956
  const results = [];
832
957
  for (const event of events) {
833
958
  if (!("args" in event && Array.isArray(event.args))) {
@@ -846,11 +971,39 @@ async function getAttestationsForDid(params) {
846
971
  if (!attestation || !attestation.uid) {
847
972
  continue;
848
973
  }
849
- results.push(parseAttestation(attestation));
974
+ results.push(parseAttestation(attestation, void 0, parseEventTxHash(event)));
850
975
  }
851
976
  results.sort((a, b) => Number(b.time - a.time));
852
977
  return results;
853
978
  }
979
+ async function getAttestationsForDid(params) {
980
+ const provider = params.provider;
981
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
982
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
983
+ return queryAttestationEvents(
984
+ params.easContractAddress,
985
+ provider,
986
+ fromBlock,
987
+ toBlock,
988
+ { recipient: didToAddress(params.did), schemas: params.schemas }
989
+ );
990
+ }
991
+ async function getAttestationsByAttester(params) {
992
+ const provider = params.provider;
993
+ const toBlock = params.toBlock ?? await provider.getBlockNumber();
994
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
995
+ if (params.limit !== void 0 && params.limit <= 0) {
996
+ return [];
997
+ }
998
+ const results = await queryAttestationEvents(
999
+ params.easContractAddress,
1000
+ provider,
1001
+ fromBlock,
1002
+ toBlock,
1003
+ { attester: params.attester, schemas: params.schemas }
1004
+ );
1005
+ return params.limit !== void 0 ? results.slice(0, params.limit) : results;
1006
+ }
854
1007
  async function listAttestations(params) {
855
1008
  const limit = params.limit ?? 20;
856
1009
  if (limit <= 0) {
@@ -861,34 +1014,15 @@ async function listAttestations(params) {
861
1014
  }
862
1015
  async function getLatestAttestations(params) {
863
1016
  const provider = params.provider;
864
- const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
865
- const currentBlock = await provider.getBlockNumber();
866
- const fromBlock = params.fromBlock ?? Math.max(0, currentBlock - 5e4);
867
- const events = await contract.queryFilter(contract.filters.Attested(), fromBlock, currentBlock);
868
- const eas = new easSdk.EAS(params.easContractAddress);
869
- eas.connect(provider);
870
- const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
871
- const results = [];
872
- for (const event of events) {
873
- if (!("args" in event && Array.isArray(event.args))) {
874
- continue;
875
- }
876
- const args = event.args;
877
- const uid = args?.[2];
878
- const schemaUid = args?.[3];
879
- if (!uid || !schemaUid) {
880
- continue;
881
- }
882
- if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
883
- continue;
884
- }
885
- const attestation = await eas.getAttestation(uid);
886
- if (!attestation || !attestation.uid) {
887
- continue;
888
- }
889
- results.push(parseAttestation(attestation));
890
- }
891
- results.sort((a, b) => Number(b.time - a.time));
1017
+ const toBlock = await provider.getBlockNumber();
1018
+ const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
1019
+ const results = await queryAttestationEvents(
1020
+ params.easContractAddress,
1021
+ provider,
1022
+ fromBlock,
1023
+ toBlock,
1024
+ { schemas: params.schemas }
1025
+ );
892
1026
  return results.slice(0, params.limit ?? 20);
893
1027
  }
894
1028
  function deduplicateReviews(attestations) {