@waku/rln 0.1.5-053bb95.0 → 0.1.5-197ba46.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.
- package/bundle/_virtual/utils.js +2 -2
- package/bundle/_virtual/utils2.js +2 -2
- package/bundle/index.js +2 -1
- package/bundle/packages/rln/dist/contract/constants.js +6 -2
- package/bundle/packages/rln/dist/contract/rln_base_contract.js +274 -141
- package/bundle/packages/rln/dist/contract/rln_contract.js +74 -89
- package/bundle/packages/rln/dist/credentials_manager.js +1 -1
- package/bundle/packages/rln/dist/identity.js +0 -8
- package/bundle/packages/rln/dist/keystore/keystore.js +28 -15
- package/bundle/packages/rln/dist/utils/bytes.js +8 -2
- package/bundle/packages/rln/dist/utils/metamask.js +2 -2
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/contract/constants.d.ts +1 -1
- package/dist/contract/constants.js +1 -1
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.js +1 -0
- package/dist/contract/index.js.map +1 -1
- package/dist/contract/rln_base_contract.d.ts +27 -25
- package/dist/contract/rln_base_contract.js +272 -139
- package/dist/contract/rln_base_contract.js.map +1 -1
- package/dist/contract/rln_contract.d.ts +4 -24
- package/dist/contract/rln_contract.js +74 -89
- package/dist/contract/rln_contract.js.map +1 -1
- package/dist/contract/types.d.ts +5 -0
- package/dist/contract/types.js.map +1 -1
- package/dist/credentials_manager.js +1 -1
- package/dist/credentials_manager.js.map +1 -1
- package/dist/identity.d.ts +0 -1
- package/dist/identity.js +0 -8
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/keystore/keystore.d.ts +1 -0
- package/dist/keystore/keystore.js +28 -15
- package/dist/keystore/keystore.js.map +1 -1
- package/dist/keystore/types.d.ts +2 -2
- package/dist/utils/bytes.js +8 -2
- package/dist/utils/bytes.js.map +1 -1
- package/package.json +1 -1
- package/src/contract/constants.ts +1 -1
- package/src/contract/index.ts +1 -0
- package/src/contract/rln_base_contract.ts +419 -211
- package/src/contract/rln_contract.ts +95 -120
- package/src/contract/types.ts +5 -0
- package/src/credentials_manager.ts +1 -1
- package/src/identity.ts +0 -9
- package/src/index.ts +3 -1
- package/src/keystore/keystore.ts +54 -29
- package/src/keystore/types.ts +2 -2
- package/src/utils/bytes.ts +10 -2
- package/bundle/_virtual/__node-resolve_empty.js +0 -6
- package/bundle/_virtual/_node-resolve_empty.js +0 -3
- package/bundle/_virtual/bn.js +0 -3
- package/bundle/_virtual/common.js +0 -3
- package/bundle/_virtual/common2.js +0 -3
- package/bundle/_virtual/hash.js +0 -3
- package/bundle/_virtual/inherits_browser.js +0 -3
- package/bundle/_virtual/ripemd.js +0 -3
- package/bundle/_virtual/sha.js +0 -3
- package/bundle/_virtual/sha3.js +0 -3
- package/bundle/_virtual/utils3.js +0 -3
- package/bundle/node_modules/@ethersproject/abi/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/abi/lib.esm/abi-coder.js +0 -96
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/abstract-coder.js +0 -148
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/address.js +0 -26
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/anonymous.js +0 -20
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/array.js +0 -210
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/boolean.js +0 -18
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/bytes.js +0 -30
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/fixed-bytes.js +0 -26
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/null.js +0 -22
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/number.js +0 -43
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/string.js +0 -19
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/tuple.js +0 -58
- package/bundle/node_modules/@ethersproject/abi/lib.esm/fragments.js +0 -854
- package/bundle/node_modules/@ethersproject/abi/lib.esm/interface.js +0 -609
- package/bundle/node_modules/@ethersproject/abstract-provider/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/abstract-provider/lib.esm/index.js +0 -66
- package/bundle/node_modules/@ethersproject/abstract-signer/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/abstract-signer/lib.esm/index.js +0 -302
- package/bundle/node_modules/@ethersproject/address/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/address/lib.esm/index.js +0 -110
- package/bundle/node_modules/@ethersproject/base64/lib.esm/base64.js +0 -20
- package/bundle/node_modules/@ethersproject/basex/lib.esm/index.js +0 -120
- package/bundle/node_modules/@ethersproject/bignumber/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/bignumber/lib.esm/bignumber.js +0 -287
- package/bundle/node_modules/@ethersproject/bytes/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/bytes/lib.esm/index.js +0 -402
- package/bundle/node_modules/@ethersproject/constants/lib.esm/addresses.js +0 -3
- package/bundle/node_modules/@ethersproject/constants/lib.esm/bignumbers.js +0 -8
- package/bundle/node_modules/@ethersproject/constants/lib.esm/hashes.js +0 -3
- package/bundle/node_modules/@ethersproject/contracts/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/contracts/lib.esm/index.js +0 -893
- package/bundle/node_modules/@ethersproject/hash/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/hash/lib.esm/ens-normalize/decoder.js +0 -256
- package/bundle/node_modules/@ethersproject/hash/lib.esm/ens-normalize/include.js +0 -36
- package/bundle/node_modules/@ethersproject/hash/lib.esm/ens-normalize/lib.js +0 -135
- package/bundle/node_modules/@ethersproject/hash/lib.esm/id.js +0 -8
- package/bundle/node_modules/@ethersproject/hash/lib.esm/namehash.js +0 -64
- package/bundle/node_modules/@ethersproject/hash/lib.esm/typed-data.js +0 -443
- package/bundle/node_modules/@ethersproject/keccak256/lib.esm/index.js +0 -8
- package/bundle/node_modules/@ethersproject/keccak256/node_modules/js-sha3/src/sha3.js +0 -660
- package/bundle/node_modules/@ethersproject/logger/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/logger/lib.esm/index.js +0 -352
- package/bundle/node_modules/@ethersproject/networks/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/networks/lib.esm/index.js +0 -248
- package/bundle/node_modules/@ethersproject/properties/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/properties/lib.esm/index.js +0 -127
- package/bundle/node_modules/@ethersproject/providers/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/providers/lib.esm/base-provider.js +0 -2007
- package/bundle/node_modules/@ethersproject/providers/lib.esm/formatter.js +0 -422
- package/bundle/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js +0 -674
- package/bundle/node_modules/@ethersproject/providers/lib.esm/web3-provider.js +0 -132
- package/bundle/node_modules/@ethersproject/rlp/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/rlp/lib.esm/index.js +0 -120
- package/bundle/node_modules/@ethersproject/sha2/lib.esm/sha2.js +0 -8
- package/bundle/node_modules/@ethersproject/signing-key/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/signing-key/lib.esm/elliptic.js +0 -2430
- package/bundle/node_modules/@ethersproject/signing-key/lib.esm/index.js +0 -76
- package/bundle/node_modules/@ethersproject/strings/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/strings/lib.esm/utf8.js +0 -219
- package/bundle/node_modules/@ethersproject/transactions/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/transactions/lib.esm/index.js +0 -279
- package/bundle/node_modules/@ethersproject/web/lib.esm/_version.js +0 -3
- package/bundle/node_modules/@ethersproject/web/lib.esm/geturl.js +0 -69
- package/bundle/node_modules/@ethersproject/web/lib.esm/index.js +0 -404
- package/bundle/node_modules/bech32/index.js +0 -187
- package/bundle/node_modules/bn.js/lib/bn.js +0 -3361
- package/bundle/node_modules/hash.js/lib/hash/common.js +0 -97
- package/bundle/node_modules/hash.js/lib/hash/hmac.js +0 -51
- package/bundle/node_modules/hash.js/lib/hash/ripemd.js +0 -152
- package/bundle/node_modules/hash.js/lib/hash/sha/1.js +0 -81
- package/bundle/node_modules/hash.js/lib/hash/sha/224.js +0 -33
- package/bundle/node_modules/hash.js/lib/hash/sha/256.js +0 -113
- package/bundle/node_modules/hash.js/lib/hash/sha/384.js +0 -39
- package/bundle/node_modules/hash.js/lib/hash/sha/512.js +0 -336
- package/bundle/node_modules/hash.js/lib/hash/sha/common.js +0 -53
- package/bundle/node_modules/hash.js/lib/hash/sha.js +0 -14
- package/bundle/node_modules/hash.js/lib/hash/utils.js +0 -282
- package/bundle/node_modules/hash.js/lib/hash.js +0 -33
- package/bundle/node_modules/inherits/inherits_browser.js +0 -33
- package/bundle/node_modules/minimalistic-assert/index.js +0 -13
- package/bundle/packages/rln/dist/contract/errors.js +0 -62
- package/dist/contract/errors.d.ts +0 -30
- package/dist/contract/errors.js +0 -61
- package/dist/contract/errors.js.map +0 -1
- package/src/contract/errors.ts +0 -75
@@ -7,15 +7,8 @@ import { DecryptedCredentials } from "../keystore/types.js";
|
|
7
7
|
import { RLN_ABI } from "./abi.js";
|
8
8
|
import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from "./constants.js";
|
9
9
|
import {
|
10
|
-
|
11
|
-
|
12
|
-
MembershipExistsError,
|
13
|
-
MembershipNotFoundError,
|
14
|
-
RateLimitExceededError,
|
15
|
-
RLNContractError,
|
16
|
-
TransactionError
|
17
|
-
} from "./errors.js";
|
18
|
-
import {
|
10
|
+
CustomQueryOptions,
|
11
|
+
FetchMembersOptions,
|
19
12
|
Member,
|
20
13
|
MembershipInfo,
|
21
14
|
MembershipRegisteredEvent,
|
@@ -27,8 +20,18 @@ const log = new Logger("waku:rln:contract:base");
|
|
27
20
|
|
28
21
|
export class RLNBaseContract {
|
29
22
|
public contract: ethers.Contract;
|
23
|
+
private deployBlock: undefined | number;
|
30
24
|
private rateLimit: number;
|
31
25
|
|
26
|
+
protected _members: Map<number, Member> = new Map();
|
27
|
+
private _membersFilter: ethers.EventFilter;
|
28
|
+
private _membershipErasedFilter: ethers.EventFilter;
|
29
|
+
private _membersExpiredFilter: ethers.EventFilter;
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Constructor for RLNBaseContract.
|
33
|
+
* Allows injecting a mocked contract for testing purposes.
|
34
|
+
*/
|
32
35
|
public constructor(options: RLNContractInitOptions) {
|
33
36
|
const {
|
34
37
|
address,
|
@@ -37,9 +40,38 @@ export class RLNBaseContract {
|
|
37
40
|
contract
|
38
41
|
} = options;
|
39
42
|
|
40
|
-
|
43
|
+
log.info("Initializing RLNBaseContract", { address, rateLimit });
|
44
|
+
|
41
45
|
this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
|
42
46
|
this.rateLimit = rateLimit;
|
47
|
+
|
48
|
+
try {
|
49
|
+
log.info("Setting up event filters");
|
50
|
+
// Initialize event filters
|
51
|
+
this._membersFilter = this.contract.filters.MembershipRegistered();
|
52
|
+
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
53
|
+
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
54
|
+
log.info("Event filters initialized successfully");
|
55
|
+
} catch (error) {
|
56
|
+
log.error("Failed to initialize event filters", { error });
|
57
|
+
throw new Error(
|
58
|
+
"Failed to initialize event filters: " + (error as Error).message
|
59
|
+
);
|
60
|
+
}
|
61
|
+
|
62
|
+
// Initialize members and subscriptions
|
63
|
+
this.fetchMembers()
|
64
|
+
.then(() => {
|
65
|
+
this.subscribeToMembers();
|
66
|
+
})
|
67
|
+
.catch((error) => {
|
68
|
+
log.error("Failed to initialize members", { error });
|
69
|
+
});
|
70
|
+
|
71
|
+
// Validate rate limit asynchronously
|
72
|
+
this.validateRateLimit(rateLimit).catch((error) => {
|
73
|
+
log.error("Failed to validate initial rate limit", { error });
|
74
|
+
});
|
43
75
|
}
|
44
76
|
|
45
77
|
/**
|
@@ -116,151 +148,302 @@ export class RLNBaseContract {
|
|
116
148
|
* @param newRateLimit The new rate limit to use
|
117
149
|
*/
|
118
150
|
public async setRateLimit(newRateLimit: number): Promise<void> {
|
119
|
-
this.validateRateLimit(newRateLimit);
|
151
|
+
await this.validateRateLimit(newRateLimit);
|
120
152
|
this.rateLimit = newRateLimit;
|
121
153
|
}
|
122
154
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
startIndex: number,
|
130
|
-
endIndex: number
|
131
|
-
): Promise<Member[]> {
|
132
|
-
try {
|
133
|
-
// Get all commitments in one call
|
134
|
-
const idCommitments =
|
135
|
-
await this.contract.getRateCommitmentsInRangeBoundsInclusive(
|
136
|
-
startIndex,
|
137
|
-
endIndex - 1 // -1 because getRateCommitmentsInRangeBoundsInclusive is inclusive
|
138
|
-
);
|
155
|
+
public get members(): Member[] {
|
156
|
+
const sortedMembers = Array.from(this._members.values()).sort(
|
157
|
+
(left, right) => left.index.toNumber() - right.index.toNumber()
|
158
|
+
);
|
159
|
+
return sortedMembers;
|
160
|
+
}
|
139
161
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
162
|
+
public async fetchMembers(options: FetchMembersOptions = {}): Promise<void> {
|
163
|
+
const registeredMemberEvents = await RLNBaseContract.queryFilter(
|
164
|
+
this.contract,
|
165
|
+
{
|
166
|
+
fromBlock: this.deployBlock,
|
167
|
+
...options,
|
168
|
+
membersFilter: this.membersFilter
|
169
|
+
}
|
170
|
+
);
|
171
|
+
const removedMemberEvents = await RLNBaseContract.queryFilter(
|
172
|
+
this.contract,
|
173
|
+
{
|
174
|
+
fromBlock: this.deployBlock,
|
175
|
+
...options,
|
176
|
+
membersFilter: this.membershipErasedFilter
|
177
|
+
}
|
178
|
+
);
|
179
|
+
const expiredMemberEvents = await RLNBaseContract.queryFilter(
|
180
|
+
this.contract,
|
181
|
+
{
|
182
|
+
fromBlock: this.deployBlock,
|
183
|
+
...options,
|
184
|
+
membersFilter: this.membersExpiredFilter
|
185
|
+
}
|
186
|
+
);
|
187
|
+
|
188
|
+
const events = [
|
189
|
+
...registeredMemberEvents,
|
190
|
+
...removedMemberEvents,
|
191
|
+
...expiredMemberEvents
|
192
|
+
];
|
193
|
+
this.processEvents(events);
|
194
|
+
}
|
195
|
+
|
196
|
+
public static async queryFilter(
|
197
|
+
contract: ethers.Contract,
|
198
|
+
options: CustomQueryOptions
|
199
|
+
): Promise<ethers.Event[]> {
|
200
|
+
const FETCH_CHUNK = 5;
|
201
|
+
const BLOCK_RANGE = 3000;
|
202
|
+
|
203
|
+
const {
|
204
|
+
fromBlock,
|
205
|
+
membersFilter,
|
206
|
+
fetchRange = BLOCK_RANGE,
|
207
|
+
fetchChunks = FETCH_CHUNK
|
208
|
+
} = options;
|
209
|
+
|
210
|
+
if (fromBlock === undefined) {
|
211
|
+
return contract.queryFilter(membersFilter);
|
212
|
+
}
|
213
|
+
|
214
|
+
if (!contract.provider) {
|
215
|
+
throw Error("No provider found on the contract.");
|
216
|
+
}
|
217
|
+
|
218
|
+
const toBlock = await contract.provider.getBlockNumber();
|
219
|
+
|
220
|
+
if (toBlock - fromBlock < fetchRange) {
|
221
|
+
return contract.queryFilter(membersFilter, fromBlock, toBlock);
|
222
|
+
}
|
223
|
+
|
224
|
+
const events: ethers.Event[][] = [];
|
225
|
+
const chunks = RLNBaseContract.splitToChunks(
|
226
|
+
fromBlock,
|
227
|
+
toBlock,
|
228
|
+
fetchRange
|
229
|
+
);
|
151
230
|
|
152
|
-
|
153
|
-
|
154
|
-
|
231
|
+
for (const portion of RLNBaseContract.takeN<[number, number]>(
|
232
|
+
chunks,
|
233
|
+
fetchChunks
|
234
|
+
)) {
|
235
|
+
const promises = portion.map(([left, right]) =>
|
236
|
+
RLNBaseContract.ignoreErrors(
|
237
|
+
contract.queryFilter(membersFilter, left, right),
|
238
|
+
[]
|
239
|
+
)
|
155
240
|
);
|
156
|
-
|
157
|
-
|
241
|
+
const fetchedEvents = await Promise.all(promises);
|
242
|
+
events.push(fetchedEvents.flatMap((v) => v));
|
243
|
+
}
|
244
|
+
|
245
|
+
return events.flatMap((v) => v);
|
246
|
+
}
|
247
|
+
|
248
|
+
public processEvents(events: ethers.Event[]): void {
|
249
|
+
const toRemoveTable = new Map<number, number[]>();
|
250
|
+
const toInsertTable = new Map<number, ethers.Event[]>();
|
251
|
+
|
252
|
+
events.forEach((evt) => {
|
253
|
+
if (!evt.args) {
|
254
|
+
return;
|
255
|
+
}
|
256
|
+
|
158
257
|
if (
|
159
|
-
|
160
|
-
|
258
|
+
evt.event === "MembershipErased" ||
|
259
|
+
evt.event === "MembershipExpired"
|
161
260
|
) {
|
162
|
-
|
163
|
-
|
164
|
-
)
|
261
|
+
let index = evt.args.index;
|
262
|
+
|
263
|
+
if (!index) {
|
264
|
+
return;
|
265
|
+
}
|
266
|
+
|
267
|
+
if (typeof index === "number" || typeof index === "string") {
|
268
|
+
index = ethers.BigNumber.from(index);
|
269
|
+
}
|
270
|
+
|
271
|
+
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
|
272
|
+
if (toRemoveVal != undefined) {
|
273
|
+
toRemoveVal.push(index.toNumber());
|
274
|
+
toRemoveTable.set(evt.blockNumber, toRemoveVal);
|
275
|
+
} else {
|
276
|
+
toRemoveTable.set(evt.blockNumber, [index.toNumber()]);
|
277
|
+
}
|
278
|
+
} else if (evt.event === "MembershipRegistered") {
|
279
|
+
let eventsPerBlock = toInsertTable.get(evt.blockNumber);
|
280
|
+
if (eventsPerBlock == undefined) {
|
281
|
+
eventsPerBlock = [];
|
282
|
+
}
|
283
|
+
|
284
|
+
eventsPerBlock.push(evt);
|
285
|
+
toInsertTable.set(evt.blockNumber, eventsPerBlock);
|
165
286
|
}
|
166
|
-
|
287
|
+
});
|
288
|
+
}
|
289
|
+
|
290
|
+
public static splitToChunks(
|
291
|
+
from: number,
|
292
|
+
to: number,
|
293
|
+
step: number
|
294
|
+
): Array<[number, number]> {
|
295
|
+
const chunks: Array<[number, number]> = [];
|
296
|
+
|
297
|
+
let left = from;
|
298
|
+
while (left < to) {
|
299
|
+
const right = left + step < to ? left + step : to;
|
300
|
+
|
301
|
+
chunks.push([left, right] as [number, number]);
|
302
|
+
|
303
|
+
left = right;
|
167
304
|
}
|
305
|
+
|
306
|
+
return chunks;
|
168
307
|
}
|
169
308
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
309
|
+
public static *takeN<T>(array: T[], size: number): Iterable<T[]> {
|
310
|
+
let start = 0;
|
311
|
+
|
312
|
+
while (start < array.length) {
|
313
|
+
const portion = array.slice(start, start + size);
|
314
|
+
|
315
|
+
yield portion;
|
316
|
+
|
317
|
+
start += size;
|
318
|
+
}
|
176
319
|
}
|
177
320
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
private async getMemberIndex(
|
183
|
-
idCommitment: string
|
184
|
-
): Promise<ethers.BigNumber | null> {
|
321
|
+
public static async ignoreErrors<T>(
|
322
|
+
promise: Promise<T>,
|
323
|
+
defaultValue: T
|
324
|
+
): Promise<T> {
|
185
325
|
try {
|
186
|
-
|
187
|
-
|
188
|
-
|
326
|
+
return await promise;
|
327
|
+
} catch (err: unknown) {
|
328
|
+
if (err instanceof Error) {
|
329
|
+
log.info(`Ignoring an error during query: ${err.message}`);
|
330
|
+
} else {
|
331
|
+
log.info(`Ignoring an unknown error during query`);
|
189
332
|
}
|
190
|
-
|
191
|
-
const membershipInfo = await this.contract.memberships(idCommitment);
|
192
|
-
return ethers.BigNumber.from(membershipInfo.index);
|
193
|
-
} catch (error) {
|
194
|
-
log.error(`Error getting member index: ${(error as Error).message}`);
|
195
|
-
throw new InvalidMembershipError(idCommitment);
|
333
|
+
return defaultValue;
|
196
334
|
}
|
197
335
|
}
|
198
336
|
|
337
|
+
public subscribeToMembers(): void {
|
338
|
+
this.contract.on(
|
339
|
+
this.membersFilter,
|
340
|
+
(
|
341
|
+
_idCommitment: bigint,
|
342
|
+
_membershipRateLimit: ethers.BigNumber,
|
343
|
+
_index: ethers.BigNumber,
|
344
|
+
event: ethers.Event
|
345
|
+
) => {
|
346
|
+
this.processEvents([event]);
|
347
|
+
}
|
348
|
+
);
|
349
|
+
|
350
|
+
this.contract.on(
|
351
|
+
this.membershipErasedFilter,
|
352
|
+
(
|
353
|
+
_idCommitment: bigint,
|
354
|
+
_membershipRateLimit: ethers.BigNumber,
|
355
|
+
_index: ethers.BigNumber,
|
356
|
+
event: ethers.Event
|
357
|
+
) => {
|
358
|
+
this.processEvents([event]);
|
359
|
+
}
|
360
|
+
);
|
361
|
+
|
362
|
+
this.contract.on(
|
363
|
+
this.membersExpiredFilter,
|
364
|
+
(
|
365
|
+
_idCommitment: bigint,
|
366
|
+
_membershipRateLimit: ethers.BigNumber,
|
367
|
+
_index: ethers.BigNumber,
|
368
|
+
event: ethers.Event
|
369
|
+
) => {
|
370
|
+
this.processEvents([event]);
|
371
|
+
}
|
372
|
+
);
|
373
|
+
}
|
374
|
+
|
199
375
|
public async getMembershipInfo(
|
200
|
-
|
201
|
-
): Promise<MembershipInfo> {
|
376
|
+
idCommitmentBigInt: bigint
|
377
|
+
): Promise<MembershipInfo | undefined> {
|
202
378
|
try {
|
203
|
-
const
|
204
|
-
await this.contract.
|
379
|
+
const membershipData =
|
380
|
+
await this.contract.memberships(idCommitmentBigInt);
|
205
381
|
const currentBlock = await this.contract.provider.getBlockNumber();
|
382
|
+
const [
|
383
|
+
depositAmount,
|
384
|
+
activeDuration,
|
385
|
+
gracePeriodStartTimestamp,
|
386
|
+
gracePeriodDuration,
|
387
|
+
rateLimit,
|
388
|
+
index,
|
389
|
+
holder,
|
390
|
+
token
|
391
|
+
] = membershipData;
|
392
|
+
|
393
|
+
const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration);
|
206
394
|
|
207
395
|
let state: MembershipState;
|
208
|
-
if (currentBlock <
|
396
|
+
if (currentBlock < gracePeriodStartTimestamp.toNumber()) {
|
209
397
|
state = MembershipState.Active;
|
210
|
-
} else if (currentBlock <
|
398
|
+
} else if (currentBlock < gracePeriodEnd.toNumber()) {
|
211
399
|
state = MembershipState.GracePeriod;
|
212
400
|
} else {
|
213
401
|
state = MembershipState.Expired;
|
214
402
|
}
|
215
403
|
|
216
|
-
const index = await this.getMemberIndex(idCommitment);
|
217
|
-
if (index === null) {
|
218
|
-
throw new MembershipNotFoundError(idCommitment);
|
219
|
-
}
|
220
|
-
|
221
404
|
return {
|
222
405
|
index,
|
223
|
-
idCommitment,
|
224
|
-
rateLimit: rateLimit
|
225
|
-
startBlock:
|
226
|
-
endBlock:
|
227
|
-
state
|
406
|
+
idCommitment: idCommitmentBigInt.toString(),
|
407
|
+
rateLimit: Number(rateLimit),
|
408
|
+
startBlock: gracePeriodStartTimestamp.toNumber(),
|
409
|
+
endBlock: gracePeriodEnd.toNumber(),
|
410
|
+
state,
|
411
|
+
depositAmount,
|
412
|
+
activeDuration,
|
413
|
+
gracePeriodDuration,
|
414
|
+
holder,
|
415
|
+
token
|
228
416
|
};
|
229
417
|
} catch (error) {
|
230
|
-
|
231
|
-
|
232
|
-
}
|
233
|
-
log.error(`Error getting membership info: ${(error as Error).message}`);
|
234
|
-
throw new InvalidMembershipError(idCommitment);
|
418
|
+
log.error("Error in getMembershipInfo:", error);
|
419
|
+
return undefined;
|
235
420
|
}
|
236
421
|
}
|
237
422
|
|
238
|
-
public async extendMembership(
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
423
|
+
public async extendMembership(
|
424
|
+
idCommitmentBigInt: bigint
|
425
|
+
): Promise<ethers.ContractTransaction> {
|
426
|
+
const tx = await this.contract.extendMemberships([idCommitmentBigInt]);
|
427
|
+
await tx.wait();
|
428
|
+
return tx;
|
244
429
|
}
|
245
430
|
|
246
431
|
public async eraseMembership(
|
247
|
-
|
432
|
+
idCommitmentBigInt: bigint,
|
248
433
|
eraseFromMembershipSet: boolean = true
|
249
|
-
): Promise<
|
250
|
-
const tx = await this.contract.eraseMemberships(
|
251
|
-
[
|
434
|
+
): Promise<ethers.ContractTransaction> {
|
435
|
+
const tx = await this.contract.functions.eraseMemberships(
|
436
|
+
[idCommitmentBigInt],
|
252
437
|
eraseFromMembershipSet
|
253
438
|
);
|
254
|
-
await
|
255
|
-
|
256
|
-
index: event.args!.index
|
257
|
-
}));
|
439
|
+
await tx.wait();
|
440
|
+
return tx;
|
258
441
|
}
|
259
442
|
|
260
443
|
public async registerMembership(
|
261
|
-
|
444
|
+
idCommitmentBigInt: bigint,
|
262
445
|
rateLimit: number = DEFAULT_RATE_LIMIT
|
263
|
-
): Promise<
|
446
|
+
): Promise<ethers.ContractTransaction> {
|
264
447
|
if (
|
265
448
|
rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
266
449
|
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
|
@@ -269,25 +452,15 @@ export class RLNBaseContract {
|
|
269
452
|
`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
|
270
453
|
);
|
271
454
|
}
|
272
|
-
|
273
|
-
await this.confirmTransaction(tx, "MembershipRegistered", (event) => ({
|
274
|
-
idCommitment: event.args!.idCommitment,
|
275
|
-
membershipRateLimit: event.args!.membershipRateLimit,
|
276
|
-
index: event.args!.index
|
277
|
-
}));
|
455
|
+
return this.contract.register(idCommitmentBigInt, rateLimit, []);
|
278
456
|
}
|
279
457
|
|
280
|
-
public async withdraw(token: string,
|
458
|
+
public async withdraw(token: string, from: string): Promise<void> {
|
281
459
|
try {
|
282
|
-
const tx = await this.contract.withdraw(token,
|
283
|
-
await
|
284
|
-
token: event.args!.token,
|
285
|
-
holder: event.args!.holder,
|
286
|
-
amount: event.args!.amount
|
287
|
-
}));
|
460
|
+
const tx = await this.contract.withdraw(token, from);
|
461
|
+
await tx.wait();
|
288
462
|
} catch (error) {
|
289
463
|
log.error(`Error in withdraw: ${(error as Error).message}`);
|
290
|
-
throw error;
|
291
464
|
}
|
292
465
|
}
|
293
466
|
|
@@ -301,20 +474,20 @@ export class RLNBaseContract {
|
|
301
474
|
|
302
475
|
// Check if the ID commitment is already registered
|
303
476
|
const existingIndex = await this.getMemberIndex(
|
304
|
-
identity.IDCommitmentBigInt
|
477
|
+
identity.IDCommitmentBigInt
|
305
478
|
);
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
identity.IDCommitmentBigInt.toString(),
|
310
|
-
existingIndex.toString()
|
479
|
+
if (existingIndex) {
|
480
|
+
throw new Error(
|
481
|
+
`ID commitment is already registered with index ${existingIndex}`
|
311
482
|
);
|
312
483
|
}
|
313
484
|
|
314
485
|
// Check if there's enough remaining rate limit
|
315
486
|
const remainingRateLimit = await this.getRemainingTotalRateLimit();
|
316
487
|
if (remainingRateLimit < this.rateLimit) {
|
317
|
-
throw new
|
488
|
+
throw new Error(
|
489
|
+
`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`
|
490
|
+
);
|
318
491
|
}
|
319
492
|
|
320
493
|
const estimatedGas = await this.contract.estimateGas.register(
|
@@ -324,23 +497,37 @@ export class RLNBaseContract {
|
|
324
497
|
);
|
325
498
|
const gasLimit = estimatedGas.add(10000);
|
326
499
|
|
327
|
-
const
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
500
|
+
const txRegisterResponse: ethers.ContractTransaction =
|
501
|
+
await this.contract.register(
|
502
|
+
identity.IDCommitmentBigInt,
|
503
|
+
this.rateLimit,
|
504
|
+
[],
|
505
|
+
{ gasLimit }
|
506
|
+
);
|
333
507
|
|
334
|
-
const
|
335
|
-
|
336
|
-
|
337
|
-
(
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
508
|
+
const txRegisterReceipt = await txRegisterResponse.wait();
|
509
|
+
|
510
|
+
if (txRegisterReceipt.status === 0) {
|
511
|
+
throw new Error("Transaction failed on-chain");
|
512
|
+
}
|
513
|
+
|
514
|
+
const memberRegistered = txRegisterReceipt.events?.find(
|
515
|
+
(event: ethers.Event) => event.event === "MembershipRegistered"
|
342
516
|
);
|
343
517
|
|
518
|
+
if (!memberRegistered || !memberRegistered.args) {
|
519
|
+
log.error(
|
520
|
+
"Failed to register membership: No MembershipRegistered event found"
|
521
|
+
);
|
522
|
+
return undefined;
|
523
|
+
}
|
524
|
+
|
525
|
+
const decodedData: MembershipRegisteredEvent = {
|
526
|
+
idCommitment: memberRegistered.args.idCommitment,
|
527
|
+
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
528
|
+
index: memberRegistered.args.index
|
529
|
+
};
|
530
|
+
|
344
531
|
log.info(
|
345
532
|
`Successfully registered membership with index ${decodedData.index} ` +
|
346
533
|
`and rate limit ${decodedData.membershipRateLimit}`
|
@@ -348,55 +535,44 @@ export class RLNBaseContract {
|
|
348
535
|
|
349
536
|
const network = await this.contract.provider.getNetwork();
|
350
537
|
const address = this.contract.address;
|
351
|
-
const membershipId = decodedData.index
|
538
|
+
const membershipId = Number(decodedData.index);
|
352
539
|
|
353
540
|
return {
|
354
541
|
identity,
|
355
542
|
membership: {
|
356
543
|
address,
|
357
|
-
treeIndex:
|
544
|
+
treeIndex: membershipId,
|
358
545
|
chainId: network.chainId.toString(),
|
359
546
|
rateLimit: decodedData.membershipRateLimit.toNumber()
|
360
547
|
}
|
361
548
|
};
|
362
549
|
} catch (error) {
|
363
|
-
if (error instanceof RLNContractError) {
|
364
|
-
throw error;
|
365
|
-
}
|
366
|
-
|
367
550
|
if (error instanceof Error) {
|
368
551
|
const errorMessage = error.message;
|
369
552
|
log.error("registerWithIdentity - error message:", errorMessage);
|
370
553
|
log.error("registerWithIdentity - error stack:", error.stack);
|
371
554
|
|
372
|
-
//
|
555
|
+
// Try to extract more specific error information
|
373
556
|
if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
|
374
|
-
throw new
|
375
|
-
|
376
|
-
await this.getRemainingTotalRateLimit()
|
557
|
+
throw new Error(
|
558
|
+
"Registration failed: Cannot exceed maximum total rate limit"
|
377
559
|
);
|
378
560
|
} else if (errorMessage.includes("InvalidIdCommitment")) {
|
379
|
-
throw new
|
380
|
-
identity.IDCommitmentBigInt.toString()
|
381
|
-
);
|
561
|
+
throw new Error("Registration failed: Invalid ID commitment");
|
382
562
|
} else if (errorMessage.includes("InvalidMembershipRateLimit")) {
|
383
|
-
throw new
|
384
|
-
this.rateLimit,
|
385
|
-
RATE_LIMIT_PARAMS.MIN_RATE,
|
386
|
-
RATE_LIMIT_PARAMS.MAX_RATE
|
387
|
-
);
|
563
|
+
throw new Error("Registration failed: Invalid membership rate limit");
|
388
564
|
} else if (errorMessage.includes("execution reverted")) {
|
389
|
-
throw new
|
565
|
+
throw new Error(
|
390
566
|
"Contract execution reverted. Check contract requirements."
|
391
567
|
);
|
568
|
+
} else {
|
569
|
+
throw new Error(`Error in registerWithIdentity: ${errorMessage}`);
|
392
570
|
}
|
393
|
-
|
394
|
-
throw new
|
395
|
-
|
396
|
-
);
|
571
|
+
} else {
|
572
|
+
throw new Error("Unknown error in registerWithIdentity", {
|
573
|
+
cause: error
|
574
|
+
});
|
397
575
|
}
|
398
|
-
|
399
|
-
throw new RLNContractError("Unknown error in registerWithIdentity");
|
400
576
|
}
|
401
577
|
}
|
402
578
|
|
@@ -416,27 +592,36 @@ export class RLNBaseContract {
|
|
416
592
|
`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`
|
417
593
|
);
|
418
594
|
|
419
|
-
const
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
595
|
+
const txRegisterResponse: ethers.ContractTransaction =
|
596
|
+
await this.contract.registerWithPermit(
|
597
|
+
permit.owner,
|
598
|
+
permit.deadline,
|
599
|
+
permit.v,
|
600
|
+
permit.r,
|
601
|
+
permit.s,
|
602
|
+
identity.IDCommitmentBigInt,
|
603
|
+
this.rateLimit,
|
604
|
+
idCommitmentsToErase.map((id) => ethers.BigNumber.from(id))
|
605
|
+
);
|
606
|
+
const txRegisterReceipt = await txRegisterResponse.wait();
|
429
607
|
|
430
|
-
const
|
431
|
-
|
432
|
-
"MembershipRegistered",
|
433
|
-
(event): MembershipRegisteredEvent => ({
|
434
|
-
idCommitment: event.args!.idCommitment,
|
435
|
-
membershipRateLimit: event.args!.membershipRateLimit,
|
436
|
-
index: event.args!.index
|
437
|
-
})
|
608
|
+
const memberRegistered = txRegisterReceipt.events?.find(
|
609
|
+
(event: ethers.Event) => event.event === "MembershipRegistered"
|
438
610
|
);
|
439
611
|
|
612
|
+
if (!memberRegistered || !memberRegistered.args) {
|
613
|
+
log.error(
|
614
|
+
"Failed to register membership with permit: No MembershipRegistered event found"
|
615
|
+
);
|
616
|
+
return undefined;
|
617
|
+
}
|
618
|
+
|
619
|
+
const decodedData: MembershipRegisteredEvent = {
|
620
|
+
idCommitment: memberRegistered.args.idCommitment,
|
621
|
+
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
622
|
+
index: memberRegistered.args.index
|
623
|
+
};
|
624
|
+
|
440
625
|
log.info(
|
441
626
|
`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
|
442
627
|
`Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`
|
@@ -444,13 +629,13 @@ export class RLNBaseContract {
|
|
444
629
|
|
445
630
|
const network = await this.contract.provider.getNetwork();
|
446
631
|
const address = this.contract.address;
|
447
|
-
const membershipId = decodedData.index
|
632
|
+
const membershipId = Number(decodedData.index);
|
448
633
|
|
449
634
|
return {
|
450
635
|
identity,
|
451
636
|
membership: {
|
452
637
|
address,
|
453
|
-
treeIndex:
|
638
|
+
treeIndex: membershipId,
|
454
639
|
chainId: network.chainId.toString(),
|
455
640
|
rateLimit: decodedData.membershipRateLimit.toNumber()
|
456
641
|
}
|
@@ -459,7 +644,7 @@ export class RLNBaseContract {
|
|
459
644
|
log.error(
|
460
645
|
`Error in registerWithPermitAndErase: ${(error as Error).message}`
|
461
646
|
);
|
462
|
-
|
647
|
+
return undefined;
|
463
648
|
}
|
464
649
|
}
|
465
650
|
|
@@ -467,34 +652,57 @@ export class RLNBaseContract {
|
|
467
652
|
* Validates that the rate limit is within the allowed range
|
468
653
|
* @throws Error if the rate limit is outside the allowed range
|
469
654
|
*/
|
470
|
-
private validateRateLimit(rateLimit: number): void {
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
)
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
655
|
+
private async validateRateLimit(rateLimit: number): Promise<void> {
|
656
|
+
const [minRate, maxRate] = await Promise.all([
|
657
|
+
this.contract.minMembershipRateLimit(),
|
658
|
+
this.contract.maxMembershipRateLimit()
|
659
|
+
]);
|
660
|
+
|
661
|
+
const minRateNum = ethers.BigNumber.from(minRate).toNumber();
|
662
|
+
const maxRateNum = ethers.BigNumber.from(maxRate).toNumber();
|
663
|
+
|
664
|
+
if (rateLimit < minRateNum || rateLimit > maxRateNum) {
|
665
|
+
throw new Error(
|
666
|
+
`Rate limit must be between ${minRateNum} and ${maxRateNum} messages per epoch`
|
479
667
|
);
|
480
668
|
}
|
481
669
|
}
|
482
670
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
):
|
491
|
-
|
492
|
-
|
671
|
+
private get membersFilter(): ethers.EventFilter {
|
672
|
+
if (!this._membersFilter) {
|
673
|
+
throw Error("Members filter was not initialized.");
|
674
|
+
}
|
675
|
+
return this._membersFilter;
|
676
|
+
}
|
677
|
+
|
678
|
+
private get membershipErasedFilter(): ethers.EventFilter {
|
679
|
+
if (!this._membershipErasedFilter) {
|
680
|
+
throw Error("MembershipErased filter was not initialized.");
|
681
|
+
}
|
682
|
+
return this._membershipErasedFilter;
|
683
|
+
}
|
493
684
|
|
494
|
-
|
495
|
-
|
685
|
+
private get membersExpiredFilter(): ethers.EventFilter {
|
686
|
+
if (!this._membersExpiredFilter) {
|
687
|
+
throw Error("MembersExpired filter was not initialized.");
|
496
688
|
}
|
689
|
+
return this._membersExpiredFilter;
|
690
|
+
}
|
497
691
|
|
498
|
-
|
692
|
+
private async getMemberIndex(
|
693
|
+
idCommitmentBigInt: bigint
|
694
|
+
): Promise<ethers.BigNumber | undefined> {
|
695
|
+
try {
|
696
|
+
const events = await this.contract.queryFilter(
|
697
|
+
this.contract.filters.MembershipRegistered(idCommitmentBigInt)
|
698
|
+
);
|
699
|
+
if (events.length === 0) return undefined;
|
700
|
+
|
701
|
+
// Get the most recent registration event
|
702
|
+
const event = events[events.length - 1];
|
703
|
+
return event.args?.index;
|
704
|
+
} catch (error) {
|
705
|
+
return undefined;
|
706
|
+
}
|
499
707
|
}
|
500
708
|
}
|