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