@waku/rln 0.0.2-c41b319.0 → 0.0.2-c8128d1.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 (45) hide show
  1. package/bundle/index.js +1 -1
  2. package/bundle/packages/interfaces/dist/protocols.js +40 -45
  3. package/bundle/packages/rln/dist/contract/abi.js +648 -0
  4. package/bundle/packages/rln/dist/contract/constants.js +8 -13
  5. package/bundle/packages/rln/dist/contract/rln_contract.js +148 -25
  6. package/bundle/packages/rln/dist/identity.js +0 -24
  7. package/bundle/packages/rln/dist/rln.js +33 -15
  8. package/bundle/packages/rln/dist/zerokit.js +22 -16
  9. package/dist/.tsbuildinfo +1 -1
  10. package/dist/contract/{abi/rlnv2.d.ts → abi.d.ts} +22 -18
  11. package/dist/contract/abi.js +647 -0
  12. package/dist/contract/abi.js.map +1 -0
  13. package/dist/contract/constants.d.ts +22 -23
  14. package/dist/contract/constants.js +7 -12
  15. package/dist/contract/constants.js.map +1 -1
  16. package/dist/contract/rln_contract.d.ts +13 -3
  17. package/dist/contract/rln_contract.js +148 -25
  18. package/dist/contract/rln_contract.js.map +1 -1
  19. package/dist/identity.d.ts +0 -1
  20. package/dist/identity.js +0 -24
  21. package/dist/identity.js.map +1 -1
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.js +2 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/rln.js +33 -14
  26. package/dist/rln.js.map +1 -1
  27. package/dist/zerokit.d.ts +5 -1
  28. package/dist/zerokit.js +22 -16
  29. package/dist/zerokit.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/contract/abi.ts +646 -0
  32. package/src/contract/constants.ts +8 -14
  33. package/src/contract/rln_contract.ts +227 -27
  34. package/src/identity.ts +0 -42
  35. package/src/index.ts +2 -2
  36. package/src/rln.ts +48 -14
  37. package/src/zerokit.ts +45 -16
  38. package/bundle/node_modules/@iden3/js-crypto/dist/browser/esm/index.js +0 -7
  39. package/bundle/node_modules/@stablelib/binary/lib/binary.js +0 -22
  40. package/bundle/node_modules/@stablelib/chacha/lib/chacha.js +0 -245
  41. package/bundle/node_modules/@stablelib/wipe/lib/wipe.js +0 -26
  42. package/bundle/packages/rln/dist/contract/abi/rlnv2.js +0 -394
  43. package/dist/contract/abi/rlnv2.js +0 -393
  44. package/dist/contract/abi/rlnv2.js.map +0 -1
  45. package/src/contract/abi/rlnv2.ts +0 -392
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import { Logger } from "@waku/utils";
2
3
  import { hexToBytes } from "@waku/utils/bytes";
3
4
  import { ethers } from "ethers";
@@ -8,7 +9,7 @@ import type { RLNInstance } from "../rln.js";
8
9
  import { MerkleRootTracker } from "../root_tracker.js";
9
10
  import { zeroPadLE } from "../utils/bytes.js";
10
11
 
11
- import { RLN_V2_ABI } from "./abi/rlnv2.js";
12
+ import { RLN_ABI } from "./abi.js";
12
13
  import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from "./constants.js";
13
14
 
14
15
  const log = new Logger("waku:rln:contract");
