@0xbow/privacy-pools-core-sdk 0.1.21 → 1.0.2
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/README.md +4 -4
- package/dist/esm/{fetchArtifacts.esm-CXLPgzhJ.js → fetchArtifacts.esm-D0mKkSYH.js} +2 -2
- package/dist/esm/{fetchArtifacts.esm-CXLPgzhJ.js.map → fetchArtifacts.esm-D0mKkSYH.js.map} +1 -1
- package/dist/esm/{fetchArtifacts.node-B3pPMuWd.js → fetchArtifacts.node-D2RwI1ei.js} +2 -2
- package/dist/esm/{fetchArtifacts.node-B3pPMuWd.js.map → fetchArtifacts.node-D2RwI1ei.js.map} +1 -1
- package/dist/esm/{index-C_oO7cOn.js → index-CysEkDfm.js} +416 -67
- package/dist/esm/index-CysEkDfm.js.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/index.d.mts +185 -46
- package/dist/node/{fetchArtifacts.esm-DoJiimN3.js → fetchArtifacts.esm-GuREmUVE.js} +2 -2
- package/dist/node/{fetchArtifacts.esm-DoJiimN3.js.map → fetchArtifacts.esm-GuREmUVE.js.map} +1 -1
- package/dist/node/{fetchArtifacts.node-B4Ey9DOV.js → fetchArtifacts.node-80qrImLq.js} +2 -2
- package/dist/node/{fetchArtifacts.node-B4Ey9DOV.js.map → fetchArtifacts.node-80qrImLq.js.map} +1 -1
- package/dist/node/{index-CwopFzOC.js → index-bIu80opN.js} +416 -67
- package/dist/node/index-bIu80opN.js.map +1 -0
- package/dist/node/index.mjs +1 -1
- package/dist/types/core/account.service.d.ts +116 -4
- package/dist/types/core/contracts.service.d.ts +9 -1
- package/dist/types/core/sdk.d.ts +2 -1
- package/dist/types/core/withdrawal.service.d.ts +2 -2
- package/dist/types/errors/account.error.d.ts +2 -0
- package/dist/types/errors/base.error.d.ts +1 -0
- package/dist/types/errors/events.error.d.ts +9 -0
- package/dist/types/{fetchArtifacts.esm-CxFhH8oR.js → fetchArtifacts.esm-Ok2Ropox.js} +1 -1
- package/dist/types/{fetchArtifacts.node-Cqk_Z-mS.js → fetchArtifacts.node-DpNFJc75.js} +1 -1
- package/dist/types/{index-CIvuCHHq.js → index-xpjDqElC.js} +415 -66
- package/dist/types/index.js +1 -1
- package/dist/types/interfaces/contracts.interface.d.ts +7 -0
- package/dist/types/types/events.d.ts +11 -1
- package/package.json +2 -2
- package/src/core/account.service.ts +479 -61
- package/src/core/contracts.service.ts +35 -0
- package/src/core/data.service.ts +2 -3
- package/src/core/sdk.ts +2 -1
- package/src/core/withdrawal.service.ts +25 -9
- package/src/crypto.ts +11 -2
- package/src/errors/account.error.ts +14 -0
- package/src/errors/base.error.ts +5 -0
- package/src/errors/events.error.ts +38 -0
- package/src/interfaces/contracts.interface.ts +8 -0
- package/src/types/events.ts +15 -1
- package/dist/esm/index-C_oO7cOn.js.map +0 -1
- package/dist/node/index-CwopFzOC.js.map +0 -1
|
@@ -9,15 +9,31 @@ import {
|
|
|
9
9
|
PoolInfo,
|
|
10
10
|
PrivacyPoolAccount,
|
|
11
11
|
} from "../types/account.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
DepositEvent,
|
|
14
|
+
PoolEventsError,
|
|
15
|
+
PoolEventsResult,
|
|
16
|
+
RagequitEvent,
|
|
17
|
+
WithdrawalEvent,
|
|
18
|
+
} from "../types/events.js";
|
|
19
|
+
|
|
13
20
|
import { Logger } from "../utils/logger.js";
|
|
14
21
|
import { AccountError } from "../errors/account.error.js";
|
|
15
22
|
import { ErrorCode } from "../errors/base.error.js";
|
|
23
|
+
import { EventError } from "../errors/events.error.js";
|
|
24
|
+
|
|
25
|
+
type AccountServiceConfig =
|
|
26
|
+
| {
|
|
27
|
+
mnemonic: string;
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
account: PrivacyPoolAccount;
|
|
31
|
+
};
|
|
16
32
|
|
|
17
33
|
/**
|
|
18
34
|
* Service responsible for managing privacy pool accounts and their associated commitments.
|
|
19
35
|
* Handles account initialization, deposit/withdrawal tracking, and history synchronization.
|
|
20
|
-
*
|
|
36
|
+
*
|
|
21
37
|
* @remarks
|
|
22
38
|
* This service maintains the state of all pool accounts and their commitments across different
|
|
23
39
|
* chains and scopes. It uses deterministic key generation to recover account state from a mnemonic.
|
|
@@ -28,34 +44,38 @@ export class AccountService {
|
|
|
28
44
|
|
|
29
45
|
/**
|
|
30
46
|
* Creates a new AccountService instance.
|
|
31
|
-
*
|
|
47
|
+
*
|
|
32
48
|
* @param dataService - Service for fetching on-chain events
|
|
33
|
-
* @param
|
|
34
|
-
* @param mnemonic - Optional mnemonic for deterministic key generation
|
|
35
|
-
*
|
|
49
|
+
* @param config - Configuration for the account service (either mnemonic or existing account)
|
|
50
|
+
* @param config.mnemonic - Optional mnemonic for deterministic key generation
|
|
51
|
+
* @param config.account - Optional existing account to initialize with
|
|
52
|
+
*
|
|
36
53
|
* @throws {AccountError} If account initialization fails
|
|
37
54
|
*/
|
|
38
55
|
constructor(
|
|
39
56
|
private readonly dataService: DataService,
|
|
40
|
-
|
|
41
|
-
account?: PrivacyPoolAccount,
|
|
57
|
+
config: AccountServiceConfig
|
|
42
58
|
) {
|
|
43
59
|
this.logger = new Logger({ prefix: "Account" });
|
|
44
|
-
|
|
60
|
+
if ("mnemonic" in config) {
|
|
61
|
+
this.account = this._initializeAccount(config.mnemonic);
|
|
62
|
+
} else {
|
|
63
|
+
this.account = config.account;
|
|
64
|
+
}
|
|
45
65
|
}
|
|
46
66
|
|
|
47
67
|
/**
|
|
48
68
|
* Initializes a new account from a mnemonic phrase.
|
|
49
|
-
*
|
|
69
|
+
*
|
|
50
70
|
* @param mnemonic - The mnemonic phrase to derive keys from
|
|
51
71
|
* @returns A new PrivacyPoolAccount with derived master keys
|
|
52
|
-
*
|
|
72
|
+
*
|
|
53
73
|
* @remarks
|
|
54
74
|
* This method derives two master keys from the mnemonic:
|
|
55
75
|
* 1. A master nullifier key from account index 0
|
|
56
76
|
* 2. A master secret key from account index 1
|
|
57
77
|
* These keys are used to deterministically generate nullifiers and secrets for deposits and withdrawals.
|
|
58
|
-
*
|
|
78
|
+
*
|
|
59
79
|
* @throws {AccountError} If account initialization fails
|
|
60
80
|
* @private
|
|
61
81
|
*/
|
|
@@ -64,11 +84,11 @@ export class AccountService {
|
|
|
64
84
|
this.logger.debug("Initializing account with mnemonic");
|
|
65
85
|
|
|
66
86
|
const masterNullifierSeed = bytesToNumber(
|
|
67
|
-
mnemonicToAccount(mnemonic, { accountIndex: 0 }).getHdKey().privateKey
|
|
87
|
+
mnemonicToAccount(mnemonic, { accountIndex: 0 }).getHdKey().privateKey!
|
|
68
88
|
);
|
|
69
89
|
|
|
70
90
|
const masterSecretSeed = bytesToNumber(
|
|
71
|
-
mnemonicToAccount(mnemonic, { accountIndex: 1 }).getHdKey().privateKey
|
|
91
|
+
mnemonicToAccount(mnemonic, { accountIndex: 1 }).getHdKey().privateKey!
|
|
72
92
|
);
|
|
73
93
|
|
|
74
94
|
const masterNullifier = poseidon([BigInt(masterNullifierSeed)]) as Secret;
|
|
@@ -78,7 +98,7 @@ export class AccountService {
|
|
|
78
98
|
masterKeys: [masterNullifier, masterSecret],
|
|
79
99
|
poolAccounts: new Map(),
|
|
80
100
|
creationTimestamp: 0n,
|
|
81
|
-
lastUpdateTimestamp: 0n
|
|
101
|
+
lastUpdateTimestamp: 0n,
|
|
82
102
|
};
|
|
83
103
|
} catch (error) {
|
|
84
104
|
throw AccountError.accountInitializationFailed(
|
|
@@ -89,7 +109,7 @@ export class AccountService {
|
|
|
89
109
|
|
|
90
110
|
/**
|
|
91
111
|
* Generates a deterministic nullifier for a deposit.
|
|
92
|
-
*
|
|
112
|
+
*
|
|
93
113
|
* @param scope - The scope of the pool
|
|
94
114
|
* @param index - The index of the deposit
|
|
95
115
|
* @returns A deterministic nullifier for the deposit
|
|
@@ -102,7 +122,7 @@ export class AccountService {
|
|
|
102
122
|
|
|
103
123
|
/**
|
|
104
124
|
* Generates a deterministic secret for a deposit.
|
|
105
|
-
*
|
|
125
|
+
*
|
|
106
126
|
* @param scope - The scope of the pool
|
|
107
127
|
* @param index - The index of the deposit
|
|
108
128
|
* @returns A deterministic secret for the deposit
|
|
@@ -115,7 +135,7 @@ export class AccountService {
|
|
|
115
135
|
|
|
116
136
|
/**
|
|
117
137
|
* Generates a deterministic nullifier for a withdrawal.
|
|
118
|
-
*
|
|
138
|
+
*
|
|
119
139
|
* @param label - The label of the commitment
|
|
120
140
|
* @param index - The index of the withdrawal
|
|
121
141
|
* @returns A deterministic nullifier for the withdrawal
|
|
@@ -128,7 +148,7 @@ export class AccountService {
|
|
|
128
148
|
|
|
129
149
|
/**
|
|
130
150
|
* Generates a deterministic secret for a withdrawal.
|
|
131
|
-
*
|
|
151
|
+
*
|
|
132
152
|
* @param label - The label of the commitment
|
|
133
153
|
* @param index - The index of the withdrawal
|
|
134
154
|
* @returns A deterministic secret for the withdrawal
|
|
@@ -141,7 +161,7 @@ export class AccountService {
|
|
|
141
161
|
|
|
142
162
|
/**
|
|
143
163
|
* Hashes a commitment using the Poseidon hash function.
|
|
144
|
-
*
|
|
164
|
+
*
|
|
145
165
|
* @param value - The value of the commitment
|
|
146
166
|
* @param label - The label of the commitment
|
|
147
167
|
* @param precommitment - The precommitment hash
|
|
@@ -151,14 +171,14 @@ export class AccountService {
|
|
|
151
171
|
private _hashCommitment(
|
|
152
172
|
value: bigint,
|
|
153
173
|
label: Hash,
|
|
154
|
-
precommitment: Hash
|
|
174
|
+
precommitment: Hash
|
|
155
175
|
): Hash {
|
|
156
176
|
return poseidon([value, label, precommitment]) as Hash;
|
|
157
177
|
}
|
|
158
178
|
|
|
159
179
|
/**
|
|
160
180
|
* Hashes a precommitment using the Poseidon hash function.
|
|
161
|
-
*
|
|
181
|
+
*
|
|
162
182
|
* @param nullifier - The nullifier for the commitment
|
|
163
183
|
* @param secret - The secret for the commitment
|
|
164
184
|
* @returns The precommitment hash
|
|
@@ -170,9 +190,9 @@ export class AccountService {
|
|
|
170
190
|
|
|
171
191
|
/**
|
|
172
192
|
* Gets all spendable commitments across all pools.
|
|
173
|
-
*
|
|
193
|
+
*
|
|
174
194
|
* @returns A map of scope to array of spendable commitments
|
|
175
|
-
*
|
|
195
|
+
*
|
|
176
196
|
* @remarks
|
|
177
197
|
* A commitment is considered spendable if:
|
|
178
198
|
* 1. It has a non-zero value
|
|
@@ -209,25 +229,29 @@ export class AccountService {
|
|
|
209
229
|
|
|
210
230
|
/**
|
|
211
231
|
* Creates nullifier and secret for a new deposit
|
|
212
|
-
*
|
|
232
|
+
*
|
|
213
233
|
* @param scope - The scope of the pool to deposit into
|
|
214
234
|
* @param index - Optional index for deterministic generation
|
|
215
235
|
* @returns The nullifier, secret, and precommitment for the deposit
|
|
216
|
-
*
|
|
236
|
+
*
|
|
217
237
|
* @remarks
|
|
218
238
|
* If no index is provided, it uses the current number of accounts for the scope.
|
|
219
239
|
* The precommitment is a hash of the nullifier and secret, used in the deposit process.
|
|
220
240
|
*/
|
|
221
241
|
public createDepositSecrets(
|
|
222
242
|
scope: Hash,
|
|
223
|
-
index?: bigint
|
|
243
|
+
index?: bigint
|
|
224
244
|
): {
|
|
225
245
|
nullifier: Secret;
|
|
226
246
|
secret: Secret;
|
|
227
247
|
precommitment: Hash;
|
|
228
248
|
} {
|
|
249
|
+
if (index && index < 0n) {
|
|
250
|
+
throw AccountError.invalidIndex(index);
|
|
251
|
+
}
|
|
252
|
+
|
|
229
253
|
const accounts = this.account.poolAccounts.get(scope);
|
|
230
|
-
index = index
|
|
254
|
+
index = index ?? BigInt(accounts?.length || 0);
|
|
231
255
|
|
|
232
256
|
const nullifier = this._genDepositNullifier(scope, index);
|
|
233
257
|
const secret = this._genDepositSecret(scope, index);
|
|
@@ -238,15 +262,15 @@ export class AccountService {
|
|
|
238
262
|
|
|
239
263
|
/**
|
|
240
264
|
* Creates nullifier and secret for spending a commitment
|
|
241
|
-
*
|
|
265
|
+
*
|
|
242
266
|
* @param commitment - The commitment to spend
|
|
243
267
|
* @returns The nullifier and secret for the new commitment
|
|
244
|
-
*
|
|
268
|
+
*
|
|
245
269
|
* @remarks
|
|
246
270
|
* The index used for generating the withdrawal nullifier and secret is based on
|
|
247
271
|
* the number of children the account already has, ensuring each withdrawal has
|
|
248
272
|
* a unique nullifier.
|
|
249
|
-
*
|
|
273
|
+
*
|
|
250
274
|
* @throws {AccountError} If no account is found for the commitment
|
|
251
275
|
*/
|
|
252
276
|
public createWithdrawalSecrets(commitment: AccountCommitment): {
|
|
@@ -275,7 +299,7 @@ export class AccountService {
|
|
|
275
299
|
|
|
276
300
|
/**
|
|
277
301
|
* Adds a new pool account after depositing
|
|
278
|
-
*
|
|
302
|
+
*
|
|
279
303
|
* @param scope - The scope of the pool
|
|
280
304
|
* @param value - The deposit value
|
|
281
305
|
* @param nullifier - The nullifier used for the deposit
|
|
@@ -284,7 +308,7 @@ export class AccountService {
|
|
|
284
308
|
* @param blockNumber - The block number of the deposit
|
|
285
309
|
* @param txHash - The transaction hash of the deposit
|
|
286
310
|
* @returns The new pool account
|
|
287
|
-
*
|
|
311
|
+
*
|
|
288
312
|
* @remarks
|
|
289
313
|
* This method creates a new account with the deposit commitment and adds it to the
|
|
290
314
|
* pool accounts map under the specified scope. The commitment hash is calculated
|
|
@@ -297,7 +321,7 @@ export class AccountService {
|
|
|
297
321
|
secret: Secret,
|
|
298
322
|
label: Hash,
|
|
299
323
|
blockNumber: bigint,
|
|
300
|
-
txHash: Hex
|
|
324
|
+
txHash: Hex
|
|
301
325
|
): PoolAccount {
|
|
302
326
|
const precommitment = this._hashPrecommitment(nullifier, secret);
|
|
303
327
|
const commitment = this._hashCommitment(value, label, precommitment);
|
|
@@ -323,7 +347,7 @@ export class AccountService {
|
|
|
323
347
|
this.account.poolAccounts.get(scope)!.push(newAccount);
|
|
324
348
|
|
|
325
349
|
this.logger.info(
|
|
326
|
-
`Added new pool account with value ${value} and label ${label}
|
|
350
|
+
`Added new pool account with value ${value} and label ${label}`
|
|
327
351
|
);
|
|
328
352
|
|
|
329
353
|
return newAccount;
|
|
@@ -331,7 +355,7 @@ export class AccountService {
|
|
|
331
355
|
|
|
332
356
|
/**
|
|
333
357
|
* Adds a new commitment to the account after spending
|
|
334
|
-
*
|
|
358
|
+
*
|
|
335
359
|
* @param parentCommitment - The commitment that was spent
|
|
336
360
|
* @param value - The remaining value after spending
|
|
337
361
|
* @param nullifier - The nullifier used for spending
|
|
@@ -339,12 +363,12 @@ export class AccountService {
|
|
|
339
363
|
* @param blockNumber - The block number of the withdrawal
|
|
340
364
|
* @param txHash - The transaction hash of the withdrawal
|
|
341
365
|
* @returns The new commitment
|
|
342
|
-
*
|
|
366
|
+
*
|
|
343
367
|
* @remarks
|
|
344
368
|
* This method finds the account containing the parent commitment, creates a new
|
|
345
369
|
* commitment with the provided parameters, and adds it to the account's children.
|
|
346
370
|
* The new commitment inherits the label from the parent commitment.
|
|
347
|
-
*
|
|
371
|
+
*
|
|
348
372
|
* @throws {AccountError} If no account is found for the commitment
|
|
349
373
|
*/
|
|
350
374
|
public addWithdrawalCommitment(
|
|
@@ -353,7 +377,7 @@ export class AccountService {
|
|
|
353
377
|
nullifier: Secret,
|
|
354
378
|
secret: Secret,
|
|
355
379
|
blockNumber: bigint,
|
|
356
|
-
txHash: Hex
|
|
380
|
+
txHash: Hex
|
|
357
381
|
): AccountCommitment {
|
|
358
382
|
let foundAccount: PoolAccount | undefined;
|
|
359
383
|
let foundScope: bigint | undefined;
|
|
@@ -362,7 +386,7 @@ export class AccountService {
|
|
|
362
386
|
foundAccount = accounts.find((account) => {
|
|
363
387
|
if (account.deposit.hash === parentCommitment.hash) return true;
|
|
364
388
|
return account.children.some(
|
|
365
|
-
(child) => child.hash === parentCommitment.hash
|
|
389
|
+
(child) => child.hash === parentCommitment.hash
|
|
366
390
|
);
|
|
367
391
|
});
|
|
368
392
|
|
|
@@ -390,7 +414,7 @@ export class AccountService {
|
|
|
390
414
|
foundAccount.children.push(newCommitment);
|
|
391
415
|
|
|
392
416
|
this.logger.info(
|
|
393
|
-
`Added new commitment with value ${value} to account with label ${parentCommitment.label}
|
|
417
|
+
`Added new commitment with value ${value} to account with label ${parentCommitment.label}`
|
|
394
418
|
);
|
|
395
419
|
|
|
396
420
|
return newCommitment;
|
|
@@ -398,16 +422,16 @@ export class AccountService {
|
|
|
398
422
|
|
|
399
423
|
/**
|
|
400
424
|
* Adds a ragequit event to an existing pool account
|
|
401
|
-
*
|
|
425
|
+
*
|
|
402
426
|
* @param label - The label of the account to add the ragequit to
|
|
403
427
|
* @param ragequit - The ragequit event to add
|
|
404
428
|
* @returns The updated pool account
|
|
405
|
-
*
|
|
429
|
+
*
|
|
406
430
|
* @remarks
|
|
407
431
|
* When an account has a ragequit event, it can no longer be spent.
|
|
408
432
|
* This method finds the account with the matching label and attaches
|
|
409
433
|
* the ragequit event to it.
|
|
410
|
-
*
|
|
434
|
+
*
|
|
411
435
|
* @throws {AccountError} If no account is found with the given label
|
|
412
436
|
*/
|
|
413
437
|
public addRagequitToAccount(
|
|
@@ -444,19 +468,403 @@ export class AccountService {
|
|
|
444
468
|
}
|
|
445
469
|
|
|
446
470
|
/**
|
|
471
|
+
* Fetches deposit events for a given pool and returns a map of precommitments to their events for efficient lookup
|
|
472
|
+
*
|
|
473
|
+
* @param pool - The pool to fetch deposit events for
|
|
474
|
+
*
|
|
475
|
+
* @returns A map of precommitments to their events
|
|
476
|
+
*/
|
|
477
|
+
public async getDepositEvents(
|
|
478
|
+
pool: PoolInfo
|
|
479
|
+
): Promise<Map<Hash, DepositEvent>> {
|
|
480
|
+
try {
|
|
481
|
+
const depositEvents = await this.dataService.getDeposits(pool);
|
|
482
|
+
|
|
483
|
+
this.logger.info(`Found deposits for pool`, {
|
|
484
|
+
poolAddress: pool.address,
|
|
485
|
+
poolChainId: pool.chainId,
|
|
486
|
+
depositCount: depositEvents.length,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const depositMap = new Map<Hash, DepositEvent>();
|
|
490
|
+
for (const event of depositEvents) {
|
|
491
|
+
const existingEvent = depositMap.get(event.precommitment);
|
|
492
|
+
|
|
493
|
+
// If no existing event, or current event is older (earlier block), use current event
|
|
494
|
+
if (!existingEvent || event.blockNumber < existingEvent.blockNumber) {
|
|
495
|
+
depositMap.set(event.precommitment, event);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return depositMap;
|
|
500
|
+
} catch (error) {
|
|
501
|
+
throw EventError.depositEventError(
|
|
502
|
+
pool.chainId,
|
|
503
|
+
pool.scope,
|
|
504
|
+
error as Error
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Fetches withdrawal events for a given pool and returns a map of spent nullifiers to their events for efficient lookup
|
|
511
|
+
*
|
|
512
|
+
* @param pool - The pool to fetch withdrawal events for
|
|
513
|
+
*
|
|
514
|
+
* @returns A map of spent nullifiers to their events
|
|
515
|
+
*/
|
|
516
|
+
public async getWithdrawalEvents(
|
|
517
|
+
pool: PoolInfo
|
|
518
|
+
): Promise<Map<Hash, WithdrawalEvent>> {
|
|
519
|
+
try {
|
|
520
|
+
const withdrawalEvents = await this.dataService.getWithdrawals(pool);
|
|
521
|
+
const withdrawalMap = new Map<Hash, WithdrawalEvent>();
|
|
522
|
+
for (const event of withdrawalEvents) {
|
|
523
|
+
withdrawalMap.set(event.spentNullifier, event);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return withdrawalMap;
|
|
527
|
+
} catch (error) {
|
|
528
|
+
throw EventError.withdrawalEventError(
|
|
529
|
+
pool.chainId,
|
|
530
|
+
pool.scope,
|
|
531
|
+
error as Error
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Fetches ragequit events for a given pool and returns a map of ragequit labels to their events for efficient lookup
|
|
538
|
+
*
|
|
539
|
+
* @param pool - The pool to fetch ragequit events for
|
|
540
|
+
*
|
|
541
|
+
* @returns A map of ragequit labels to their events
|
|
542
|
+
*/
|
|
543
|
+
public async getRagequitEvents(
|
|
544
|
+
pool: PoolInfo
|
|
545
|
+
): Promise<Map<Hash, RagequitEvent>> {
|
|
546
|
+
try {
|
|
547
|
+
const ragequitEvents = await this.dataService.getRagequits(pool);
|
|
548
|
+
const ragequitMap = new Map<Hash, RagequitEvent>();
|
|
549
|
+
for (const event of ragequitEvents) {
|
|
550
|
+
ragequitMap.set(event.label, event);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return ragequitMap;
|
|
554
|
+
} catch (error) {
|
|
555
|
+
throw EventError.ragequitEventError(
|
|
556
|
+
pool.chainId,
|
|
557
|
+
pool.scope,
|
|
558
|
+
error as Error
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Fetches events for a given set of pools
|
|
565
|
+
*
|
|
566
|
+
* @param pools - The pools to fetch events for
|
|
567
|
+
*
|
|
568
|
+
* @returns A map of pool scopes to their events
|
|
569
|
+
*/
|
|
570
|
+
public async getEvents(pools: PoolInfo[]): Promise<PoolEventsResult> {
|
|
571
|
+
const events: PoolEventsResult = new Map();
|
|
572
|
+
|
|
573
|
+
const poolEventResults = await Promise.allSettled(
|
|
574
|
+
pools.map(async (pool) => {
|
|
575
|
+
this.logger.info(`Fetching events for pool`, {
|
|
576
|
+
poolAddress: pool.address,
|
|
577
|
+
poolChainId: pool.chainId,
|
|
578
|
+
poolDeploymentBlock: pool.deploymentBlock,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
const [depositEvents, withdrawalEvents, ragequitEvents] =
|
|
582
|
+
await Promise.all([
|
|
583
|
+
this.getDepositEvents(pool),
|
|
584
|
+
this.getWithdrawalEvents(pool),
|
|
585
|
+
this.getRagequitEvents(pool),
|
|
586
|
+
]);
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
scope: pool.scope,
|
|
590
|
+
depositEvents,
|
|
591
|
+
withdrawalEvents,
|
|
592
|
+
ragequitEvents,
|
|
593
|
+
};
|
|
594
|
+
})
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
for (const result of poolEventResults) {
|
|
598
|
+
if (result.status === "fulfilled") {
|
|
599
|
+
const { scope, depositEvents, withdrawalEvents, ragequitEvents } =
|
|
600
|
+
result.value;
|
|
601
|
+
events.set(scope, {
|
|
602
|
+
depositEvents,
|
|
603
|
+
withdrawalEvents,
|
|
604
|
+
ragequitEvents,
|
|
605
|
+
});
|
|
606
|
+
} else {
|
|
607
|
+
events.set(result.reason.details?.scope as Hash, {
|
|
608
|
+
reason: result.reason.message,
|
|
609
|
+
scope: result.reason.details?.scope as Hash,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return events;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Processes deposit events for a given scope and adds them to the account
|
|
619
|
+
* Deterministically generate deposit secrets and check if they match on-chain deposits
|
|
620
|
+
*
|
|
621
|
+
* @param scope - The scope of the pool
|
|
622
|
+
* @param depositEvents - The map of deposit events
|
|
623
|
+
*
|
|
624
|
+
*/
|
|
625
|
+
private _processDepositEvents(
|
|
626
|
+
scope: Hash,
|
|
627
|
+
depositEvents: Map<Hash, DepositEvent>
|
|
628
|
+
): void {
|
|
629
|
+
const MAX_CONSECUTIVE_MISSES = 10; // Large enough to avoid tx failures
|
|
630
|
+
|
|
631
|
+
const foundIndices = new Set<bigint>();
|
|
632
|
+
let consecutiveMisses = 0;
|
|
633
|
+
|
|
634
|
+
for (let index = BigInt(0); ; index++) {
|
|
635
|
+
// Generate nullifier, secret, and precommitment for this index
|
|
636
|
+
const { nullifier, secret, precommitment } = this.createDepositSecrets(
|
|
637
|
+
scope,
|
|
638
|
+
index
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
// Look for a deposit with this precommitment
|
|
642
|
+
const event = depositEvents.get(precommitment);
|
|
643
|
+
|
|
644
|
+
if (!event) {
|
|
645
|
+
consecutiveMisses++;
|
|
646
|
+
if (consecutiveMisses >= MAX_CONSECUTIVE_MISSES) {
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Can reset counter in case if user had any tx failures for
|
|
653
|
+
// newer deposits
|
|
654
|
+
consecutiveMisses = 0;
|
|
655
|
+
foundIndices.add(index);
|
|
656
|
+
|
|
657
|
+
// Create a new pool account for this deposit
|
|
658
|
+
this.addPoolAccount(
|
|
659
|
+
scope,
|
|
660
|
+
event.value,
|
|
661
|
+
nullifier,
|
|
662
|
+
secret,
|
|
663
|
+
event.label,
|
|
664
|
+
event.blockNumber,
|
|
665
|
+
event.transactionHash
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
this.logger.debug(`Found deposit at index ${index} for scope ${scope}`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Processes withdrawal events for a given scope and adds them to the account
|
|
674
|
+
*
|
|
675
|
+
* @param scope - The scope of the pool
|
|
676
|
+
* @param withdrawalEvents - The map of withdrawal events
|
|
677
|
+
*
|
|
678
|
+
* @remarks
|
|
679
|
+
* This method performs the following steps for each pool:
|
|
680
|
+
* 1. Identifies the earliest deposit block for each scope
|
|
681
|
+
* 2. For each account, reconstructs the withdrawal history by:
|
|
682
|
+
* - Generating nullifiers sequentially
|
|
683
|
+
* - Matching them against on-chain events
|
|
684
|
+
* - Adding matched withdrawals to the account state
|
|
685
|
+
*
|
|
686
|
+
* @throws {DataError} If event fetching fails
|
|
687
|
+
* @private
|
|
688
|
+
*
|
|
689
|
+
*/
|
|
690
|
+
private _processWithdrawalEvents(
|
|
691
|
+
scope: Hash,
|
|
692
|
+
withdrawalEvents: Map<Hash, WithdrawalEvent>
|
|
693
|
+
): void {
|
|
694
|
+
const accounts = this.account.poolAccounts.get(scope);
|
|
695
|
+
|
|
696
|
+
// Skip if no accounts for this scope
|
|
697
|
+
if (!accounts || accounts.length === 0) {
|
|
698
|
+
this.logger.info(`No accounts found for pool with this scope`, {
|
|
699
|
+
scope,
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Process each account in parallel for better performance
|
|
706
|
+
for (const account of accounts) {
|
|
707
|
+
let currentCommitment = account.deposit;
|
|
708
|
+
let index = BigInt(0);
|
|
709
|
+
|
|
710
|
+
// Continue processing withdrawals until no more are found secuentially
|
|
711
|
+
while (true) {
|
|
712
|
+
// Generate nullifier for this withdrawal
|
|
713
|
+
const nullifierHash = poseidon([currentCommitment.nullifier]) as Hash;
|
|
714
|
+
|
|
715
|
+
// Look for a withdrawal event with this nullifier
|
|
716
|
+
const withdrawal = withdrawalEvents.get(nullifierHash);
|
|
717
|
+
if (!withdrawal) {
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Generate secret for this withdrawal
|
|
722
|
+
const nullifier = this._genWithdrawalNullifier(account.label, index);
|
|
723
|
+
const secret = this._genWithdrawalSecret(account.label, index);
|
|
724
|
+
|
|
725
|
+
// Add the withdrawal commitment to the account
|
|
726
|
+
const newCommitment = this.addWithdrawalCommitment(
|
|
727
|
+
currentCommitment,
|
|
728
|
+
currentCommitment.value - withdrawal.withdrawn,
|
|
729
|
+
nullifier,
|
|
730
|
+
secret,
|
|
731
|
+
withdrawal.blockNumber,
|
|
732
|
+
withdrawal.transactionHash
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
// Update current commitment to the newly created one
|
|
736
|
+
currentCommitment = newCommitment;
|
|
737
|
+
|
|
738
|
+
// Increment index for next potential withdrawal
|
|
739
|
+
index++;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Processes ragequit events for a given scope and adds them to the account
|
|
746
|
+
*
|
|
747
|
+
* @param scope - The scope of the pool
|
|
748
|
+
* @param ragequitEvents - The map of ragequit events
|
|
749
|
+
*
|
|
750
|
+
* @remarks
|
|
751
|
+
* This method performs the following steps for each pool:
|
|
752
|
+
* 1. Adds ragequit events to accounts if found
|
|
753
|
+
*
|
|
754
|
+
* @throws {DataError} If event fetching fails
|
|
755
|
+
* @private
|
|
756
|
+
*
|
|
757
|
+
*/
|
|
758
|
+
private _processRagequitEvents(
|
|
759
|
+
scope: Hash,
|
|
760
|
+
ragequitEvents: Map<Hash, RagequitEvent>
|
|
761
|
+
): void {
|
|
762
|
+
const accounts = this.account.poolAccounts.get(scope);
|
|
763
|
+
|
|
764
|
+
if (!accounts || accounts.length === 0) {
|
|
765
|
+
this.logger.info(`No accounts found for pool with this scope`, {
|
|
766
|
+
scope,
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
for (const account of accounts) {
|
|
773
|
+
const ragequit = ragequitEvents.get(account.label);
|
|
774
|
+
if (ragequit) {
|
|
775
|
+
this.addRagequitToAccount(account.label, ragequit);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Initializes an AccountService instance with events for a given set of pools
|
|
782
|
+
*
|
|
783
|
+
* @param dataService - The data service to use for fetching events
|
|
784
|
+
* @param source - The source to use for initializing the account. Either a mnemonic or an existing account service instance
|
|
785
|
+
* @param pools - The pools to fetch events for
|
|
786
|
+
*
|
|
787
|
+
* @remarks
|
|
788
|
+
* This method performs the following steps for each pool:
|
|
789
|
+
* 1. Fetches deposit, withdrawal, and ragequit events for each pool
|
|
790
|
+
* 2. Processes deposit events and creates pool accounts
|
|
791
|
+
* 3. Processes withdrawal events and adds commitments to pool accounts
|
|
792
|
+
* 4. Processes ragequit events and adds ragequit to pool accounts
|
|
793
|
+
*
|
|
794
|
+
* @returns The initialized AccountService instance and array of errors if any pool events fetching fails
|
|
795
|
+
*
|
|
796
|
+
* if any pool events fetching fails, the account will be initialized without the events for that pool
|
|
797
|
+
* user can then call to this method again with the same account and missing pools to fetch the missing events
|
|
798
|
+
*
|
|
799
|
+
* @throws {AccountError} If account state reconstruction fails or if duplicate pools are found
|
|
800
|
+
*/
|
|
801
|
+
static async initializeWithEvents(
|
|
802
|
+
dataService: DataService,
|
|
803
|
+
source:
|
|
804
|
+
| {
|
|
805
|
+
mnemonic: string;
|
|
806
|
+
}
|
|
807
|
+
| {
|
|
808
|
+
service: AccountService;
|
|
809
|
+
},
|
|
810
|
+
pools: PoolInfo[]
|
|
811
|
+
): Promise<{ account: AccountService; errors: PoolEventsError[] }> {
|
|
812
|
+
// Log the start of the history retrieval process
|
|
813
|
+
const logger = new Logger({ prefix: "Account" });
|
|
814
|
+
logger.info(`Fetching events for pools`, { poolLength: pools.length });
|
|
815
|
+
|
|
816
|
+
// verify that pools don't contain duplicates based on scope
|
|
817
|
+
const uniqueScopes = new Set<bigint>();
|
|
818
|
+
for (const pool of pools) {
|
|
819
|
+
if (uniqueScopes.has(pool.scope)) {
|
|
820
|
+
throw AccountError.duplicatePools(pool.scope);
|
|
821
|
+
}
|
|
822
|
+
uniqueScopes.add(pool.scope);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const errors: PoolEventsError[] = [];
|
|
826
|
+
const account = new AccountService(
|
|
827
|
+
dataService,
|
|
828
|
+
"mnemonic" in source
|
|
829
|
+
? { mnemonic: source.mnemonic }
|
|
830
|
+
: { account: source.service.account }
|
|
831
|
+
);
|
|
832
|
+
|
|
833
|
+
const events = await account.getEvents(pools);
|
|
834
|
+
|
|
835
|
+
for (const [scope, result] of events.entries()) {
|
|
836
|
+
if ("reason" in result) {
|
|
837
|
+
errors.push(result);
|
|
838
|
+
} else {
|
|
839
|
+
// Process deposit events an create pool accounts
|
|
840
|
+
account._processDepositEvents(scope, result.depositEvents);
|
|
841
|
+
|
|
842
|
+
// Process withdrawal events and add commitments to pool accounts
|
|
843
|
+
account._processWithdrawalEvents(scope, result.withdrawalEvents);
|
|
844
|
+
|
|
845
|
+
// Process ragequit events and add ragequit to pool accounts
|
|
846
|
+
account._processRagequitEvents(scope, result.ragequitEvents);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return { account, errors };
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* @deprecated Use `initializeWithEvents` for instantiating an account with history reconstruction
|
|
447
855
|
* Retrieves the history of deposits and withdrawals for the given pools.
|
|
448
|
-
*
|
|
856
|
+
*
|
|
449
857
|
* @param pools - Array of pool configurations to sync history for
|
|
450
|
-
*
|
|
858
|
+
*
|
|
451
859
|
* @remarks
|
|
452
860
|
* This method performs the following steps:
|
|
453
861
|
* 1. Initializes pool accounts for each pool if they don't exist
|
|
454
862
|
* 2. For each pool, fetches deposit events and reconstructs accounts
|
|
455
863
|
* 3. Processes withdrawals and ragequits to update account state
|
|
456
|
-
*
|
|
864
|
+
*
|
|
457
865
|
* The account reconstruction is deterministic based on the master keys,
|
|
458
866
|
* allowing the full state to be recovered from on-chain events.
|
|
459
|
-
*
|
|
867
|
+
*
|
|
460
868
|
* @throws {DataError} If event fetching fails
|
|
461
869
|
* @throws {AccountError} If account state reconstruction fails
|
|
462
870
|
*/
|
|
@@ -476,14 +884,14 @@ export class AccountService {
|
|
|
476
884
|
pools.map(async (pool) => {
|
|
477
885
|
// Log which pool is being processed
|
|
478
886
|
this.logger.info(
|
|
479
|
-
`Processing pool ${pool.address} on chain ${pool.chainId} from block ${pool.deploymentBlock}
|
|
887
|
+
`Processing pool ${pool.address} on chain ${pool.chainId} from block ${pool.deploymentBlock}`
|
|
480
888
|
);
|
|
481
889
|
|
|
482
890
|
// Fetch all deposit events for this pool
|
|
483
891
|
const deposits = await this.dataService.getDeposits(pool);
|
|
484
892
|
|
|
485
893
|
this.logger.info(
|
|
486
|
-
`Found ${deposits.length} deposits for pool ${pool.address}
|
|
894
|
+
`Found ${deposits.length} deposits for pool ${pool.address}`
|
|
487
895
|
);
|
|
488
896
|
|
|
489
897
|
// Create a map of deposits by precommitment for efficient lookup
|
|
@@ -531,7 +939,7 @@ export class AccountService {
|
|
|
531
939
|
secret,
|
|
532
940
|
deposit.label,
|
|
533
941
|
deposit.blockNumber,
|
|
534
|
-
deposit.transactionHash
|
|
942
|
+
deposit.transactionHash
|
|
535
943
|
);
|
|
536
944
|
|
|
537
945
|
// Track the found deposit
|
|
@@ -544,13 +952,13 @@ export class AccountService {
|
|
|
544
952
|
// If no accounts were found for this scope, log and skip further processing
|
|
545
953
|
if (this.account.poolAccounts.get(pool.scope)!.length === 0) {
|
|
546
954
|
this.logger.info(
|
|
547
|
-
`No Pool Accounts were found for scope ${pool.scope}
|
|
955
|
+
`No Pool Accounts were found for scope ${pool.scope}`
|
|
548
956
|
);
|
|
549
957
|
return;
|
|
550
958
|
}
|
|
551
959
|
|
|
552
960
|
this.logger.info(
|
|
553
|
-
`Found ${foundDeposits.length} deposits for pool ${pool.address}
|
|
961
|
+
`Found ${foundDeposits.length} deposits for pool ${pool.address}`
|
|
554
962
|
);
|
|
555
963
|
})
|
|
556
964
|
);
|
|
@@ -562,9 +970,9 @@ export class AccountService {
|
|
|
562
970
|
|
|
563
971
|
/**
|
|
564
972
|
* Processes withdrawal events for all pools and updates account state.
|
|
565
|
-
*
|
|
973
|
+
*
|
|
566
974
|
* @param pools - Array of pool configurations to process withdrawals for
|
|
567
|
-
*
|
|
975
|
+
*
|
|
568
976
|
* @remarks
|
|
569
977
|
* This method performs the following steps for each pool:
|
|
570
978
|
* 1. Identifies the earliest deposit block for each scope
|
|
@@ -575,7 +983,7 @@ export class AccountService {
|
|
|
575
983
|
* - Matching them against on-chain events
|
|
576
984
|
* - Adding matched withdrawals to the account state
|
|
577
985
|
* 5. Adds ragequit events to accounts if found
|
|
578
|
-
*
|
|
986
|
+
*
|
|
579
987
|
* @throws {DataError} If event fetching fails
|
|
580
988
|
* @private
|
|
581
989
|
*/
|
|
@@ -603,8 +1011,14 @@ export class AccountService {
|
|
|
603
1011
|
}
|
|
604
1012
|
|
|
605
1013
|
// Fetch withdrawal and ragequit events from the first deposit block
|
|
606
|
-
const withdrawals = await this.dataService.getWithdrawals(
|
|
607
|
-
|
|
1014
|
+
const withdrawals = await this.dataService.getWithdrawals(
|
|
1015
|
+
pool,
|
|
1016
|
+
firstDepositBlock
|
|
1017
|
+
);
|
|
1018
|
+
const ragequits = await this.dataService.getRagequits(
|
|
1019
|
+
pool,
|
|
1020
|
+
firstDepositBlock
|
|
1021
|
+
);
|
|
608
1022
|
|
|
609
1023
|
this.logger.info(
|
|
610
1024
|
`Found ${withdrawals.length} withdrawals for pool ${pool.address}`
|
|
@@ -634,7 +1048,9 @@ export class AccountService {
|
|
|
634
1048
|
// Continue processing withdrawals until no more are found
|
|
635
1049
|
while (true) {
|
|
636
1050
|
// Generate nullifier for this withdrawal
|
|
637
|
-
const nullifierHash = poseidon([
|
|
1051
|
+
const nullifierHash = poseidon([
|
|
1052
|
+
currentCommitment.nullifier,
|
|
1053
|
+
]) as Hash;
|
|
638
1054
|
|
|
639
1055
|
// Look for a withdrawal event with this nullifier
|
|
640
1056
|
const withdrawal = withdrawalMap.get(nullifierHash);
|
|
@@ -643,7 +1059,10 @@ export class AccountService {
|
|
|
643
1059
|
}
|
|
644
1060
|
|
|
645
1061
|
// Generate secret for this withdrawal
|
|
646
|
-
const nullifier = this._genWithdrawalNullifier(
|
|
1062
|
+
const nullifier = this._genWithdrawalNullifier(
|
|
1063
|
+
account.label,
|
|
1064
|
+
index
|
|
1065
|
+
);
|
|
647
1066
|
const secret = this._genWithdrawalSecret(account.label, index);
|
|
648
1067
|
|
|
649
1068
|
// Add the withdrawal commitment to the account
|
|
@@ -671,5 +1090,4 @@ export class AccountService {
|
|
|
671
1090
|
})
|
|
672
1091
|
);
|
|
673
1092
|
}
|
|
674
|
-
|
|
675
1093
|
}
|