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