@waku/rln 0.1.6-b7e9b08.0 → 0.1.6-c8b7131.0

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.
Files changed (95) hide show
  1. package/bundle/index.js +2 -1
  2. package/bundle/node_modules/@ethersproject/keccak256/lib.esm/index.js +1 -1
  3. package/bundle/node_modules/{@ethersproject/keccak256/node_modules/js-sha3 → js-sha3}/src/sha3.js +2 -2
  4. package/bundle/packages/core/dist/lib/connection_manager/connection_manager.js +0 -3
  5. package/bundle/packages/core/dist/lib/connection_manager/keep_alive_manager.js +3 -3
  6. package/bundle/packages/core/dist/lib/filter/filter.js +0 -3
  7. package/bundle/packages/core/dist/lib/light_push/light_push.js +0 -3
  8. package/bundle/packages/core/dist/lib/message/version_0.js +1 -4
  9. package/bundle/packages/core/dist/lib/metadata/metadata.js +0 -3
  10. package/bundle/packages/core/dist/lib/store/store.js +3 -3
  11. package/bundle/packages/rln/dist/codec.js +3 -3
  12. package/bundle/packages/rln/dist/contract/constants.js +8 -1
  13. package/bundle/packages/rln/dist/contract/rln_base_contract.js +138 -77
  14. package/bundle/packages/rln/dist/contract/rln_contract.js +5 -5
  15. package/bundle/packages/rln/dist/credentials_manager.js +28 -20
  16. package/bundle/packages/rln/dist/identity.js +8 -6
  17. package/bundle/packages/rln/dist/keystore/keystore.js +18 -14
  18. package/bundle/packages/rln/dist/message.js +11 -0
  19. package/bundle/packages/rln/dist/proof.js +2 -2
  20. package/bundle/packages/rln/dist/rln.js +3 -3
  21. package/bundle/packages/rln/dist/utils/bytes.js +103 -58
  22. package/bundle/packages/rln/dist/utils/epoch.js +3 -3
  23. package/bundle/packages/rln/dist/utils/hash.js +3 -3
  24. package/bundle/packages/rln/dist/zerokit.js +17 -17
  25. package/bundle/packages/utils/dist/common/sharding/index.js +4 -4
  26. package/dist/.tsbuildinfo +1 -1
  27. package/dist/contract/constants.d.ts +6 -0
  28. package/dist/contract/constants.js +6 -0
  29. package/dist/contract/constants.js.map +1 -1
  30. package/dist/contract/index.d.ts +1 -0
  31. package/dist/contract/index.js +1 -0
  32. package/dist/contract/index.js.map +1 -1
  33. package/dist/contract/rln_base_contract.d.ts +32 -22
  34. package/dist/contract/rln_base_contract.js +135 -74
  35. package/dist/contract/rln_base_contract.js.map +1 -1
  36. package/dist/contract/rln_contract.js +2 -2
  37. package/dist/contract/rln_contract.js.map +1 -1
  38. package/dist/contract/types.d.ts +5 -0
  39. package/dist/contract/types.js.map +1 -1
  40. package/dist/credentials_manager.d.ts +4 -0
  41. package/dist/credentials_manager.js +26 -17
  42. package/dist/credentials_manager.js.map +1 -1
  43. package/dist/identity.d.ts +5 -2
  44. package/dist/identity.js +8 -5
  45. package/dist/identity.js.map +1 -1
  46. package/dist/index.d.ts +2 -1
  47. package/dist/index.js +1 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/keystore/keystore.js +14 -10
  50. package/dist/keystore/keystore.js.map +1 -1
  51. package/dist/keystore/types.d.ts +2 -2
  52. package/dist/message.d.ts +5 -4
  53. package/dist/message.js +2 -0
  54. package/dist/message.js.map +1 -1
  55. package/dist/proof.js +2 -2
  56. package/dist/proof.js.map +1 -1
  57. package/dist/utils/bytes.d.ts +42 -20
  58. package/dist/utils/bytes.js +102 -57
  59. package/dist/utils/bytes.js.map +1 -1
  60. package/dist/utils/hash.js +5 -5
  61. package/dist/utils/hash.js.map +1 -1
  62. package/dist/utils/index.d.ts +1 -1
  63. package/dist/utils/index.js +1 -1
  64. package/dist/utils/index.js.map +1 -1
  65. package/dist/zerokit.js +17 -17
  66. package/dist/zerokit.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/contract/constants.ts +9 -0
  69. package/src/contract/index.ts +1 -0
  70. package/src/contract/rln_base_contract.ts +177 -113
  71. package/src/contract/rln_contract.ts +5 -2
  72. package/src/contract/types.ts +5 -0
  73. package/src/credentials_manager.ts +47 -25
  74. package/src/identity.ts +11 -7
  75. package/src/index.ts +3 -1
  76. package/src/keystore/keystore.ts +34 -24
  77. package/src/keystore/types.ts +2 -2
  78. package/src/message.ts +7 -4
  79. package/src/proof.ts +2 -2
  80. package/src/utils/bytes.ts +118 -72
  81. package/src/utils/hash.ts +15 -5
  82. package/src/utils/index.ts +1 -6
  83. package/src/zerokit.ts +30 -22
  84. package/bundle/packages/utils/node_modules/@waku/interfaces/dist/connection_manager.js +0 -19
  85. package/bundle/packages/utils/node_modules/@waku/interfaces/dist/health_indicator.js +0 -12
  86. package/bundle/packages/utils/node_modules/@waku/interfaces/dist/protocols.js +0 -92
  87. package/dist/contract/test-utils.d.ts +0 -39
  88. package/dist/contract/test-utils.js +0 -118
  89. package/dist/contract/test-utils.js.map +0 -1
  90. package/src/contract/test-utils.ts +0 -179
  91. /package/bundle/packages/{utils/node_modules/@waku/interfaces → interfaces}/dist/constants.js +0 -0
  92. /package/bundle/{node_modules → packages/rln/node_modules}/uuid/dist/esm-browser/native.js +0 -0
  93. /package/bundle/{node_modules → packages/rln/node_modules}/uuid/dist/esm-browser/rng.js +0 -0
  94. /package/bundle/{node_modules → packages/rln/node_modules}/uuid/dist/esm-browser/stringify.js +0 -0
  95. /package/bundle/{node_modules → packages/rln/node_modules}/uuid/dist/esm-browser/v4.js +0 -0
