@invisible-labs/sdk 0.6.0-devnet.1

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.
Files changed (54) hide show
  1. package/README.md +210 -0
  2. package/dist/chunk-7DSVN3MV.js +1145 -0
  3. package/dist/chunk-7DSVN3MV.js.map +1 -0
  4. package/dist/chunk-JNNVPZEU.js +599 -0
  5. package/dist/chunk-JNNVPZEU.js.map +1 -0
  6. package/dist/chunk-JRMKCE3S.js +50 -0
  7. package/dist/chunk-JRMKCE3S.js.map +1 -0
  8. package/dist/chunk-LBC7ALYO.js +3783 -0
  9. package/dist/chunk-LBC7ALYO.js.map +1 -0
  10. package/dist/chunk-NCY4FCYU.js +1911 -0
  11. package/dist/chunk-NCY4FCYU.js.map +1 -0
  12. package/dist/chunk-OHIM2YWU.js +126 -0
  13. package/dist/chunk-OHIM2YWU.js.map +1 -0
  14. package/dist/chunk-SZAYO2L5.js +123 -0
  15. package/dist/chunk-SZAYO2L5.js.map +1 -0
  16. package/dist/chunk-TQNNTV5F.js +17 -0
  17. package/dist/chunk-TQNNTV5F.js.map +1 -0
  18. package/dist/chunk-VR6T6OJS.js +24 -0
  19. package/dist/chunk-VR6T6OJS.js.map +1 -0
  20. package/dist/chunk-XUWBGET5.js +221 -0
  21. package/dist/chunk-XUWBGET5.js.map +1 -0
  22. package/dist/coordinator-7Y45MCCZ.js +4 -0
  23. package/dist/coordinator-7Y45MCCZ.js.map +1 -0
  24. package/dist/createSession-D3ym-Ira.d.ts +177 -0
  25. package/dist/dkgWorker.d.ts +2 -0
  26. package/dist/dkgWorker.js +61 -0
  27. package/dist/dkgWorker.js.map +1 -0
  28. package/dist/events.d.ts +54 -0
  29. package/dist/events.js +143 -0
  30. package/dist/events.js.map +1 -0
  31. package/dist/frostRuntime-JESDHN6O.js +4 -0
  32. package/dist/frostRuntime-JESDHN6O.js.map +1 -0
  33. package/dist/index.d.ts +499 -0
  34. package/dist/index.js +132 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/lp.d.ts +388 -0
  37. package/dist/lp.js +1711 -0
  38. package/dist/lp.js.map +1 -0
  39. package/dist/presets.d.ts +43 -0
  40. package/dist/presets.js +70 -0
  41. package/dist/presets.js.map +1 -0
  42. package/dist/session-DFuy54C-.d.ts +31 -0
  43. package/dist/stats.d.ts +47 -0
  44. package/dist/stats.js +21 -0
  45. package/dist/stats.js.map +1 -0
  46. package/dist/storage.d.ts +82 -0
  47. package/dist/storage.js +246 -0
  48. package/dist/storage.js.map +1 -0
  49. package/dist/types-ZhV7TIQY.d.ts +76 -0
  50. package/dist/types.generated-CHGSbmLp.d.ts +67 -0
  51. package/dist/user.d.ts +377 -0
  52. package/dist/user.js +1225 -0
  53. package/dist/user.js.map +1 -0
  54. package/package.json +82 -0
