@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
package/bundle/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  export { RLNDecoder, RLNEncoder } from './packages/rln/dist/codec.js';
2
2
  export { RLN_ABI } from './packages/rln/dist/contract/abi.js';
3
3
  export { RLNContract } from './packages/rln/dist/contract/rln_contract.js';
4
- export { LINEA_CONTRACT } from './packages/rln/dist/contract/constants.js';
4
+ export { DEFAULT_Q, DEFAULT_RATE_LIMIT, LINEA_CONTRACT, RATE_LIMIT_PARAMS, RATE_LIMIT_TIERS } from './packages/rln/dist/contract/constants.js';
5
+ export { MembershipState } from './packages/rln/dist/contract/types.js';
5
6
  export { RLNBaseContract } from './packages/rln/dist/contract/rln_base_contract.js';
6
7
  export { createRLN } from './packages/rln/dist/create.js';
7
8
  export { RLNCredentialsManager } from './packages/rln/dist/credentials_manager.js';
@@ -1,4 +1,4 @@
1
- import sha3 from '../node_modules/js-sha3/src/sha3.js';
1
+ import sha3 from '../../../js-sha3/src/sha3.js';
2
2
  import { arrayify } from '../../bytes/lib.esm/index.js';
3
3
 
4
4
  function keccak256(data) {
@@ -1,5 +1,5 @@
1
- import { getDefaultExportFromCjs, commonjsGlobal } from '../../../../../../_virtual/_commonjsHelpers.js';
2
- import { __module as sha3$1 } from '../../../../../../_virtual/sha3.js';
1
+ import { getDefaultExportFromCjs, commonjsGlobal } from '../../../_virtual/_commonjsHelpers.js';
2
+ import { __module as sha3$1 } from '../../../_virtual/sha3.js';
3
3
 
4
4
  /**
5
5
  * [js-sha3]{@link https://github.com/emn178/js-sha3}
@@ -15,9 +15,6 @@ import '../../../../../node_modules/@multiformats/multiaddr/dist/src/multiaddr.j
15
15
  import '../../../../interfaces/dist/protocols.js';
16
16
  import '../../../../interfaces/dist/connection_manager.js';
17
17
  import '../../../../interfaces/dist/health_indicator.js';
18
- import '../../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
19
- import '../../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
20
- import '../../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
21
18
  import { Logger } from '../../../../utils/dist/logger/index.js';
22
19
  import './keep_alive_manager.js';
23
20
 
@@ -1,6 +1,6 @@
1
- import '../../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
2
- import '../../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
3
- import '../../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
1
+ import '../../../../interfaces/dist/protocols.js';
2
+ import '../../../../interfaces/dist/connection_manager.js';
3
+ import '../../../../interfaces/dist/health_indicator.js';
4
4
  import '../../../../../node_modules/multiformats/dist/src/bases/base10.js';
5
5
  import '../../../../../node_modules/multiformats/dist/src/bases/base16.js';
6
6
  import '../../../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -1,9 +1,6 @@
1
1
  import '../../../../interfaces/dist/protocols.js';
2
2
  import '../../../../interfaces/dist/connection_manager.js';
3
3
  import '../../../../interfaces/dist/health_indicator.js';
4
- import '../../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
5
- import '../../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
6
- import '../../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
7
4
  import '../../../../../node_modules/multiformats/dist/src/bases/base10.js';
8
5
  import '../../../../../node_modules/multiformats/dist/src/bases/base16.js';
9
6
  import '../../../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -10,9 +10,6 @@ import '../../../../proto/dist/generated/store_v3.js';
10
10
  import '../../../../proto/dist/generated/peer_exchange.js';
11
11
  import '../../../../proto/dist/generated/metadata.js';
12
12
  import '../../../../proto/dist/generated/sds_message.js';
13
- import '../../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
14
- import '../../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
15
- import '../../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
16
13
  import '../../../../../node_modules/multiformats/dist/src/bases/base10.js';
17
14
  import '../../../../../node_modules/multiformats/dist/src/bases/base16.js';
18
15
  import '../../../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -29,9 +29,6 @@ class DecodedMessage {
29
29
  get contentTopic() {
30
30
  return this.proto.contentTopic;
31
31
  }
32
- get _rawTimestamp() {
33
- return this.proto.timestamp;
34
- }
35
32
  get timestamp() {
36
33
  // In the case we receive a value that is bigger than JS's max number,
37
34
  // we catch the error and return undefined.
@@ -53,7 +50,7 @@ class DecodedMessage {
53
50
  get version() {
54
51
  // https://rfc.vac.dev/spec/14/
55
52
  // > If omitted, the value SHOULD be interpreted as version 0.
56
- return this.proto.version ?? 0;
53
+ return this.proto.version ?? Version;
57
54
  }
58
55
  get rateLimitProof() {
59
56
  return this.proto.rateLimitProof;
@@ -10,9 +10,6 @@ import '../../../../proto/dist/generated/store_v3.js';
10
10
  import '../../../../proto/dist/generated/peer_exchange.js';
11
11
  import '../../../../proto/dist/generated/metadata.js';
12
12
  import '../../../../proto/dist/generated/sds_message.js';
13
- import '../../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
14
- import '../../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
15
- import '../../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
16
13
  import '../../../../../node_modules/multiformats/dist/src/bases/base10.js';
17
14
  import '../../../../../node_modules/multiformats/dist/src/bases/base16.js';
18
15
  import '../../../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -1,6 +1,6 @@
1
- import '../../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
2
- import '../../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
3
- import '../../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
1
+ import '../../../../interfaces/dist/protocols.js';
2
+ import '../../../../interfaces/dist/connection_manager.js';
3
+ import '../../../../interfaces/dist/health_indicator.js';
4
4
  import '../../../../../node_modules/multiformats/dist/src/bases/base10.js';
5
5
  import '../../../../../node_modules/multiformats/dist/src/bases/base16.js';
6
6
  import '../../../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -1,6 +1,6 @@
1
- import '../../utils/node_modules/@waku/interfaces/dist/protocols.js';
2
- import '../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
3
- import '../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
1
+ import '../../interfaces/dist/protocols.js';
2
+ import '../../interfaces/dist/connection_manager.js';
3
+ import '../../interfaces/dist/health_indicator.js';
4
4
  import '../../../node_modules/multiformats/dist/src/bases/base10.js';
5
5
  import '../../../node_modules/multiformats/dist/src/bases/base16.js';
6
6
  import '../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -12,6 +12,7 @@ const LINEA_CONTRACT = {
12
12
  */
13
13
  const RATE_LIMIT_TIERS = {
14
14
  LOW: 20, // Suggested minimum rate - 20 messages per epoch
15
+ MEDIUM: 200,
15
16
  HIGH: 600 // Suggested maximum rate - 600 messages per epoch
16
17
  };
17
18
  // Global rate limit parameters
@@ -21,6 +22,12 @@ const RATE_LIMIT_PARAMS = {
21
22
  MAX_TOTAL_RATE: 160_000, // Maximum total rate limit across all memberships
22
23
  EPOCH_LENGTH: 600 // Epoch length in seconds (10 minutes)
23
24
  };
25
+ /**
26
+ * Default Q value for the RLN contract
27
+ * This is the upper bound for the ID commitment
28
+ * @see https://github.com/waku-org/specs/blob/master/standards/core/rln-contract.md#implementation-suggestions
29
+ */
30
+ const DEFAULT_Q = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
24
31
  const DEFAULT_RATE_LIMIT = RATE_LIMIT_PARAMS.MAX_RATE;
25
32
 
26
- export { DEFAULT_RATE_LIMIT, LINEA_CONTRACT, RATE_LIMIT_PARAMS, RATE_LIMIT_TIERS };
33
+ export { DEFAULT_Q, DEFAULT_RATE_LIMIT, LINEA_CONTRACT, RATE_LIMIT_PARAMS, RATE_LIMIT_TIERS };
@@ -1,6 +1,6 @@
1
- import '../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
2
- import '../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
3
- import '../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
1
+ import '../../../interfaces/dist/protocols.js';
2
+ import '../../../interfaces/dist/connection_manager.js';
3
+ import '../../../interfaces/dist/health_indicator.js';
4
4
  import '../../../../node_modules/multiformats/dist/src/bases/base10.js';
5
5
  import '../../../../node_modules/multiformats/dist/src/bases/base16.js';
6
6
  import '../../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -13,6 +13,7 @@ import '../../../../node_modules/multiformats/dist/src/bases/base8.js';
13
13
  import '../../../../node_modules/multiformats/dist/src/bases/identity.js';
14
14
  import '../../../../node_modules/multiformats/dist/src/codecs/json.js';
15
15
  import { Logger } from '../../../utils/dist/logger/index.js';
16
+ import { BytesUtils } from '../utils/bytes.js';
16
17
  import { RLN_ABI } from './abi.js';
17
18
  import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './constants.js';
18
19
  import { MembershipState } from './types.js';
@@ -24,15 +25,32 @@ class RLNBaseContract {
24
25
  contract;
25
26
  deployBlock;
26
27
  rateLimit;
28
+ minRateLimit;
29
+ maxRateLimit;
27
30
  _members = new Map();
28
31
  _membersFilter;
29
32
  _membershipErasedFilter;
30
33
  _membersExpiredFilter;
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
37
  constructor(options) {
38
+ const { address, signer, rateLimit = DEFAULT_RATE_LIMIT, contract } = options;
39
+ log.info("Initializing RLNBaseContract", { address, rateLimit });
40
+ this.contract = contract || new Contract(address, RLN_ABI, signer);
41
+ this.rateLimit = rateLimit;
42
+ try {
43
+ log.info("Setting up event filters");
44
+ // Initialize event filters
45
+ this._membersFilter = this.contract.filters.MembershipRegistered();
46
+ this._membershipErasedFilter = this.contract.filters.MembershipErased();
47
+ this._membersExpiredFilter = this.contract.filters.MembershipExpired();
48
+ log.info("Event filters initialized successfully");
49
+ }
50
+ catch (error) {
51
+ log.error("Failed to initialize event filters", { error });
52
+ throw new Error("Failed to initialize event filters: " + error.message);
53
+ }
36
54
  // Initialize members and subscriptions
37
55
  this.fetchMembers()
38
56
  .then(() => {
@@ -41,14 +59,21 @@ class RLNBaseContract {
41
59
  .catch((error) => {
42
60
  log.error("Failed to initialize members", { error });
43
61
  });
44
- const { address, signer, rateLimit = DEFAULT_RATE_LIMIT, contract } = options;
45
- this.validateRateLimit(rateLimit);
46
- this.contract = contract || new Contract(address, RLN_ABI, signer);
47
- this.rateLimit = rateLimit;
48
- // Initialize event filters
49
- this._membersFilter = this.contract.filters.MembershipRegistered();
50
- this._membershipErasedFilter = this.contract.filters.MembershipErased();
51
- this._membersExpiredFilter = this.contract.filters.MembershipExpired();
62
+ }
63
+ /**
64
+ * Static async factory to create and initialize RLNBaseContract
65
+ */
66
+ static async create(options) {
67
+ const instance = new RLNBaseContract(options);
68
+ const [min, max] = await Promise.all([
69
+ instance.contract.minMembershipRateLimit(),
70
+ instance.contract.maxMembershipRateLimit(),
71
+ instance.contract.Q()
72
+ ]);
73
+ instance.minRateLimit = BigNumber.from(min).toNumber();
74
+ instance.maxRateLimit = BigNumber.from(max).toNumber();
75
+ instance.validateRateLimit(instance.rateLimit);
76
+ return instance;
52
77
  }
53
78
  /**
54
79
  * Gets the current rate limit for this contract instance
@@ -69,20 +94,20 @@ class RLNBaseContract {
69
94
  return this.contract.provider;
70
95
  }
71
96
  /**
72
- * Gets the minimum allowed rate limit from the contract
73
- * @returns Promise<number> The minimum rate limit in messages per epoch
97
+ * Gets the minimum allowed rate limit (cached)
74
98
  */
75
- async getMinRateLimit() {
76
- const minRate = await this.contract.minMembershipRateLimit();
77
- return BigNumber.from(minRate).toNumber();
99
+ getMinRateLimit() {
100
+ if (this.minRateLimit === undefined)
101
+ throw new Error("minRateLimit not initialized");
102
+ return this.minRateLimit;
78
103
  }
79
104
  /**
80
- * Gets the maximum allowed rate limit from the contract
81
- * @returns Promise<number> The maximum rate limit in messages per epoch
105
+ * Gets the maximum allowed rate limit (cached)
82
106
  */
83
- async getMaxRateLimit() {
84
- const maxRate = await this.contract.maxMembershipRateLimit();
85
- return BigNumber.from(maxRate).toNumber();
107
+ getMaxRateLimit() {
108
+ if (this.maxRateLimit === undefined)
109
+ throw new Error("maxRateLimit not initialized");
110
+ return this.maxRateLimit;
86
111
  }
87
112
  /**
88
113
  * Gets the maximum total rate limit across all memberships
@@ -115,7 +140,7 @@ class RLNBaseContract {
115
140
  * Updates the rate limit for future registrations
116
141
  * @param newRateLimit The new rate limit to use
117
142
  */
118
- async setRateLimit(newRateLimit) {
143
+ setRateLimit(newRateLimit) {
119
144
  this.validateRateLimit(newRateLimit);
120
145
  this.rateLimit = newRateLimit;
121
146
  }
@@ -247,75 +272,67 @@ class RLNBaseContract {
247
272
  this.processEvents([event]);
248
273
  });
249
274
  }
250
- /**
251
- * Helper method to get remaining messages in current epoch
252
- * @param membershipId The ID of the membership to check
253
- * @returns number of remaining messages allowed in current epoch
254
- */
255
- async getRemainingMessages(membershipId) {
256
- try {
257
- const [startTime, , rateLimit] = await this.contract.getMembershipInfo(membershipId);
258
- // Calculate current epoch
259
- const currentTime = Math.floor(Date.now() / 1000);
260
- const epochsPassed = Math.floor((currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH);
261
- const currentEpochStart = startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
262
- // Get message count in current epoch using contract's function
263
- const messageCount = await this.contract.getMessageCount(membershipId, currentEpochStart);
264
- return Math.max(0, BigNumber.from(rateLimit)
265
- .sub(BigNumber.from(messageCount))
266
- .toNumber());
267
- }
268
- catch (error) {
269
- log.error(`Error getting remaining messages: ${error.message}`);
270
- return 0; // Fail safe: assume no messages remaining on error
271
- }
272
- }
273
- async getMembershipInfo(idCommitment) {
275
+ async getMembershipInfo(idCommitmentBigInt) {
274
276
  try {
275
- const [startBlock, endBlock, rateLimit] = await this.contract.getMembershipInfo(idCommitment);
277
+ const membershipData = await this.contract.memberships(idCommitmentBigInt);
276
278
  const currentBlock = await this.contract.provider.getBlockNumber();
279
+ const [depositAmount, activeDuration, gracePeriodStartTimestamp, gracePeriodDuration, rateLimit, index, holder, token] = membershipData;
280
+ const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration);
277
281
  let state;
278
- if (currentBlock < startBlock) {
282
+ if (currentBlock < gracePeriodStartTimestamp.toNumber()) {
279
283
  state = MembershipState.Active;
280
284
  }
281
- else if (currentBlock < endBlock) {
285
+ else if (currentBlock < gracePeriodEnd.toNumber()) {
282
286
  state = MembershipState.GracePeriod;
283
287
  }
284
288
  else {
285
289
  state = MembershipState.Expired;
286
290
  }
287
- const index = await this.getMemberIndex(idCommitment);
288
- if (!index)
289
- return undefined;
290
291
  return {
291
292
  index,
292
- idCommitment,
293
- rateLimit: rateLimit.toNumber(),
294
- startBlock: startBlock.toNumber(),
295
- endBlock: endBlock.toNumber(),
296
- state
293
+ idCommitment: idCommitmentBigInt.toString(),
294
+ rateLimit: Number(rateLimit),
295
+ startBlock: gracePeriodStartTimestamp.toNumber(),
296
+ endBlock: gracePeriodEnd.toNumber(),
297
+ state,
298
+ depositAmount,
299
+ activeDuration,
300
+ gracePeriodDuration,
301
+ holder,
302
+ token
297
303
  };
298
304
  }
299
305
  catch (error) {
306
+ log.error("Error in getMembershipInfo:", error);
300
307
  return undefined;
301
308
  }
302
309
  }
303
- async extendMembership(idCommitment) {
304
- return this.contract.extendMemberships([idCommitment]);
310
+ async extendMembership(idCommitmentBigInt) {
311
+ const tx = await this.contract.extendMemberships([idCommitmentBigInt]);
312
+ await tx.wait();
313
+ return tx;
305
314
  }
306
- async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
307
- return this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
315
+ async eraseMembership(idCommitmentBigInt, eraseFromMembershipSet = true) {
316
+ if (!(await this.isExpired(idCommitmentBigInt)) ||
317
+ !(await this.isInGracePeriod(idCommitmentBigInt))) {
318
+ throw new Error("Membership is not expired or in grace period");
319
+ }
320
+ const estimatedGas = await this.contract.estimateGas["eraseMemberships(uint256[],bool)"]([idCommitmentBigInt], eraseFromMembershipSet);
321
+ const gasLimit = estimatedGas.add(10000);
322
+ const tx = await this.contract["eraseMemberships(uint256[],bool)"]([idCommitmentBigInt], eraseFromMembershipSet, { gasLimit });
323
+ await tx.wait();
324
+ return tx;
308
325
  }
309
- async registerMembership(idCommitment, rateLimit = DEFAULT_RATE_LIMIT) {
326
+ async registerMembership(idCommitmentBigInt, rateLimit = DEFAULT_RATE_LIMIT) {
310
327
  if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
311
328
  rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
312
329
  throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
313
330
  }
314
- return this.contract.register(idCommitment, rateLimit, []);
331
+ return this.contract.register(idCommitmentBigInt, rateLimit, []);
315
332
  }
316
- async withdraw(token, holder) {
333
+ async withdraw(token, walletAddress) {
317
334
  try {
318
- const tx = await this.contract.withdraw(token, { from: holder });
335
+ const tx = await this.contract.withdraw(token, walletAddress);
319
336
  await tx.wait();
320
337
  }
321
338
  catch (error) {
@@ -325,8 +342,9 @@ class RLNBaseContract {
325
342
  async registerWithIdentity(identity) {
326
343
  try {
327
344
  log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
345
+ const idCommitmentBigIntBE = BytesUtils.buildBigIntFromUint8ArrayBE(identity.IDCommitment);
328
346
  // Check if the ID commitment is already registered
329
- const existingIndex = await this.getMemberIndex(identity.IDCommitmentBigInt.toString());
347
+ const existingIndex = await this.getMemberIndex(idCommitmentBigIntBE);
330
348
  if (existingIndex) {
331
349
  throw new Error(`ID commitment is already registered with index ${existingIndex}`);
332
350
  }
@@ -335,9 +353,11 @@ class RLNBaseContract {
335
353
  if (remainingRateLimit < this.rateLimit) {
336
354
  throw new Error(`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`);
337
355
  }
338
- const estimatedGas = await this.contract.estimateGas.register(identity.IDCommitmentBigInt, this.rateLimit, []);
356
+ const estimatedGas = await this.contract.estimateGas.register(idCommitmentBigIntBE, this.rateLimit, []);
339
357
  const gasLimit = estimatedGas.add(10000);
340
- const txRegisterResponse = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit });
358
+ const txRegisterResponse = await this.contract.register(idCommitmentBigIntBE, this.rateLimit, [], {
359
+ gasLimit
360
+ });
341
361
  const txRegisterReceipt = await txRegisterResponse.wait();
342
362
  if (txRegisterReceipt.status === 0) {
343
363
  throw new Error("Transaction failed on-chain");
@@ -399,7 +419,7 @@ class RLNBaseContract {
399
419
  async registerWithPermitAndErase(identity, permit, idCommitmentsToErase) {
400
420
  try {
401
421
  log.info(`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`);
402
- const txRegisterResponse = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, identity.IDCommitmentBigInt, this.rateLimit, idCommitmentsToErase.map((id) => BigNumber.from(id)));
422
+ const txRegisterResponse = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, BytesUtils.buildBigIntFromUint8ArrayBE(identity.IDCommitment), this.rateLimit, idCommitmentsToErase.map((id) => BigNumber.from(id)));
403
423
  const txRegisterReceipt = await txRegisterResponse.wait();
404
424
  const memberRegistered = txRegisterReceipt.events?.find((event) => event.event === "MembershipRegistered");
405
425
  if (!memberRegistered || !memberRegistered.args) {
@@ -432,13 +452,15 @@ class RLNBaseContract {
432
452
  }
433
453
  }
434
454
  /**
435
- * Validates that the rate limit is within the allowed range
455
+ * Validates that the rate limit is within the allowed range (sync)
436
456
  * @throws Error if the rate limit is outside the allowed range
437
457
  */
438
458
  validateRateLimit(rateLimit) {
439
- if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
440
- rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
441
- throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`);
459
+ if (this.minRateLimit === undefined || this.maxRateLimit === undefined) {
460
+ throw new Error("Rate limits not initialized");
461
+ }
462
+ if (rateLimit < this.minRateLimit || rateLimit > this.maxRateLimit) {
463
+ throw new Error(`Rate limit must be between ${this.minRateLimit} and ${this.maxRateLimit} messages per epoch`);
442
464
  }
443
465
  }
444
466
  get membersFilter() {
@@ -459,9 +481,9 @@ class RLNBaseContract {
459
481
  }
460
482
  return this._membersExpiredFilter;
461
483
  }
462
- async getMemberIndex(idCommitment) {
484
+ async getMemberIndex(idCommitmentBigInt) {
463
485
  try {
464
- const events = await this.contract.queryFilter(this.contract.filters.MembershipRegistered(idCommitment));
486
+ const events = await this.contract.queryFilter(this.contract.filters.MembershipRegistered(idCommitmentBigInt));
465
487
  if (events.length === 0)
466
488
  return undefined;
467
489
  // Get the most recent registration event
@@ -472,6 +494,45 @@ class RLNBaseContract {
472
494
  return undefined;
473
495
  }
474
496
  }
497
+ async getMembershipStatus(idCommitment) {
498
+ const [isExpired, isInGrace] = await Promise.all([
499
+ this.contract.isExpired(idCommitment),
500
+ this.contract.isInGracePeriod(idCommitment)
501
+ ]);
502
+ if (isExpired)
503
+ return "expired";
504
+ if (isInGrace)
505
+ return "grace";
506
+ return "active";
507
+ }
508
+ /**
509
+ * Checks if a membership is expired for the given idCommitment
510
+ * @param idCommitmentBigInt The idCommitment as bigint
511
+ * @returns Promise<boolean> True if expired, false otherwise
512
+ */
513
+ async isExpired(idCommitmentBigInt) {
514
+ try {
515
+ return await this.contract.isExpired(idCommitmentBigInt);
516
+ }
517
+ catch (error) {
518
+ log.error("Error in isExpired:", error);
519
+ return false;
520
+ }
521
+ }
522
+ /**
523
+ * Checks if a membership is in grace period for the given idCommitment
524
+ * @param idCommitmentBigInt The idCommitment as bigint
525
+ * @returns Promise<boolean> True if in grace period, false otherwise
526
+ */
527
+ async isInGracePeriod(idCommitmentBigInt) {
528
+ try {
529
+ return await this.contract.isInGracePeriod(idCommitmentBigInt);
530
+ }
531
+ catch (error) {
532
+ log.error("Error in isInGracePeriod:", error);
533
+ return false;
534
+ }
535
+ }
475
536
  }
476
537
 
477
538
  export { RLNBaseContract };
@@ -1,10 +1,10 @@
1
- import '../../../utils/node_modules/@waku/interfaces/dist/protocols.js';
2
- import '../../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
3
- import '../../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
1
+ import '../../../interfaces/dist/protocols.js';
2
+ import '../../../interfaces/dist/connection_manager.js';
3
+ import '../../../interfaces/dist/health_indicator.js';
4
4
  import { hexToBytes } from '../../../utils/dist/bytes/index.js';
5
5
  import { Logger } from '../../../utils/dist/logger/index.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
  import { RLNBaseContract } from './rln_base_contract.js';
9
9
  import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
10
10
 
@@ -83,7 +83,7 @@ class RLNContract extends RLNBaseContract {
83
83
  if (typeof index === "number" || typeof index === "string") {
84
84
  index = BigNumber.from(index);
85
85
  }
86
- const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
86
+ const idCommitment = BytesUtils.zeroPadLE(hexToBytes(_idCommitment), 32);
87
87
  rlnInstance.zerokit.insertMember(idCommitment);
88
88
  const numericIndex = index.toNumber();
89
89
  this._members.set(numericIndex, {
@@ -1,8 +1,8 @@
1
1
  import { hmac } from '../../../node_modules/@noble/hashes/esm/hmac.js';
2
- import { sha256 } from '../../../node_modules/@noble/hashes/esm/sha256.js';
3
- import '../../utils/node_modules/@waku/interfaces/dist/protocols.js';
4
- import '../../utils/node_modules/@waku/interfaces/dist/connection_manager.js';
5
- import '../../utils/node_modules/@waku/interfaces/dist/health_indicator.js';
2
+ import { sha256 } from '../../../node_modules/@noble/hashes/esm/sha2.js';
3
+ import '../../interfaces/dist/protocols.js';
4
+ import '../../interfaces/dist/connection_manager.js';
5
+ import '../../interfaces/dist/health_indicator.js';
6
6
  import '../../../node_modules/multiformats/dist/src/bases/base10.js';
7
7
  import '../../../node_modules/multiformats/dist/src/bases/base16.js';
8
8
  import '../../../node_modules/multiformats/dist/src/bases/base2.js';
@@ -15,12 +15,12 @@ import '../../../node_modules/multiformats/dist/src/bases/base8.js';
15
15
  import '../../../node_modules/multiformats/dist/src/bases/identity.js';
16
16
  import '../../../node_modules/multiformats/dist/src/codecs/json.js';
17
17
  import { Logger } from '../../utils/dist/logger/index.js';
18
- import { LINEA_CONTRACT } from './contract/constants.js';
18
+ import { LINEA_CONTRACT, DEFAULT_Q } from './contract/constants.js';
19
19
  import { RLNBaseContract } from './contract/rln_base_contract.js';
20
20
  import { IdentityCredential } from './identity.js';
21
21
  import { Keystore } from './keystore/keystore.js';
22
+ import { BytesUtils } from './utils/bytes.js';
22
23
  import { extractMetaMaskSigner } from './utils/metamask.js';
23
- import { buildBigIntFromUint8Array } from './utils/bytes.js';
24
24
  import './utils/epoch.js';
25
25
 
26
26
  const log = new Logger("waku:credentials");
@@ -64,7 +64,7 @@ class RLNCredentialsManager {
64
64
  }
65
65
  this.credentials = credentials;
66
66
  this.signer = signer;
67
- this.contract = new RLNBaseContract({
67
+ this.contract = await RLNBaseContract.create({
68
68
  address: address,
69
69
  signer: signer,
70
70
  rateLimit: rateLimit ?? this.zerokit?.rateLimit
@@ -95,7 +95,7 @@ class RLNCredentialsManager {
95
95
  }
96
96
  else {
97
97
  log.info("Using local implementation to generate identity");
98
- identity = this.generateSeededIdentityCredential(options.signature);
98
+ identity = await this.generateSeededIdentityCredential(options.signature);
99
99
  }
100
100
  }
101
101
  if (!identity) {
@@ -191,24 +191,32 @@ class RLNCredentialsManager {
191
191
  * @param seed A string seed to generate the identity from
192
192
  * @returns IdentityCredential
193
193
  */
194
- generateSeededIdentityCredential(seed) {
194
+ async generateSeededIdentityCredential(seed) {
195
195
  log.info("Generating seeded identity credential");
196
196
  // Convert the seed to bytes
197
197
  const encoder = new TextEncoder();
198
198
  const seedBytes = encoder.encode(seed);
199
199
  // Generate deterministic values using HMAC-SHA256
200
200
  // We use different context strings for each component to ensure they're different
201
- const idTrapdoor = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
202
- const idNullifier = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
203
- // Generate IDSecretHash as a hash of IDTrapdoor and IDNullifier
204
- const combinedBytes = new Uint8Array([...idTrapdoor, ...idNullifier]);
205
- const idSecretHash = sha256(combinedBytes);
206
- // Generate IDCommitment as a hash of IDSecretHash
207
- const idCommitment = sha256(idSecretHash);
208
- // Convert IDCommitment to BigInt
209
- const idCommitmentBigInt = buildBigIntFromUint8Array(idCommitment);
210
- log.info("Successfully generated identity credential");
211
- return new IdentityCredential(idTrapdoor, idNullifier, idSecretHash, idCommitment, idCommitmentBigInt);
201
+ const idTrapdoorBE = hmac(sha256, seedBytes, encoder.encode("IDTrapdoor"));
202
+ const idNullifierBE = hmac(sha256, seedBytes, encoder.encode("IDNullifier"));
203
+ const combinedBytes = new Uint8Array([...idTrapdoorBE, ...idNullifierBE]);
204
+ const idSecretHashBE = sha256(combinedBytes);
205
+ const idCommitmentRawBE = sha256(idSecretHashBE);
206
+ const idCommitmentBE = this.reduceIdCommitment(idCommitmentRawBE);
207
+ log.info("Successfully generated identity credential, storing in Big Endian format");
208
+ return new IdentityCredential(idTrapdoorBE, idNullifierBE, idSecretHashBE, idCommitmentBE);
209
+ }
210
+ /**
211
+ * Helper: take 32-byte BE, reduce mod Q, return 32-byte BE
212
+ */
213
+ reduceIdCommitment(bytesBE, limit = DEFAULT_Q) {
214
+ const nBE = BytesUtils.buildBigIntFromUint8ArrayBE(bytesBE);
215
+ if (nBE >= limit) {
216
+ log.warn(`ID commitment is greater than Q, reducing it by Q: ${nBE} % ${limit}`);
217
+ return BytesUtils.bigIntToUint8Array32BE(nBE % limit);
218
+ }
219
+ return bytesBE;
212
220
  }
213
221
  }
214
222