From 10aadbe2c805cd7f77124c411dd367e01fbe4995 Mon Sep 17 00:00:00 2001 From: Danish Arora Date: Mon, 19 May 2025 16:18:31 +0530 Subject: [PATCH] refactor(rln): cache min/max rate limits at contract instantiation --- .../rln/src/contract/rln_base_contract.ts | 73 +++++++++++-------- packages/rln/src/credentials_manager.ts | 2 +- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/packages/rln/src/contract/rln_base_contract.ts b/packages/rln/src/contract/rln_base_contract.ts index 17065d2a26..9e96b68566 100644 --- a/packages/rln/src/contract/rln_base_contract.ts +++ b/packages/rln/src/contract/rln_base_contract.ts @@ -1,5 +1,5 @@ import { Logger } from "@waku/utils"; -import { Contract, ethers } from "ethers"; +import { ethers } from "ethers"; import { IdentityCredential } from "../identity.js"; import { DecryptedCredentials } from "../keystore/types.js"; @@ -22,6 +22,8 @@ export class RLNBaseContract { public contract: ethers.Contract; private deployBlock: undefined | number; private rateLimit: number; + private minRateLimit?: number; + private maxRateLimit?: number; protected _members: Map = new Map(); private _membersFilter: ethers.EventFilter; @@ -29,10 +31,9 @@ export class RLNBaseContract { private _membersExpiredFilter: ethers.EventFilter; /** - * Constructor for RLNBaseContract. - * Allows injecting a mocked contract for testing purposes. + * Private constructor for RLNBaseContract. Use static create() instead. */ - public constructor(options: RLNContractInitOptions) { + private constructor(options: RLNContractInitOptions) { const { address, signer, @@ -67,11 +68,24 @@ export class RLNBaseContract { .catch((error) => { log.error("Failed to initialize members", { error }); }); + } - // Validate rate limit asynchronously - this.validateRateLimit(rateLimit).catch((error) => { - log.error("Failed to validate initial rate limit", { error }); - }); + /** + * Static async factory to create and initialize RLNBaseContract + */ + public static async create( + options: RLNContractInitOptions + ): Promise { + const instance = new RLNBaseContract(options); + const [min, max] = await Promise.all([ + instance.contract.minMembershipRateLimit(), + instance.contract.maxMembershipRateLimit() + ]); + instance.minRateLimit = ethers.BigNumber.from(min).toNumber(); + instance.maxRateLimit = ethers.BigNumber.from(max).toNumber(); + + instance.validateRateLimit(instance.rateLimit); + return instance; } /** @@ -96,21 +110,21 @@ export class RLNBaseContract { } /** - * Gets the minimum allowed rate limit from the contract - * @returns Promise The minimum rate limit in messages per epoch + * Gets the minimum allowed rate limit (cached) */ - public async getMinRateLimit(): Promise { - const minRate = await this.contract.minMembershipRateLimit(); - return ethers.BigNumber.from(minRate).toNumber(); + public getMinRateLimit(): number { + if (this.minRateLimit === undefined) + throw new Error("minRateLimit not initialized"); + return this.minRateLimit; } /** - * Gets the maximum allowed rate limit from the contract - * @returns Promise The maximum rate limit in messages per epoch + * Gets the maximum allowed rate limit (cached) */ - public async getMaxRateLimit(): Promise { - const maxRate = await this.contract.maxMembershipRateLimit(); - return ethers.BigNumber.from(maxRate).toNumber(); + public getMaxRateLimit(): number { + if (this.maxRateLimit === undefined) + throw new Error("maxRateLimit not initialized"); + return this.maxRateLimit; } /** @@ -147,8 +161,8 @@ export class RLNBaseContract { * Updates the rate limit for future registrations * @param newRateLimit The new rate limit to use */ - public async setRateLimit(newRateLimit: number): Promise { - await this.validateRateLimit(newRateLimit); + public setRateLimit(newRateLimit: number): void { + this.validateRateLimit(newRateLimit); this.rateLimit = newRateLimit; } @@ -662,21 +676,16 @@ export class RLNBaseContract { } /** - * Validates that the rate limit is within the allowed range + * Validates that the rate limit is within the allowed range (sync) * @throws Error if the rate limit is outside the allowed range */ - private async validateRateLimit(rateLimit: number): Promise { - const [minRate, maxRate] = await Promise.all([ - this.contract.minMembershipRateLimit(), - this.contract.maxMembershipRateLimit() - ]); - - const minRateNum = ethers.BigNumber.from(minRate).toNumber(); - const maxRateNum = ethers.BigNumber.from(maxRate).toNumber(); - - if (rateLimit < minRateNum || rateLimit > maxRateNum) { + private validateRateLimit(rateLimit: number): void { + if (this.minRateLimit === undefined || this.maxRateLimit === undefined) { + throw new Error("Rate limits not initialized"); + } + if (rateLimit < this.minRateLimit || rateLimit > this.maxRateLimit) { throw new Error( - `Rate limit must be between ${minRateNum} and ${maxRateNum} messages per epoch` + `Rate limit must be between ${this.minRateLimit} and ${this.maxRateLimit} messages per epoch` ); } } diff --git a/packages/rln/src/credentials_manager.ts b/packages/rln/src/credentials_manager.ts index fb60ac0b48..70544ed4a2 100644 --- a/packages/rln/src/credentials_manager.ts +++ b/packages/rln/src/credentials_manager.ts @@ -80,7 +80,7 @@ export class RLNCredentialsManager { this.credentials = credentials; this.signer = signer!; - this.contract = new RLNBaseContract({ + this.contract = await RLNBaseContract.create({ address: address!, signer: signer!, rateLimit: rateLimit ?? this.zerokit?.rateLimit