@@ -3,6 +3,7 @@ import { ethers } from "ethers";
3
3
 
4
4
  import { IdentityCredential } from "../identity.js";
5
5
  import { DecryptedCredentials } from "../keystore/types.js";
6
+ import { BytesUtils } from "../utils/bytes.js";
6
7
 
7
8
  import { RLN_ABI } from "./abi.js";
8
9
  import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from "./constants.js";
@@ -22,6 +23,8 @@ export class RLNBaseContract {
22
23
  public contract: ethers.Contract;
23
24
  private deployBlock: undefined | number;
24
25
  private rateLimit: number;
26
+ private minRateLimit?: number;
27
+ private maxRateLimit?: number;
25
28
 
26
29
  protected _members: Map<number, Member> = new Map();
27
30
  private _membersFilter: ethers.EventFilter;
@@ -29,19 +32,9 @@ export class RLNBaseContract {
29
32
  private _membersExpiredFilter: ethers.EventFilter;
30
33
 
31
34
  /**
32
- * Constructor for RLNBaseContract.
33
- * Allows injecting a mocked contract for testing purposes.
35
+ * Private constructor for RLNBaseContract. Use static create() instead.
34
36
  */
35
- public constructor(options: RLNContractInitOptions) {
36
- // Initialize members and subscriptions
37
- this.fetchMembers()
38
- .then(() => {
39
- this.subscribeToMembers();
40
- })
41
- .catch((error) => {
42
- log.error("Failed to initialize members", { error });
43
- });
44
-
37
+ protected constructor(options: RLNContractInitOptions) {
45
38
  const {
46
39
  address,
47
40
  signer,
@@ -49,15 +42,52 @@ export class RLNBaseContract {
49
42
  contract
50
43
  } = options;
51
44
 
52
- this.validateRateLimit(rateLimit);
45
+ log.info("Initializing RLNBaseContract", { address, rateLimit });
53
46
 
54
47
  this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
55
48
  this.rateLimit = rateLimit;
56
49
 
57
- // Initialize event filters
58
- this._membersFilter = this.contract.filters.MembershipRegistered();
59
- this._membershipErasedFilter = this.contract.filters.MembershipErased();
60
- this._membersExpiredFilter = this.contract.filters.MembershipExpired();
50
+ try {
51
+ log.info("Setting up event filters");
52
+ // Initialize event filters
53
+ this._membersFilter = this.contract.filters.MembershipRegistered();
54
+ this._membershipErasedFilter = this.contract.filters.MembershipErased();
55
+ this._membersExpiredFilter = this.contract.filters.MembershipExpired();
56
+ log.info("Event filters initialized successfully");
57
+ } catch (error) {
58
+ log.error("Failed to initialize event filters", { error });
59
+ throw new Error(
60
+ "Failed to initialize event filters: " + (error as Error).message
61
+ );
62
+ }
63
+
64
+ // Initialize members and subscriptions
65
+ this.fetchMembers()
66
+ .then(() => {
67
+ this.subscribeToMembers();
68
+ })
69
+ .catch((error) => {
70
+ log.error("Failed to initialize members", { error });
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Static async factory to create and initialize RLNBaseContract
76
+ */
77
+ public static async create(
78
+ options: RLNContractInitOptions
79
+ ): Promise<RLNBaseContract> {
80
+ const instance = new RLNBaseContract(options);
81
+ const [min, max] = await Promise.all([
82
+ instance.contract.minMembershipRateLimit(),
83
+ instance.contract.maxMembershipRateLimit(),
84
+ instance.contract.Q()
85
+ ]);
86
+ instance.minRateLimit = ethers.BigNumber.from(min).toNumber();
87
+ instance.maxRateLimit = ethers.BigNumber.from(max).toNumber();
88
+
89
+ instance.validateRateLimit(instance.rateLimit);
90
+ return instance;
61
91
  }
62
92
 
63
93
  /**
@@ -82,21 +112,21 @@ export class RLNBaseContract {
82
112
  }
83
113
 
84
114
  /**
85
- * Gets the minimum allowed rate limit from the contract
86
- * @returns Promise<number> The minimum rate limit in messages per epoch
115
+ * Gets the minimum allowed rate limit (cached)
87
116
  */
88
- public async getMinRateLimit(): Promise<number> {
89
- const minRate = await this.contract.minMembershipRateLimit();
90
- return ethers.BigNumber.from(minRate).toNumber();
117
+ public getMinRateLimit(): number {
118
+ if (this.minRateLimit === undefined)
119
+ throw new Error("minRateLimit not initialized");
120
+ return this.minRateLimit;
91
121
  }
92
122
 
93
123
  /**
94
- * Gets the maximum allowed rate limit from the contract
95
- * @returns Promise<number> The maximum rate limit in messages per epoch
124
+ * Gets the maximum allowed rate limit (cached)
96
125
  */
97
- public async getMaxRateLimit(): Promise<number> {
98
- const maxRate = await this.contract.maxMembershipRateLimit();
99
- return ethers.BigNumber.from(maxRate).toNumber();
126
+ public getMaxRateLimit(): number {
127
+ if (this.maxRateLimit === undefined)
128
+ throw new Error("maxRateLimit not initialized");
129
+ return this.maxRateLimit;
100
130
  }
101
131
 
102
132
  /**
@@ -133,7 +163,7 @@ export class RLNBaseContract {
133
163
  * Updates the rate limit for future registrations
134
164
  * @param newRateLimit The new rate limit to use
135
165
  */
136
- public async setRateLimit(newRateLimit: number): Promise<void> {
166
+ public setRateLimit(newRateLimit: number): void {
137
167
  this.validateRateLimit(newRateLimit);
138
168
  this.rateLimit = newRateLimit;
139
169
  }
@@ -324,7 +354,7 @@ export class RLNBaseContract {
324
354
  this.contract.on(
325
355
  this.membersFilter,
326
356
  (
327
- _idCommitment: string,
357
+ _idCommitment: bigint,
328
358
  _membershipRateLimit: ethers.BigNumber,
329
359
  _index: ethers.BigNumber,
330
360
  event: ethers.Event
@@ -336,7 +366,7 @@ export class RLNBaseContract {
336
366
  this.contract.on(
337
367
  this.membershipErasedFilter,
338
368
  (
339
- _idCommitment: string,
369
+ _idCommitment: bigint,
340
370
  _membershipRateLimit: ethers.BigNumber,
341
371
  _index: ethers.BigNumber,
342
372
  event: ethers.Event
@@ -348,7 +378,7 @@ export class RLNBaseContract {
348
378
  this.contract.on(
349
379
  this.membersExpiredFilter,
350
380
  (
351
- _idCommitment: string,
381
+ _idCommitment: bigint,
352
382
  _membershipRateLimit: ethers.BigNumber,
353
383
  _index: ethers.BigNumber,
354
384
  event: ethers.Event
@@ -358,94 +388,89 @@ export class RLNBaseContract {
358
388
  );
359
389
  }
360
390
 
361
- /**
362
- * Helper method to get remaining messages in current epoch
363
- * @param membershipId The ID of the membership to check
364
- * @returns number of remaining messages allowed in current epoch
365
- */
366
- public async getRemainingMessages(membershipId: number): Promise<number> {
367
- try {
368
- const [startTime, , rateLimit] =
369
- await this.contract.getMembershipInfo(membershipId);
370
-
371
- // Calculate current epoch
372
- const currentTime = Math.floor(Date.now() / 1000);
373
- const epochsPassed = Math.floor(
374
- (currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH
375
- );
376
- const currentEpochStart =
377
- startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
378
-
379
- // Get message count in current epoch using contract's function
380
- const messageCount = await this.contract.getMessageCount(
381
- membershipId,
382
- currentEpochStart
383
- );
384
- return Math.max(
385
- 0,
386
- ethers.BigNumber.from(rateLimit)
387
- .sub(ethers.BigNumber.from(messageCount))
388
- .toNumber()
389
- );
390
- } catch (error) {
391
- log.error(
392
- `Error getting remaining messages: ${(error as Error).message}`
393
- );
394
- return 0; // Fail safe: assume no messages remaining on error
395
- }
396
- }
397
-
398
391
  public async getMembershipInfo(
399
- idCommitment: string
392
+ idCommitmentBigInt: bigint
400
393
  ): Promise<MembershipInfo | undefined> {
401
394
  try {
402
- const [startBlock, endBlock, rateLimit] =
403
- await this.contract.getMembershipInfo(idCommitment);
395
+ const membershipData =
396
+ await this.contract.memberships(idCommitmentBigInt);
404
397
  const currentBlock = await this.contract.provider.getBlockNumber();
398
+ const [
399
+ depositAmount,
400
+ activeDuration,
401
+ gracePeriodStartTimestamp,
402
+ gracePeriodDuration,
403
+ rateLimit,
404
+ index,
405
+ holder,
406
+ token
407
+ ] = membershipData;
408
+
409
+ const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration);
405
410
 
406
411
  let state: MembershipState;
407
- if (currentBlock < startBlock) {
412
+ if (currentBlock < gracePeriodStartTimestamp.toNumber()) {
408
413
  state = MembershipState.Active;
409
- } else if (currentBlock < endBlock) {
414
+ } else if (currentBlock < gracePeriodEnd.toNumber()) {
410
415
  state = MembershipState.GracePeriod;
411
416
  } else {
412
417
  state = MembershipState.Expired;
413
418
  }
414
419
 
415
- const index = await this.getMemberIndex(idCommitment);
416
- if (!index) return undefined;
417
-
418
420
  return {
419
421
  index,
420
- idCommitment,
421
- rateLimit: rateLimit.toNumber(),
422
- startBlock: startBlock.toNumber(),
423
- endBlock: endBlock.toNumber(),
424
- state
422
+ idCommitment: idCommitmentBigInt.toString(),
423
+ rateLimit: Number(rateLimit),
424
+ startBlock: gracePeriodStartTimestamp.toNumber(),
425
+ endBlock: gracePeriodEnd.toNumber(),
426
+ state,
427
+ depositAmount,
428
+ activeDuration,
429
+ gracePeriodDuration,
430
+ holder,
431
+ token
425
432
  };
426
433
  } catch (error) {
434
+ log.error("Error in getMembershipInfo:", error);
427
435
  return undefined;
428
436
  }
429
437
  }
430
438
 
431
439
  public async extendMembership(
432
- idCommitment: string
440
+ idCommitmentBigInt: bigint
433
441
  ): Promise<ethers.ContractTransaction> {
434
- return this.contract.extendMemberships([idCommitment]);
442
+ const tx = await this.contract.extendMemberships([idCommitmentBigInt]);
443
+ await tx.wait();
444
+ return tx;
435
445
  }
436
446
 
437
447
  public async eraseMembership(
438
- idCommitment: string,
448
+ idCommitmentBigInt: bigint,
439
449
  eraseFromMembershipSet: boolean = true
440
450
  ): Promise<ethers.ContractTransaction> {
441
- return this.contract.eraseMemberships(
442
- [idCommitment],
443
- eraseFromMembershipSet
451
+ if (
452
+ !(await this.isExpired(idCommitmentBigInt)) ||
453
+ !(await this.isInGracePeriod(idCommitmentBigInt))
454
+ ) {
455
+ throw new Error("Membership is not expired or in grace period");
456
+ }
457
+
458
+ const estimatedGas = await this.contract.estimateGas[
459
+ "eraseMemberships(uint256[],bool)"
460
+ ]([idCommitmentBigInt], eraseFromMembershipSet);
461
+ const gasLimit = estimatedGas.add(10000);
462
+
463
+ const tx = await this.contract["eraseMemberships(uint256[],bool)"](
464
+ [idCommitmentBigInt],
465
+ eraseFromMembershipSet,
466
+ { gasLimit }
444
467
  );
468
+ await tx.wait();
469
+ return tx;
445
470
  }
446
471
 
447
472
  public async registerMembership(
448
- idCommitment: string,
473
+ idCommitmentBigInt: bigint,
449
474
  rateLimit: number = DEFAULT_RATE_LIMIT
450
475
  ): Promise<ethers.ContractTransaction> {
451
476
  if (
@@ -456,18 +481,17 @@ export class RLNBaseContract {
456
481
  `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
457
482
  );
458
483
  }
459
- return this.contract.register(idCommitment, rateLimit, []);
484
+ return this.contract.register(idCommitmentBigInt, rateLimit, []);
460
485
  }
461
486
 
462
- public async withdraw(token: string, holder: string): Promise<void> {
487
+ public async withdraw(token: string, walletAddress: string): Promise<void> {
463
488
  try {
464
- const tx = await this.contract.withdraw(token, { from: holder });
489
+ const tx = await this.contract.withdraw(token, walletAddress);
465
490
  await tx.wait();
466
491
  } catch (error) {
467
492
  log.error(`Error in withdraw: ${(error as Error).message}`);
468
493
  }
469
494
  }
470
-
471
495
  public async registerWithIdentity(
472
496
  identity: IdentityCredential
473
497
  ): Promise<DecryptedCredentials | undefined> {
@@ -476,10 +500,12 @@ export class RLNBaseContract {
476
500
  `Registering identity with rate limit: ${this.rateLimit} messages/epoch`
477
501
  );
478
502
 
479
- // Check if the ID commitment is already registered
480
- const existingIndex = await this.getMemberIndex(
481
- identity.IDCommitmentBigInt.toString()
503
+ const idCommitmentBigIntBE = BytesUtils.buildBigIntFromUint8ArrayBE(
504
+ identity.IDCommitment
482
505
  );
506
+
507
+ // Check if the ID commitment is already registered
508
+ const existingIndex = await this.getMemberIndex(idCommitmentBigIntBE);
483
509
  if (existingIndex) {
484
510
  throw new Error(
485
511
  `ID commitment is already registered with index ${existingIndex}`
@@ -495,19 +521,16 @@ export class RLNBaseContract {
495
521
  }
496
522
 
497
523
  const estimatedGas = await this.contract.estimateGas.register(
498
- identity.IDCommitmentBigInt,
524
+ idCommitmentBigIntBE,
499
525
  this.rateLimit,
500
526
  []
501
527
  );
502
528
  const gasLimit = estimatedGas.add(10000);
503
529
 
504
530
  const txRegisterResponse: ethers.ContractTransaction =
505
- await this.contract.register(
506
- identity.IDCommitmentBigInt,
507
- this.rateLimit,
508
- [],
509
- { gasLimit }
510
- );
531
+ await this.contract.register(idCommitmentBigIntBE, this.rateLimit, [], {
532
+ gasLimit
533
+ });
511
534
 
512
535
  const txRegisterReceipt = await txRegisterResponse.wait();
513
536
 
@@ -516,7 +539,7 @@ export class RLNBaseContract {
516
539
  }
517
540
 
518
541
  const memberRegistered = txRegisterReceipt.events?.find(
519
- (event) => event.event === "MembershipRegistered"
542
+ (event: ethers.Event) => event.event === "MembershipRegistered"
520
543
  );
521
544
 
522
545
  if (!memberRegistered || !memberRegistered.args) {
@@ -603,14 +626,14 @@ export class RLNBaseContract {
603
626
  permit.v,
604
627
  permit.r,
605
628
  permit.s,
606
- identity.IDCommitmentBigInt,
629
+ BytesUtils.buildBigIntFromUint8ArrayBE(identity.IDCommitment),
607
630
  this.rateLimit,
608
631
  idCommitmentsToErase.map((id) => ethers.BigNumber.from(id))
609
632
  );
610
633
  const txRegisterReceipt = await txRegisterResponse.wait();
611
634
 
612
635
  const memberRegistered = txRegisterReceipt.events?.find(
613
- (event) => event.event === "MembershipRegistered"
636
+ (event: ethers.Event) => event.event === "MembershipRegistered"
614
637
  );
615
638
 
616
639
  if (!memberRegistered || !memberRegistered.args) {
@@ -653,16 +676,16 @@ export class RLNBaseContract {
653
676
  }
654
677
 
655
678
  /**
656
- * Validates that the rate limit is within the allowed range
679
+ * Validates that the rate limit is within the allowed range (sync)
657
680
  * @throws Error if the rate limit is outside the allowed range
658
681
  */
659
682
  private validateRateLimit(rateLimit: number): void {
660
- if (
661
- rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
662
- rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
663
- ) {
683
+ if (this.minRateLimit === undefined || this.maxRateLimit === undefined) {
684
+ throw new Error("Rate limits not initialized");
685
+ }
686
+ if (rateLimit < this.minRateLimit || rateLimit > this.maxRateLimit) {
664
687
  throw new Error(
665
- `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`
688
+ `Rate limit must be between ${this.minRateLimit} and ${this.maxRateLimit} messages per epoch`
666
689
  );
667
690
  }
668
691
  }
@@ -689,11 +712,11 @@ export class RLNBaseContract {
689
712
  }
690
713
 
691
714
  private async getMemberIndex(
692
- idCommitment: string
715
+ idCommitmentBigInt: bigint
693
716
  ): Promise<ethers.BigNumber | undefined> {
694
717
  try {
695
718
  const events = await this.contract.queryFilter(
696
- this.contract.filters.MembershipRegistered(idCommitment)
719
+ this.contract.filters.MembershipRegistered(idCommitmentBigInt)
697
720
  );
698
721
  if (events.length === 0) return undefined;
699
722
 
@@ -704,4 +727,45 @@ export class RLNBaseContract {
704
727
  return undefined;
705
728
  }
706
729
  }
730
+
731
+ public async getMembershipStatus(
732
+ idCommitment: bigint
733
+ ): Promise<"expired" | "grace" | "active"> {
734
+ const [isExpired, isInGrace] = await Promise.all([
735
+ this.contract.isExpired(idCommitment),
736
+ this.contract.isInGracePeriod(idCommitment)
737
+ ]);
738
+
739
+ if (isExpired) return "expired";
740
+ if (isInGrace) return "grace";
741
+ return "active";
742
+ }
743
+
744
+ /**
745
+ * Checks if a membership is expired for the given idCommitment
746
+ * @param idCommitmentBigInt The idCommitment as bigint
747
+ * @returns Promise<boolean> True if expired, false otherwise
748
+ */
749
+ public async isExpired(idCommitmentBigInt: bigint): Promise<boolean> {
750
+ try {
751
+ return await this.contract.isExpired(idCommitmentBigInt);
752
+ } catch (error) {
753
+ log.error("Error in isExpired:", error);
754
+ return false;
755
+ }
756
+ }
757
+
758
+ /**
759
+ * Checks if a membership is in grace period for the given idCommitment
760
+ * @param idCommitmentBigInt The idCommitment as bigint
761
+ * @returns Promise<boolean> True if in grace period, false otherwise
762
+ */
763
+ public async isInGracePeriod(idCommitmentBigInt: bigint): Promise<boolean> {
764
+ try {
765
+ return await this.contract.isInGracePeriod(idCommitmentBigInt);
766
+ } catch (error) {
767
+ log.error("Error in isInGracePeriod:", error);
768
+ return false;
769
+ }
770
+ }
707
771
  }
@@ -4,7 +4,7 @@ import { ethers } from "ethers";
4
4
 
5
5
  import type { RLNInstance } from "../rln.js";
6
6
  import { MerkleRootTracker } from "../root_tracker.js";
7
- import { zeroPadLE } from "../utils/bytes.js";
7
+ import { BytesUtils } from "../utils/bytes.js";
8
8
 
9
9
  import { RLNBaseContract } from "./rln_base_contract.js";
10
10
  import { RLNContractInitOptions } from "./types.js";
@@ -110,7 +110,10 @@ export class RLNContract extends RLNBaseContract {
110
110
  index = ethers.BigNumber.from(index);
111
111
  }
112
112
 
113
- const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
113
+ const idCommitment = BytesUtils.zeroPadLE(
114
+ hexToBytes(_idCommitment),
115
+ 32
116
+ );
114
117
  rlnInstance.zerokit.insertMember(idCommitment);
115
118
 
116
119
  const numericIndex = index.toNumber();
@@ -38,6 +38,11 @@ export interface MembershipInfo {
38
38
  startBlock: number;
39
39
  endBlock: number;
40
40
  state: MembershipState;
41
+ depositAmount: ethers.BigNumber;
42
+ activeDuration: number;
43
+ gracePeriodDuration: number;
44
+ holder: string;
45
+ token: string;
41
46
  }
42
47
 
43
48
  export enum MembershipState {
@@ -1,9 +1,9 @@
1
1
  import { hmac } from "@noble/hashes/hmac";
2
- import { sha256 } from "@noble/hashes/sha256";
2
+ import { sha256 } from "@noble/hashes/sha2";
3
3
  import { Logger } from "@waku/utils";
4
4
  import { ethers } from "ethers";
5
5
 
6
- import { LINEA_CONTRACT } from "./contract/constants.js";
6
+ import { DEFAULT_Q, LINEA_CONTRACT } from "./contract/constants.js";
7
7
  import { RLNBaseContract } from "./contract/rln_base_contract.js";
8
8
  import { IdentityCredential } from "./identity.js";
9
9
  import { Keystore } from "./keystore/index.js";
@@ -13,10 +13,8 @@ import type {
13
13
  } from "./keystore/index.js";
14
14
  import { KeystoreEntity, Password } from "./keystore/types.js";
15
15
  import { RegisterMembershipOptions, StartRLNOptions } from "./types.js";
16
- import {
17
- buildBigIntFromUint8Array,
18
- extractMetaMaskSigner
19
- } from "./utils/index.js";
16
+ import { BytesUtils } from "./utils/bytes.js";
17
+ import { extractMetaMaskSigner } from "./utils/index.js";
20
18
  import { Zerokit } from "./zerokit.js";
21
19
 
22
20
  const log = new Logger("waku:credentials");
@@ -80,7 +78,7 @@ export class RLNCredentialsManager {
80
78
 
81
79
  this.credentials = credentials;
82
80
  this.signer = signer!;
83
- this.contract = new RLNBaseContract({
81
+ this.contract = await RLNBaseContract.create({
84
82
  address: address!,
85
83
  signer: signer!,
86
84
  rateLimit: rateLimit ?? this.zerokit?.rateLimit
@@ -116,7 +114,9 @@ export class RLNCredentialsManager {
116
114
  );
117
115
  } else {
118
116
  log.info("Using local implementation to generate identity");
119
- identity = this.generateSeededIdentityCredential(options.signature);
117
+ identity = await this.generateSeededIdentityCredential(
118
+ options.signature
119
+ );
120
120
  }
121
121
  }
122
122
 
@@ -249,7 +249,9 @@ export class RLNCredentialsManager {
249
249
  * @param seed A string seed to generate the identity from
250
250
  * @returns IdentityCredential
251
251
  */
252
- private generateSeededIdentityCredential(seed: string): IdentityCredential {
252
+ private async generateSeededIdentityCredential(
253
+ seed: string
254
+ ): Promise<IdentityCredential> {
253
255
  log.info("Generating seeded identity credential");
254
256
  // Convert the seed to bytes
255
257
  const encoder = new TextEncoder();
@@ -257,26 +259,46 @@ export class RLNCredentialsManager {
257
259
 
258
260
  // Generate deterministic values using HMAC-SHA256
259
261
  // We use different context strings for each component to ensure they're different
260
- const idTrapdoor = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
261
- const idNullifier = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
262
-
263
- // Generate IDSecretHash as a hash of IDTrapdoor and IDNullifier
264
- const combinedBytes = new Uint8Array([...idTrapdoor, ...idNullifier]);
265
- const idSecretHash = sha256(combinedBytes);
262
+ const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
263
+ const idNullifierBE = hmac(
264
+ sha256,
265
+ seedBytes,
266
+ encoder.encode("IDNullifier")
267
+ );
266
268
 
267
- // Generate IDCommitment as a hash of IDSecretHash
268
- const idCommitment = sha256(idSecretHash);
269
+ const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
270
+ const idSecretHashBE = sha256(combinedBytes);
269
271
 
270
- // Convert IDCommitment to BigInt
271
- const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
272
+ const idCommitmentRawBE = sha256(idSecretHashBE);
273
+ const idCommitmentBE = this.reduceIdCommitment(idCommitmentRawBE);
272
274
 
273
- log.info("Successfully generated identity credential");
275
+ log.info(
276
+ "Successfully generated identity credential, storing in Big Endian format"
277
+ );
274
278
  return new IdentityCredential(
275
- idTrapdoor,
276
- idNullifier,
277
- idSecretHash,
278
- idCommitment,
279
- idCommitmentBigInt
279
+ idTrapdoorBE,
280
+ idNullifierBE,
281
+ idSecretHashBE,
282
+ idCommitmentBE
280
283
  );
281
284
  }
285
+
286
+ /**
287
+ * Helper: take 32-byte BE, reduce mod Q, return 32-byte BE
288
+ */
289
+ private reduceIdCommitment(
290
+ bytesBE: Uint8Array,
291
+ limit: bigint = DEFAULT_Q
292
+ ): Uint8Array {
293
+ const nBE = BytesUtils.buildBigIntFromUint8ArrayBE(bytesBE);
294
+
295
+ if (nBE >= limit) {
296
+ log.warn(
297
+ `ID commitment is greater than Q, reducing it by Q: ${nBE} % ${limit}`
298
+ );
299
+ return BytesUtils.bigIntToUint8Array32BE(nBE % limit);
300
+ }
301
+
302
+ return bytesBE;
303
+ }
282
304
  }
package/src/identity.ts CHANGED
@@ -1,13 +1,19 @@
1
- import { buildBigIntFromUint8Array } from "./utils/index.js";
1
+ import { BytesUtils } from "./utils/bytes.js";
2
2
 
3
3
  export class IdentityCredential {
4
+ public IDCommitmentBigInt: bigint;
5
+ /**
6
+ * All variables are in little-endian format
7
+ */
4
8
  public constructor(
5
9
  public readonly IDTrapdoor: Uint8Array,
6
10
  public readonly IDNullifier: Uint8Array,
7
11
  public readonly IDSecretHash: Uint8Array,
8
- public readonly IDCommitment: Uint8Array,
9
- public readonly IDCommitmentBigInt: bigint
10
- ) {}
12
+ public readonly IDCommitment: Uint8Array
13
+ ) {
14
+ this.IDCommitmentBigInt =
15
+ BytesUtils.buildBigIntFromUint8ArrayBE(IDCommitment);
16
+ }
11
17
 
12
18
  public static fromBytes(memKeys: Uint8Array): IdentityCredential {
13
19
  if (memKeys.length < 128) {
@@ -18,14 +24,12 @@ export class IdentityCredential {
18
24
  const idNullifier = memKeys.subarray(32, 64);
19
25
  const idSecretHash = memKeys.subarray(64, 96);
20
26
  const idCommitment = memKeys.subarray(96, 128);
21
- const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment, 32);
22
27
 
23
28
  return new IdentityCredential(
24
29
  idTrapdoor,
25
30
  idNullifier,
26
31
  idSecretHash,
27
- idCommitment,
28
- idCommitmentBigInt
32
+ idCommitment
29
33
  );
30
34
  }
31
35
  }
package/src/index.ts CHANGED
@@ -34,7 +34,9 @@ export type {
34
34
  Keccak256Hash,
35
35
  KeystoreEntity,
36
36
  MembershipHash,
37
- MembershipInfo,
37
+ KeystoreMembershipInfo,
38
38
  Password,
39
39
  Sha256Hash
40
40
  } from "./keystore/types.js";
41
+
42
+ export * from "./contract/index.js";