@waku/rln 0.1.5-ff0222a.0 → 0.1.6-b7e9b08.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 (158) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bundle/_virtual/index2.js +1 -1
  3. package/bundle/index.js +2 -2
  4. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/checksum.js +3 -3
  5. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/cipher.js +4 -4
  6. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/class.js +7 -7
  7. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/functional.js +7 -7
  8. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/index.js +6 -6
  9. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/kdf.js +5 -5
  10. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/password.js +1 -1
  11. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/schema-validation-generated.js +1 -1
  12. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/schema-validation.js +2 -2
  13. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/lib/types.js +1 -1
  14. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/_assert.js +1 -1
  15. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/_sha2.js +3 -3
  16. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/_u64.js +1 -1
  17. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/cryptoBrowser.js +1 -1
  18. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/hmac.js +3 -3
  19. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/pbkdf2.js +4 -4
  20. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/scrypt.js +5 -5
  21. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/sha256.js +3 -3
  22. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/sha512.js +4 -4
  23. package/bundle/{packages/rln → node_modules/@chainsafe/bls-keystore}/node_modules/@noble/hashes/utils.js +2 -2
  24. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/aes.js +3 -3
  25. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/pbkdf2.js +7 -7
  26. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +3 -3
  27. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/scrypt.js +3 -3
  28. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/sha256.js +3 -3
  29. package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +7 -7
  30. package/bundle/node_modules/@ethersproject/abi/lib.esm/_version.js +1 -1
  31. package/bundle/node_modules/@ethersproject/abstract-provider/lib.esm/_version.js +1 -1
  32. package/bundle/node_modules/@ethersproject/abstract-signer/lib.esm/_version.js +1 -1
  33. package/bundle/node_modules/@ethersproject/address/lib.esm/_version.js +1 -1
  34. package/bundle/node_modules/@ethersproject/bignumber/lib.esm/_version.js +1 -1
  35. package/bundle/node_modules/@ethersproject/contracts/lib.esm/_version.js +1 -1
  36. package/bundle/node_modules/@ethersproject/contracts/lib.esm/index.js +1 -1
  37. package/bundle/node_modules/@ethersproject/hash/lib.esm/_version.js +1 -1
  38. package/bundle/node_modules/@ethersproject/networks/lib.esm/_version.js +1 -1
  39. package/bundle/node_modules/@ethersproject/networks/lib.esm/index.js +19 -1
  40. package/bundle/node_modules/@ethersproject/properties/lib.esm/_version.js +1 -1
  41. package/bundle/node_modules/@ethersproject/providers/lib.esm/_version.js +1 -1
  42. package/bundle/node_modules/@ethersproject/signing-key/lib.esm/_version.js +1 -1
  43. package/bundle/node_modules/@ethersproject/signing-key/lib.esm/elliptic.js +58 -10
  44. package/bundle/node_modules/@ethersproject/strings/lib.esm/_version.js +1 -1
  45. package/bundle/node_modules/@ethersproject/transactions/lib.esm/_version.js +1 -1
  46. package/bundle/node_modules/@ethersproject/web/lib.esm/_version.js +1 -1
  47. package/bundle/node_modules/@noble/hashes/esm/_assert.js +6 -32
  48. package/bundle/node_modules/@noble/hashes/esm/_md.js +22 -11
  49. package/bundle/node_modules/@noble/hashes/esm/_u64.js +4 -3
  50. package/bundle/{packages/rln/node_modules → node_modules}/@noble/hashes/esm/hmac.js +19 -10
  51. package/bundle/{packages/rln/node_modules/@noble/hashes/esm/sha256.js → node_modules/@noble/hashes/esm/sha2.js} +36 -50
  52. package/bundle/node_modules/@noble/hashes/esm/sha256.js +5 -102
  53. package/bundle/node_modules/@noble/hashes/esm/sha3.js +30 -24
  54. package/bundle/node_modules/@noble/hashes/esm/utils.js +69 -18
  55. package/bundle/node_modules/bn.js/lib/bn.js +1 -0
  56. package/bundle/node_modules/ethereum-cryptography/esm/sha256.js +1 -1
  57. package/bundle/packages/core/dist/lib/connection_manager/connection_manager.js +3 -0
  58. package/bundle/packages/core/dist/lib/connection_manager/keep_alive_manager.js +3 -3
  59. package/bundle/packages/core/dist/lib/filter/filter.js +3 -0
  60. package/bundle/packages/core/dist/lib/light_push/light_push.js +3 -0
  61. package/bundle/packages/core/dist/lib/metadata/metadata.js +3 -0
  62. package/bundle/packages/core/dist/lib/store/store.js +3 -3
  63. package/bundle/packages/proto/dist/generated/sds_message.js +59 -3
  64. package/bundle/packages/rln/dist/codec.js +3 -3
  65. package/bundle/packages/rln/dist/contract/constants.js +1 -1
  66. package/bundle/packages/rln/dist/contract/{rln_light_contract.js → rln_base_contract.js} +180 -180
  67. package/bundle/packages/rln/dist/contract/rln_contract.js +12 -422
  68. package/bundle/packages/rln/dist/contract/types.js +9 -0
  69. package/bundle/packages/rln/dist/create.js +1 -1
  70. package/bundle/packages/rln/dist/credentials_manager.js +215 -0
  71. package/bundle/packages/rln/dist/identity.js +0 -9
  72. package/bundle/packages/rln/dist/keystore/cipher.js +3 -3
  73. package/bundle/packages/rln/dist/keystore/keystore.js +32 -18
  74. package/bundle/packages/rln/dist/rln.js +59 -169
  75. package/bundle/packages/rln/dist/utils/epoch.js +3 -3
  76. package/bundle/packages/rln/dist/zerokit.js +5 -5
  77. package/bundle/packages/utils/dist/common/sharding/index.js +4 -4
  78. package/bundle/packages/utils/node_modules/@waku/interfaces/dist/connection_manager.js +19 -0
  79. package/bundle/packages/utils/node_modules/@waku/interfaces/dist/health_indicator.js +12 -0
  80. package/bundle/packages/utils/node_modules/@waku/interfaces/dist/protocols.js +92 -0
  81. package/dist/.tsbuildinfo +1 -1
  82. package/dist/contract/constants.d.ts +1 -1
  83. package/dist/contract/constants.js +1 -1
  84. package/dist/contract/constants.js.map +1 -1
  85. package/dist/contract/{rln_light_contract.d.ts → rln_base_contract.d.ts} +25 -53
  86. package/dist/contract/{rln_light_contract.js → rln_base_contract.js} +177 -177
  87. package/dist/contract/rln_base_contract.js.map +1 -0
  88. package/dist/contract/rln_contract.d.ts +5 -122
  89. package/dist/contract/rln_contract.js +8 -417
  90. package/dist/contract/rln_contract.js.map +1 -1
  91. package/dist/contract/types.d.ts +40 -0
  92. package/dist/contract/types.js +8 -0
  93. package/dist/contract/types.js.map +1 -0
  94. package/dist/create.js +1 -1
  95. package/dist/create.js.map +1 -1
  96. package/dist/credentials_manager.d.ts +44 -0
  97. package/dist/credentials_manager.js +197 -0
  98. package/dist/credentials_manager.js.map +1 -0
  99. package/dist/identity.d.ts +0 -1
  100. package/dist/identity.js +0 -9
  101. package/dist/identity.js.map +1 -1
  102. package/dist/index.d.ts +3 -3
  103. package/dist/index.js +3 -3
  104. package/dist/index.js.map +1 -1
  105. package/dist/keystore/keystore.d.ts +1 -0
  106. package/dist/keystore/keystore.js +27 -13
  107. package/dist/keystore/keystore.js.map +1 -1
  108. package/dist/rln.d.ts +9 -52
  109. package/dist/rln.js +54 -163
  110. package/dist/rln.js.map +1 -1
  111. package/dist/types.d.ts +27 -0
  112. package/dist/types.js +2 -0
  113. package/dist/types.js.map +1 -0
  114. package/dist/zerokit.d.ts +3 -3
  115. package/dist/zerokit.js +5 -5
  116. package/dist/zerokit.js.map +1 -1
  117. package/package.json +1 -1
  118. package/src/contract/constants.ts +1 -1
  119. package/src/contract/{rln_light_contract.ts → rln_base_contract.ts} +294 -312
  120. package/src/contract/rln_contract.ts +9 -663
  121. package/src/contract/types.ts +48 -0
  122. package/src/create.ts +1 -1
  123. package/src/credentials_manager.ts +282 -0
  124. package/src/identity.ts +0 -10
  125. package/src/index.ts +4 -4
  126. package/src/keystore/keystore.ts +49 -25
  127. package/src/rln.ts +67 -258
  128. package/src/types.ts +31 -0
  129. package/src/zerokit.ts +3 -3
  130. package/bundle/packages/rln/dist/rln_light.js +0 -149
  131. package/bundle/packages/rln/node_modules/@noble/hashes/esm/_assert.js +0 -43
  132. package/bundle/packages/rln/node_modules/@noble/hashes/esm/_sha2.js +0 -116
  133. package/bundle/packages/rln/node_modules/@noble/hashes/esm/utils.js +0 -43
  134. package/dist/contract/rln_light_contract.js.map +0 -1
  135. package/dist/rln_light.d.ts +0 -64
  136. package/dist/rln_light.js +0 -144
  137. package/dist/rln_light.js.map +0 -1
  138. package/src/rln_light.ts +0 -235
  139. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/index.js +0 -0
  140. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/md5.js +0 -0
  141. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/nil.js +0 -0
  142. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/parse.js +0 -0
  143. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/regex.js +0 -0
  144. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/rng.js +0 -0
  145. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/sha1.js +0 -0
  146. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/stringify.js +0 -0
  147. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v1.js +0 -0
  148. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v3.js +0 -0
  149. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v35.js +0 -0
  150. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v4.js +0 -0
  151. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/v5.js +0 -0
  152. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/validate.js +0 -0
  153. /package/bundle/{packages/rln/node_modules → node_modules}/@chainsafe/bls-keystore/node_modules/uuid/dist/esm-browser/version.js +0 -0
  154. /package/bundle/{packages/rln/node_modules → node_modules}/uuid/dist/esm-browser/native.js +0 -0
  155. /package/bundle/{packages/rln/node_modules → node_modules}/uuid/dist/esm-browser/rng.js +0 -0
  156. /package/bundle/{packages/rln/node_modules → node_modules}/uuid/dist/esm-browser/stringify.js +0 -0
  157. /package/bundle/{packages/rln/node_modules → node_modules}/uuid/dist/esm-browser/v4.js +0 -0
  158. /package/bundle/packages/{interfaces → utils/node_modules/@waku/interfaces}/dist/constants.js +0 -0