@@ -30,7 +31,7 @@ interface RLNContractInitOptions extends RLNContractOptions {
30
31
 
31
32
  export interface MembershipRegisteredEvent {
32
33
  idCommitment: string;
33
- rateLimit: number;
34
+ membershipRateLimit: ethers.BigNumber;
34
35
  index: ethers.BigNumber;
35
36
  }
36
37
 
@@ -66,6 +67,7 @@ export class RLNContract {
66
67
  private _members: Map<number, Member> = new Map();
67
68
  private _membersFilter: ethers.EventFilter;
68
69
  private _membersRemovedFilter: ethers.EventFilter;
70
+ private _membersExpiredFilter: ethers.EventFilter;
69
71
 
70
72
  /**
71
73
  * Asynchronous initializer for RLNContract.
@@ -108,13 +110,13 @@ export class RLNContract {
108
110
  const initialRoot = rlnInstance.zerokit.getMerkleRoot();
109
111
 
110
112
  // Use the injected contract if provided; otherwise, instantiate a new one.
111
- this.contract =
112
- contract || new ethers.Contract(address, RLN_V2_ABI, signer);
113
+ this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
113
114
  this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
114
115
 
115
- // Initialize event filters for MembershipRegistered and MembershipRemoved
116
+ // Initialize event filters
116
117
  this._membersFilter = this.contract.filters.MembershipRegistered();
117
- this._membersRemovedFilter = this.contract.filters.MembershipRemoved();
118
+ this._membersRemovedFilter = this.contract.filters.MembershipErased();
119
+ this._membersExpiredFilter = this.contract.filters.MembershipExpired();
118
120
  }
119
121
 
120
122
  /**
@@ -124,6 +126,20 @@ export class RLNContract {
124
126
  return this.rateLimit;
125
127
  }
126
128
 
129
+ /**
130
+ * Gets the contract address
131
+ */
132
+ public get address(): string {
133
+ return this.contract.address;
134
+ }
135
+
136
+ /**
137
+ * Gets the contract provider
138
+ */
139
+ public get provider(): ethers.providers.Provider {
140
+ return this.contract.provider;
141
+ }
142
+
127
143
  /**
128
144
  * Gets the minimum allowed rate limit from the contract
129
145
  * @returns Promise<number> The minimum rate limit in messages per epoch
@@ -196,11 +212,18 @@ export class RLNContract {
196
212
 
197
213
  private get membersRemovedFilter(): ethers.EventFilter {
198
214
  if (!this._membersRemovedFilter) {
199
- throw Error("MembersRemoved filter was not initialized.");
215
+ throw Error("MembersErased filter was not initialized.");
200
216
  }
201
217
  return this._membersRemovedFilter;
202
218
  }
203
219
 
220
+ private get membersExpiredFilter(): ethers.EventFilter {
221
+ if (!this._membersExpiredFilter) {
222
+ throw Error("MembersExpired filter was not initialized.");
223
+ }
224
+ return this._membersExpiredFilter;
225
+ }
226
+
204
227
  public async fetchMembers(
205
228
  rlnInstance: RLNInstance,
206
229
  options: FetchMembersOptions = {}
@@ -215,8 +238,17 @@ export class RLNContract {
215
238
  ...options,
216
239
  membersFilter: this.membersRemovedFilter
217
240
  });
241
+ const expiredMemberEvents = await queryFilter(this.contract, {
242
+ fromBlock: this.deployBlock,
243
+ ...options,
244
+ membersFilter: this.membersExpiredFilter
245
+ });
218
246
 
219
- const events = [...registeredMemberEvents, ...removedMemberEvents];
247
+ const events = [
248
+ ...registeredMemberEvents,
249
+ ...removedMemberEvents,
250
+ ...expiredMemberEvents
251
+ ];
220
252
  this.processEvents(rlnInstance, events);
221
253
  }
222
254
 
@@ -229,8 +261,22 @@ export class RLNContract {
229
261
  return;
230
262
  }
231
263
 
232
- if (evt.event === "MembershipRemoved") {
233
- const index = evt.args.index as ethers.BigNumber;
264
+ if (
265
+ evt.event === "MembershipErased" ||
266
+ evt.event === "MembershipExpired"
267
+ ) {
268
+ // Both MembershipErased and MembershipExpired events should remove members
269
+ let index = evt.args.index;
270
+
271
+ if (!index) {
272
+ return;
273
+ }
274
+
275
+ // Convert index to ethers.BigNumber if it's not already
276
+ if (typeof index === "number" || typeof index === "string") {
277
+ index = ethers.BigNumber.from(index);
278
+ }
279
+
234
280
  const toRemoveVal = toRemoveTable.get(evt.blockNumber);
235
281
  if (toRemoveVal != undefined) {
236
282
  toRemoveVal.push(index.toNumber());
@@ -262,16 +308,25 @@ export class RLNContract {
262
308
  if (!evt.args) return;
263
309
 
264
310
  const _idCommitment = evt.args.idCommitment as string;
265
- const index = evt.args.index as ethers.BigNumber;
311
+ let index = evt.args.index;
266
312
 
313
+ // Ensure index is an ethers.BigNumber
267
314
  if (!_idCommitment || !index) {
268
315
  return;
269
316
  }
270
317
 
318
+ // Convert index to ethers.BigNumber if it's not already
319
+ if (typeof index === "number" || typeof index === "string") {
320
+ index = ethers.BigNumber.from(index);
321
+ }
322
+
271
323
  const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
272
324
  rlnInstance.zerokit.insertMember(idCommitment);
273
- this._members.set(index.toNumber(), {
274
- index,
325
+
326
+ // Always store the numeric index as the key, but the BigNumber as the value
327
+ const numericIndex = index.toNumber();
328
+ this._members.set(numericIndex, {
329
+ index, // This is always a BigNumber
275
330
  idCommitment: _idCommitment
276
331
  });
277
332
  });
@@ -303,7 +358,7 @@ export class RLNContract {
303
358
  this.membersFilter,
304
359
  (
305
360
  _idCommitment: string,
306
- _rateLimit: number,
361
+ _membershipRateLimit: ethers.BigNumber,
307
362
  _index: ethers.BigNumber,
308
363
  event: ethers.Event
309
364
  ) => {
@@ -315,6 +370,19 @@ export class RLNContract {
315
370
  this.membersRemovedFilter,
316
371
  (
317
372
  _idCommitment: string,
373
+ _membershipRateLimit: ethers.BigNumber,
374
+ _index: ethers.BigNumber,
375
+ event: ethers.Event
376
+ ) => {
377
+ this.processEvents(rlnInstance, [event]);
378
+ }
379
+ );
380
+
381
+ this.contract.on(
382
+ this.membersExpiredFilter,
383
+ (
384
+ _idCommitment: string,
385
+ _membershipRateLimit: ethers.BigNumber,
318
386
  _index: ethers.BigNumber,
319
387
  event: ethers.Event
320
388
  ) => {
@@ -327,10 +395,43 @@ export class RLNContract {
327
395
  identity: IdentityCredential
328
396
  ): Promise<DecryptedCredentials | undefined> {
329
397
  try {
398
+ console.log("registerWithIdentity - starting registration process");
399
+ console.log("registerWithIdentity - identity:", identity);
400
+ console.log(
401
+ "registerWithIdentity - IDCommitmentBigInt:",
402
+ identity.IDCommitmentBigInt.toString()
403
+ );
404
+ console.log("registerWithIdentity - rate limit:", this.rateLimit);
405
+
330
406
  log.info(
331
407
  `Registering identity with rate limit: ${this.rateLimit} messages/epoch`
332
408
  );
333
409
 
410
+ // Check if the ID commitment is already registered
411
+ const existingIndex = await this.getMemberIndex(
412
+ identity.IDCommitmentBigInt.toString()
413
+ );
414
+ if (existingIndex) {
415
+ console.error(
416
+ `ID commitment is already registered with index ${existingIndex}`
417
+ );
418
+ throw new Error(
419
+ `ID commitment is already registered with index ${existingIndex}`
420
+ );
421
+ }
422
+
423
+ // Check if there's enough remaining rate limit
424
+ const remainingRateLimit = await this.getRemainingTotalRateLimit();
425
+ if (remainingRateLimit < this.rateLimit) {
426
+ console.error(
427
+ `Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`
428
+ );
429
+ throw new Error(
430
+ `Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`
431
+ );
432
+ }
433
+
434
+ console.log("registerWithIdentity - calling contract.register");
334
435
  const txRegisterResponse: ethers.ContractTransaction =
335
436
  await this.contract.register(
336
437
  identity.IDCommitmentBigInt,
@@ -338,35 +439,97 @@ export class RLNContract {
338
439
  [],
339
440
  { gasLimit: 300000 }
340
441
  );
442
+ console.log(
443
+ "registerWithIdentity - txRegisterResponse:",
444
+ txRegisterResponse
445
+ );
446
+ console.log("registerWithIdentity - hash:", txRegisterResponse.hash);
447
+ console.log(
448
+ "registerWithIdentity - waiting for transaction confirmation..."
449
+ );
450
+
341
451
  const txRegisterReceipt = await txRegisterResponse.wait();
452
+ console.log(
453
+ "registerWithIdentity - txRegisterReceipt:",
454
+ txRegisterReceipt
455
+ );
456
+ console.log(
457
+ "registerWithIdentity - transaction status:",
458
+ txRegisterReceipt.status
459
+ );
460
+ console.log(
461
+ "registerWithIdentity - block number:",
462
+ txRegisterReceipt.blockNumber
463
+ );
464
+ console.log(
465
+ "registerWithIdentity - gas used:",
466
+ txRegisterReceipt.gasUsed.toString()
467
+ );
468
+
469
+ // Check transaction status
470
+ if (txRegisterReceipt.status === 0) {
471
+ console.error("Transaction failed on-chain");
472
+ throw new Error("Transaction failed on-chain");
473
+ }
342
474
 
343
475
  const memberRegistered = txRegisterReceipt.events?.find(
344
476
  (event) => event.event === "MembershipRegistered"
345
477
  );
478
+ console.log(
479
+ "registerWithIdentity - memberRegistered event:",
480
+ memberRegistered
481
+ );
346
482
 
347
483
  if (!memberRegistered || !memberRegistered.args) {
348
- log.error(
484
+ console.log(
485
+ "registerWithIdentity - ERROR: no memberRegistered event found"
486
+ );
487
+ console.log(
488
+ "registerWithIdentity - all events:",
489
+ txRegisterReceipt.events
490
+ );
491
+ console.error(
349
492
  "Failed to register membership: No MembershipRegistered event found"
350
493
  );
351
494
  return undefined;
352
495
  }
353
496
 
497
+ console.log(
498
+ "registerWithIdentity - memberRegistered args:",
499
+ memberRegistered.args
500
+ );
354
501
  const decodedData: MembershipRegisteredEvent = {
355
502
  idCommitment: memberRegistered.args.idCommitment,
356
- rateLimit: memberRegistered.args.rateLimit,
503
+ membershipRateLimit: memberRegistered.args.membershipRateLimit,
357
504
  index: memberRegistered.args.index
358
505
  };
506
+ console.log("registerWithIdentity - decodedData:", decodedData);
507
+ console.log(
508
+ "registerWithIdentity - index:",
509
+ decodedData.index.toString()
510
+ );
511
+ console.log(
512
+ "registerWithIdentity - membershipRateLimit:",
513
+ decodedData.membershipRateLimit.toString()
514
+ );
359
515
 
360
516
  log.info(
361
517
  `Successfully registered membership with index ${decodedData.index} ` +
362
- `and rate limit ${decodedData.rateLimit}`
518
+ `and rate limit ${decodedData.membershipRateLimit}`
363
519
  );
364
520
 
521
+ console.log("registerWithIdentity - getting network information");
365
522
  const network = await this.contract.provider.getNetwork();
523
+ console.log("registerWithIdentity - network:", network);
524
+ console.log("registerWithIdentity - chainId:", network.chainId);
525
+
366
526
  const address = this.contract.address;
527
+ console.log("registerWithIdentity - contract address:", address);
528
+
367
529
  const membershipId = decodedData.index.toNumber();
530
+ console.log("registerWithIdentity - membershipId:", membershipId);
368
531
 
369
- return {
532
+ const result = {
370
533
  identity,
371
534
  membership: {
372
535
  address,
@@ -374,9 +537,41 @@ export class RLNContract {
374
537
  chainId: network.chainId
375
538
  }
376
539
  };
540
+ console.log("registerWithIdentity - returning result:", result);
541
+
542
+ return result;
377
543
  } catch (error) {
378
- log.error(`Error in registerWithIdentity: ${(error as Error).message}`);
379
- return undefined;
544
+ console.log("registerWithIdentity - ERROR:", error);
545
+
546
+ // Improved error handling to decode contract errors
547
+ if (error instanceof Error) {
548
+ const errorMessage = error.message;
549
+ console.log("registerWithIdentity - error message:", errorMessage);
550
+ console.log("registerWithIdentity - error stack:", error.stack);
551
+
552
+ // Try to extract more specific error information
553
+ if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
554
+ console.error(
555
+ "Registration failed: Cannot exceed maximum total rate limit"
556
+ );
557
+ } else if (errorMessage.includes("InvalidIdCommitment")) {
558
+ console.error("Registration failed: Invalid ID commitment");
559
+ } else if (errorMessage.includes("InvalidMembershipRateLimit")) {
560
+ console.error("Registration failed: Invalid membership rate limit");
561
+ } else if (errorMessage.includes("execution reverted")) {
562
+ // Generic revert
563
+ console.error(
564
+ "Contract execution reverted. Check contract requirements."
565
+ );
566
+ } else {
567
+ console.error(`Error in registerWithIdentity: ${errorMessage}`);
568
+ }
569
+ } else {
570
+ console.error("Unknown error in registerWithIdentity");
571
+ }
572
+
573
+ // Re-throw the error to allow callers to handle it
574
+ throw error;
380
575
  }
381
576
  }
382
577
 
@@ -454,13 +649,13 @@ export class RLNContract {
454
649
 
455
650
  const decodedData: MembershipRegisteredEvent = {
456
651
  idCommitment: memberRegistered.args.idCommitment,
457
- rateLimit: memberRegistered.args.rateLimit,
652
+ membershipRateLimit: memberRegistered.args.membershipRateLimit,
458
653
  index: memberRegistered.args.index
459
654
  };
460
655
 
461
656
  log.info(
462
657
  `Successfully registered membership with permit. Index: ${decodedData.index}, ` +
463
- `Rate limit: ${decodedData.rateLimit}, Erased ${idCommitmentsToErase.length} commitments`
658
+ `Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`
464
659
  );
465
660
 
466
661
  const network = await this.contract.provider.getNetwork();
@@ -531,18 +726,20 @@ export class RLNContract {
531
726
 
532
727
  public async extendMembership(
533
728
  idCommitment: string
534
- ): Promise<ethers.ContractTransaction> {
535
- return this.contract.extendMemberships([idCommitment]);
729
+ ): Promise<ethers.ContractReceipt> {
730
+ const tx = await this.contract.extendMemberships([idCommitment]);
731
+ return await tx.wait();
536
732
  }
537
733
 
538
734
  public async eraseMembership(
539
735
  idCommitment: string,
540
736
  eraseFromMembershipSet: boolean = true
541
- ): Promise<ethers.ContractTransaction> {
542
- return this.contract.eraseMemberships(
737
+ ): Promise<ethers.ContractReceipt> {
738
+ const tx = await this.contract.eraseMemberships(
543
739
  [idCommitment],
544
740
  eraseFromMembershipSet
545
741
  );
742
+ return await tx.wait();
546
743
  }
547
744
 
548
745
  public async registerMembership(
@@ -557,7 +754,10 @@ export class RLNContract {
557
754
  `Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
558
755
  );
559
756
  }
560
- return this.contract.register(idCommitment, rateLimit, []);
757
+ console.log("registering membership", idCommitment, rateLimit);
758
+ const txn = this.contract.register(idCommitment, rateLimit, []);
759
+ console.log("txn", txn);
760
+ return txn;
561
761
  }
562
762
 
563
763
  private async getMemberIndex(
package/src/identity.ts CHANGED
@@ -1,8 +1,3 @@
1
- import { arrayify } from "@ethersproject/bytes";
2
- import { keccak256 } from "@ethersproject/keccak256";
3
- import { Poseidon } from "@iden3/js-crypto";
4
- import { streamXOR } from "@stablelib/chacha";
5
-
6
1
  import { buildBigIntFromUint8Array } from "./utils/index.js";
7
2
 
8
3
  export class IdentityCredential {
@@ -33,41 +28,4 @@ export class IdentityCredential {
33
28
  idCommitmentBigInt
34
29
  );
35
30
  }
36
-
37
- public static generateSeeded(signature: Uint8Array): IdentityCredential {
38
- // Generate deterministic seed from signature
39
- const seed = arrayify(keccak256(signature));
40
-
41
- // Use ChaCha for deterministic randomness (as in Rust code)
42
- const nonce = new Uint8Array(12);
43
- const idSecretHash = new Uint8Array(32);
44
- streamXOR(seed, nonce, idSecretHash, idSecretHash);
45
-
46
- // Convert to bigint for Poseidon
47
- const secretBigInt = BigInt(
48
- "0x" + Buffer.from(idSecretHash).toString("hex")
49
- );
50
-
51
- // Generate commitment using Poseidon
52
- const idCommitmentBigInt = Poseidon.hash([secretBigInt]);
53
-
54
- // Convert commitment back to Uint8Array
55
- const idCommitment = arrayify(
56
- "0x" + idCommitmentBigInt.toString(16).padStart(64, "0")
57
- );
58
-
59
- // Generate deterministic trapdoor and nullifier from the secret hash
60
- const idTrapdoor = new Uint8Array(32);
61
- const idNullifier = new Uint8Array(32);
62
- streamXOR(idSecretHash, nonce, idTrapdoor, idTrapdoor);
63
- streamXOR(idTrapdoor, nonce, idNullifier, idNullifier);
64
-
65
- return new IdentityCredential(
66
- idTrapdoor,
67
- idNullifier,
68
- idSecretHash,
69
- idCommitment,
70
- idCommitmentBigInt
71
- );
72
- }
73
31
  }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RLNDecoder, RLNEncoder } from "./codec.js";
2
- import { RLN_V2_ABI } from "./contract/abi/rlnv2.js";
2
+ import { RLN_ABI } from "./contract/abi.js";
3
3
  import { RLNContract, SEPOLIA_CONTRACT } from "./contract/index.js";
4
4
  import { createRLN } from "./create.js";
5
5
  import { IdentityCredential } from "./identity.js";
@@ -21,5 +21,5 @@ export {
21
21
  RLNContract,
22
22
  SEPOLIA_CONTRACT,
23
23
  extractMetaMaskSigner,
24
- RLN_V2_ABI
24
+ RLN_ABI
25
25
  };
package/src/rln.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  type RLNDecoder,
16
16
  type RLNEncoder
17
17
  } from "./codec.js";
18
+ import { DEFAULT_RATE_LIMIT } from "./contract/constants.js";
18
19
  import { RLNContract, SEPOLIA_CONTRACT } from "./contract/index.js";
19
20
  import { IdentityCredential } from "./identity.js";
20
21
  import { Keystore } from "./keystore/index.js";
@@ -32,15 +33,46 @@ import { Zerokit } from "./zerokit.js";
32
33
  const log = new Logger("waku:rln");
33
34
 
34
35
  async function loadWitnessCalculator(): Promise<WitnessCalculator> {
35
- const url = new URL("./resources/rln.wasm", import.meta.url);
36
- const response = await fetch(url);
37
- return await wc.builder(new Uint8Array(await response.arrayBuffer()), false);
36
+ try {
37
+ const url = new URL("./resources/rln.wasm", import.meta.url);
38
+ const response = await fetch(url);
39
+
40
+ if (!response.ok) {
41
+ throw new Error(
42
+ `Failed to fetch witness calculator: ${response.status} ${response.statusText}`
43
+ );
44
+ }
45
+
46
+ return await wc.builder(
47
+ new Uint8Array(await response.arrayBuffer()),
48
+ false
49
+ );
50
+ } catch (error) {
51
+ log.error("Error loading witness calculator:", error);
52
+ throw new Error(
53
+ `Failed to load witness calculator: ${error instanceof Error ? error.message : String(error)}`
54
+ );
55
+ }
38
56
  }
39
57
 
40
58
  async function loadZkey(): Promise<Uint8Array> {
41
- const url = new URL("./resources/rln_final.zkey", import.meta.url);
42
- const response = await fetch(url);
43
- return new Uint8Array(await response.arrayBuffer());
59
+ try {
60
+ const url = new URL("./resources/rln_final.zkey", import.meta.url);
61
+ const response = await fetch(url);
62
+
63
+ if (!response.ok) {
64
+ throw new Error(
65
+ `Failed to fetch zkey: ${response.status} ${response.statusText}`
66
+ );
67
+ }
68
+
69
+ return new Uint8Array(await response.arrayBuffer());
70
+ } catch (error) {
71
+ log.error("Error loading zkey:", error);
72
+ throw new Error(
73
+ `Failed to load zkey: ${error instanceof Error ? error.message : String(error)}`
74
+ );
75
+ }
44
76
  }
45
77
 
46
78
  /**
@@ -61,7 +93,7 @@ export async function create(): Promise<RLNInstance> {
61
93
 
62
94
  const DEPTH = 20;
63
95
  const zkRLN = zerokitRLN.newRLN(DEPTH, zkey, vkey);
64
- const zerokit = new Zerokit(zkRLN, witnessCalculator);
96
+ const zerokit = new Zerokit(zkRLN, witnessCalculator, DEFAULT_RATE_LIMIT);
65
97
 
66
98
  return new RLNInstance(zerokit);
67
99
  } catch (error) {
@@ -142,7 +174,7 @@ export class RLNInstance {
142
174
  this._contract = await RLNContract.init(this, {
143
175
  address: address!,
144
176
  signer: signer!,
145
- rateLimit: options.rateLimit
177
+ rateLimit: options.rateLimit ?? this.zerokit.getRateLimit
146
178
  });
147
179
  this.started = true;
148
180
  } finally {
@@ -214,15 +246,17 @@ export class RLNInstance {
214
246
  throw Error("RLN Contract is not initialized.");
215
247
  }
216
248
 
217
- let identity: IdentityCredential | undefined =
218
- "identity" in options ? options.identity : undefined;
249
+ let identity = "identity" in options && options.identity;
219
250
 
220
251
  if ("signature" in options) {
221
- identity = IdentityCredential.generateSeeded(
222
- ethers.utils.arrayify(options.signature)
252
+ identity = this.zerokit.generateSeededIdentityCredential(
253
+ options.signature
223
254
  );
224
255
  }
225
256
 
257
+ // eslint-disable-next-line no-console
258
+ console.log("registering membership", identity);
259
+
226
260
  if (!identity) {
227
261
  throw Error("Missing signature or identity to register membership.");
228
262
  }
@@ -272,7 +306,7 @@ export class RLNInstance {
272
306
  }
273
307
 
274
308
  const registryAddress = credentials.membership.address;
275
- const currentRegistryAddress = this._contract.contract.address;
309
+ const currentRegistryAddress = this._contract.address;
276
310
  if (registryAddress !== currentRegistryAddress) {
277
311
  throw Error(
278
312
  `Failed to verify chain coordinates: credentials contract address=${registryAddress} is not equal to registryContract address=${currentRegistryAddress}`
@@ -280,7 +314,7 @@ export class RLNInstance {
280
314
  }
281
315
 
282
316
  const chainId = credentials.membership.chainId;
283
- const network = await this._contract.contract.provider.getNetwork();
317
+ const network = await this._contract.provider.getNetwork();
284
318
  const currentChainId = network.chainId;
285
319
  if (chainId !== currentChainId) {
286
320
  throw Error(