package/dist/lp.js ADDED
@@ -0,0 +1,1711 @@
1
+ import { isValidLpPositionId, LP_DEFAULT_REFILL_BATCH_SIZE, LP_DEFAULT_TARGET_SHARDS, LP_MAX_TARGET_SHARDS, LP_MAX_REFILL_BATCH_SIZE } from './chunk-TQNNTV5F.js';
2
+ import { publicKey, assertAttested, lamports } from './chunk-SZAYO2L5.js';
3
+ import { requestEnvelope } from './chunk-XUWBGET5.js';
4
+ import { getSessionState } from './chunk-JRMKCE3S.js';
5
+ import { CommandError, PolicyValidationError, RoutingError, LpLifecycleError } from './chunk-OHIM2YWU.js';
6
+ import './chunk-VR6T6OJS.js';
7
+
8
+ // src/lp/positionCode.ts
9
+ var LEGACY_CODE_PREFIX = "ilp1";
10
+ var CODE_PREFIX = "ilp2";
11
+ var SECRET_HEX_LENGTH = 64;
12
+ var encoder = new TextEncoder();
13
+ async function generateLpPositionCode(lpPositionId) {
14
+ validateLpPositionId(lpPositionId);
15
+ const secret = new Uint8Array(32);
16
+ getCrypto().getRandomValues(secret);
17
+ const keyPair = await getCrypto().subtle.generateKey({ name: "Ed25519" }, true, [
18
+ "sign",
19
+ "verify"
20
+ ]);
21
+ const [privatePkcs8, publicRaw] = await Promise.all([
22
+ getCrypto().subtle.exportKey("pkcs8", keyPair.privateKey),
23
+ getCrypto().subtle.exportKey("raw", keyPair.publicKey)
24
+ ]);
25
+ const code = encodeCode(
26
+ lpPositionId,
27
+ bytesToHex(secret),
28
+ bytesToHex(new Uint8Array(privatePkcs8)),
29
+ bytesToHex(new Uint8Array(publicRaw))
30
+ );
31
+ secret.fill(0);
32
+ return deriveLpPositionCodeAuth(code);
33
+ }
34
+ async function deriveLpPositionCodeAuth(input) {
35
+ const code = normalizeLpPositionCode(input);
36
+ const parsed = parseCode(code);
37
+ const secret = hexToBytes(parsed.secretHex);
38
+ try {
39
+ const authPublicKey = parsed.authPublicKeyHex ? hexToBytes(parsed.authPublicKeyHex) : new Uint8Array();
40
+ const authPrivateKeyPkcs8 = parsed.authPrivateKeyPkcs8Hex ? hexToBytes(parsed.authPrivateKeyPkcs8Hex) : new Uint8Array();
41
+ if (authPublicKey.length > 0 || authPrivateKeyPkcs8.length > 0) {
42
+ await assertAuthKeyPair(authPrivateKeyPkcs8, authPublicKey);
43
+ }
44
+ const refillPublicKey = await deriveBytes(
45
+ "invisible-lp-refill-public-key-v0",
46
+ parsed.lpPositionId,
47
+ secret
48
+ );
49
+ const withdrawalCommitment = await deriveBytes(
50
+ "invisible-lp-redemption-commitment-v0",
51
+ parsed.lpPositionId,
52
+ secret
53
+ );
54
+ return {
55
+ code,
56
+ lpPositionId: parsed.lpPositionId,
57
+ authPublicKey,
58
+ authPrivateKeyPkcs8,
59
+ refillPublicKey,
60
+ withdrawalCommitment
61
+ };
62
+ } finally {
63
+ secret.fill(0);
64
+ }
65
+ }
66
+ function normalizeLpPositionCode(input) {
67
+ return input.trim();
68
+ }
69
+ function bytesToHex(bytes) {
70
+ return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
71
+ }
72
+ async function signLpCommandBytes(auth, bytes) {
73
+ if (auth.authPrivateKeyPkcs8.length === 0 || auth.authPublicKey.length === 0) {
74
+ throw new Error("LP Position Code cannot sign owner commands; create a new ilp2 code");
75
+ }
76
+ const privateKey = await getCrypto().subtle.importKey(
77
+ "pkcs8",
78
+ bufferSource(auth.authPrivateKeyPkcs8),
79
+ { name: "Ed25519" },
80
+ false,
81
+ ["sign"]
82
+ );
83
+ return new Uint8Array(
84
+ await getCrypto().subtle.sign({ name: "Ed25519" }, privateKey, bufferSource(bytes))
85
+ );
86
+ }
87
+ function encodeCode(lpPositionId, secretHex, authPrivateKeyPkcs8Hex, authPublicKeyHex) {
88
+ return `${CODE_PREFIX}:${lpPositionId}:${secretHex}:${authPrivateKeyPkcs8Hex}:${authPublicKeyHex}`;
89
+ }
90
+ function parseCode(code) {
91
+ const parts = code.split(":");
92
+ if (parts.length === 3 && parts[0] === LEGACY_CODE_PREFIX) {
93
+ const [, lpPositionId2, secretHex2] = parts;
94
+ validateLpPositionId(lpPositionId2);
95
+ if (!new RegExp(`^[0-9a-fA-F]{${SECRET_HEX_LENGTH}}$`).test(secretHex2)) {
96
+ throw new Error("LP Position Code secret must be 32 bytes hex");
97
+ }
98
+ return { lpPositionId: lpPositionId2, secretHex: secretHex2.toLowerCase() };
99
+ }
100
+ if (parts.length !== 5 || parts[0] !== CODE_PREFIX) {
101
+ throw new Error("LP Position Code must use the ilp2 format");
102
+ }
103
+ const [, lpPositionId, secretHex, authPrivateKeyPkcs8Hex, authPublicKeyHex] = parts;
104
+ validateLpPositionId(lpPositionId);
105
+ if (!new RegExp(`^[0-9a-fA-F]{${SECRET_HEX_LENGTH}}$`).test(secretHex)) {
106
+ throw new Error("LP Position Code secret must be 32 bytes hex");
107
+ }
108
+ if (!/^[0-9a-fA-F]+$/.test(authPrivateKeyPkcs8Hex) || authPrivateKeyPkcs8Hex.length < 64) {
109
+ throw new Error("LP Position Code auth private key must be pkcs8 hex");
110
+ }
111
+ if (!new RegExp(`^[0-9a-fA-F]{${SECRET_HEX_LENGTH}}$`).test(authPublicKeyHex)) {
112
+ throw new Error("LP Position Code auth public key must be 32 bytes hex");
113
+ }
114
+ return {
115
+ lpPositionId,
116
+ secretHex: secretHex.toLowerCase(),
117
+ authPrivateKeyPkcs8Hex: authPrivateKeyPkcs8Hex.toLowerCase(),
118
+ authPublicKeyHex: authPublicKeyHex.toLowerCase()
119
+ };
120
+ }
121
+ function validateLpPositionId(lpPositionId) {
122
+ if (!isValidLpPositionId(lpPositionId)) {
123
+ throw new Error("LP Position Code has an invalid position id");
124
+ }
125
+ }
126
+ async function deriveBytes(domain, lpPositionId, secret) {
127
+ const domainBytes = encoder.encode(`${domain}:${lpPositionId}:`);
128
+ const material = new Uint8Array(domainBytes.length + secret.length);
129
+ material.set(domainBytes);
130
+ material.set(secret, domainBytes.length);
131
+ const digest = await getCrypto().subtle.digest("SHA-256", material);
132
+ material.fill(0);
133
+ return new Uint8Array(digest);
134
+ }
135
+ async function assertAuthKeyPair(privatePkcs8, publicRaw) {
136
+ if (publicRaw.length !== 32) {
137
+ throw new Error("LP Position Code auth public key must be 32 bytes");
138
+ }
139
+ const [privateKey, publicKey2] = await Promise.all([
140
+ getCrypto().subtle.importKey("pkcs8", bufferSource(privatePkcs8), { name: "Ed25519" }, false, [
141
+ "sign"
142
+ ]),
143
+ getCrypto().subtle.importKey("raw", bufferSource(publicRaw), { name: "Ed25519" }, false, [
144
+ "verify"
145
+ ])
146
+ ]);
147
+ const challenge = encoder.encode("invisible-lp-auth-self-test");
148
+ const signature = await getCrypto().subtle.sign({ name: "Ed25519" }, privateKey, challenge);
149
+ const ok = await getCrypto().subtle.verify({ name: "Ed25519" }, publicKey2, signature, challenge);
150
+ if (!ok) {
151
+ throw new Error("LP Position Code auth key pair is invalid");
152
+ }
153
+ }
154
+ function bufferSource(bytes) {
155
+ return new Uint8Array(bytes).buffer;
156
+ }
157
+ function hexToBytes(hex) {
158
+ const bytes = new Uint8Array(hex.length / 2);
159
+ for (let index = 0; index < bytes.length; index += 1) {
160
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
161
+ }
162
+ return bytes;
163
+ }
164
+ function getCrypto() {
165
+ if (globalThis.crypto === void 0 || globalThis.crypto.subtle === void 0) {
166
+ throw new Error("Web Crypto is required for LP Position Code handling");
167
+ }
168
+ return globalThis.crypto;
169
+ }
170
+
171
+ // src/lp/withdrawalValidation.ts
172
+ var SINGLE_DESTINATION_WITHDRAWAL_MESSAGE = "single-destination withdrawal is privacy-degrading; pass allowManyToOne: true to acknowledge";
173
+ var DESTINATION_SOURCE_COUNT_MESSAGE = "LP withdrawal requires one destination per withdrawable source unless allowManyToOne is true";
174
+ var SOURCE_COUNT_MESSAGE = "LP withdrawal source count must be a positive integer";
175
+ function validateLpWithdrawalPlan(input) {
176
+ const {
177
+ depositPrincipalLamports,
178
+ earnedFeeLamports,
179
+ destinationAddresses,
180
+ allowManyToOne,
181
+ sourceCount
182
+ } = input;
183
+ lamports(depositPrincipalLamports);
184
+ lamports(earnedFeeLamports);
185
+ if (depositPrincipalLamports === 0 && earnedFeeLamports === 0) {
186
+ throw new PolicyValidationError(
187
+ "INVALID_AMOUNT",
188
+ "LP withdrawal requires a non-zero principal or earned-fee claim"
189
+ );
190
+ }
191
+ validateDestinationAddresses(destinationAddresses);
192
+ if (sourceCount !== void 0 && (!Number.isInteger(sourceCount) || sourceCount < 1)) {
193
+ throw new PolicyValidationError("INVALID_WITHDRAWAL_PLAN", SOURCE_COUNT_MESSAGE);
194
+ }
195
+ if (sourceCount !== void 0 && allowManyToOne !== true && destinationAddresses.length !== sourceCount) {
196
+ throw new PolicyValidationError("INVALID_WITHDRAWAL_PLAN", DESTINATION_SOURCE_COUNT_MESSAGE);
197
+ }
198
+ if (destinationAddresses.length === 1 && sourceCount !== 1 && allowManyToOne !== true) {
199
+ throw new PolicyValidationError(
200
+ "INVALID_WITHDRAWAL_PLAN",
201
+ SINGLE_DESTINATION_WITHDRAWAL_MESSAGE
202
+ );
203
+ }
204
+ }
205
+ function validateDestinationAddresses(destinationAddresses) {
206
+ if (destinationAddresses.length === 0) {
207
+ throw new PolicyValidationError(
208
+ "INVALID_WITHDRAWAL_PLAN",
209
+ "at least one destination address is required"
210
+ );
211
+ }
212
+ const seen = /* @__PURE__ */ new Set();
213
+ for (const address of destinationAddresses) {
214
+ publicKey(address);
215
+ if (seen.has(address)) {
216
+ throw new PolicyValidationError(
217
+ "INVALID_WITHDRAWAL_PLAN",
218
+ `duplicate withdrawal destination: ${address}`
219
+ );
220
+ }
221
+ seen.add(address);
222
+ }
223
+ }
224
+
225
+ // src/lp/coordinatorClient.ts
226
+ var COMMAND_TTL_MS = 3e4;
227
+ var LP_COMMAND_REQUEST_TIMEOUT_MS = 12e4;
228
+ var encoder2 = new TextEncoder();
229
+ var protocolPromise = null;
230
+ function createLpCoordinatorClient(session) {
231
+ return {
232
+ initDkg(input, auth) {
233
+ return initDkg(session, input, auth);
234
+ },
235
+ dkgRound1(input, auth) {
236
+ return dkgRound1(session, input, auth);
237
+ },
238
+ dkgRound2(input, auth) {
239
+ return dkgRound2(session, input, auth);
240
+ },
241
+ openPosition(input, auth) {
242
+ return openPosition(session, input, auth);
243
+ },
244
+ getPosition(lpPositionId, auth) {
245
+ return getPosition(session, lpPositionId, auth);
246
+ },
247
+ finalizeDkgBatch(input, auth) {
248
+ return finalizeDkgBatch(session, input, auth);
249
+ },
250
+ refillPosition(input, auth) {
251
+ return refillPosition(session, input, auth);
252
+ },
253
+ reconcileFunding(lpPositionId, auth) {
254
+ return reconcileFunding(session, lpPositionId, auth);
255
+ },
256
+ planFunding(input, auth) {
257
+ return planFunding(session, input, auth);
258
+ },
259
+ confirmFunding(input, auth) {
260
+ return confirmFunding(session, input, auth);
261
+ },
262
+ prepareTopUp(lpPositionId, auth) {
263
+ return prepareTopUp(session, lpPositionId, auth);
264
+ },
265
+ getTopUpStatus(input, auth) {
266
+ return getTopUpStatus(session, input, auth);
267
+ },
268
+ makeAvailable(input, auth) {
269
+ return makeAvailable(session, input, auth);
270
+ },
271
+ executeWithdrawal(input, auth) {
272
+ return executeWithdrawal(session, input, auth);
273
+ }
274
+ };
275
+ }
276
+ async function initDkg(session, input, auth) {
277
+ const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_init", {
278
+ case: "dkgInit",
279
+ value: { index: input.index }
280
+ });
281
+ if (response.case !== "dkgInit") throw new Error("Unexpected LP DKG init response");
282
+ return { sessionId: response.value.session_id };
283
+ }
284
+ async function dkgRound1(session, input, auth) {
285
+ const response = await request(session, auth, "dkg_round1", {
286
+ case: "dkgRound1",
287
+ value: {
288
+ session_id: input.sessionId,
289
+ package: Uint8Array.from(input.package)
290
+ }
291
+ });
292
+ if (response.case !== "dkgRound1") throw new Error("Unexpected LP DKG round1 response");
293
+ return {
294
+ package: response.value.package,
295
+ round2ForLp: response.value.round2_for_lp
296
+ };
297
+ }
298
+ async function dkgRound2(session, input, auth) {
299
+ const response = await request(session, auth, "dkg_round2", {
300
+ case: "dkgRound2",
301
+ value: {
302
+ session_id: input.sessionId,
303
+ package: Uint8Array.from(input.package),
304
+ delegated_share: Uint8Array.from(input.delegatedShare)
305
+ }
306
+ });
307
+ if (response.case !== "dkgRound2") throw new Error("Unexpected LP DKG round2 response");
308
+ return {
309
+ address: response.value.address,
310
+ jointPubkey: response.value.joint_pubkey,
311
+ shardId: response.value.shard_id,
312
+ index: response.value.index
313
+ };
314
+ }
315
+ async function openPosition(session, input, auth) {
316
+ const response = await requestForPosition(session, input.lpPositionId, auth, "position_open", {
317
+ case: "positionOpen",
318
+ value: {
319
+ auth_public_key: Uint8Array.from(input.authPublicKey),
320
+ refill_public_key: Uint8Array.from(input.refillPublicKey),
321
+ redemption_commitment: Uint8Array.from(input.withdrawalCommitment),
322
+ committed_lamports: input.committedLamports,
323
+ shard_count: input.shardCount,
324
+ shard_amount_lamports: input.shardAmountLamports,
325
+ refill_threshold: input.refillThreshold,
326
+ addresses: input.addresses,
327
+ created_at_ms: input.createdAtMs
328
+ }
329
+ });
330
+ return expectPosition(response);
331
+ }
332
+ async function getPosition(session, lpPositionId, auth) {
333
+ let cursor;
334
+ let position;
335
+ const seenCursors = /* @__PURE__ */ new Set();
336
+ for (; ; ) {
337
+ const response = await requestForPosition(session, lpPositionId, auth, "position_get", {
338
+ case: "positionGet",
339
+ value: cursor === void 0 ? {} : { redemption_history_cursor: cursor }
340
+ });
341
+ const page = expectPosition(response);
342
+ const mergedPosition = position === void 0 ? page : mergePositionHistoryPages(position, page);
343
+ position = mergedPosition;
344
+ const actorSync = page.actorSync;
345
+ if (actorSync?.redemptionHistoryHasMore !== true) return mergedPosition;
346
+ const nextCursor = actorSync.redemptionHistoryNextCursor;
347
+ if (nextCursor === void 0 || seenCursors.has(nextCursor)) {
348
+ throw new CommandError(
349
+ "REJECTED_STATE",
350
+ "coordinator LP actor sync returned an invalid redemption history cursor"
351
+ );
352
+ }
353
+ seenCursors.add(nextCursor);
354
+ cursor = nextCursor;
355
+ }
356
+ }
357
+ async function finalizeDkgBatch(session, input, auth) {
358
+ const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_finalize", {
359
+ case: "dkgFinalize",
360
+ value: {
361
+ start_index: input.startIndex,
362
+ addresses: input.addresses,
363
+ finalized_at_ms: input.finalizedAtMs
364
+ }
365
+ });
366
+ return expectPosition(response);
367
+ }
368
+ async function refillPosition(session, input, auth) {
369
+ const response = await requestForPosition(session, input.lpPositionId, auth, "refill", {
370
+ case: "refill",
371
+ value: {
372
+ shard_count: input.shardCount,
373
+ shard_amount_lamports: input.shardAmountLamports,
374
+ addresses: input.addresses,
375
+ authorized_at_ms: input.authorizedAtMs
376
+ }
377
+ });
378
+ return expectPosition(response);
379
+ }
380
+ async function reconcileFunding(session, lpPositionId, auth) {
381
+ const response = await requestForPosition(session, lpPositionId, auth, "reconcile_funding", {
382
+ case: "reconcileFunding",
383
+ value: {}
384
+ });
385
+ return expectPosition(response);
386
+ }
387
+ async function planFunding(session, input, auth) {
388
+ const response = await requestForPosition(session, input.lpPositionId, auth, "funding_plan", {
389
+ case: "fundingPlan",
390
+ value: {
391
+ shard_count: input.shardCount,
392
+ planned_at_ms: input.nowMs
393
+ }
394
+ });
395
+ if (response.case !== "shards") throw new Error("Unexpected LP funding plan response");
396
+ return response.value.map(mapShard);
397
+ }
398
+ async function confirmFunding(session, input, auth) {
399
+ const response = await requestForPosition(
400
+ session,
401
+ input.lpPositionId,
402
+ auth,
403
+ "funding_confirmation",
404
+ {
405
+ case: "fundingConfirmation",
406
+ value: {
407
+ shard_id: input.shardId,
408
+ amount_lamports: input.amountLamports,
409
+ funding_tx_signature: input.fundingTxSignature
410
+ }
411
+ }
412
+ );
413
+ if (response.case !== "shard") throw new Error("Unexpected LP funding confirmation response");
414
+ return mapShard(response.value);
415
+ }
416
+ async function prepareTopUp(session, lpPositionId, auth) {
417
+ const response = await requestForPosition(session, lpPositionId, auth, "prepare_top_up", {
418
+ case: "prepareTopUp",
419
+ value: {}
420
+ });
421
+ if (response.case !== "topUpRequest") throw new Error("Unexpected LP top-up response");
422
+ return mapTopUpRequest(response.value);
423
+ }
424
+ async function getTopUpStatus(session, input, auth) {
425
+ const response = await requestForPosition(session, input.lpPositionId, auth, "top_up_status", {
426
+ case: "topUpStatus",
427
+ value: { top_up_id: input.topUpId }
428
+ });
429
+ if (response.case !== "topUpRequest") throw new Error("Unexpected LP top-up response");
430
+ return mapTopUpRequest(response.value);
431
+ }
432
+ async function makeAvailable(session, input, auth) {
433
+ const response = await requestForPosition(session, input.lpPositionId, auth, "make_available", {
434
+ case: "makeAvailable",
435
+ value: {
436
+ shard_id: input.shardId
437
+ }
438
+ });
439
+ if (response.case !== "shard") throw new Error("Unexpected LP availability response");
440
+ return mapShard(response.value);
441
+ }
442
+ async function executeWithdrawal(session, input, auth) {
443
+ validateLpWithdrawalPlan({
444
+ destinationAddresses: input.destinationAddresses,
445
+ depositPrincipalLamports: input.depositPrincipalLamports,
446
+ earnedFeeLamports: input.earnedFeeLamports,
447
+ allowManyToOne: input.allowManyToOne
448
+ });
449
+ const response = await requestForPosition(session, input.lpPositionId, auth, "withdraw_request", {
450
+ case: "withdrawRequest",
451
+ value: {
452
+ destination_addresses: input.destinationAddresses,
453
+ deposit_principal_lamports: input.depositPrincipalLamports,
454
+ earned_fee_lamports: input.earnedFeeLamports,
455
+ requested_at_ms: input.requestedAtMs
456
+ }
457
+ });
458
+ if (response.case !== "withdrawalExecution") {
459
+ throw new Error("Unexpected LP withdrawal execution response");
460
+ }
461
+ return {
462
+ withdrawalId: response.value.withdrawal_id,
463
+ txSignatures: [...response.value.tx_signatures]
464
+ };
465
+ }
466
+ function request(session, auth, action, payload) {
467
+ return requestForPosition(session, auth.lpPositionId, auth, action, payload);
468
+ }
469
+ async function requestForPosition(session, lpPositionId, auth, action, payload) {
470
+ assertAuthMatchesPosition(lpPositionId, auth);
471
+ const protocol = await loadProtocol();
472
+ const state = getSessionState(session);
473
+ const attestation = state.channel?.attestation;
474
+ if (!attestation) throw new Error("LP coordinator attestation is not available");
475
+ const command = {
476
+ lp_position_id: lpPositionId,
477
+ action,
478
+ nonce: randomBytes(32),
479
+ expires_at_ms: Date.now() + COMMAND_TTL_MS,
480
+ coordinator_attestation_hash: await coordinatorAttestationHash(
481
+ attestation.binaryHash,
482
+ attestation.pubkeyHex
483
+ ),
484
+ payload
485
+ };
486
+ const signature = await signLpCommandBytes(auth, protocol.encodeLpCommandForSigning(command));
487
+ const { type, payload: envelopePayload } = protocol.lpSignedCommandEnvelope(command, signature);
488
+ const envelope = await requestEnvelope(session, type, envelopePayload, {
489
+ timeoutMs: LP_COMMAND_REQUEST_TIMEOUT_MS
490
+ });
491
+ return protocol.asLpCommandResponsePayload(envelope);
492
+ }
493
+ function assertAuthMatchesPosition(lpPositionId, auth) {
494
+ if (lpPositionId === auth.lpPositionId) return;
495
+ throw new PolicyValidationError(
496
+ "INVALID_POSITION_AUTH",
497
+ "LP Position Code does not match the target LP position"
498
+ );
499
+ }
500
+ function expectPosition(response) {
501
+ if (response.case !== "position") throw new Error("Unexpected LP position response");
502
+ return mapPosition(response.value);
503
+ }
504
+ function mapPosition(position) {
505
+ return {
506
+ id: position.lp_position_id,
507
+ status: position.status,
508
+ targetShardCount: position.target_shard_count,
509
+ authPublicKey: bytesToHex(position.auth_public_key),
510
+ refillPublicKey: bytesToHex(position.refill_public_key),
511
+ withdrawalCommitment: bytesToHex(position.redemption_commitment),
512
+ committedLamports: position.committed_lamports,
513
+ earnedLamports: position.earned_lamports,
514
+ refillThreshold: position.refill_threshold,
515
+ shards: position.shards.map(mapShard),
516
+ createdAt: position.created_at_ms,
517
+ ...position.actor_sync === void 0 ? {} : { actorSync: mapLpActorSync(position.actor_sync) }
518
+ };
519
+ }
520
+ function mapLpActorSync(actorSync) {
521
+ const redeemableBucketValues = [
522
+ actorSync.redeemable_principal_lamports,
523
+ actorSync.redeemable_fee_lamports,
524
+ actorSync.lifetime_earned_lamports,
525
+ actorSync.redeemable_principal_source_count,
526
+ actorSync.redeemable_fee_source_count
527
+ ];
528
+ const redeemableBucketCount = redeemableBucketValues.filter(
529
+ (value) => value !== void 0
530
+ ).length;
531
+ if (redeemableBucketCount > 0 && redeemableBucketCount < redeemableBucketValues.length) {
532
+ throw new CommandError(
533
+ "REJECTED_STATE",
534
+ "coordinator LP actor sync redeemable buckets must be all-or-none"
535
+ );
536
+ }
537
+ const redeemableProjectionAvailable = redeemableBucketCount === redeemableBucketValues.length;
538
+ return {
539
+ lpPositionId: actorSync.lp_position_id,
540
+ status: actorSync.status,
541
+ sourceLiquidityUse: actorSync.source_liquidity_use,
542
+ redeemableProjectionAvailable,
543
+ redeemablePrincipalLamports: actorSync.redeemable_principal_lamports ?? 0,
544
+ redeemableFeeLamports: actorSync.redeemable_fee_lamports ?? 0,
545
+ lifetimeEarnedLamports: actorSync.lifetime_earned_lamports ?? 0,
546
+ redeemablePrincipalSourceCount: actorSync.redeemable_principal_source_count ?? 0,
547
+ redeemableFeeSourceCount: actorSync.redeemable_fee_source_count ?? 0,
548
+ pendingReservationCount: actorSync.pending_reservation_count,
549
+ reservedPayoutLamports: actorSync.reserved_payout_lamports,
550
+ estimatedNetworkFeeLamports: actorSync.estimated_network_fee_lamports,
551
+ reimbursementDueCount: actorSync.reimbursement_due_count,
552
+ reimbursementDueSourceCount: actorSync.reimbursement_due_source_count,
553
+ reimbursementDuePayoutLamports: actorSync.reimbursement_due_payout_lamports,
554
+ reimbursementDueNetworkFeeLamports: actorSync.reimbursement_due_network_fee_lamports,
555
+ withdrawalRequestedLamports: actorSync.withdrawal_requested_lamports,
556
+ withdrawalLandedLamports: actorSync.withdrawal_landed_lamports,
557
+ withdrawalLandedFeeLamports: actorSync.withdrawal_landed_fee_lamports,
558
+ withdrawalCanceledLamports: actorSync.withdrawal_canceled_lamports,
559
+ withdrawalNetworkFeeLossLamports: actorSync.withdrawal_network_fee_loss_lamports,
560
+ withdrawalTxHashes: [...actorSync.withdrawal_tx_hashes],
561
+ redemptionHistory: (actorSync.redemption_history ?? []).map(mapLpRedemptionHistory),
562
+ redemptionHistoryHasMore: actorSync.redemption_history_has_more ?? false,
563
+ ...actorSync.redemption_history_next_cursor === void 0 ? {} : { redemptionHistoryNextCursor: actorSync.redemption_history_next_cursor },
564
+ stateVersion: actorSync.state_version,
565
+ updatedAtMs: actorSync.updated_at_ms
566
+ };
567
+ }
568
+ function mergePositionHistoryPages(first, next) {
569
+ if (first.actorSync === void 0) return next;
570
+ if (next.actorSync === void 0) return first;
571
+ const historyByRedemptionId = /* @__PURE__ */ new Map();
572
+ for (const history of first.actorSync.redemptionHistory ?? []) {
573
+ historyByRedemptionId.set(history.redemptionId, history);
574
+ }
575
+ for (const history of next.actorSync.redemptionHistory ?? []) {
576
+ historyByRedemptionId.set(history.redemptionId, history);
577
+ }
578
+ return {
579
+ ...first,
580
+ actorSync: {
581
+ ...first.actorSync,
582
+ ...next.actorSync,
583
+ redemptionHistory: [...historyByRedemptionId.values()]
584
+ }
585
+ };
586
+ }
587
+ function mapLpRedemptionHistory(history) {
588
+ return {
589
+ redemptionId: history.redemption_id,
590
+ state: history.state,
591
+ acceptedLamports: history.accepted_lamports,
592
+ acceptedDepositPrincipalLamports: history.accepted_deposit_principal_lamports,
593
+ acceptedEarnedFeeLamports: history.accepted_earned_fee_lamports,
594
+ landedLamports: history.landed_lamports,
595
+ landedDepositPrincipalLamports: history.landed_deposit_principal_lamports,
596
+ landedEarnedFeeLamports: history.landed_earned_fee_lamports,
597
+ canceledLamports: history.canceled_lamports,
598
+ canceledDepositPrincipalLamports: history.canceled_deposit_principal_lamports,
599
+ canceledEarnedFeeLamports: history.canceled_earned_fee_lamports,
600
+ networkFeeLamports: history.network_fee_lamports,
601
+ networkFeeLossLamports: history.network_fee_loss_lamports,
602
+ failedAttempts: history.failed_attempts,
603
+ destinationAddresses: [...history.destination_addresses],
604
+ destinationCount: history.destination_count,
605
+ txSignatures: [...history.tx_signatures],
606
+ txSignatureCount: history.tx_signature_count,
607
+ detailsTruncated: history.details_truncated,
608
+ stateVersion: history.state_version,
609
+ createdAtMs: history.created_at_ms,
610
+ updatedAtMs: history.updated_at_ms
611
+ };
612
+ }
613
+ function mapShard(shard) {
614
+ return {
615
+ kind: "lp",
616
+ lpPositionId: shard.lp_position_id,
617
+ shardId: shard.shard_id,
618
+ index: shard.index,
619
+ address: shard.address,
620
+ amountLamports: shard.amount_lamports,
621
+ ...shard.required_lamports === void 0 ? {} : { requiredLamports: shard.required_lamports },
622
+ status: mapStatus(shard.status),
623
+ fundingTxSignature: shard.funding_tx_signature,
624
+ payoutTxSignature: shard.payout_tx_signature,
625
+ coolingUntilMs: shard.cooling_until_ms
626
+ };
627
+ }
628
+ function mapTopUpRequest(request2) {
629
+ return {
630
+ topUpId: request2.top_up_id,
631
+ positionId: request2.lp_position_id,
632
+ depositAddress: request2.deposit_address,
633
+ expiresAtMs: request2.expires_at_ms,
634
+ status: request2.status
635
+ };
636
+ }
637
+ function mapStatus(status) {
638
+ switch (status) {
639
+ case "pregenerated":
640
+ return "PREGENERATED";
641
+ case "funding_queued":
642
+ return "FUNDING_QUEUED";
643
+ case "funded":
644
+ return "FUNDED";
645
+ case "cooling":
646
+ return "COOLING";
647
+ case "available":
648
+ return "AVAILABLE";
649
+ case "reserved":
650
+ return "RESERVED";
651
+ case "retired":
652
+ return "RETIRED";
653
+ case "redeemed":
654
+ return "REDEEMED";
655
+ }
656
+ }
657
+ async function coordinatorAttestationHash(binaryHash, pubkeyHex) {
658
+ const material = encoder2.encode(
659
+ `invisible-lp-attestation-v1\0${binaryHash.toLowerCase()}\0${pubkeyHex.toLowerCase()}`
660
+ );
661
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", material));
662
+ }
663
+ function randomBytes(length) {
664
+ const bytes = new Uint8Array(length);
665
+ crypto.getRandomValues(bytes);
666
+ return bytes;
667
+ }
668
+ function loadProtocol() {
669
+ protocolPromise ??= import('./coordinator-7Y45MCCZ.js');
670
+ return protocolPromise;
671
+ }
672
+
673
+ // src/lp/dkgBatch.ts
674
+ var MAX_LP_DKG_CONCURRENCY = 8;
675
+ var DEFAULT_LP_DKG_CONCURRENCY = MAX_LP_DKG_CONCURRENCY;
676
+ var LP_DKG_COMPLETED_SHARD_POLL_INTERVAL_MS = 500;
677
+ var LP_DKG_COMPLETED_SHARD_POLL_TIMEOUT_MS = 5 * 6e4 + 15e3;
678
+ var LP_DKG_ACTIVE_SESSION_RETRY_INTERVAL_MS = 2e3;
679
+ var LP_DKG_ACTIVE_SESSION_RETRY_TIMEOUT_MS = LP_DKG_COMPLETED_SHARD_POLL_TIMEOUT_MS;
680
+ async function createLpDkgBatch(input) {
681
+ if (input.useDeterministicTestAddresses === true) {
682
+ const addresses2 = Array.from(
683
+ { length: input.count },
684
+ (_, offset) => testAddress(input.lpPositionId, input.startIndex + offset)
685
+ );
686
+ for (const [offset, address] of addresses2.entries()) {
687
+ input.onProgress?.({
688
+ completed: offset + 1,
689
+ total: input.count,
690
+ address
691
+ });
692
+ }
693
+ return addresses2;
694
+ }
695
+ const workerCount = resolveConcurrency(input.count, input.concurrency);
696
+ const workers = Array.from({ length: workerCount }, () => createWorker(input.workerFactory));
697
+ const addresses = new Array(input.count);
698
+ let completed = 0;
699
+ let nextOffset = 0;
700
+ try {
701
+ await Promise.all(
702
+ workers.map(async (worker) => {
703
+ for (; ; ) {
704
+ const offset = nextOffset;
705
+ nextOffset += 1;
706
+ if (offset >= input.count) return;
707
+ const index = input.startIndex + offset;
708
+ const address = await createLpDkgShard(input, worker, index);
709
+ addresses[offset] = address;
710
+ completed += 1;
711
+ input.onProgress?.({
712
+ completed,
713
+ total: input.count,
714
+ address
715
+ });
716
+ }
717
+ })
718
+ );
719
+ return addresses;
720
+ } finally {
721
+ workers.forEach((worker) => worker.terminate());
722
+ }
723
+ }
724
+ async function createLpDkgShard(input, worker, index) {
725
+ const deadline = Date.now() + LP_DKG_ACTIVE_SESSION_RETRY_TIMEOUT_MS;
726
+ let sawActiveSession = false;
727
+ for (; ; ) {
728
+ if (sawActiveSession) {
729
+ const completedAddress = await getCompletedShardAddress(input, index);
730
+ if (completedAddress !== null) return completedAddress;
731
+ }
732
+ try {
733
+ return await runLpDkgShard(input, worker, index);
734
+ } catch (error) {
735
+ if (isLpDkgCompletedOrVisibleError(error)) {
736
+ return waitForCompletedShardAddress(input, index);
737
+ }
738
+ if (!isLpDkgActiveSessionError(error)) throw error;
739
+ sawActiveSession = true;
740
+ const completedAddress = await getCompletedShardAddress(input, index);
741
+ if (completedAddress !== null) return completedAddress;
742
+ if (Date.now() >= deadline) throw error;
743
+ await sleep(LP_DKG_ACTIVE_SESSION_RETRY_INTERVAL_MS);
744
+ }
745
+ }
746
+ }
747
+ async function runLpDkgShard(input, worker, index) {
748
+ const roundId = `lp_dkg_${Date.now()}_${index}_${Math.random().toString(16).slice(2)}`;
749
+ const initResponse = await input.client.initDkg(
750
+ { lpPositionId: input.lpPositionId, index },
751
+ input.auth
752
+ );
753
+ const round1 = await workerRequest(worker, {
754
+ id: roundId,
755
+ type: "round1"
756
+ });
757
+ if (round1.type !== "round1") throw new Error("LP DKG worker returned wrong round1 type");
758
+ const round1Response = await input.client.dkgRound1(
759
+ {
760
+ sessionId: initResponse.sessionId,
761
+ package: round1.package
762
+ },
763
+ input.auth
764
+ );
765
+ const round2 = await workerRequest(worker, {
766
+ id: roundId,
767
+ type: "round2",
768
+ teeRound1: Array.from(round1Response.package),
769
+ teeRound2ForLp: Array.from(round1Response.round2ForLp)
770
+ });
771
+ if (round2.type !== "round2") throw new Error("LP DKG worker returned wrong round2 type");
772
+ const round2Response = await input.client.dkgRound2(
773
+ {
774
+ sessionId: initResponse.sessionId,
775
+ package: round2.package,
776
+ delegatedShare: round2.delegatedShare
777
+ },
778
+ input.auth
779
+ );
780
+ if (!bytesEqual(Uint8Array.from(round2.jointPubkey), round2Response.jointPubkey)) {
781
+ throw new Error(`LP DKG joint pubkey mismatch at index ${index}`);
782
+ }
783
+ if (round2Response.index !== index) {
784
+ throw new Error(`LP DKG index mismatch: expected ${index}, got ${round2Response.index}`);
785
+ }
786
+ return round2Response.address;
787
+ }
788
+ async function waitForCompletedShardAddress(input, index) {
789
+ const deadline = Date.now() + LP_DKG_COMPLETED_SHARD_POLL_TIMEOUT_MS;
790
+ for (; ; ) {
791
+ const address = await getCompletedShardAddress(input, index);
792
+ if (address !== null) return address;
793
+ if (Date.now() >= deadline) {
794
+ throw new Error(`LP DKG completed shard ${index} did not become visible`);
795
+ }
796
+ await sleep(LP_DKG_COMPLETED_SHARD_POLL_INTERVAL_MS);
797
+ }
798
+ }
799
+ async function getCompletedShardAddress(input, index) {
800
+ const position = await input.client.getPosition(input.lpPositionId, input.auth);
801
+ return position.shards.find((candidate) => candidate.index === index)?.address ?? null;
802
+ }
803
+ function workerRequest(worker, request2) {
804
+ return new Promise((resolve, reject) => {
805
+ const onMessage = (event) => {
806
+ const message = event.data;
807
+ if (message.id !== request2.id) return;
808
+ cleanup();
809
+ if (message.type === "error") {
810
+ reject(new Error(message.error));
811
+ } else {
812
+ resolve(message);
813
+ }
814
+ };
815
+ const onError = (event) => {
816
+ cleanup();
817
+ reject(new Error(event.message));
818
+ };
819
+ const cleanup = () => {
820
+ worker.removeEventListener("message", onMessage);
821
+ worker.removeEventListener("error", onError);
822
+ };
823
+ worker.addEventListener("message", onMessage);
824
+ worker.addEventListener("error", onError);
825
+ worker.postMessage(request2);
826
+ });
827
+ }
828
+ function createWorker(workerFactory) {
829
+ if (workerFactory !== void 0) return workerFactory();
830
+ return new Worker(new URL("./dkgWorker.js", import.meta.url), { type: "module" });
831
+ }
832
+ function bytesEqual(a, b) {
833
+ if (a.byteLength !== b.byteLength) return false;
834
+ let diff = 0;
835
+ for (let index = 0; index < a.byteLength; index += 1) {
836
+ diff |= a[index] ^ b[index];
837
+ }
838
+ return diff === 0;
839
+ }
840
+ function isLpDkgCompletedOrVisibleError(error) {
841
+ if (!(error instanceof Error) || !error.message.includes("LP DKG index")) return false;
842
+ return error.message.includes("already completed for position") || error.message.includes("already exists for position");
843
+ }
844
+ function isLpDkgActiveSessionError(error) {
845
+ return error instanceof Error && error.message.includes("LP DKG session already active for position index");
846
+ }
847
+ function resolveConcurrency(count, requested) {
848
+ if (count <= 0) return 0;
849
+ const concurrency = requested ?? DEFAULT_LP_DKG_CONCURRENCY;
850
+ if (!Number.isFinite(concurrency)) return Math.min(count, DEFAULT_LP_DKG_CONCURRENCY);
851
+ return Math.max(1, Math.min(count, MAX_LP_DKG_CONCURRENCY, Math.floor(concurrency)));
852
+ }
853
+ function sleep(delayMs) {
854
+ return new Promise((resolve) => {
855
+ globalThis.setTimeout(resolve, delayMs);
856
+ });
857
+ }
858
+ function testAddress(lpPositionId, index) {
859
+ return `${lpPositionId}Shard${String(index).padStart(5, "0")}11111111111111111111`;
860
+ }
861
+
862
+ // src/lp/lifecycle.ts
863
+ var LP_ID_PREFIX = "lp";
864
+ var DEFAULT_SHARD_FUNDING_LAMPORTS = 101e6;
865
+ var DEFAULT_REFILL_THRESHOLD_RATIO = 0.2;
866
+ var LP_DKG_ACTIVE_SESSION_RETRY_DELAY_MS = 2e3;
867
+ var LP_DKG_ACTIVE_SESSION_RETRY_TIMEOUT_MS2 = 5 * 6e4 + 15e3;
868
+ var LP_WITHDRAWAL_CONFIRMATION_POLL_INTERVAL_MS = 3e3;
869
+ var LP_WITHDRAWAL_CONFIRMATION_TIMEOUT_MS = 15 * 6e4;
870
+ var LP_TOP_UP_WAIT_POLL_INTERVAL_MS = 1e3;
871
+ var LP_REDEMPTION_HISTORY_OPEN_STATES = /* @__PURE__ */ new Set([
872
+ "requested",
873
+ "match_requested",
874
+ "reserved",
875
+ "pending"
876
+ ]);
877
+ var LP_REDEMPTION_RECONCILING_MESSAGE = "LP redemption is still settling on the coordinator; it will reconcile from sync";
878
+ var LP_MISSING_POSITION_CODE_MESSAGE = "Save or recover the LP Position Code before using LP funds";
879
+ var POSITION_CODE_MISMATCH_MESSAGE = "LP Position Code does not match this TEE position";
880
+ var FUNDING_PLAN_EMPTY_MESSAGE = "TEE returned an empty LP funding plan";
881
+ var WITHDRAWAL_MISSING_RECORD_MESSAGE = "LP withdrawal record is missing";
882
+ var WITHDRAWAL_RECORD_MISMATCH_MESSAGE = "LP withdrawal record does not match this confirmation request";
883
+ var WITHDRAWAL_NO_LAMPORTS_MESSAGE = "LP redemption did not land any lamports on-chain";
884
+ var WITHDRAWAL_HISTORY_MISMATCH_MESSAGE = "LP redemption history does not match this request";
885
+ var UNSUPPORTED_FEE_POLICY_MESSAGE = "LP fee policy overrides are not advertised by the coordinator";
886
+ var FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE = "LP earned-fee source count is not available; pass allowManyToOne: true to acknowledge many-to-one withdrawal risk";
887
+ var LpRedemptionReconcilingError = class extends LpLifecycleError {
888
+ constructor() {
889
+ super("LP_REDEMPTION_RECONCILING", LP_REDEMPTION_RECONCILING_MESSAGE);
890
+ this.name = "LpRedemptionReconcilingError";
891
+ }
892
+ };
893
+ var LpPositionAuthMissingError = class extends LpLifecycleError {
894
+ constructor() {
895
+ super("LP_POSITION_AUTH_MISSING", LP_MISSING_POSITION_CODE_MESSAGE);
896
+ this.name = "LpPositionAuthMissingError";
897
+ }
898
+ };
899
+ function isLpRedemptionReconcilingError(error) {
900
+ return error instanceof LpRedemptionReconcilingError || error instanceof Error && error.message === LP_REDEMPTION_RECONCILING_MESSAGE;
901
+ }
902
+ function isLpPositionAuthMissingError(error) {
903
+ return error instanceof LpPositionAuthMissingError || error instanceof Error && error.message === LP_MISSING_POSITION_CODE_MESSAGE;
904
+ }
905
+ var lifecycleClients = /* @__PURE__ */ new WeakMap();
906
+ function createLpLifecycleClient(session, runtime = {}) {
907
+ const state = {
908
+ authByPosition: /* @__PURE__ */ new Map(),
909
+ positions: /* @__PURE__ */ new Map(),
910
+ redemptions: /* @__PURE__ */ new Map(),
911
+ topUps: /* @__PURE__ */ new Map()
912
+ };
913
+ const client = runtime.coordinatorClient ?? createLpCoordinatorClient(session);
914
+ const nowMs = runtime.nowMs ?? Date.now;
915
+ const sleep2 = runtime.sleep ?? defaultSleep;
916
+ const requireAuth = (positionId) => {
917
+ const auth = state.authByPosition.get(positionId);
918
+ if (auth === void 0) throw new LpPositionAuthMissingError();
919
+ return auth;
920
+ };
921
+ const rememberPosition = (position) => {
922
+ const snapshot = clonePosition(position);
923
+ state.positions.set(snapshot.id, snapshot);
924
+ hydrateLpRedemptionsFromPosition(state, snapshot);
925
+ return position;
926
+ };
927
+ const rememberAuth = (auth) => {
928
+ state.authByPosition.set(auth.lpPositionId, auth);
929
+ };
930
+ const refreshPosition2 = async (positionId) => {
931
+ const auth = requireAuth(positionId);
932
+ return rememberPosition(await client.getPosition(positionId, auth));
933
+ };
934
+ const completeDkgBatch2 = async (positionId, options = {}) => {
935
+ assertAttested(session);
936
+ const auth = requireAuth(positionId);
937
+ let activeSessionRetryStartedAtMs = null;
938
+ for (; ; ) {
939
+ const current = rememberPosition(await client.getPosition(positionId, auth));
940
+ options.onPosition?.(current);
941
+ const startIndex = current.shards.length;
942
+ const remaining = current.targetShardCount - startIndex;
943
+ if (remaining <= 0) return current;
944
+ emitDkgProgress(options, current, startIndex, remaining, 0);
945
+ let expectedAddresses = null;
946
+ try {
947
+ const addresses = await createLpDkgBatch({
948
+ lpPositionId: positionId,
949
+ startIndex,
950
+ count: remaining,
951
+ auth,
952
+ client,
953
+ concurrency: options.concurrency ?? runtime.dkgConcurrency,
954
+ workerFactory: options.workerFactory ?? runtime.workerFactory,
955
+ useDeterministicTestAddresses: runtime.useDeterministicTestAddresses,
956
+ onProgress: (progress) => {
957
+ emitDkgProgress(options, current, startIndex, remaining, progress.completed);
958
+ }
959
+ });
960
+ expectedAddresses = addresses;
961
+ const position = await client.finalizeDkgBatch(
962
+ {
963
+ lpPositionId: positionId,
964
+ startIndex,
965
+ addresses,
966
+ finalizedAtMs: nowMs()
967
+ },
968
+ auth
969
+ );
970
+ options.onPosition?.(position);
971
+ emitDkgProgress(options, position, startIndex, remaining, remaining);
972
+ return rememberPosition(position);
973
+ } catch (error) {
974
+ const reconciled = await tryRefreshCompletedDkgWindow({
975
+ client,
976
+ positionId,
977
+ auth,
978
+ startIndex,
979
+ total: remaining,
980
+ expectedAddresses,
981
+ state
982
+ });
983
+ if (reconciled !== null) return reconciled;
984
+ if (isLpDkgIndexDriftError(error)) {
985
+ activeSessionRetryStartedAtMs = null;
986
+ continue;
987
+ }
988
+ if (!isLpDkgActiveSessionError2(error)) throw error;
989
+ const currentTime = nowMs();
990
+ activeSessionRetryStartedAtMs ??= currentTime;
991
+ if (currentTime - activeSessionRetryStartedAtMs >= LP_DKG_ACTIVE_SESSION_RETRY_TIMEOUT_MS2) {
992
+ throw error;
993
+ }
994
+ await sleep2(LP_DKG_ACTIVE_SESSION_RETRY_DELAY_MS);
995
+ }
996
+ }
997
+ };
998
+ return {
999
+ async createPosition(args = {}) {
1000
+ assertNoUnsupportedFeePolicy(args.feePolicy);
1001
+ const shardCount = args.shardCount ?? LP_DEFAULT_TARGET_SHARDS;
1002
+ assertShardCount(shardCount, LP_MAX_TARGET_SHARDS, "shardCount");
1003
+ assertAttested(session);
1004
+ const positionId = runtime.idFactory?.() ?? generateId(LP_ID_PREFIX);
1005
+ const auth = await generateLpPositionCode(positionId);
1006
+ rememberAuth(auth);
1007
+ args.onPositionCode?.({ positionId, lpPositionCode: auth.code });
1008
+ const committedLamports = args.committedLamports ?? DEFAULT_SHARD_FUNDING_LAMPORTS;
1009
+ const position = rememberPosition(
1010
+ await client.openPosition(
1011
+ {
1012
+ lpPositionId: positionId,
1013
+ authPublicKey: Array.from(auth.authPublicKey),
1014
+ refillPublicKey: Array.from(auth.refillPublicKey),
1015
+ withdrawalCommitment: Array.from(auth.withdrawalCommitment),
1016
+ committedLamports,
1017
+ shardCount,
1018
+ shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
1019
+ refillThreshold: args.refillThreshold ?? Math.ceil(shardCount * DEFAULT_REFILL_THRESHOLD_RATIO),
1020
+ addresses: [],
1021
+ createdAtMs: nowMs()
1022
+ },
1023
+ auth
1024
+ )
1025
+ );
1026
+ return { positionId, lpPositionCode: auth.code, position };
1027
+ },
1028
+ async recoverPosition(args) {
1029
+ const auth = await deriveLpPositionCodeAuth(args.code);
1030
+ rememberAuth(auth);
1031
+ const position = rememberPosition(await client.getPosition(auth.lpPositionId, auth));
1032
+ assertPositionMatchesAuth(position, auth);
1033
+ return position;
1034
+ },
1035
+ confirmPositionCodeSaved(_positionId) {
1036
+ return;
1037
+ },
1038
+ completeDkgBatch: completeDkgBatch2,
1039
+ refreshPosition: refreshPosition2,
1040
+ async refill(positionId, args = {}) {
1041
+ const requestedShardCount = args.shardCount ?? LP_DEFAULT_REFILL_BATCH_SIZE;
1042
+ assertShardCount(requestedShardCount, LP_MAX_REFILL_BATCH_SIZE, "shardCount");
1043
+ assertAttested(session);
1044
+ const auth = requireAuth(positionId);
1045
+ const current = await refreshPosition2(positionId);
1046
+ args.onPosition?.(current);
1047
+ const refillShardCount = resolveLpRefillShardCount(
1048
+ current,
1049
+ requestedShardCount,
1050
+ args.shardCount === void 0
1051
+ );
1052
+ const authorizedPosition = rememberPosition(
1053
+ await client.refillPosition(
1054
+ {
1055
+ lpPositionId: positionId,
1056
+ shardCount: refillShardCount,
1057
+ shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
1058
+ addresses: [],
1059
+ authorizedAtMs: nowMs()
1060
+ },
1061
+ auth
1062
+ )
1063
+ );
1064
+ args.onPosition?.(authorizedPosition);
1065
+ if (authorizedPosition.shards.length >= authorizedPosition.targetShardCount) {
1066
+ return authorizedPosition;
1067
+ }
1068
+ return completeDkgBatch2(positionId, {
1069
+ onProgress: args.onProgress,
1070
+ onPosition: args.onPosition,
1071
+ workerFactory: args.workerFactory
1072
+ });
1073
+ },
1074
+ async prepareInitialFunding(positionId) {
1075
+ assertAttested(session);
1076
+ const auth = requireAuth(positionId);
1077
+ const currentPosition = rememberPosition(await client.getPosition(positionId, auth));
1078
+ if (!isLpPositionReadyForInitialFunding(currentPosition)) return null;
1079
+ const initialShard = currentPosition.shards.find((shard) => shard.index === 0);
1080
+ if (initialShard === void 0) return null;
1081
+ if (initialShard.status === "FUNDING_QUEUED") {
1082
+ return fundingPlanFromShard(currentPosition, initialShard);
1083
+ }
1084
+ if (initialShard.status !== "PREGENERATED") return null;
1085
+ const existingQueuedShard = currentPosition.shards.find(
1086
+ (shard) => shard.status === "FUNDING_QUEUED"
1087
+ );
1088
+ if (existingQueuedShard !== void 0) return null;
1089
+ const [plannedShard] = await client.planFunding(
1090
+ { lpPositionId: positionId, shardCount: 1, nowMs: nowMs() },
1091
+ auth
1092
+ );
1093
+ if (plannedShard === void 0) throw new Error(FUNDING_PLAN_EMPTY_MESSAGE);
1094
+ if (plannedShard.index !== 0) {
1095
+ throw new PolicyValidationError(
1096
+ "INVALID_POSITION_AUTH",
1097
+ "Coordinator funding plan did not select LP_DKG_0"
1098
+ );
1099
+ }
1100
+ const position = rememberPosition(await client.getPosition(positionId, auth));
1101
+ return fundingPlanFromShard(position, plannedShard);
1102
+ },
1103
+ async prepareTopUp(positionId) {
1104
+ assertAttested(session);
1105
+ const auth = requireAuth(positionId);
1106
+ const request2 = await client.prepareTopUp(positionId, auth);
1107
+ state.topUps.set(request2.topUpId, request2);
1108
+ return request2;
1109
+ },
1110
+ async waitForTopUp(topUpId, options = {}) {
1111
+ const cached = state.topUps.get(topUpId);
1112
+ if (cached !== void 0 && options.positionId !== void 0 && cached.positionId !== options.positionId) {
1113
+ throw new PolicyValidationError(
1114
+ "INVALID_POSITION_AUTH",
1115
+ "Top-up request does not belong to the requested LP position"
1116
+ );
1117
+ }
1118
+ const initial = cached ?? (options.positionId === void 0 ? void 0 : await client.getTopUpStatus(
1119
+ { lpPositionId: options.positionId, topUpId },
1120
+ requireAuth(options.positionId)
1121
+ ));
1122
+ if (initial === void 0) {
1123
+ throw new PolicyValidationError(
1124
+ "INVALID_POSITION_AUTH",
1125
+ "Top-up request is not known to this LP session"
1126
+ );
1127
+ }
1128
+ const pollIntervalMs = options.pollIntervalMs ?? LP_TOP_UP_WAIT_POLL_INTERVAL_MS;
1129
+ let current = initial;
1130
+ for (; ; ) {
1131
+ throwIfTopUpWaitAborted(options.signal);
1132
+ if (current.status !== "watching") return current;
1133
+ const auth = requireAuth(current.positionId);
1134
+ current = await client.getTopUpStatus({ lpPositionId: current.positionId, topUpId }, auth);
1135
+ state.topUps.set(topUpId, current);
1136
+ if (current.status !== "watching") return current;
1137
+ if (nowMs() >= current.expiresAtMs) {
1138
+ current = await client.getTopUpStatus(
1139
+ { lpPositionId: current.positionId, topUpId },
1140
+ auth
1141
+ );
1142
+ state.topUps.set(topUpId, current);
1143
+ if (current.status !== "watching") return current;
1144
+ current = { ...current, status: "expired" };
1145
+ state.topUps.set(topUpId, current);
1146
+ return current;
1147
+ }
1148
+ await sleep2(pollIntervalMs);
1149
+ }
1150
+ },
1151
+ async reconcileFunding(positionId) {
1152
+ assertAttested(session);
1153
+ const auth = requireAuth(positionId);
1154
+ return rememberPosition(await client.reconcileFunding(positionId, auth));
1155
+ },
1156
+ async withdrawPosition(positionId, args) {
1157
+ validateExplicitWithdrawalInput(args);
1158
+ validateWithdrawalDestinations(args);
1159
+ assertAttested(session);
1160
+ const auth = requireAuth(positionId);
1161
+ const before = rememberPosition(await client.reconcileFunding(positionId, auth));
1162
+ const existingRedemption = findOpenRedemptionHistory(before, args);
1163
+ if (existingRedemption !== void 0) {
1164
+ const execution2 = {
1165
+ withdrawalId: existingRedemption.redemptionId,
1166
+ txSignatures: [...existingRedemption.txSignatures]
1167
+ };
1168
+ const redemption2 = {
1169
+ ...buildLpRedemptionRecord({
1170
+ execution: execution2,
1171
+ position: before,
1172
+ destinationAddresses: existingRedemption.destinationAddresses,
1173
+ acceptedLamports: existingRedemption.acceptedLamports,
1174
+ requestedTargetLamports: existingRedemption.acceptedLamports,
1175
+ landedBaselineLamports: 0,
1176
+ landedFeeBaselineLamports: 0,
1177
+ txSignatureBaseline: [],
1178
+ nowMs: existingRedemption.createdAtMs
1179
+ }),
1180
+ redeemedLamports: existingRedemption.landedLamports,
1181
+ coordinatorStateVersion: existingRedemption.stateVersion
1182
+ };
1183
+ state.redemptions.set(execution2.withdrawalId, redemption2);
1184
+ return { execution: execution2, position: before, redemption: redemption2 };
1185
+ }
1186
+ const claim = resolveWithdrawalClaim(before, args);
1187
+ const sourceCount = lpWithdrawalSourceCountForClaim(before, {
1188
+ ...claim,
1189
+ allowManyToOne: args.allowManyToOne
1190
+ });
1191
+ const validationInput = {
1192
+ lpPositionId: positionId,
1193
+ destinationAddresses: args.destinationAddresses,
1194
+ depositPrincipalLamports: claim.depositPrincipalLamports,
1195
+ earnedFeeLamports: claim.earnedFeeLamports,
1196
+ requestedAtMs: nowMs(),
1197
+ allowManyToOne: args.allowManyToOne ?? false,
1198
+ sourceCount
1199
+ };
1200
+ validateLpWithdrawalPlan(validationInput);
1201
+ const execution = await client.executeWithdrawal(
1202
+ {
1203
+ lpPositionId: validationInput.lpPositionId,
1204
+ destinationAddresses: validationInput.destinationAddresses,
1205
+ depositPrincipalLamports: validationInput.depositPrincipalLamports,
1206
+ earnedFeeLamports: validationInput.earnedFeeLamports,
1207
+ requestedAtMs: validationInput.requestedAtMs,
1208
+ allowManyToOne: args.allowManyToOne === true || sourceCount === 1
1209
+ },
1210
+ auth
1211
+ );
1212
+ const pendingRedemption = buildLpRedemptionRecord({
1213
+ execution,
1214
+ position: before,
1215
+ destinationAddresses: args.destinationAddresses,
1216
+ acceptedLamports: claim.depositPrincipalLamports + claim.earnedFeeLamports,
1217
+ requestedTargetLamports: (before.actorSync?.withdrawalRequestedLamports ?? 0) + claim.depositPrincipalLamports + claim.earnedFeeLamports,
1218
+ landedBaselineLamports: before.actorSync?.withdrawalLandedLamports ?? 0,
1219
+ landedFeeBaselineLamports: before.actorSync?.withdrawalLandedFeeLamports ?? 0,
1220
+ txSignatureBaseline: before.actorSync?.withdrawalTxHashes ?? [],
1221
+ nowMs: nowMs()
1222
+ });
1223
+ state.redemptions.set(execution.withdrawalId, pendingRedemption);
1224
+ const position = await client.getPosition(positionId, auth).then((refreshed) => rememberPosition(refreshed)).catch(() => before);
1225
+ const redemption = { ...pendingRedemption, lpPositionId: position.id };
1226
+ state.redemptions.set(execution.withdrawalId, redemption);
1227
+ return { execution, position, redemption };
1228
+ },
1229
+ async waitForWithdrawalConfirmation(positionId, execution, options = {}) {
1230
+ const auth = requireAuth(positionId);
1231
+ let record = resolveWithdrawalRecord({
1232
+ state,
1233
+ positionId,
1234
+ execution,
1235
+ options
1236
+ });
1237
+ state.redemptions.set(record.redemptionId, record);
1238
+ const deadlineMs = nowMs() + (runtime.withdrawalConfirmationTimeoutMs ?? LP_WITHDRAWAL_CONFIRMATION_TIMEOUT_MS);
1239
+ const pollIntervalMs = runtime.withdrawalConfirmationPollIntervalMs ?? LP_WITHDRAWAL_CONFIRMATION_POLL_INTERVAL_MS;
1240
+ for (; ; ) {
1241
+ const position = rememberPosition(await client.getPosition(positionId, auth));
1242
+ options.onPosition?.(position);
1243
+ const reconciled = reconcileLpRedemptionFromActorSync(record, position);
1244
+ record = reconciled;
1245
+ state.redemptions.set(record.redemptionId, reconciled);
1246
+ options.onRedemptionUpdate?.(reconciled);
1247
+ if (reconciled.status === "confirmed") {
1248
+ return {
1249
+ withdrawalId: execution.withdrawalId,
1250
+ txSignatures: reconciled.txSignatures,
1251
+ redeemedLamports: reconciled.redeemedLamports
1252
+ };
1253
+ }
1254
+ if (reconciled.status === "failed") {
1255
+ throw new Error(reconciled.error ?? WITHDRAWAL_NO_LAMPORTS_MESSAGE);
1256
+ }
1257
+ if (nowMs() > deadlineMs) {
1258
+ state.redemptions.set(record.redemptionId, { ...reconciled, status: "reconciling" });
1259
+ options.onRedemptionUpdate?.({ ...reconciled, status: "reconciling" });
1260
+ throw new LpRedemptionReconcilingError();
1261
+ }
1262
+ await sleep2(pollIntervalMs);
1263
+ }
1264
+ },
1265
+ async withdrawFees(_positionId, args) {
1266
+ publicKey(args.destination);
1267
+ assertAttested(session);
1268
+ throw new RoutingError(
1269
+ "VERSION_NOT_SUPPORTED",
1270
+ "LP fee withdrawal is not advertised by the coordinator"
1271
+ );
1272
+ },
1273
+ async listPositions() {
1274
+ return [...state.positions.values()].map(clonePosition);
1275
+ }
1276
+ };
1277
+ }
1278
+ function defaultLpLifecycleClient(session) {
1279
+ let client = lifecycleClients.get(session);
1280
+ if (client === void 0) {
1281
+ client = createLpLifecycleClient(session);
1282
+ lifecycleClients.set(session, client);
1283
+ }
1284
+ return client;
1285
+ }
1286
+ function emitDkgProgress(options, position, startIndex, total, completed) {
1287
+ options.onProgress?.({
1288
+ phase: "dkg",
1289
+ lpPositionId: position.id,
1290
+ startIndex,
1291
+ targetShardCount: startIndex + total,
1292
+ completed: Math.min(completed, total),
1293
+ total
1294
+ });
1295
+ }
1296
+ async function tryRefreshCompletedDkgWindow({
1297
+ client,
1298
+ positionId,
1299
+ auth,
1300
+ startIndex,
1301
+ total,
1302
+ expectedAddresses,
1303
+ state
1304
+ }) {
1305
+ const position = await client.getPosition(positionId, auth).catch(() => null);
1306
+ if (position === null) return null;
1307
+ if (!hasCompletedDkgWindow(position, startIndex, total, expectedAddresses)) return null;
1308
+ state.positions.set(position.id, clonePosition(position));
1309
+ return position;
1310
+ }
1311
+ function hasCompletedDkgWindow(position, startIndex, total, expectedAddresses) {
1312
+ for (let offset = 0; offset < total; offset += 1) {
1313
+ const shard = position.shards.find((candidate) => candidate.index === startIndex + offset);
1314
+ if (shard === void 0) {
1315
+ return false;
1316
+ }
1317
+ if (expectedAddresses !== null && shard.address !== expectedAddresses[offset]) {
1318
+ return false;
1319
+ }
1320
+ }
1321
+ return true;
1322
+ }
1323
+ function assertNoUnsupportedFeePolicy(feePolicy) {
1324
+ if (feePolicy === void 0) return;
1325
+ throw new RoutingError("VERSION_NOT_SUPPORTED", UNSUPPORTED_FEE_POLICY_MESSAGE);
1326
+ }
1327
+ function fundingPlanFromShard(position, shard) {
1328
+ if (shard.requiredLamports === void 0 || shard.requiredLamports <= 0) return null;
1329
+ return {
1330
+ address: shard.address,
1331
+ requiredLamports: shard.requiredLamports,
1332
+ qrPayload: `solana:${shard.address}`,
1333
+ position
1334
+ };
1335
+ }
1336
+ function assertPositionMatchesAuth(position, auth) {
1337
+ if (position.authPublicKey !== bytesToHex(auth.authPublicKey) || position.refillPublicKey !== bytesToHex(auth.refillPublicKey) || position.withdrawalCommitment !== bytesToHex(auth.withdrawalCommitment)) {
1338
+ throw new Error(POSITION_CODE_MISMATCH_MESSAGE);
1339
+ }
1340
+ }
1341
+ function assertShardCount(value, max, field) {
1342
+ if (!Number.isInteger(value) || value < 1 || value > max) {
1343
+ throw new PolicyValidationError(
1344
+ "INVALID_AMOUNT",
1345
+ `${field} must be an integer in 1..${max}, received ${String(value)}`
1346
+ );
1347
+ }
1348
+ }
1349
+ function resolveLpRefillShardCount(position, requestedShardCount, capDefaultBatch) {
1350
+ const remainingShardCapacity = LP_MAX_TARGET_SHARDS - position.shards.length;
1351
+ if (remainingShardCapacity <= 0) {
1352
+ throw new PolicyValidationError(
1353
+ "INVALID_AMOUNT",
1354
+ `LP refill target_shard_count is already at ${LP_MAX_TARGET_SHARDS}`
1355
+ );
1356
+ }
1357
+ const refillShardCount = capDefaultBatch ? Math.min(requestedShardCount, remainingShardCapacity) : requestedShardCount;
1358
+ if (position.shards.length + refillShardCount > LP_MAX_TARGET_SHARDS) {
1359
+ throw new PolicyValidationError(
1360
+ "INVALID_AMOUNT",
1361
+ `LP refill target_shard_count must be <= ${LP_MAX_TARGET_SHARDS}`
1362
+ );
1363
+ }
1364
+ return refillShardCount;
1365
+ }
1366
+ function isLpPositionReadyForInitialFunding(position) {
1367
+ return position.status === "active" && position.shards.length >= position.targetShardCount;
1368
+ }
1369
+ function resolveWithdrawalClaim(position, args) {
1370
+ if (args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0) {
1371
+ return {
1372
+ depositPrincipalLamports: args.depositPrincipalLamports ?? 0,
1373
+ earnedFeeLamports: args.earnedFeeLamports ?? 0
1374
+ };
1375
+ }
1376
+ const legacyDepositPrincipalLamports = position.shards.filter((shard) => shard.status === "AVAILABLE" && shard.amountLamports > 0).reduce((sum, shard) => sum + shard.amountLamports, 0);
1377
+ const actorRedeemablePrincipalLamports = Math.max(
1378
+ 0,
1379
+ position.actorSync?.redeemablePrincipalLamports ?? 0
1380
+ );
1381
+ const actorRedeemableFeeLamports = Math.max(0, position.actorSync?.redeemableFeeLamports ?? 0);
1382
+ const hasActorProjection = hasActorRedeemableProjection(position.actorSync);
1383
+ const depositPrincipalLamports = resolveRedeemablePrincipalLamports(
1384
+ position.actorSync,
1385
+ legacyDepositPrincipalLamports,
1386
+ actorRedeemablePrincipalLamports,
1387
+ actorRedeemableFeeLamports
1388
+ );
1389
+ const earnedFeeLamports = hasActorProjection ? actorRedeemableFeeLamports : Math.max(0, position.earnedLamports);
1390
+ const totalLamports = depositPrincipalLamports + earnedFeeLamports;
1391
+ if (!Number.isSafeInteger(totalLamports) || totalLamports <= 0) {
1392
+ throw new PolicyValidationError(
1393
+ "INVALID_AMOUNT",
1394
+ "LP withdrawal requires available principal or earned fees"
1395
+ );
1396
+ }
1397
+ return { depositPrincipalLamports, earnedFeeLamports };
1398
+ }
1399
+ function findOpenRedemptionHistory(position, args) {
1400
+ const hasExplicitClaim = args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0;
1401
+ const requestedPrincipalLamports = args.depositPrincipalLamports ?? 0;
1402
+ const requestedFeeLamports = args.earnedFeeLamports ?? 0;
1403
+ return position.actorSync?.redemptionHistory?.find(
1404
+ (history) => LP_REDEMPTION_HISTORY_OPEN_STATES.has(history.state) && history.detailsTruncated !== true && sameStringList(history.destinationAddresses, args.destinationAddresses) && (!hasExplicitClaim || history.acceptedDepositPrincipalLamports === requestedPrincipalLamports && history.acceptedEarnedFeeLamports === requestedFeeLamports)
1405
+ );
1406
+ }
1407
+ function hasActorSyncLpActivity(sync) {
1408
+ if (sync === void 0) return false;
1409
+ return sync.status !== "idle" || sync.pendingReservationCount > 0 || sync.reservedPayoutLamports > 0 || sync.reimbursementDueCount > 0 || sync.reimbursementDueSourceCount > 0 || sync.reimbursementDuePayoutLamports > 0 || sync.reimbursementDueNetworkFeeLamports > 0 || sync.withdrawalRequestedLamports > 0 || sync.withdrawalLandedLamports > 0 || sync.withdrawalLandedFeeLamports > 0 || sync.withdrawalCanceledLamports > 0 || sync.withdrawalNetworkFeeLossLamports > 0 || sync.withdrawalTxHashes.length > 0;
1410
+ }
1411
+ function resolveRedeemablePrincipalLamports(sync, legacyAvailableLamports, actorRedeemablePrincipalLamports, actorRedeemableFeeLamports) {
1412
+ if (sync?.redeemableProjectionAvailable !== true) return legacyAvailableLamports;
1413
+ if (legacyAvailableLamports <= 0) return actorRedeemablePrincipalLamports;
1414
+ if (actorRedeemablePrincipalLamports >= legacyAvailableLamports || hasActorSyncLpActivity(sync)) {
1415
+ return actorRedeemablePrincipalLamports;
1416
+ }
1417
+ return Math.max(
1418
+ actorRedeemablePrincipalLamports,
1419
+ legacyAvailableLamports - actorRedeemableFeeLamports
1420
+ );
1421
+ }
1422
+ function hasActorRedeemableProjection(sync) {
1423
+ return sync?.redeemableProjectionAvailable === true;
1424
+ }
1425
+ function validateExplicitWithdrawalInput(args) {
1426
+ if (args.depositPrincipalLamports === void 0 && args.earnedFeeLamports === void 0) return;
1427
+ validateLpWithdrawalPlan({
1428
+ destinationAddresses: args.destinationAddresses,
1429
+ depositPrincipalLamports: args.depositPrincipalLamports ?? 0,
1430
+ earnedFeeLamports: args.earnedFeeLamports ?? 0,
1431
+ allowManyToOne: args.allowManyToOne,
1432
+ sourceCount: args.destinationAddresses.length
1433
+ });
1434
+ }
1435
+ function validateWithdrawalDestinations(args) {
1436
+ validateLpWithdrawalPlan({
1437
+ destinationAddresses: args.destinationAddresses,
1438
+ depositPrincipalLamports: 1,
1439
+ earnedFeeLamports: 0,
1440
+ allowManyToOne: true
1441
+ });
1442
+ }
1443
+ function resolveWithdrawalRecord({
1444
+ state,
1445
+ positionId,
1446
+ execution,
1447
+ options
1448
+ }) {
1449
+ const record = state.redemptions.get(execution.withdrawalId) ?? options.redemptionRecord;
1450
+ if (record === void 0) throw new Error(WITHDRAWAL_MISSING_RECORD_MESSAGE);
1451
+ if (record.lpPositionId !== positionId || record.redemptionId !== execution.withdrawalId) {
1452
+ throw new PolicyValidationError("INVALID_REDEMPTION_PLAN", WITHDRAWAL_RECORD_MISMATCH_MESSAGE);
1453
+ }
1454
+ return {
1455
+ ...record,
1456
+ destinationAddresses: [...record.destinationAddresses],
1457
+ txSignatures: [...record.txSignatures],
1458
+ txSignatureBaseline: [...record.txSignatureBaseline],
1459
+ landedFeeBaselineLamports: record.landedFeeBaselineLamports ?? 0
1460
+ };
1461
+ }
1462
+ function lpWithdrawalSourceCountForClaim(position, claim) {
1463
+ let remainingPrincipalLamports = claim.depositPrincipalLamports;
1464
+ let principalSourceCount = 0;
1465
+ const principalCandidates = [...position.shards].filter((shard) => shard.status === "AVAILABLE" && shard.amountLamports > 0).sort((left, right) => left.shardId.localeCompare(right.shardId));
1466
+ for (const shard of principalCandidates) {
1467
+ if (remainingPrincipalLamports <= 0) break;
1468
+ principalSourceCount += 1;
1469
+ remainingPrincipalLamports -= shard.amountLamports;
1470
+ }
1471
+ const actorRedeemablePrincipalLamports = position.actorSync?.redeemablePrincipalLamports ?? 0;
1472
+ const actorRedeemablePrincipalSourceCount = position.actorSync?.redeemablePrincipalSourceCount ?? 0;
1473
+ if (claim.depositPrincipalLamports > 0 && position.actorSync?.redeemableProjectionAvailable === true && actorRedeemablePrincipalLamports >= claim.depositPrincipalLamports && actorRedeemablePrincipalSourceCount > 0) {
1474
+ principalSourceCount = actorRedeemablePrincipalSourceCount;
1475
+ }
1476
+ const observedEarnedFeeSourceCount = position.actorSync?.redeemableProjectionAvailable === true ? position.actorSync.redeemableFeeSourceCount ?? 0 : position.actorSync?.redeemableFeeSourceCount || position.actorSync?.reimbursementDueSourceCount || 0;
1477
+ if (claim.earnedFeeLamports > 0 && observedEarnedFeeSourceCount < 1 && claim.allowManyToOne !== true) {
1478
+ throw new PolicyValidationError(
1479
+ "INVALID_WITHDRAWAL_PLAN",
1480
+ FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE
1481
+ );
1482
+ }
1483
+ const earnedFeeSourceCount = claim.earnedFeeLamports > 0 ? Math.max(1, observedEarnedFeeSourceCount) : 0;
1484
+ return principalSourceCount + earnedFeeSourceCount;
1485
+ }
1486
+ function buildLpRedemptionRecord({
1487
+ execution,
1488
+ position,
1489
+ destinationAddresses,
1490
+ acceptedLamports,
1491
+ requestedTargetLamports,
1492
+ landedBaselineLamports,
1493
+ landedFeeBaselineLamports,
1494
+ txSignatureBaseline,
1495
+ nowMs
1496
+ }) {
1497
+ return {
1498
+ redemptionId: execution.withdrawalId,
1499
+ lpPositionId: position.id,
1500
+ requestedAt: nowMs,
1501
+ status: "pending",
1502
+ destinationAddresses: [...destinationAddresses],
1503
+ acceptedLamports,
1504
+ requestedTargetLamports,
1505
+ landedBaselineLamports,
1506
+ landedFeeBaselineLamports,
1507
+ redeemedLamports: 0,
1508
+ txSignatures: [...execution.txSignatures],
1509
+ txSignatureBaseline: [...txSignatureBaseline]
1510
+ };
1511
+ }
1512
+ function reconcileLpRedemptionFromActorSync(record, position) {
1513
+ const sync = position.actorSync;
1514
+ if (sync === void 0) return record;
1515
+ const history = sync.redemptionHistory?.find(
1516
+ (candidate) => candidate.redemptionId === record.redemptionId
1517
+ );
1518
+ if (history !== void 0) {
1519
+ return reconcileLpRedemptionFromHistory(record, history);
1520
+ }
1521
+ const resolvedLamports = sync.withdrawalLandedLamports + sync.withdrawalLandedFeeLamports + sync.withdrawalCanceledLamports + sync.withdrawalNetworkFeeLossLamports;
1522
+ const settled = sync.withdrawalRequestedLamports >= record.requestedTargetLamports && sync.withdrawalRequestedLamports <= resolvedLamports;
1523
+ const redeemedLamports = Math.max(
1524
+ sync.withdrawalLandedLamports - record.landedBaselineLamports + sync.withdrawalLandedFeeLamports - (record.landedFeeBaselineLamports ?? 0),
1525
+ 0
1526
+ );
1527
+ const baselineSignatures = new Set(record.txSignatureBaseline);
1528
+ const txSignatures = sync.withdrawalTxHashes.filter(
1529
+ (signature) => !baselineSignatures.has(signature)
1530
+ );
1531
+ if (!settled) return { ...record, redeemedLamports, txSignatures };
1532
+ if (redeemedLamports <= 0) {
1533
+ return {
1534
+ ...record,
1535
+ status: "failed",
1536
+ redeemedLamports,
1537
+ txSignatures,
1538
+ error: WITHDRAWAL_NO_LAMPORTS_MESSAGE
1539
+ };
1540
+ }
1541
+ return { ...record, status: "confirmed", redeemedLamports, txSignatures };
1542
+ }
1543
+ function reconcileLpRedemptionFromHistory(record, history) {
1544
+ const observedStateVersion = record.coordinatorStateVersion ?? 0;
1545
+ if (history.stateVersion < observedStateVersion) return record;
1546
+ if (record.acceptedLamports !== void 0 && history.acceptedLamports !== record.acceptedLamports || history.detailsTruncated !== true && !sameStringList(history.destinationAddresses, record.destinationAddresses)) {
1547
+ return {
1548
+ ...record,
1549
+ status: "failed",
1550
+ coordinatorStateVersion: Math.max(observedStateVersion, history.stateVersion),
1551
+ error: WITHDRAWAL_HISTORY_MISMATCH_MESSAGE
1552
+ };
1553
+ }
1554
+ const txSignatures = uniqueStrings([...record.txSignatures, ...history.txSignatures]);
1555
+ const base = {
1556
+ ...record,
1557
+ coordinatorStateVersion: history.stateVersion,
1558
+ redeemedLamports: history.landedLamports,
1559
+ txSignatures,
1560
+ destinationCount: history.destinationCount,
1561
+ txSignatureCount: history.txSignatureCount,
1562
+ detailsTruncated: history.detailsTruncated,
1563
+ error: void 0
1564
+ };
1565
+ if (history.state === "confirmed") {
1566
+ return history.landedLamports > 0 ? { ...base, status: "confirmed" } : { ...base, status: "failed", error: WITHDRAWAL_NO_LAMPORTS_MESSAGE };
1567
+ }
1568
+ if (history.state === "rejected") {
1569
+ return {
1570
+ ...base,
1571
+ status: "failed",
1572
+ error: WITHDRAWAL_NO_LAMPORTS_MESSAGE
1573
+ };
1574
+ }
1575
+ return {
1576
+ ...base,
1577
+ status: record.status === "reconciling" ? "reconciling" : "pending"
1578
+ };
1579
+ }
1580
+ function hydrateLpRedemptionsFromPosition(state, position) {
1581
+ for (const history of position.actorSync?.redemptionHistory ?? []) {
1582
+ const existing = state.redemptions.get(history.redemptionId);
1583
+ if (existing === void 0) {
1584
+ state.redemptions.set(history.redemptionId, lpWithdrawalRecordFromHistory(position, history));
1585
+ continue;
1586
+ }
1587
+ state.redemptions.set(
1588
+ history.redemptionId,
1589
+ reconcileLpRedemptionFromHistory(existing, history)
1590
+ );
1591
+ }
1592
+ }
1593
+ function lpWithdrawalRecordFromHistory(position, history) {
1594
+ const record = {
1595
+ redemptionId: history.redemptionId,
1596
+ lpPositionId: position.id,
1597
+ requestedAt: history.createdAtMs,
1598
+ status: "pending",
1599
+ destinationAddresses: [...history.destinationAddresses],
1600
+ acceptedLamports: history.acceptedLamports,
1601
+ requestedTargetLamports: history.acceptedLamports,
1602
+ landedBaselineLamports: 0,
1603
+ landedFeeBaselineLamports: 0,
1604
+ redeemedLamports: history.landedLamports,
1605
+ txSignatures: [...history.txSignatures],
1606
+ txSignatureBaseline: [],
1607
+ coordinatorStateVersion: history.stateVersion,
1608
+ destinationCount: history.destinationCount,
1609
+ txSignatureCount: history.txSignatureCount,
1610
+ detailsTruncated: history.detailsTruncated
1611
+ };
1612
+ return reconcileLpRedemptionFromHistory(record, history);
1613
+ }
1614
+ function sameStringList(left, right) {
1615
+ return left.length === right.length && left.every((value, index) => value === right[index]);
1616
+ }
1617
+ function uniqueStrings(values) {
1618
+ return Array.from(new Set(values));
1619
+ }
1620
+ function isLpDkgActiveSessionError2(error) {
1621
+ return error instanceof Error && error.message.includes("LP DKG session already active for position index");
1622
+ }
1623
+ function isLpDkgIndexDriftError(error) {
1624
+ if (!(error instanceof Error)) return false;
1625
+ return error.message.includes("LP DKG index") && error.message.includes("already exists for position") || error.message.includes("LP DKG expected index");
1626
+ }
1627
+ function clonePosition(position) {
1628
+ return {
1629
+ ...position,
1630
+ shards: position.shards.map((shard) => ({ ...shard })),
1631
+ actorSync: position.actorSync === void 0 ? void 0 : {
1632
+ ...position.actorSync,
1633
+ redemptionHistory: position.actorSync.redemptionHistory?.map((history) => ({
1634
+ ...history,
1635
+ destinationAddresses: [...history.destinationAddresses],
1636
+ txSignatures: [...history.txSignatures]
1637
+ }))
1638
+ }
1639
+ };
1640
+ }
1641
+ function generateId(prefix) {
1642
+ return `${prefix}_${Array.from(randomBytes2(8)).map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
1643
+ }
1644
+ function randomBytes2(length) {
1645
+ const bytes = new Uint8Array(length);
1646
+ crypto.getRandomValues(bytes);
1647
+ return bytes;
1648
+ }
1649
+ function throwIfTopUpWaitAborted(signal) {
1650
+ if (signal?.aborted !== true) return;
1651
+ throw new DOMException("LP top-up wait aborted", "AbortError");
1652
+ }
1653
+ function defaultSleep(delayMs) {
1654
+ return new Promise((resolve) => {
1655
+ globalThis.setTimeout(resolve, delayMs);
1656
+ });
1657
+ }
1658
+
1659
+ // src/lp/index.ts
1660
+ function createPosition(session, args = {}) {
1661
+ return defaultLpLifecycleClient(session).createPosition(args);
1662
+ }
1663
+ function recoverPosition(session, args) {
1664
+ return defaultLpLifecycleClient(session).recoverPosition(args);
1665
+ }
1666
+ function confirmPositionCodeSaved(session, positionId) {
1667
+ defaultLpLifecycleClient(session).confirmPositionCodeSaved(positionId);
1668
+ }
1669
+ async function completeDkgBatch(session, positionId, opts = {}) {
1670
+ return await defaultLpLifecycleClient(session).completeDkgBatch(positionId, opts);
1671
+ }
1672
+ function refreshPosition(session, positionId) {
1673
+ return defaultLpLifecycleClient(session).refreshPosition(positionId);
1674
+ }
1675
+ function refill(session, positionId, args = {}) {
1676
+ return defaultLpLifecycleClient(session).refill(positionId, args);
1677
+ }
1678
+ function prepareInitialFunding(session, positionId) {
1679
+ return defaultLpLifecycleClient(session).prepareInitialFunding(positionId);
1680
+ }
1681
+ function prepareTopUp2(session, positionId) {
1682
+ return defaultLpLifecycleClient(session).prepareTopUp(positionId);
1683
+ }
1684
+ function waitForTopUp(session, topUpId, options = {}) {
1685
+ return defaultLpLifecycleClient(session).waitForTopUp(topUpId, options);
1686
+ }
1687
+ function reconcileFunding2(session, positionId) {
1688
+ return defaultLpLifecycleClient(session).reconcileFunding(positionId);
1689
+ }
1690
+ function withdrawPosition(session, positionId, args) {
1691
+ return defaultLpLifecycleClient(session).withdrawPosition(positionId, args);
1692
+ }
1693
+ function waitForWithdrawalConfirmation(session, positionId, execution, options = {}) {
1694
+ return defaultLpLifecycleClient(session).waitForWithdrawalConfirmation(
1695
+ positionId,
1696
+ execution,
1697
+ options
1698
+ );
1699
+ }
1700
+ async function withdrawFees(session, positionId, args) {
1701
+ publicKey(args.destination);
1702
+ assertAttested(session);
1703
+ return await defaultLpLifecycleClient(session).withdrawFees(positionId, args);
1704
+ }
1705
+ function listPositions(session) {
1706
+ return defaultLpLifecycleClient(session).listPositions();
1707
+ }
1708
+
1709
+ export { LP_COMMAND_REQUEST_TIMEOUT_MS, LP_MISSING_POSITION_CODE_MESSAGE, LP_REDEMPTION_RECONCILING_MESSAGE, bytesToHex, completeDkgBatch, confirmPositionCodeSaved, createLpCoordinatorClient, createLpDkgBatch, createLpLifecycleClient, createPosition, defaultLpLifecycleClient, deriveLpPositionCodeAuth, generateLpPositionCode, isLpPositionAuthMissingError, isLpRedemptionReconcilingError, listPositions, normalizeLpPositionCode, prepareInitialFunding, prepareTopUp2 as prepareTopUp, reconcileFunding2 as reconcileFunding, recoverPosition, refill, refreshPosition, signLpCommandBytes, waitForTopUp, waitForWithdrawalConfirmation, withdrawFees, withdrawPosition };
1710
+ //# sourceMappingURL=lp.js.map
1711
+ //# sourceMappingURL=lp.js.map