@@ -1,84 +1,47 @@
1
1
  import { Logger } from "@waku/utils";
2
2
  import { ethers } from "ethers";
3
3
 
4
- import type { IdentityCredential } from "../identity.js";
5
- import type { DecryptedCredentials } from "../keystore/index.js";
4
+ import { IdentityCredential } from "../identity.js";
5
+ import { DecryptedCredentials } from "../keystore/types.js";
6
6
 
7
7
  import { RLN_ABI } from "./abi.js";
8
8
  import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from "./constants.js";
9
-
10
- const log = new Logger("waku:rln:contract");
11
-
12
- type Member = {
13
- idCommitment: string;
14
- index: ethers.BigNumber;
15
- };
16
-
17
- interface RLNContractOptions {
18
- signer: ethers.Signer;
19
- address: string;
20
- rateLimit?: number;
21
- }
22
-
23
- interface RLNContractInitOptions extends RLNContractOptions {
24
- contract?: ethers.Contract;
25
- }
26
-
27
- export interface MembershipRegisteredEvent {
28
- idCommitment: string;
29
- membershipRateLimit: ethers.BigNumber;
30
- index: ethers.BigNumber;
31
- }
32
-
33
- type FetchMembersOptions = {
34
- fromBlock?: number;
35
- fetchRange?: number;
36
- fetchChunks?: number;
37
- };
38
-
39
- export interface MembershipInfo {
40
- index: ethers.BigNumber;
41
- idCommitment: string;
42
- rateLimit: number;
43
- startBlock: number;
44
- endBlock: number;
45
- state: MembershipState;
46
- }
47
-
48
- export enum MembershipState {
49
- Active = "Active",
50
- GracePeriod = "GracePeriod",
51
- Expired = "Expired",
52
- ErasedAwaitsWithdrawal = "ErasedAwaitsWithdrawal"
53
- }
54
-
55
- export class RLNLightContract {
9
+ import {
10
+ CustomQueryOptions,
11
+ FetchMembersOptions,
12
+ Member,
13
+ MembershipInfo,
14
+ MembershipRegisteredEvent,
15
+ MembershipState,
16
+ RLNContractInitOptions
17
+ } from "./types.js";
18
+
19
+ const log = new Logger("waku:rln:contract:base");
20
+
21
+ export class RLNBaseContract {
56
22
  public contract: ethers.Contract;
57
-
58
23
  private deployBlock: undefined | number;
59
24
  private rateLimit: number;
60
25
 
61
- private _members: Map<number, Member> = new Map();
26
+ protected _members: Map<number, Member> = new Map();
62
27
  private _membersFilter: ethers.EventFilter;
63
28
  private _membershipErasedFilter: ethers.EventFilter;
64
29
  private _membersExpiredFilter: ethers.EventFilter;
65
30
 
66
31
  /**
67
- * Asynchronous initializer for RLNContract.
32
+ * Constructor for RLNBaseContract.
68
33
  * Allows injecting a mocked contract for testing purposes.
69
34
  */
70
- public static async init(
71
- options: RLNContractInitOptions
72
- ): Promise<RLNLightContract> {
73
- const rlnContract = new RLNLightContract(options);
74
-
75
- await rlnContract.fetchMembers();
76
- rlnContract.subscribeToMembers();
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
+ });
77
44
 
78
- return rlnContract;
79
- }
80
-
81
- private constructor(options: RLNContractInitOptions) {
82
45
  const {
83
46
  address,
84
47
  signer,
@@ -86,19 +49,10 @@ export class RLNLightContract {
86
49
  contract
87
50
  } = options;
88
51
 
89
- if (
90
- rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
91
- rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
92
- ) {
93
- throw new Error(
94
- `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`
95
- );
96
- }
97
-
98
- this.rateLimit = rateLimit;
52
+ this.validateRateLimit(rateLimit);
99
53
 
100
- // Use the injected contract if provided; otherwise, instantiate a new one.
101
54
  this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
55
+ this.rateLimit = rateLimit;
102
56
 
103
57
  // Initialize event filters
104
58
  this._membersFilter = this.contract.filters.MembershipRegistered();
@@ -151,7 +105,7 @@ export class RLNLightContract {
151
105
  */
152
106
  public async getMaxTotalRateLimit(): Promise<number> {
153
107
  const maxTotalRate = await this.contract.maxTotalRateLimit();
154
- return ethers.BigNumber.from(maxTotalRate).toNumber();
108
+ return maxTotalRate.toNumber();
155
109
  }
156
110
 
157
111
  /**
@@ -160,7 +114,7 @@ export class RLNLightContract {
160
114
  */
161
115
  public async getCurrentTotalRateLimit(): Promise<number> {
162
116
  const currentTotal = await this.contract.currentTotalRateLimit();
163
- return ethers.BigNumber.from(currentTotal).toNumber();
117
+ return currentTotal.toNumber();
164
118
  }
165
119
 
166
120
  /**
@@ -172,9 +126,7 @@ export class RLNLightContract {
172
126
  this.contract.maxTotalRateLimit(),
173
127
  this.contract.currentTotalRateLimit()
174
128
  ]);
175
- return ethers.BigNumber.from(maxTotal)
176
- .sub(ethers.BigNumber.from(currentTotal))
177
- .toNumber();
129
+ return Number(maxTotal) - Number(currentTotal);
178
130
  }
179
131
 
180
132
  /**
@@ -182,6 +134,7 @@ export class RLNLightContract {
182
134
  * @param newRateLimit The new rate limit to use
183
135
  */
184
136
  public async setRateLimit(newRateLimit: number): Promise<void> {
137
+ this.validateRateLimit(newRateLimit);
185
138
  this.rateLimit = newRateLimit;
186
139
  }
187
140
 
@@ -192,43 +145,31 @@ export class RLNLightContract {
192
145
  return sortedMembers;
193
146
  }
194
147
 
195
- private get membersFilter(): ethers.EventFilter {
196
- if (!this._membersFilter) {
197
- throw Error("Members filter was not initialized.");
198
- }
199
- return this._membersFilter;
200
- }
201
-
202
- private get membershipErasedFilter(): ethers.EventFilter {
203
- if (!this._membershipErasedFilter) {
204
- throw Error("MembershipErased filter was not initialized.");
205
- }
206
- return this._membershipErasedFilter;
207
- }
208
-
209
- private get membersExpiredFilter(): ethers.EventFilter {
210
- if (!this._membersExpiredFilter) {
211
- throw Error("MembersExpired filter was not initialized.");
212
- }
213
- return this._membersExpiredFilter;
214
- }
215
-
216
148
  public async fetchMembers(options: FetchMembersOptions = {}): Promise<void> {
217
- const registeredMemberEvents = await queryFilter(this.contract, {
218
- fromBlock: this.deployBlock,
219
- ...options,
220
- membersFilter: this.membersFilter
221
- });
222
- const removedMemberEvents = await queryFilter(this.contract, {
223
- fromBlock: this.deployBlock,
224
- ...options,
225
- membersFilter: this.membershipErasedFilter
226
- });
227
- const expiredMemberEvents = await queryFilter(this.contract, {
228
- fromBlock: this.deployBlock,
229
- ...options,
230
- membersFilter: this.membersExpiredFilter
231
- });
149
+ const registeredMemberEvents = await RLNBaseContract.queryFilter(
150
+ this.contract,
151
+ {
152
+ fromBlock: this.deployBlock,
153
+ ...options,
154
+ membersFilter: this.membersFilter
155
+ }
156
+ );
157
+ const removedMemberEvents = await RLNBaseContract.queryFilter(
158
+ this.contract,
159
+ {
160
+ fromBlock: this.deployBlock,
161
+ ...options,
162
+ membersFilter: this.membershipErasedFilter
163
+ }
164
+ );
165
+ const expiredMemberEvents = await RLNBaseContract.queryFilter(
166
+ this.contract,
167
+ {
168
+ fromBlock: this.deployBlock,
169
+ ...options,
170
+ membersFilter: this.membersExpiredFilter
171
+ }
172
+ );
232
173
 
233
174
  const events = [
234
175
  ...registeredMemberEvents,
@@ -238,6 +179,58 @@ export class RLNLightContract {
238
179
  this.processEvents(events);
239
180
  }
240
181
 
182
+ public static async queryFilter(
183
+ contract: ethers.Contract,
184
+ options: CustomQueryOptions
185
+ ): Promise<ethers.Event[]> {
186
+ const FETCH_CHUNK = 5;
187
+ const BLOCK_RANGE = 3000;
188
+
189
+ const {
190
+ fromBlock,
191
+ membersFilter,
192
+ fetchRange = BLOCK_RANGE,
193
+ fetchChunks = FETCH_CHUNK
194
+ } = options;
195
+
196
+ if (fromBlock === undefined) {
197
+ return contract.queryFilter(membersFilter);
198
+ }
199
+
200
+ if (!contract.provider) {
201
+ throw Error("No provider found on the contract.");
202
+ }
203
+
204
+ const toBlock = await contract.provider.getBlockNumber();
205
+
206
+ if (toBlock - fromBlock < fetchRange) {
207
+ return contract.queryFilter(membersFilter, fromBlock, toBlock);
208
+ }
209
+
210
+ const events: ethers.Event[][] = [];
211
+ const chunks = RLNBaseContract.splitToChunks(
212
+ fromBlock,
213
+ toBlock,
214
+ fetchRange
215
+ );
216
+
217
+ for (const portion of RLNBaseContract.takeN<[number, number]>(
218
+ chunks,
219
+ fetchChunks
220
+ )) {
221
+ const promises = portion.map(([left, right]) =>
222
+ RLNBaseContract.ignoreErrors(
223
+ contract.queryFilter(membersFilter, left, right),
224
+ []
225
+ )
226
+ );
227
+ const fetchedEvents = await Promise.all(promises);
228
+ events.push(fetchedEvents.flatMap((v) => v));
229
+ }
230
+
231
+ return events.flatMap((v) => v);
232
+ }
233
+
241
234
  public processEvents(events: ethers.Event[]): void {
242
235
  const toRemoveTable = new Map<number, number[]>();
243
236
  const toInsertTable = new Map<number, ethers.Event[]>();
@@ -280,6 +273,53 @@ export class RLNLightContract {
280
273
  });
281
274
  }
282
275
 
276
+ public static splitToChunks(
277
+ from: number,
278
+ to: number,
279
+ step: number
280
+ ): Array<[number, number]> {
281
+ const chunks: Array<[number, number]> = [];
282
+
283
+ let left = from;
284
+ while (left < to) {
285
+ const right = left + step < to ? left + step : to;
286
+
287
+ chunks.push([left, right] as [number, number]);
288
+
289
+ left = right;
290
+ }
291
+
292
+ return chunks;
293
+ }
294
+
295
+ public static *takeN<T>(array: T[], size: number): Iterable<T[]> {
296
+ let start = 0;
297
+
298
+ while (start < array.length) {
299
+ const portion = array.slice(start, start + size);
300
+
301
+ yield portion;
302
+
303
+ start += size;
304
+ }
305
+ }
306
+
307
+ public static async ignoreErrors<T>(
308
+ promise: Promise<T>,
309
+ defaultValue: T
310
+ ): Promise<T> {
311
+ try {
312
+ return await promise;
313
+ } catch (err: unknown) {
314
+ if (err instanceof Error) {
315
+ log.info(`Ignoring an error during query: ${err.message}`);
316
+ } else {
317
+ log.info(`Ignoring an unknown error during query`);
318
+ }
319
+ return defaultValue;
320
+ }
321
+ }
322
+
283
323
  public subscribeToMembers(): void {
284
324
  this.contract.on(
285
325
  this.membersFilter,
@@ -318,6 +358,116 @@ export class RLNLightContract {
318
358
  );
319
359
  }
320
360
 
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
+ public async getMembershipInfo(
399
+ idCommitment: string
400
+ ): Promise<MembershipInfo | undefined> {
401
+ try {
402
+ const [startBlock, endBlock, rateLimit] =
403
+ await this.contract.getMembershipInfo(idCommitment);
404
+ const currentBlock = await this.contract.provider.getBlockNumber();
405
+
406
+ let state: MembershipState;
407
+ if (currentBlock < startBlock) {
408
+ state = MembershipState.Active;
409
+ } else if (currentBlock < endBlock) {
410
+ state = MembershipState.GracePeriod;
411
+ } else {
412
+ state = MembershipState.Expired;
413
+ }
414
+
415
+ const index = await this.getMemberIndex(idCommitment);
416
+ if (!index) return undefined;
417
+
418
+ return {
419
+ index,
420
+ idCommitment,
421
+ rateLimit: rateLimit.toNumber(),
422
+ startBlock: startBlock.toNumber(),
423
+ endBlock: endBlock.toNumber(),
424
+ state
425
+ };
426
+ } catch (error) {
427
+ return undefined;
428
+ }
429
+ }
430
+
431
+ public async extendMembership(
432
+ idCommitment: string
433
+ ): Promise<ethers.ContractTransaction> {
434
+ return this.contract.extendMemberships([idCommitment]);
435
+ }
436
+
437
+ public async eraseMembership(
438
+ idCommitment: string,
439
+ eraseFromMembershipSet: boolean = true
440
+ ): Promise<ethers.ContractTransaction> {
441
+ return this.contract.eraseMemberships(
442
+ [idCommitment],
443
+ eraseFromMembershipSet
444
+ );
445
+ }
446
+
447
+ public async registerMembership(
448
+ idCommitment: string,
449
+ rateLimit: number = DEFAULT_RATE_LIMIT
450
+ ): Promise<ethers.ContractTransaction> {
451
+ if (
452
+ rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
453
+ rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
454
+ ) {
455
+ throw new Error(
456
+ `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
457
+ );
458
+ }
459
+ return this.contract.register(idCommitment, rateLimit, []);
460
+ }
461
+
462
+ public async withdraw(token: string, holder: string): Promise<void> {
463
+ try {
464
+ const tx = await this.contract.withdraw(token, { from: holder });
465
+ await tx.wait();
466
+ } catch (error) {
467
+ log.error(`Error in withdraw: ${(error as Error).message}`);
468
+ }
469
+ }
470
+
321
471
  public async registerWithIdentity(
322
472
  identity: IdentityCredential
323
473
  ): Promise<DecryptedCredentials | undefined> {
@@ -389,13 +539,13 @@ export class RLNLightContract {
389
539
 
390
540
  const network = await this.contract.provider.getNetwork();
391
541
  const address = this.contract.address;
392
- const membershipId = decodedData.index.toString();
542
+ const membershipId = Number(decodedData.index);
393
543
 
394
544
  return {
395
545
  identity,
396
546
  membership: {
397
547
  address,
398
- treeIndex: parseInt(membershipId),
548
+ treeIndex: membershipId,
399
549
  chainId: network.chainId.toString(),
400
550
  rateLimit: decodedData.membershipRateLimit.toNumber()
401
551
  }
@@ -430,43 +580,6 @@ export class RLNLightContract {
430
580
  }
431
581
  }
432
582
 
433
- /**
434
- * Helper method to get remaining messages in current epoch
435
- * @param membershipId The ID of the membership to check
436
- * @returns number of remaining messages allowed in current epoch
437
- */
438
- public async getRemainingMessages(membershipId: number): Promise<number> {
439
- try {
440
- const [startTime, , rateLimit] =
441
- await this.contract.getMembershipInfo(membershipId);
442
-
443
- // Calculate current epoch
444
- const currentTime = Math.floor(Date.now() / 1000);
445
- const epochsPassed = Math.floor(
446
- (currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH
447
- );
448
- const currentEpochStart =
449
- startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
450
-
451
- // Get message count in current epoch using contract's function
452
- const messageCount = await this.contract.getMessageCount(
453
- membershipId,
454
- currentEpochStart
455
- );
456
- return Math.max(
457
- 0,
458
- ethers.BigNumber.from(rateLimit)
459
- .sub(ethers.BigNumber.from(messageCount))
460
- .toNumber()
461
- );
462
- } catch (error) {
463
- log.error(
464
- `Error getting remaining messages: ${(error as Error).message}`
465
- );
466
- return 0; // Fail safe: assume no messages remaining on error
467
- }
468
- }
469
-
470
583
  public async registerWithPermitAndErase(
471
584
  identity: IdentityCredential,
472
585
  permit: {
@@ -520,13 +633,13 @@ export class RLNLightContract {
520
633
 
521
634
  const network = await this.contract.provider.getNetwork();
522
635
  const address = this.contract.address;
523
- const membershipId = decodedData.index.toString();
636
+ const membershipId = Number(decodedData.index);
524
637
 
525
638
  return {
526
639
  identity,
527
640
  membership: {
528
641
  address,
529
- treeIndex: parseInt(membershipId),
642
+ treeIndex: membershipId,
530
643
  chainId: network.chainId.toString(),
531
644
  rateLimit: decodedData.membershipRateLimit.toNumber()
532
645
  }
@@ -539,77 +652,40 @@ export class RLNLightContract {
539
652
  }
540
653
  }
541
654
 
542
- public async withdraw(token: string, holder: string): Promise<void> {
543
- try {
544
- const tx = await this.contract.withdraw(token, { from: holder });
545
- await tx.wait();
546
- } catch (error) {
547
- log.error(`Error in withdraw: ${(error as Error).message}`);
655
+ /**
656
+ * Validates that the rate limit is within the allowed range
657
+ * @throws Error if the rate limit is outside the allowed range
658
+ */
659
+ private validateRateLimit(rateLimit: number): void {
660
+ if (
661
+ rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
662
+ rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
663
+ ) {
664
+ throw new Error(
665
+ `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`
666
+ );
548
667
  }
549
668
  }
550
669
 
551
- public async getMembershipInfo(
552
- idCommitment: string
553
- ): Promise<MembershipInfo | undefined> {
554
- try {
555
- const [startBlock, endBlock, rateLimit] =
556
- await this.contract.getMembershipInfo(idCommitment);
557
- const currentBlock = await this.contract.provider.getBlockNumber();
558
-
559
- let state: MembershipState;
560
- if (currentBlock < startBlock) {
561
- state = MembershipState.Active;
562
- } else if (currentBlock < endBlock) {
563
- state = MembershipState.GracePeriod;
564
- } else {
565
- state = MembershipState.Expired;
566
- }
567
-
568
- const index = await this.getMemberIndex(idCommitment);
569
- if (!index) return undefined;
570
-
571
- return {
572
- index,
573
- idCommitment,
574
- rateLimit: rateLimit.toNumber(),
575
- startBlock: startBlock.toNumber(),
576
- endBlock: endBlock.toNumber(),
577
- state
578
- };
579
- } catch (error) {
580
- return undefined;
670
+ private get membersFilter(): ethers.EventFilter {
671
+ if (!this._membersFilter) {
672
+ throw Error("Members filter was not initialized.");
581
673
  }
674
+ return this._membersFilter;
582
675
  }
583
676
 
584
- public async extendMembership(
585
- idCommitment: string
586
- ): Promise<ethers.ContractTransaction> {
587
- return this.contract.extendMemberships([idCommitment]);
588
- }
589
-
590
- public async eraseMembership(
591
- idCommitment: string,
592
- eraseFromMembershipSet: boolean = true
593
- ): Promise<ethers.ContractTransaction> {
594
- return this.contract.eraseMemberships(
595
- [idCommitment],
596
- eraseFromMembershipSet
597
- );
677
+ private get membershipErasedFilter(): ethers.EventFilter {
678
+ if (!this._membershipErasedFilter) {
679
+ throw Error("MembershipErased filter was not initialized.");
680
+ }
681
+ return this._membershipErasedFilter;
598
682
  }
599
683
 
600
- public async registerMembership(
601
- idCommitment: string,
602
- rateLimit: number = DEFAULT_RATE_LIMIT
603
- ): Promise<ethers.ContractTransaction> {
604
- if (
605
- rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
606
- rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
607
- ) {
608
- throw new Error(
609
- `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
610
- );
684
+ private get membersExpiredFilter(): ethers.EventFilter {
685
+ if (!this._membersExpiredFilter) {
686
+ throw Error("MembersExpired filter was not initialized.");
611
687
  }
612
- return this.contract.register(idCommitment, rateLimit, []);
688
+ return this._membersExpiredFilter;
613
689
  }
614
690
 
615
691
  private async getMemberIndex(
@@ -629,97 +705,3 @@ export class RLNLightContract {
629
705
  }
630
706
  }
631
707
  }
632
-
633
- interface CustomQueryOptions extends FetchMembersOptions {
634
- membersFilter: ethers.EventFilter;
635
- }
636
-
637
- // These values should be tested on other networks
638
- const FETCH_CHUNK = 5;
639
- const BLOCK_RANGE = 3000;
640
-
641
- async function queryFilter(
642
- contract: ethers.Contract,
643
- options: CustomQueryOptions
644
- ): Promise<ethers.Event[]> {
645
- const {
646
- fromBlock,
647
- membersFilter,
648
- fetchRange = BLOCK_RANGE,
649
- fetchChunks = FETCH_CHUNK
650
- } = options;
651
-
652
- if (fromBlock === undefined) {
653
- return contract.queryFilter(membersFilter);
654
- }
655
-
656
- if (!contract.provider) {
657
- throw Error("No provider found on the contract.");
658
- }
659
-
660
- const toBlock = await contract.provider.getBlockNumber();
661
-
662
- if (toBlock - fromBlock < fetchRange) {
663
- return contract.queryFilter(membersFilter, fromBlock, toBlock);
664
- }
665
-
666
- const events: ethers.Event[][] = [];
667
- const chunks = splitToChunks(fromBlock, toBlock, fetchRange);
668
-
669
- for (const portion of takeN<[number, number]>(chunks, fetchChunks)) {
670
- const promises = portion.map(([left, right]) =>
671
- ignoreErrors(contract.queryFilter(membersFilter, left, right), [])
672
- );
673
- const fetchedEvents = await Promise.all(promises);
674
- events.push(fetchedEvents.flatMap((v) => v));
675
- }
676
-
677
- return events.flatMap((v) => v);
678
- }
679
-
680
- function splitToChunks(
681
- from: number,
682
- to: number,
683
- step: number
684
- ): Array<[number, number]> {
685
- const chunks: Array<[number, number]> = [];
686
-
687
- let left = from;
688
- while (left < to) {
689
- const right = left + step < to ? left + step : to;
690
-
691
- chunks.push([left, right] as [number, number]);
692
-
693
- left = right;
694
- }
695
-
696
- return chunks;
697
- }
698
-
699
- function* takeN<T>(array: T[], size: number): Iterable<T[]> {
700
- let start = 0;
701
-
702
- while (start < array.length) {
703
- const portion = array.slice(start, start + size);
704
-
705
- yield portion;
706
-
707
- start += size;
708
- }
709
- }
710
-
711
- async function ignoreErrors<T>(
712
- promise: Promise<T>,
713
- defaultValue: T
714
- ): Promise<T> {
715
- try {
716
- return await promise;
717
- } catch (err: unknown) {
718
- if (err instanceof Error) {
719
- log.info(`Ignoring an error during query: ${err.message}`);
720
- } else {
721
- log.info(`Ignoring an unknown error during query`);
722
- }
723
- return defaultValue;
724
- }
725
- }