@waku/rln 0.1.4-d27db21.0 → 0.1.5-053bb95.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/CHANGELOG.md +19 -0
- package/README.md +5 -0
- package/bundle/_virtual/utils.js +2 -2
- package/bundle/_virtual/utils2.js +2 -2
- package/bundle/index.js +3 -1
- package/bundle/node_modules/@ethersproject/abi/lib.esm/coders/abstract-coder.js +1 -1
- package/bundle/node_modules/@ethersproject/abi/lib.esm/interface.js +1 -1
- package/bundle/node_modules/@ethersproject/abstract-provider/lib.esm/index.js +1 -1
- package/bundle/node_modules/@ethersproject/abstract-signer/lib.esm/index.js +11 -11
- package/bundle/node_modules/@ethersproject/bytes/lib.esm/_version.js +1 -1
- package/bundle/node_modules/@ethersproject/contracts/lib.esm/index.js +7 -7
- package/bundle/node_modules/@ethersproject/hash/lib.esm/ens-normalize/lib.js +2 -2
- package/bundle/node_modules/@ethersproject/hash/lib.esm/typed-data.js +2 -2
- package/bundle/node_modules/@ethersproject/keccak256/node_modules/js-sha3/src/sha3.js +1 -1
- package/bundle/node_modules/@ethersproject/logger/lib.esm/_version.js +1 -1
- package/bundle/node_modules/@ethersproject/properties/lib.esm/index.js +1 -1
- package/bundle/node_modules/@ethersproject/providers/lib.esm/base-provider.js +44 -44
- package/bundle/node_modules/@ethersproject/providers/lib.esm/json-rpc-provider.js +8 -8
- package/bundle/node_modules/@ethersproject/rlp/lib.esm/_version.js +1 -1
- package/bundle/node_modules/@ethersproject/transactions/lib.esm/index.js +1 -1
- package/bundle/node_modules/@ethersproject/web/lib.esm/geturl.js +1 -1
- package/bundle/node_modules/@ethersproject/web/lib.esm/index.js +2 -2
- package/bundle/node_modules/@multiformats/multiaddr/dist/src/multiaddr.js +1 -0
- package/bundle/node_modules/@noble/hashes/esm/sha3.js +1 -1
- package/bundle/node_modules/bn.js/lib/bn.js +1 -1
- package/bundle/packages/core/dist/lib/connection_manager/connection_manager.js +6 -6
- package/bundle/packages/rln/dist/contract/abi.js +502 -248
- package/bundle/packages/rln/dist/contract/constants.js +5 -9
- package/bundle/packages/rln/dist/contract/errors.js +62 -0
- package/bundle/packages/rln/dist/contract/rln_base_contract.js +347 -0
- package/bundle/packages/rln/dist/contract/rln_contract.js +81 -392
- package/bundle/packages/rln/dist/contract/types.js +9 -0
- package/bundle/packages/rln/dist/create.js +1 -1
- package/bundle/packages/rln/dist/credentials_manager.js +215 -0
- package/bundle/packages/rln/dist/identity.js +8 -0
- package/bundle/packages/rln/dist/keystore/keystore.js +20 -28
- package/bundle/packages/rln/dist/rln.js +56 -166
- package/bundle/packages/rln/dist/zerokit.js +5 -5
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/checksum.js +2 -2
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/cipher.js +3 -3
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/class.js +4 -4
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/functional.js +4 -4
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/kdf.js +4 -4
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/password.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/schema-validation-generated.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/lib/schema-validation.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/aes.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/pbkdf2.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/random.js +2 -2
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/scrypt.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/sha256.js +1 -1
- package/bundle/packages/rln/node_modules/@chainsafe/bls-keystore/node_modules/ethereum-cryptography/utils.js +3 -3
- package/bundle/packages/rln/node_modules/@noble/hashes/_assert.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/_sha2.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/_u64.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/cryptoBrowser.js +1 -1
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/_assert.js +43 -0
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/_sha2.js +116 -0
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/hmac.js +79 -0
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/sha256.js +126 -0
- package/bundle/packages/rln/node_modules/@noble/hashes/esm/utils.js +43 -0
- package/bundle/packages/rln/node_modules/@noble/hashes/hmac.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/pbkdf2.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/scrypt.js +3 -3
- package/bundle/packages/rln/node_modules/@noble/hashes/sha256.js +2 -2
- package/bundle/packages/rln/node_modules/@noble/hashes/sha512.js +3 -3
- package/bundle/packages/rln/node_modules/@noble/hashes/utils.js +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/codec.test-utils.d.ts +36 -0
- package/dist/codec.test-utils.js +56 -0
- package/dist/codec.test-utils.js.map +1 -0
- package/dist/contract/abi.d.ts +21 -17
- package/dist/contract/abi.js +502 -248
- package/dist/contract/abi.js.map +1 -1
- package/dist/contract/constants.d.ts +23 -19
- package/dist/contract/constants.js +3 -3
- package/dist/contract/constants.js.map +1 -1
- package/dist/contract/errors.d.ts +30 -0
- package/dist/contract/errors.js +61 -0
- package/dist/contract/errors.js.map +1 -0
- package/dist/contract/rln_base_contract.d.ts +88 -0
- package/dist/contract/rln_base_contract.js +330 -0
- package/dist/contract/rln_base_contract.js.map +1 -0
- package/dist/contract/rln_contract.d.ts +19 -109
- package/dist/contract/rln_contract.js +80 -390
- package/dist/contract/rln_contract.js.map +1 -1
- package/dist/contract/test-setup.d.ts +26 -0
- package/dist/contract/test-setup.js +56 -0
- package/dist/contract/test-setup.js.map +1 -0
- package/dist/contract/test-utils.d.ts +39 -0
- package/dist/contract/test-utils.js +118 -0
- package/dist/contract/test-utils.js.map +1 -0
- package/dist/contract/types.d.ts +40 -0
- package/dist/contract/types.js +8 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/create.js +1 -1
- package/dist/create.js.map +1 -1
- package/dist/credentials_manager.d.ts +44 -0
- package/dist/credentials_manager.js +197 -0
- package/dist/credentials_manager.js.map +1 -0
- package/dist/identity.d.ts +1 -0
- package/dist/identity.js +8 -0
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/keystore.d.ts +0 -1
- package/dist/keystore/keystore.js +20 -28
- package/dist/keystore/keystore.js.map +1 -1
- package/dist/keystore/types.d.ts +2 -1
- package/dist/rln.d.ts +9 -52
- package/dist/rln.js +54 -163
- package/dist/rln.js.map +1 -1
- package/dist/types.d.ts +27 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/zerokit.d.ts +3 -3
- package/dist/zerokit.js +5 -5
- package/dist/zerokit.js.map +1 -1
- package/package.json +1 -1
- package/src/codec.test-utils.ts +80 -0
- package/src/contract/abi.ts +502 -248
- package/src/contract/constants.ts +3 -3
- package/src/contract/errors.ts +75 -0
- package/src/contract/rln_base_contract.ts +500 -0
- package/src/contract/rln_contract.ts +102 -619
- package/src/contract/test-setup.ts +86 -0
- package/src/contract/test-utils.ts +179 -0
- package/src/contract/types.ts +48 -0
- package/src/create.ts +1 -1
- package/src/credentials_manager.ts +282 -0
- package/src/identity.ts +9 -0
- package/src/index.ts +17 -2
- package/src/keystore/keystore.ts +32 -46
- package/src/keystore/types.ts +2 -1
- package/src/rln.ts +67 -258
- package/src/types.ts +31 -0
- package/src/zerokit.ts +3 -3
@@ -1,8 +1,8 @@
|
|
1
1
|
import { RLN_ABI } from './abi.js';
|
2
2
|
|
3
|
-
const
|
4
|
-
chainId:
|
5
|
-
address: "
|
3
|
+
const LINEA_CONTRACT = {
|
4
|
+
chainId: "59141",
|
5
|
+
address: "0xb9cd878c90e49f797b4431fbf4fb333108cb90e6",
|
6
6
|
abi: RLN_ABI
|
7
7
|
};
|
8
8
|
/**
|
@@ -12,16 +12,12 @@ const SEPOLIA_CONTRACT = {
|
|
12
12
|
*/
|
13
13
|
const RATE_LIMIT_TIERS = {
|
14
14
|
LOW: 20, // Suggested minimum rate - 20 messages per epoch
|
15
|
-
MEDIUM: 200,
|
16
15
|
HIGH: 600 // Suggested maximum rate - 600 messages per epoch
|
17
16
|
};
|
18
17
|
// Global rate limit parameters
|
19
18
|
const RATE_LIMIT_PARAMS = {
|
20
19
|
MIN_RATE: RATE_LIMIT_TIERS.LOW,
|
21
|
-
MAX_RATE: RATE_LIMIT_TIERS.HIGH
|
22
|
-
MAX_TOTAL_RATE: 160_000, // Maximum total rate limit across all memberships
|
23
|
-
EPOCH_LENGTH: 600 // Epoch length in seconds (10 minutes)
|
24
|
-
};
|
20
|
+
MAX_RATE: RATE_LIMIT_TIERS.HIGH};
|
25
21
|
const DEFAULT_RATE_LIMIT = RATE_LIMIT_PARAMS.MAX_RATE;
|
26
22
|
|
27
|
-
export { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS, RATE_LIMIT_TIERS
|
23
|
+
export { DEFAULT_RATE_LIMIT, LINEA_CONTRACT, RATE_LIMIT_PARAMS, RATE_LIMIT_TIERS };
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class RLNContractError extends Error {
|
2
|
+
constructor(message) {
|
3
|
+
super(message);
|
4
|
+
this.name = "RLNContractError";
|
5
|
+
}
|
6
|
+
}
|
7
|
+
class MembershipError extends RLNContractError {
|
8
|
+
constructor(message) {
|
9
|
+
super(message);
|
10
|
+
this.name = "MembershipError";
|
11
|
+
}
|
12
|
+
}
|
13
|
+
class RateLimitError extends RLNContractError {
|
14
|
+
constructor(message) {
|
15
|
+
super(message);
|
16
|
+
this.name = "RateLimitError";
|
17
|
+
}
|
18
|
+
}
|
19
|
+
class InvalidMembershipError extends MembershipError {
|
20
|
+
constructor(idCommitment) {
|
21
|
+
super(`Invalid membership ID commitment: ${idCommitment}`);
|
22
|
+
this.name = "InvalidMembershipError";
|
23
|
+
}
|
24
|
+
}
|
25
|
+
class MembershipNotFoundError extends MembershipError {
|
26
|
+
constructor(idCommitment) {
|
27
|
+
super(`Membership not found for ID commitment: ${idCommitment}`);
|
28
|
+
this.name = "MembershipNotFoundError";
|
29
|
+
}
|
30
|
+
}
|
31
|
+
class MembershipExistsError extends MembershipError {
|
32
|
+
constructor(idCommitment, index) {
|
33
|
+
super(`Membership already exists for ID commitment: ${idCommitment} at index ${index}`);
|
34
|
+
this.name = "MembershipExistsError";
|
35
|
+
}
|
36
|
+
}
|
37
|
+
class RateLimitExceededError extends RateLimitError {
|
38
|
+
constructor(requested, available) {
|
39
|
+
super(`Rate limit exceeded. Requested: ${requested}, Available: ${available}`);
|
40
|
+
this.name = "RateLimitExceededError";
|
41
|
+
}
|
42
|
+
}
|
43
|
+
class InvalidRateLimitError extends RateLimitError {
|
44
|
+
constructor(rateLimit, minRate, maxRate) {
|
45
|
+
super(`Invalid rate limit: ${rateLimit}. Must be between ${minRate} and ${maxRate}`);
|
46
|
+
this.name = "InvalidRateLimitError";
|
47
|
+
}
|
48
|
+
}
|
49
|
+
class ContractStateError extends RLNContractError {
|
50
|
+
constructor(message) {
|
51
|
+
super(`Contract state error: ${message}`);
|
52
|
+
this.name = "ContractStateError";
|
53
|
+
}
|
54
|
+
}
|
55
|
+
class TransactionError extends RLNContractError {
|
56
|
+
constructor(message) {
|
57
|
+
super(`Transaction failed: ${message}`);
|
58
|
+
this.name = "TransactionError";
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
export { ContractStateError, InvalidMembershipError, InvalidRateLimitError, MembershipError, MembershipExistsError, MembershipNotFoundError, RLNContractError, RateLimitError, RateLimitExceededError, TransactionError };
|
@@ -0,0 +1,347 @@
|
|
1
|
+
import '../../../interfaces/dist/protocols.js';
|
2
|
+
import '../../../interfaces/dist/connection_manager.js';
|
3
|
+
import '../../../interfaces/dist/health_indicator.js';
|
4
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base10.js';
|
5
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base16.js';
|
6
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base2.js';
|
7
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base256emoji.js';
|
8
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base32.js';
|
9
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base36.js';
|
10
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base58.js';
|
11
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base64.js';
|
12
|
+
import '../../../../node_modules/multiformats/dist/src/bases/base8.js';
|
13
|
+
import '../../../../node_modules/multiformats/dist/src/bases/identity.js';
|
14
|
+
import '../../../../node_modules/multiformats/dist/src/codecs/json.js';
|
15
|
+
import { Logger } from '../../../utils/dist/logger/index.js';
|
16
|
+
import { RLN_ABI } from './abi.js';
|
17
|
+
import { DEFAULT_RATE_LIMIT, RATE_LIMIT_PARAMS } from './constants.js';
|
18
|
+
import { RLNContractError, InvalidMembershipError, MembershipNotFoundError, MembershipExistsError, RateLimitExceededError, InvalidRateLimitError, TransactionError } from './errors.js';
|
19
|
+
import { MembershipState } from './types.js';
|
20
|
+
import { Contract } from '../../../../node_modules/@ethersproject/contracts/lib.esm/index.js';
|
21
|
+
import { BigNumber } from '../../../../node_modules/@ethersproject/bignumber/lib.esm/bignumber.js';
|
22
|
+
|
23
|
+
const log = new Logger("waku:rln:contract:base");
|
24
|
+
class RLNBaseContract {
|
25
|
+
contract;
|
26
|
+
rateLimit;
|
27
|
+
constructor(options) {
|
28
|
+
const { address, signer, rateLimit = DEFAULT_RATE_LIMIT, contract } = options;
|
29
|
+
this.validateRateLimit(rateLimit);
|
30
|
+
this.contract = contract || new Contract(address, RLN_ABI, signer);
|
31
|
+
this.rateLimit = rateLimit;
|
32
|
+
}
|
33
|
+
/**
|
34
|
+
* Gets the current rate limit for this contract instance
|
35
|
+
*/
|
36
|
+
getRateLimit() {
|
37
|
+
return this.rateLimit;
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* Gets the contract address
|
41
|
+
*/
|
42
|
+
get address() {
|
43
|
+
return this.contract.address;
|
44
|
+
}
|
45
|
+
/**
|
46
|
+
* Gets the contract provider
|
47
|
+
*/
|
48
|
+
get provider() {
|
49
|
+
return this.contract.provider;
|
50
|
+
}
|
51
|
+
/**
|
52
|
+
* Gets the minimum allowed rate limit from the contract
|
53
|
+
* @returns Promise<number> The minimum rate limit in messages per epoch
|
54
|
+
*/
|
55
|
+
async getMinRateLimit() {
|
56
|
+
const minRate = await this.contract.minMembershipRateLimit();
|
57
|
+
return BigNumber.from(minRate).toNumber();
|
58
|
+
}
|
59
|
+
/**
|
60
|
+
* Gets the maximum allowed rate limit from the contract
|
61
|
+
* @returns Promise<number> The maximum rate limit in messages per epoch
|
62
|
+
*/
|
63
|
+
async getMaxRateLimit() {
|
64
|
+
const maxRate = await this.contract.maxMembershipRateLimit();
|
65
|
+
return BigNumber.from(maxRate).toNumber();
|
66
|
+
}
|
67
|
+
/**
|
68
|
+
* Gets the maximum total rate limit across all memberships
|
69
|
+
* @returns Promise<number> The maximum total rate limit in messages per epoch
|
70
|
+
*/
|
71
|
+
async getMaxTotalRateLimit() {
|
72
|
+
const maxTotalRate = await this.contract.maxTotalRateLimit();
|
73
|
+
return maxTotalRate.toNumber();
|
74
|
+
}
|
75
|
+
/**
|
76
|
+
* Gets the current total rate limit usage across all memberships
|
77
|
+
* @returns Promise<number> The current total rate limit usage in messages per epoch
|
78
|
+
*/
|
79
|
+
async getCurrentTotalRateLimit() {
|
80
|
+
const currentTotal = await this.contract.currentTotalRateLimit();
|
81
|
+
return currentTotal.toNumber();
|
82
|
+
}
|
83
|
+
/**
|
84
|
+
* Gets the remaining available total rate limit that can be allocated
|
85
|
+
* @returns Promise<number> The remaining rate limit that can be allocated
|
86
|
+
*/
|
87
|
+
async getRemainingTotalRateLimit() {
|
88
|
+
const [maxTotal, currentTotal] = await Promise.all([
|
89
|
+
this.contract.maxTotalRateLimit(),
|
90
|
+
this.contract.currentTotalRateLimit()
|
91
|
+
]);
|
92
|
+
return Number(maxTotal) - Number(currentTotal);
|
93
|
+
}
|
94
|
+
/**
|
95
|
+
* Updates the rate limit for future registrations
|
96
|
+
* @param newRateLimit The new rate limit to use
|
97
|
+
*/
|
98
|
+
async setRateLimit(newRateLimit) {
|
99
|
+
this.validateRateLimit(newRateLimit);
|
100
|
+
this.rateLimit = newRateLimit;
|
101
|
+
}
|
102
|
+
/**
|
103
|
+
* Gets all members in the given range
|
104
|
+
* @param startIndex Start index (inclusive)
|
105
|
+
* @param endIndex End index (exclusive)
|
106
|
+
*/
|
107
|
+
async getMembersInRange(startIndex, endIndex) {
|
108
|
+
try {
|
109
|
+
// Get all commitments in one call
|
110
|
+
const idCommitments = await this.contract.getRateCommitmentsInRangeBoundsInclusive(startIndex, endIndex - 1 // -1 because getRateCommitmentsInRangeBoundsInclusive is inclusive
|
111
|
+
);
|
112
|
+
// Get membership info for each commitment in a single batch
|
113
|
+
const membershipPromises = idCommitments.map((idCommitment) => this.contract
|
114
|
+
.memberships(idCommitment)
|
115
|
+
.then((info) => ({
|
116
|
+
idCommitment: idCommitment.toString(),
|
117
|
+
index: BigNumber.from(info.index)
|
118
|
+
}))
|
119
|
+
.catch(() => null) // Skip invalid members
|
120
|
+
);
|
121
|
+
// Wait for all promises to resolve
|
122
|
+
const members = (await Promise.all(membershipPromises)).filter((m) => m !== null);
|
123
|
+
return members;
|
124
|
+
}
|
125
|
+
catch (error) {
|
126
|
+
if (error instanceof Error &&
|
127
|
+
error.message.includes("InvalidPaginationQuery")) {
|
128
|
+
throw new RLNContractError(`Invalid pagination range: start=${startIndex}, end=${endIndex}`);
|
129
|
+
}
|
130
|
+
throw error;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
/**
|
134
|
+
* Gets all current members
|
135
|
+
*/
|
136
|
+
async getAllMembers() {
|
137
|
+
const nextIndex = (await this.contract.nextFreeIndex()).toNumber();
|
138
|
+
return this.getMembersInRange(0, nextIndex);
|
139
|
+
}
|
140
|
+
/**
|
141
|
+
* Gets the member index if it exists, or null if it doesn't
|
142
|
+
* Throws only on actual errors (invalid input, network issues, etc)
|
143
|
+
*/
|
144
|
+
async getMemberIndex(idCommitment) {
|
145
|
+
try {
|
146
|
+
const isValid = await this.contract.isInMembershipSet(idCommitment);
|
147
|
+
if (!isValid) {
|
148
|
+
return null;
|
149
|
+
}
|
150
|
+
const membershipInfo = await this.contract.memberships(idCommitment);
|
151
|
+
return BigNumber.from(membershipInfo.index);
|
152
|
+
}
|
153
|
+
catch (error) {
|
154
|
+
log.error(`Error getting member index: ${error.message}`);
|
155
|
+
throw new InvalidMembershipError(idCommitment);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
async getMembershipInfo(idCommitment) {
|
159
|
+
try {
|
160
|
+
const [startBlock, endBlock, rateLimit] = await this.contract.getMembershipInfo(idCommitment);
|
161
|
+
const currentBlock = await this.contract.provider.getBlockNumber();
|
162
|
+
let state;
|
163
|
+
if (currentBlock < startBlock) {
|
164
|
+
state = MembershipState.Active;
|
165
|
+
}
|
166
|
+
else if (currentBlock < endBlock) {
|
167
|
+
state = MembershipState.GracePeriod;
|
168
|
+
}
|
169
|
+
else {
|
170
|
+
state = MembershipState.Expired;
|
171
|
+
}
|
172
|
+
const index = await this.getMemberIndex(idCommitment);
|
173
|
+
if (index === null) {
|
174
|
+
throw new MembershipNotFoundError(idCommitment);
|
175
|
+
}
|
176
|
+
return {
|
177
|
+
index,
|
178
|
+
idCommitment,
|
179
|
+
rateLimit: rateLimit.toNumber(),
|
180
|
+
startBlock: startBlock.toNumber(),
|
181
|
+
endBlock: endBlock.toNumber(),
|
182
|
+
state
|
183
|
+
};
|
184
|
+
}
|
185
|
+
catch (error) {
|
186
|
+
if (error instanceof RLNContractError) {
|
187
|
+
throw error;
|
188
|
+
}
|
189
|
+
log.error(`Error getting membership info: ${error.message}`);
|
190
|
+
throw new InvalidMembershipError(idCommitment);
|
191
|
+
}
|
192
|
+
}
|
193
|
+
async extendMembership(idCommitment) {
|
194
|
+
const tx = await this.contract.extendMemberships([idCommitment]);
|
195
|
+
await this.confirmTransaction(tx, "MembershipExtended", (event) => ({
|
196
|
+
idCommitment: event.args.idCommitment,
|
197
|
+
endBlock: event.args.endBlock
|
198
|
+
}));
|
199
|
+
}
|
200
|
+
async eraseMembership(idCommitment, eraseFromMembershipSet = true) {
|
201
|
+
const tx = await this.contract.eraseMemberships([idCommitment], eraseFromMembershipSet);
|
202
|
+
await this.confirmTransaction(tx, "MembershipErased", (event) => ({
|
203
|
+
idCommitment: event.args.idCommitment,
|
204
|
+
index: event.args.index
|
205
|
+
}));
|
206
|
+
}
|
207
|
+
async registerMembership(idCommitment, rateLimit = DEFAULT_RATE_LIMIT) {
|
208
|
+
if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
209
|
+
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
210
|
+
throw new Error(`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`);
|
211
|
+
}
|
212
|
+
const tx = await this.contract.register(idCommitment, rateLimit, []);
|
213
|
+
await this.confirmTransaction(tx, "MembershipRegistered", (event) => ({
|
214
|
+
idCommitment: event.args.idCommitment,
|
215
|
+
membershipRateLimit: event.args.membershipRateLimit,
|
216
|
+
index: event.args.index
|
217
|
+
}));
|
218
|
+
}
|
219
|
+
async withdraw(token, holder) {
|
220
|
+
try {
|
221
|
+
const tx = await this.contract.withdraw(token, { from: holder });
|
222
|
+
await this.confirmTransaction(tx, "TokenWithdrawn", (event) => ({
|
223
|
+
token: event.args.token,
|
224
|
+
holder: event.args.holder,
|
225
|
+
amount: event.args.amount
|
226
|
+
}));
|
227
|
+
}
|
228
|
+
catch (error) {
|
229
|
+
log.error(`Error in withdraw: ${error.message}`);
|
230
|
+
throw error;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
async registerWithIdentity(identity) {
|
234
|
+
try {
|
235
|
+
log.info(`Registering identity with rate limit: ${this.rateLimit} messages/epoch`);
|
236
|
+
// Check if the ID commitment is already registered
|
237
|
+
const existingIndex = await this.getMemberIndex(identity.IDCommitmentBigInt.toString());
|
238
|
+
if (existingIndex !== null) {
|
239
|
+
throw new MembershipExistsError(identity.IDCommitmentBigInt.toString(), existingIndex.toString());
|
240
|
+
}
|
241
|
+
// Check if there's enough remaining rate limit
|
242
|
+
const remainingRateLimit = await this.getRemainingTotalRateLimit();
|
243
|
+
if (remainingRateLimit < this.rateLimit) {
|
244
|
+
throw new RateLimitExceededError(this.rateLimit, remainingRateLimit);
|
245
|
+
}
|
246
|
+
const estimatedGas = await this.contract.estimateGas.register(identity.IDCommitmentBigInt, this.rateLimit, []);
|
247
|
+
const gasLimit = estimatedGas.add(10000);
|
248
|
+
const tx = await this.contract.register(identity.IDCommitmentBigInt, this.rateLimit, [], { gasLimit });
|
249
|
+
const decodedData = await this.confirmTransaction(tx, "MembershipRegistered", (event) => ({
|
250
|
+
idCommitment: event.args.idCommitment,
|
251
|
+
membershipRateLimit: event.args.membershipRateLimit,
|
252
|
+
index: event.args.index
|
253
|
+
}));
|
254
|
+
log.info(`Successfully registered membership with index ${decodedData.index} ` +
|
255
|
+
`and rate limit ${decodedData.membershipRateLimit}`);
|
256
|
+
const network = await this.contract.provider.getNetwork();
|
257
|
+
const address = this.contract.address;
|
258
|
+
const membershipId = decodedData.index.toString();
|
259
|
+
return {
|
260
|
+
identity,
|
261
|
+
membership: {
|
262
|
+
address,
|
263
|
+
treeIndex: parseInt(membershipId),
|
264
|
+
chainId: network.chainId.toString(),
|
265
|
+
rateLimit: decodedData.membershipRateLimit.toNumber()
|
266
|
+
}
|
267
|
+
};
|
268
|
+
}
|
269
|
+
catch (error) {
|
270
|
+
if (error instanceof RLNContractError) {
|
271
|
+
throw error;
|
272
|
+
}
|
273
|
+
if (error instanceof Error) {
|
274
|
+
const errorMessage = error.message;
|
275
|
+
log.error("registerWithIdentity - error message:", errorMessage);
|
276
|
+
log.error("registerWithIdentity - error stack:", error.stack);
|
277
|
+
// Map contract errors to our custom errors
|
278
|
+
if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
|
279
|
+
throw new RateLimitExceededError(this.rateLimit, await this.getRemainingTotalRateLimit());
|
280
|
+
}
|
281
|
+
else if (errorMessage.includes("InvalidIdCommitment")) {
|
282
|
+
throw new InvalidMembershipError(identity.IDCommitmentBigInt.toString());
|
283
|
+
}
|
284
|
+
else if (errorMessage.includes("InvalidMembershipRateLimit")) {
|
285
|
+
throw new InvalidRateLimitError(this.rateLimit, RATE_LIMIT_PARAMS.MIN_RATE, RATE_LIMIT_PARAMS.MAX_RATE);
|
286
|
+
}
|
287
|
+
else if (errorMessage.includes("execution reverted")) {
|
288
|
+
throw new TransactionError("Contract execution reverted. Check contract requirements.");
|
289
|
+
}
|
290
|
+
throw new RLNContractError(`Error in registerWithIdentity: ${errorMessage}`);
|
291
|
+
}
|
292
|
+
throw new RLNContractError("Unknown error in registerWithIdentity");
|
293
|
+
}
|
294
|
+
}
|
295
|
+
async registerWithPermitAndErase(identity, permit, idCommitmentsToErase) {
|
296
|
+
try {
|
297
|
+
log.info(`Registering identity with permit and rate limit: ${this.rateLimit} messages/epoch`);
|
298
|
+
const tx = await this.contract.registerWithPermit(permit.owner, permit.deadline, permit.v, permit.r, permit.s, identity.IDCommitmentBigInt, this.rateLimit, idCommitmentsToErase.map((id) => BigNumber.from(id)));
|
299
|
+
const decodedData = await this.confirmTransaction(tx, "MembershipRegistered", (event) => ({
|
300
|
+
idCommitment: event.args.idCommitment,
|
301
|
+
membershipRateLimit: event.args.membershipRateLimit,
|
302
|
+
index: event.args.index
|
303
|
+
}));
|
304
|
+
log.info(`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
|
305
|
+
`Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`);
|
306
|
+
const network = await this.contract.provider.getNetwork();
|
307
|
+
const address = this.contract.address;
|
308
|
+
const membershipId = decodedData.index.toString();
|
309
|
+
return {
|
310
|
+
identity,
|
311
|
+
membership: {
|
312
|
+
address,
|
313
|
+
treeIndex: parseInt(membershipId),
|
314
|
+
chainId: network.chainId.toString(),
|
315
|
+
rateLimit: decodedData.membershipRateLimit.toNumber()
|
316
|
+
}
|
317
|
+
};
|
318
|
+
}
|
319
|
+
catch (error) {
|
320
|
+
log.error(`Error in registerWithPermitAndErase: ${error.message}`);
|
321
|
+
throw error;
|
322
|
+
}
|
323
|
+
}
|
324
|
+
/**
|
325
|
+
* Validates that the rate limit is within the allowed range
|
326
|
+
* @throws Error if the rate limit is outside the allowed range
|
327
|
+
*/
|
328
|
+
validateRateLimit(rateLimit) {
|
329
|
+
if (rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
330
|
+
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE) {
|
331
|
+
throw new InvalidRateLimitError(rateLimit, RATE_LIMIT_PARAMS.MIN_RATE, RATE_LIMIT_PARAMS.MAX_RATE);
|
332
|
+
}
|
333
|
+
}
|
334
|
+
/**
|
335
|
+
* Helper to confirm a transaction and extract event data
|
336
|
+
*/
|
337
|
+
async confirmTransaction(tx, expectedEvent, transform) {
|
338
|
+
const receipt = await tx.wait();
|
339
|
+
const event = receipt.events?.find((e) => e.event === expectedEvent);
|
340
|
+
if (!event || !event.args) {
|
341
|
+
throw new TransactionError(`Expected event ${expectedEvent} not found`);
|
342
|
+
}
|
343
|
+
return transform(event);
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
export { RLNBaseContract };
|