@invisible-labs/sdk 0.9.0-devnet → 0.11.0-devnet

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 (38) hide show
  1. package/README.md +25 -5
  2. package/dist/.invisible-sdk-build-target.json +2 -2
  3. package/dist/{chunk-RWKG7N7I.js → chunk-A4R3DHKN.js} +2 -2
  4. package/dist/chunk-A4R3DHKN.js.map +1 -0
  5. package/dist/{chunk-6AKBRYKO.js → chunk-DOTYDYVB.js} +3 -3
  6. package/dist/{chunk-6AKBRYKO.js.map → chunk-DOTYDYVB.js.map} +1 -1
  7. package/dist/{chunk-EOPZVOVP.js → chunk-LVCDODTN.js} +582 -19
  8. package/dist/chunk-LVCDODTN.js.map +1 -0
  9. package/dist/{chunk-QF5TSUQE.js → chunk-M45APS7S.js} +405 -51
  10. package/dist/chunk-M45APS7S.js.map +1 -0
  11. package/dist/{chunk-ISIWWGSR.js → chunk-NOKBFHDP.js} +5 -5
  12. package/dist/{chunk-ISIWWGSR.js.map → chunk-NOKBFHDP.js.map} +1 -1
  13. package/dist/{chunk-HWRMSK7E.js → chunk-TNLSUQGJ.js} +4 -4
  14. package/dist/{chunk-HWRMSK7E.js.map → chunk-TNLSUQGJ.js.map} +1 -1
  15. package/dist/{chunk-EPWTRF5U.js → chunk-TYTVF3A4.js} +3 -3
  16. package/dist/{chunk-EPWTRF5U.js.map → chunk-TYTVF3A4.js.map} +1 -1
  17. package/dist/{chunk-AIKC2XQS.js → chunk-YYTRFM2F.js} +3 -3
  18. package/dist/{chunk-AIKC2XQS.js.map → chunk-YYTRFM2F.js.map} +1 -1
  19. package/dist/coordinator-EQEL23A3.js +4 -0
  20. package/dist/{coordinator-DHL3AP7V.js.map → coordinator-EQEL23A3.js.map} +1 -1
  21. package/dist/events.js +3 -3
  22. package/dist/index.d.ts +4 -4
  23. package/dist/index.js +9 -9
  24. package/dist/index.js.map +1 -1
  25. package/dist/lp.d.ts +46 -5
  26. package/dist/lp.js +675 -212
  27. package/dist/lp.js.map +1 -1
  28. package/dist/stats.js +1 -1
  29. package/dist/storage.js +2 -2
  30. package/dist/{types.generated-DrM2M4DZ.d.ts → types.generated-BpbFQlYB.d.ts} +75 -1
  31. package/dist/user.d.ts +9 -4
  32. package/dist/user.js +53 -22
  33. package/dist/user.js.map +1 -1
  34. package/package.json +2 -2
  35. package/dist/chunk-EOPZVOVP.js.map +0 -1
  36. package/dist/chunk-QF5TSUQE.js.map +0 -1
  37. package/dist/chunk-RWKG7N7I.js.map +0 -1
  38. package/dist/coordinator-DHL3AP7V.js +0 -4
package/dist/lp.js CHANGED
@@ -1,10 +1,104 @@
1
- import { isValidLpPositionId, LP_MAX_TOTAL_SHARDS, LP_MAX_INITIAL_SHARDS, LP_MAX_REFILL_BATCH_SIZE, LP_SHARD_PAGE_SIZE, LP_DEFAULT_REFILL_BATCH_SIZE, LP_DEFAULT_TARGET_SHARDS } from './chunk-HWRMSK7E.js';
2
- import { publicKey, assertAttested, lamports } from './chunk-EPWTRF5U.js';
3
- import { requestEnvelope, CoordinatorMessageType } from './chunk-ISIWWGSR.js';
4
- import { getSessionState } from './chunk-6AKBRYKO.js';
5
- import { CommandError, WalletError, TransportError, PolicyValidationError, RoutingError, LpLifecycleError } from './chunk-RWKG7N7I.js';
1
+ import { isValidLpPositionId, LP_MAX_TOTAL_SHARDS, LP_MAX_INITIAL_SHARDS, LP_MAX_REFILL_BATCH_SIZE, LP_SHARD_PAGE_SIZE, LP_DEFAULT_REFILL_BATCH_SIZE, LP_DEFAULT_TARGET_SHARDS } from './chunk-TNLSUQGJ.js';
2
+ import { publicKey, assertAttested, lamports } from './chunk-TYTVF3A4.js';
3
+ import { requestEnvelope, CoordinatorMessageType } from './chunk-NOKBFHDP.js';
4
+ import { getSessionState } from './chunk-DOTYDYVB.js';
5
+ import { CommandError, TransportError, PolicyValidationError, LpLifecycleError, RoutingError, WalletError } from './chunk-A4R3DHKN.js';
6
6
  import './chunk-VR6T6OJS.js';
7
7
 
8
+ // src/lp/actorSync.ts
9
+ function normalizeLpActorSync(sync) {
10
+ const total = sync.totalNetFundsLamports;
11
+ if (total === void 0) return sync;
12
+ const available = sync.redeemablePrincipalLamports + sync.redeemableFeeLamports;
13
+ if (sync.redeemableProjectionAvailable === true && Number.isSafeInteger(total) && total >= 0 && Number.isSafeInteger(sync.redeemablePrincipalLamports) && sync.redeemablePrincipalLamports >= 0 && Number.isSafeInteger(sync.redeemableFeeLamports) && sync.redeemableFeeLamports >= 0 && Number.isSafeInteger(available) && available >= 0 && total >= available) {
14
+ return sync;
15
+ }
16
+ return { ...sync, totalNetFundsLamports: void 0 };
17
+ }
18
+ function mergeLpActorSync(previous, next) {
19
+ if (next === void 0) return void 0;
20
+ if (previous?.lpPositionId === next.lpPositionId && (next.stateVersion < previous.stateVersion || next.stateVersion === previous.stateVersion && next.updatedAtMs < previous.updatedAtMs)) {
21
+ return normalizeLpActorSync(previous);
22
+ }
23
+ return normalizeLpActorSync(next);
24
+ }
25
+
26
+ // src/lp/feePolicy.ts
27
+ var LP_FEE_POLICY_VERSION = 1;
28
+ var LP_PARTNER_COMMISSION_BPS = 6e3;
29
+ var LP_BASIS_POINT_DENOMINATOR = 10000n;
30
+ var LP_FEE_POLICY_HASHES = {
31
+ internal: "53479c39d822899c0abe21990aeff40ea74681277b2744ef86234152cc94ca5a",
32
+ partner: "3e1a307504043bc0b25f03ccffdff1b42cd2b38699f000f7eb70ce9433a5b2a7"
33
+ };
34
+ var LP_ECONOMIC_UNSIGNED_PATTERN = /^(0|[1-9][0-9]*)$/;
35
+ var LP_ECONOMIC_SIGNED_PATTERN = /^(0|-?[1-9][0-9]*)$/;
36
+ var LP_ECONOMIC_UNSIGNED_MAX = (1n << 128n) - 1n;
37
+ var LP_ECONOMIC_SIGNED_MIN = -(1n << 127n);
38
+ var LP_ECONOMIC_SIGNED_MAX = (1n << 127n) - 1n;
39
+ var LP_FEE_POLICY_INVALID_MESSAGE = "Coordinator LP fee policy evidence is missing or invalid";
40
+ function validateLpFeePolicy(value) {
41
+ if (typeof value !== "object" || value === null) {
42
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
43
+ }
44
+ const policy = value;
45
+ if (policy.version !== LP_FEE_POLICY_VERSION || policy.profile !== "internal" && policy.profile !== "partner" || policy.commission_bps !== (policy.profile === "partner" ? LP_PARTNER_COMMISSION_BPS : 0) || typeof policy.policy_hash !== "string" || policy.policy_hash !== LP_FEE_POLICY_HASHES[policy.profile]) {
46
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
47
+ }
48
+ return {
49
+ version: LP_FEE_POLICY_VERSION,
50
+ profile: policy.profile,
51
+ commission_bps: policy.profile === "partner" ? LP_PARTNER_COMMISSION_BPS : 0,
52
+ policy_hash: policy.policy_hash
53
+ };
54
+ }
55
+ function requireLpFeePolicyCapability(version) {
56
+ if (version !== LP_FEE_POLICY_VERSION) {
57
+ throw new RoutingError(
58
+ "VERSION_NOT_SUPPORTED",
59
+ "Coordinator does not support the LP fee policy contract"
60
+ );
61
+ }
62
+ }
63
+ function validateLpPositionFeePolicy(position, previous) {
64
+ if (position.feePolicy === void 0) {
65
+ if (previous?.feePolicy !== void 0 || position.actorSync?.feePolicy !== void 0 || position.actorSync?.feeBasisRevision !== void 0 || position.actorSync?.grossFeeLamports !== void 0 || position.actorSync?.protocolCommissionLamports !== void 0 || position.actorSync?.waivedProtocolCommissionLamports !== void 0) {
66
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
67
+ }
68
+ return;
69
+ }
70
+ requireLpFeePolicyCapability(position.lpFeePolicyVersion);
71
+ const policy = validateLpFeePolicy(position.feePolicy);
72
+ const sync = position.actorSync;
73
+ const actorPolicy = validateLpFeePolicy(sync?.feePolicy);
74
+ if (JSON.stringify(policy) !== JSON.stringify(actorPolicy) || previous !== void 0 && (previous.feePolicy === void 0 || JSON.stringify(policy) !== JSON.stringify(validateLpFeePolicy(previous.feePolicy))) || sync?.lpPositionId !== position.id || sync?.redeemableProjectionAvailable !== true || !Number.isSafeInteger(sync.feeBasisRevision) || sync.feeBasisRevision < 0 || !Number.isSafeInteger(sync.grossFeeLamports) || sync.grossFeeLamports < 0 || !Number.isSafeInteger(sync.protocolCommissionLamports) || sync.protocolCommissionLamports < 0 || !Number.isSafeInteger(sync.waivedProtocolCommissionLamports) || sync.waivedProtocolCommissionLamports < 0 || sync.economicReturn === void 0) {
75
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
76
+ }
77
+ if (BigInt(sync.protocolCommissionLamports) + BigInt(sync.waivedProtocolCommissionLamports) !== BigInt(sync.grossFeeLamports) * BigInt(policy.commission_bps) / LP_BASIS_POINT_DENOMINATOR) {
78
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
79
+ }
80
+ for (const history of sync.redemptionHistory ?? []) {
81
+ if (history.feePolicyHash !== policy.policy_hash || !Number.isSafeInteger(history.feeBasisRevision) || history.feeBasisRevision < 0) {
82
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
83
+ }
84
+ }
85
+ const summary = sync.economicReturn;
86
+ const totals = [
87
+ summary.providerRewardLamports,
88
+ summary.sourceExpenseCompensationLamports,
89
+ summary.chargedReimbursementLossLamports
90
+ ];
91
+ if (!totals.every(
92
+ (value) => typeof value === "string" && LP_ECONOMIC_UNSIGNED_PATTERN.test(value) && BigInt(value) <= LP_ECONOMIC_UNSIGNED_MAX
93
+ ) || typeof summary.netEconomicReturnLamports !== "string" || !LP_ECONOMIC_SIGNED_PATTERN.test(summary.netEconomicReturnLamports)) {
94
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
95
+ }
96
+ const net = BigInt(summary.netEconomicReturnLamports);
97
+ if (net < LP_ECONOMIC_SIGNED_MIN || net > LP_ECONOMIC_SIGNED_MAX || net !== BigInt(summary.providerRewardLamports) - BigInt(summary.chargedReimbursementLossLamports) - BigInt(sync.protocolCommissionLamports)) {
98
+ throw new CommandError("REJECTED_STATE", LP_FEE_POLICY_INVALID_MESSAGE);
99
+ }
100
+ }
101
+
8
102
  // src/lp/positionCode.ts
9
103
  var LEGACY_CODE_PREFIX = "ilp1";
10
104
  var CODE_PREFIX = "ilp2";
@@ -235,6 +329,7 @@ var LP_POSITION_HYDRATION_TIMEOUT_MS = LP_COMMAND_REQUEST_TIMEOUT_MS;
235
329
  var LP_POSITION_SNAPSHOT_MAX_ATTEMPTS = 3;
236
330
  var LP_SHARD_SNAPSHOT_CHANGED_MESSAGE = "LP shard snapshot changed";
237
331
  var LP_SHARD_PAGE_INVALID_MESSAGE = "coordinator LP shard page is invalid";
332
+ var LP_POSITION_OPEN_IDENTITY_MISMATCH_MESSAGE = "Coordinator returned a different LP position identity or creation intent";
238
333
  var LP_SHARD_SNAPSHOT_HASH_PATTERN = /^[0-9a-f]{64}$/;
239
334
  var LP_CREATION_CHALLENGE_REQUEST_TIMEOUT_MS = 3e4;
240
335
  var LP_CREATION_WALLET_SIGNATURE_TIMEOUT_MS = 45e3;
@@ -248,62 +343,108 @@ var protocolPromise = null;
248
343
  function createLpCoordinatorClient(session, options = {}) {
249
344
  const sessionRef = { current: session };
250
345
  const reconnectSession = options.reconnectSession === void 0 ? void 0 : async () => {
346
+ options.signal?.throwIfAborted();
251
347
  const nextSession = await options.reconnectSession();
348
+ options.signal?.throwIfAborted();
252
349
  sessionRef.current = nextSession;
253
350
  return nextSession;
254
351
  };
352
+ const run = (operation, signal) => withLpClientSignal(options.signal, signal, operation);
255
353
  return {
256
354
  authorizePositionCreation(intent, signer) {
257
- return authorizePositionCreation(sessionRef.current, intent, signer);
355
+ return run((signal) => authorizePositionCreation(sessionRef.current, intent, signer, signal));
258
356
  },
259
- initDkg(input, auth) {
260
- return initDkg(sessionRef.current, input, auth);
357
+ initDkg(input, auth, options2) {
358
+ return run((signal) => initDkg(sessionRef.current, input, auth, { signal }), options2?.signal);
261
359
  },
262
360
  abortDkgSessions(lpPositionId, auth) {
263
361
  return abortDkgSessions(sessionRef.current, lpPositionId, auth);
264
362
  },
265
- dkgRound1(input, auth) {
266
- return dkgRound1(sessionRef.current, input, auth);
363
+ dkgRound1(input, auth, options2) {
364
+ return run(
365
+ (signal) => dkgRound1(sessionRef.current, input, auth, { signal }),
366
+ options2?.signal
367
+ );
267
368
  },
268
- dkgRound2(input, auth) {
269
- return dkgRound2(sessionRef.current, input, auth);
369
+ dkgRound2(input, auth, options2) {
370
+ return run(
371
+ (signal) => dkgRound2(sessionRef.current, input, auth, { signal }),
372
+ options2?.signal
373
+ );
270
374
  },
271
375
  openPosition(input, auth) {
272
- return openPosition(sessionRef.current, input, auth, reconnectSession);
376
+ return run(
377
+ (signal) => openPosition(sessionRef.current, input, auth, reconnectSession, signal)
378
+ );
273
379
  },
274
380
  getPosition(lpPositionId, auth, options2) {
275
- return getPosition(sessionRef.current, lpPositionId, auth, options2);
381
+ return run(
382
+ (signal) => getPosition(sessionRef.current, lpPositionId, auth, { ...options2, signal }),
383
+ options2?.signal
384
+ );
276
385
  },
277
386
  finalizeDkgBatch(input, auth) {
278
- return finalizeDkgBatch(sessionRef.current, input, auth);
387
+ return run((signal) => finalizeDkgBatch(sessionRef.current, input, auth, { signal }));
279
388
  },
280
389
  refillPosition(input, auth) {
281
- return refillPosition(sessionRef.current, input, auth);
390
+ return run((signal) => refillPosition(sessionRef.current, input, auth, { signal }));
282
391
  },
283
392
  reconcileFunding(lpPositionId, auth) {
284
- return reconcileFunding(sessionRef.current, lpPositionId, auth);
393
+ return run((signal) => reconcileFunding(sessionRef.current, lpPositionId, auth, { signal }));
285
394
  },
286
395
  planFunding(input, auth) {
287
- return planFunding(sessionRef.current, input, auth);
396
+ return run((signal) => planFunding(sessionRef.current, input, auth, { signal }));
288
397
  },
289
398
  confirmFunding(input, auth) {
290
- return confirmFunding(sessionRef.current, input, auth);
399
+ return run((signal) => confirmFunding(sessionRef.current, input, auth, { signal }));
291
400
  },
292
401
  prepareTopUp(lpPositionId, auth) {
293
- return prepareTopUp(sessionRef.current, lpPositionId, auth);
402
+ return run((signal) => prepareTopUp(sessionRef.current, lpPositionId, auth, { signal }));
294
403
  },
295
404
  getTopUpStatus(input, auth) {
296
- return getTopUpStatus(sessionRef.current, input, auth);
405
+ return run((signal) => getTopUpStatus(sessionRef.current, input, auth, { signal }));
297
406
  },
298
407
  makeAvailable(input, auth) {
299
- return makeAvailable(sessionRef.current, input, auth);
408
+ return run((signal) => makeAvailable(sessionRef.current, input, auth, { signal }));
300
409
  },
301
410
  executeWithdrawal(input, auth) {
302
- return executeWithdrawal(sessionRef.current, input, auth);
411
+ return run((signal) => executeWithdrawal(sessionRef.current, input, auth, { signal }));
412
+ }
413
+ };
414
+ }
415
+ async function withLpClientSignal(clientSignal, requestSignal, operation) {
416
+ const perform = async (signal) => {
417
+ try {
418
+ signal?.throwIfAborted();
419
+ return await operation(signal);
420
+ } catch (error) {
421
+ if (clientSignal?.aborted && signal?.reason === clientSignal.reason && error instanceof Error && error.name === "AbortError") {
422
+ throw clientSignal.reason;
423
+ }
424
+ throw error;
303
425
  }
304
426
  };
427
+ if (clientSignal === void 0) return perform(requestSignal);
428
+ if (requestSignal === void 0 || requestSignal === clientSignal) {
429
+ clientSignal.throwIfAborted();
430
+ return perform(clientSignal);
431
+ }
432
+ const controller = new AbortController();
433
+ const abortClient = () => controller.abort(clientSignal.reason);
434
+ const abortRequest = () => controller.abort(requestSignal.reason);
435
+ clientSignal.addEventListener("abort", abortClient, { once: true });
436
+ requestSignal.addEventListener("abort", abortRequest, { once: true });
437
+ try {
438
+ if (clientSignal.aborted) abortClient();
439
+ if (requestSignal.aborted) abortRequest();
440
+ controller.signal.throwIfAborted();
441
+ return await perform(controller.signal);
442
+ } finally {
443
+ clientSignal.removeEventListener("abort", abortClient);
444
+ requestSignal.removeEventListener("abort", abortRequest);
445
+ }
305
446
  }
306
- async function authorizePositionCreation(session, intent, signer) {
447
+ async function authorizePositionCreation(session, intent, signer, signal) {
307
448
  const signerPublicKey = publicKey(signer.publicKey);
308
449
  const challengeRequestStartedAtMs = Date.now();
309
450
  const challengeEnvelope = await requestEnvelope(
@@ -321,7 +462,7 @@ async function authorizePositionCreation(session, intent, signer) {
321
462
  }
322
463
  }
323
464
  },
324
- { timeoutMs: LP_CREATION_CHALLENGE_REQUEST_TIMEOUT_MS }
465
+ { timeoutMs: LP_CREATION_CHALLENGE_REQUEST_TIMEOUT_MS, signal }
325
466
  );
326
467
  const challenge = challengeEnvelope.payload;
327
468
  if (challenge.case !== "challenge") {
@@ -330,6 +471,16 @@ async function authorizePositionCreation(session, intent, signer) {
330
471
  "Coordinator returned an unexpected LP creation authorization response"
331
472
  );
332
473
  }
474
+ requireLpFeePolicyCapability(challenge.value.lp_fee_policy_version);
475
+ const feePolicy = validateLpFeePolicy(challenge.value.fee_policy);
476
+ const consentLine = `LP Fee Policy: ${feePolicy.policy_hash}`;
477
+ const policyResource = `&fee_policy_hash=${feePolicy.policy_hash}&fee_policy_version=${feePolicy.version}`;
478
+ if (!challenge.value.message.split("\n").includes(consentLine) || !challenge.value.message.includes(policyResource)) {
479
+ throw new CommandError(
480
+ "REJECTED_STATE",
481
+ "Coordinator LP creation challenge does not bind the authorized fee policy"
482
+ );
483
+ }
333
484
  const localAuthorizationDeadlineMs = challengeRequestStartedAtMs + challenge.value.valid_for_ms;
334
485
  const walletTimeoutMs = lpCreationAuthorizationStepTimeout(
335
486
  localAuthorizationDeadlineMs,
@@ -367,7 +518,7 @@ async function authorizePositionCreation(session, intent, signer) {
367
518
  session,
368
519
  CoordinatorMessageType.LpCreateAuthorization,
369
520
  proofPayload,
370
- { timeoutMs: proofTimeoutMs }
521
+ { timeoutMs: proofTimeoutMs, signal }
371
522
  );
372
523
  } catch (error) {
373
524
  if (!isAmbiguousCoordinatorRequestFailure(error)) throw error;
@@ -380,7 +531,7 @@ async function authorizePositionCreation(session, intent, signer) {
380
531
  session,
381
532
  CoordinatorMessageType.LpCreateAuthorization,
382
533
  proofPayload,
383
- { timeoutMs: proofRetryTimeoutMs }
534
+ { timeoutMs: proofRetryTimeoutMs, signal }
384
535
  );
385
536
  }
386
537
  const authorization = proofEnvelope.payload;
@@ -401,7 +552,11 @@ async function authorizePositionCreation(session, intent, signer) {
401
552
  LP_CREATION_AUTHORIZATION_MARGIN_MS,
402
553
  LP_CREATION_MIN_STEP_TIMEOUT_MS
403
554
  );
404
- return { authorizationId: authorization.value.authorization_id };
555
+ requireLpFeePolicyCapability(authorization.value.lp_fee_policy_version);
556
+ if (JSON.stringify(validateLpFeePolicy(authorization.value.fee_policy)) !== JSON.stringify(feePolicy)) {
557
+ throw new CommandError("REJECTED_STATE", "Coordinator changed the authorized LP fee policy");
558
+ }
559
+ return { authorizationId: authorization.value.authorization_id, feePolicy };
405
560
  }
406
561
  function lpCreationAuthorizationStepTimeout(localDeadlineMs, reservedMs, maximumTimeoutMs) {
407
562
  const availableMs = localDeadlineMs - Date.now() - reservedMs;
@@ -433,15 +588,22 @@ async function signLpCreationMessageWithTimeout(signer, message, timeoutMs) {
433
588
  if (timeoutId !== void 0) clearTimeout(timeoutId);
434
589
  }
435
590
  }
436
- async function initDkg(session, input, auth) {
437
- const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_init", {
438
- case: "dkgInit",
439
- value: { index: input.index }
440
- });
591
+ async function initDkg(session, input, auth, options = {}) {
592
+ const response = await requestForPosition(
593
+ session,
594
+ input.lpPositionId,
595
+ auth,
596
+ "dkg_init",
597
+ {
598
+ case: "dkgInit",
599
+ value: { index: input.index }
600
+ },
601
+ options
602
+ );
441
603
  if (response.case !== "dkgInit") throw new Error("Unexpected LP DKG init response");
442
604
  return { sessionId: response.value.session_id };
443
605
  }
444
- async function abortDkgSessions(session, lpPositionId, auth) {
606
+ async function abortDkgSessions(session, lpPositionId, auth, signal) {
445
607
  const response = await requestForPosition(
446
608
  session,
447
609
  lpPositionId,
@@ -451,34 +613,46 @@ async function abortDkgSessions(session, lpPositionId, auth) {
451
613
  case: "dkgAbort",
452
614
  value: {}
453
615
  },
454
- { timeoutMs: LP_DKG_ABORT_REQUEST_TIMEOUT_MS }
616
+ { timeoutMs: LP_DKG_ABORT_REQUEST_TIMEOUT_MS, signal }
455
617
  );
456
618
  if (response.case !== "dkgAbort") throw new Error("Unexpected LP DKG abort response");
457
619
  return { abortedSessionCount: response.value.aborted_session_count };
458
620
  }
459
- async function dkgRound1(session, input, auth) {
460
- const response = await request(session, auth, "dkg_round1", {
461
- case: "dkgRound1",
462
- value: {
463
- session_id: input.sessionId,
464
- package: Uint8Array.from(input.package)
465
- }
466
- });
621
+ async function dkgRound1(session, input, auth, options = {}) {
622
+ const response = await request(
623
+ session,
624
+ auth,
625
+ "dkg_round1",
626
+ {
627
+ case: "dkgRound1",
628
+ value: {
629
+ session_id: input.sessionId,
630
+ package: Uint8Array.from(input.package)
631
+ }
632
+ },
633
+ options
634
+ );
467
635
  if (response.case !== "dkgRound1") throw new Error("Unexpected LP DKG round1 response");
468
636
  return {
469
637
  package: response.value.package,
470
638
  round2ForLp: response.value.round2_for_lp
471
639
  };
472
640
  }
473
- async function dkgRound2(session, input, auth) {
474
- const response = await request(session, auth, "dkg_round2", {
475
- case: "dkgRound2",
476
- value: {
477
- session_id: input.sessionId,
478
- package: Uint8Array.from(input.package),
479
- delegated_share: Uint8Array.from(input.delegatedShare)
480
- }
481
- });
641
+ async function dkgRound2(session, input, auth, options = {}) {
642
+ const response = await request(
643
+ session,
644
+ auth,
645
+ "dkg_round2",
646
+ {
647
+ case: "dkgRound2",
648
+ value: {
649
+ session_id: input.sessionId,
650
+ package: Uint8Array.from(input.package),
651
+ delegated_share: Uint8Array.from(input.delegatedShare)
652
+ }
653
+ },
654
+ options
655
+ );
482
656
  if (response.case !== "dkgRound2") throw new Error("Unexpected LP DKG round2 response");
483
657
  return {
484
658
  address: response.value.address,
@@ -487,11 +661,12 @@ async function dkgRound2(session, input, auth) {
487
661
  index: response.value.index
488
662
  };
489
663
  }
490
- async function openPosition(session, input, auth, reconnectSession) {
664
+ async function openPosition(session, input, auth, reconnectSession, signal) {
491
665
  const payload = {
492
666
  case: "positionOpen",
493
667
  value: {
494
668
  creation_authorization_id: input.creationAuthorizationId,
669
+ fee_policy: input.feePolicy,
495
670
  auth_public_key: Uint8Array.from(input.authPublicKey),
496
671
  refill_public_key: Uint8Array.from(input.refillPublicKey),
497
672
  redemption_commitment: Uint8Array.from(input.withdrawalCommitment),
@@ -505,12 +680,14 @@ async function openPosition(session, input, auth, reconnectSession) {
505
680
  };
506
681
  const commandDeadlineMs = Date.now() + LP_POSITION_OPEN_COMMAND_TTL_MS;
507
682
  const commandOptions = {
683
+ signal,
508
684
  commandNonce: randomBytes(32),
509
685
  commandExpiresAtMs: commandDeadlineMs,
510
686
  deadlineMs: commandDeadlineMs,
511
687
  timeoutMs: LP_POSITION_OPEN_REQUEST_TIMEOUT_MS
512
688
  };
513
689
  let response;
690
+ let responseSession = session;
514
691
  try {
515
692
  response = await requestForPosition(
516
693
  session,
@@ -522,19 +699,31 @@ async function openPosition(session, input, auth, reconnectSession) {
522
699
  );
523
700
  } catch (error) {
524
701
  if (!isAmbiguousLpPositionOpenFailure(error)) throw error;
525
- const replaySession = reconnectSession === void 0 ? session : await reconnectSessionWithinDeadline(reconnectSession, commandDeadlineMs);
526
- response = await requestForPosition(
527
- replaySession,
528
- input.lpPositionId,
529
- auth,
530
- "position_open",
531
- payload,
532
- commandOptions
702
+ responseSession = reconnectSession === void 0 ? session : await reconnectSessionWithinDeadline(reconnectSession, commandDeadlineMs, signal);
703
+ }
704
+ const position = await hydratePositionResponse(
705
+ responseSession,
706
+ auth.lpPositionId,
707
+ auth,
708
+ response,
709
+ {
710
+ deadlineMs: commandDeadlineMs,
711
+ signal
712
+ }
713
+ );
714
+ if (position.authPublicKey !== bytesToHex(Uint8Array.from(input.authPublicKey)) || position.refillPublicKey !== bytesToHex(Uint8Array.from(input.refillPublicKey)) || position.withdrawalCommitment !== bytesToHex(Uint8Array.from(input.withdrawalCommitment)) || position.committedLamports !== input.committedLamports || position.targetShardCount !== input.shardCount || position.refillThreshold !== input.refillThreshold || position.createdAt !== input.createdAtMs) {
715
+ throw new CommandError("REJECTED_STATE", LP_POSITION_OPEN_IDENTITY_MISMATCH_MESSAGE);
716
+ }
717
+ if (input.feePolicy !== void 0 && position.feePolicy?.policy_hash !== input.feePolicy.policy_hash) {
718
+ throw new CommandError(
719
+ "REJECTED_STATE",
720
+ "Coordinator opened the LP position with a different fee policy"
533
721
  );
534
722
  }
535
- return hydratePositionResponse(session, auth.lpPositionId, auth, response);
723
+ return position;
536
724
  }
537
- async function reconnectSessionWithinDeadline(reconnectSession, deadlineMs) {
725
+ async function reconnectSessionWithinDeadline(reconnectSession, deadlineMs, signal) {
726
+ signal?.throwIfAborted();
538
727
  const remainingMs = deadlineMs - Date.now();
539
728
  if (remainingMs <= 0) {
540
729
  throw new CommandError(
@@ -543,8 +732,12 @@ async function reconnectSessionWithinDeadline(reconnectSession, deadlineMs) {
543
732
  );
544
733
  }
545
734
  let timer;
735
+ let removeAbort;
546
736
  try {
547
737
  return await new Promise((resolve, reject) => {
738
+ const onAbort = () => reject(signal?.reason);
739
+ signal?.addEventListener("abort", onAbort, { once: true });
740
+ removeAbort = () => signal?.removeEventListener("abort", onAbort);
548
741
  timer = setTimeout(() => {
549
742
  reject(
550
743
  new CommandError(
@@ -553,10 +746,14 @@ async function reconnectSessionWithinDeadline(reconnectSession, deadlineMs) {
553
746
  )
554
747
  );
555
748
  }, remainingMs);
556
- Promise.resolve().then(reconnectSession).then(resolve, reject);
749
+ Promise.resolve().then(() => {
750
+ signal?.throwIfAborted();
751
+ return reconnectSession();
752
+ }).then(resolve, reject);
557
753
  });
558
754
  } finally {
559
755
  if (timer !== void 0) clearTimeout(timer);
756
+ removeAbort?.();
560
757
  }
561
758
  }
562
759
  function isAmbiguousLpPositionOpenFailure(error) {
@@ -650,6 +847,7 @@ async function hydratePositionResponse(session, lpPositionId, auth, initialRespo
650
847
  if (first.shards_snapshot_hash !== void 0)
651
848
  assertShardPage(first, historyPage, lpPositionId, auth, 0);
652
849
  const historyView = mapPosition(historyPage);
850
+ validateLpPositionFeePolicy(historyView, position);
653
851
  const merged = mergePositionHistoryPages(position, historyView);
654
852
  position = first.shards_snapshot_hash === void 0 ? merged : {
655
853
  ...position,
@@ -677,7 +875,7 @@ function assertShardPage(first, page, lpPositionId, auth, startIndex) {
677
875
  const invalid = () => {
678
876
  throw new CommandError("REJECTED_STATE", LP_SHARD_PAGE_INVALID_MESSAGE);
679
877
  };
680
- if (total === void 0 || !Number.isInteger(total) || total < 1 || total > LP_MAX_TOTAL_SHARDS || total > first.target_shard_count || first.status !== "dkg_pending" && total !== first.target_shard_count || first.shards_snapshot_hash === void 0 || !LP_SHARD_SNAPSHOT_HASH_PATTERN.test(first.shards_snapshot_hash) || page.lp_position_id !== lpPositionId || page.shards_snapshot_hash !== first.shards_snapshot_hash || page.shards_total_count !== total || page.target_shard_count !== first.target_shard_count || page.status !== "dkg_pending" && total !== page.target_shard_count || page.created_at_ms !== first.created_at_ms || page.committed_lamports !== first.committed_lamports || page.earned_lamports !== first.earned_lamports || page.refill_threshold !== first.refill_threshold || bytesToHex(page.auth_public_key) !== bytesToHex(auth.authPublicKey) || bytesToHex(page.refill_public_key) !== bytesToHex(first.refill_public_key) || bytesToHex(page.redemption_commitment) !== bytesToHex(first.redemption_commitment) || page.shards.length !== Math.min(LP_SHARD_PAGE_SIZE, total - startIndex) || page.shards.length === 0)
878
+ if (total === void 0 || !Number.isInteger(total) || total < 1 || total > LP_MAX_TOTAL_SHARDS || total > first.target_shard_count || first.status !== "dkg_pending" && total !== first.target_shard_count || first.shards_snapshot_hash === void 0 || !LP_SHARD_SNAPSHOT_HASH_PATTERN.test(first.shards_snapshot_hash) || page.lp_position_id !== lpPositionId || page.shards_snapshot_hash !== first.shards_snapshot_hash || page.shards_total_count !== total || page.target_shard_count !== first.target_shard_count || page.status !== "dkg_pending" && total !== page.target_shard_count || page.created_at_ms !== first.created_at_ms || page.committed_lamports !== first.committed_lamports || page.earned_lamports !== first.earned_lamports || page.refill_threshold !== first.refill_threshold || page.fee_policy?.policy_hash !== first.fee_policy?.policy_hash || page.lp_fee_policy_version !== first.lp_fee_policy_version || bytesToHex(page.auth_public_key) !== bytesToHex(auth.authPublicKey) || bytesToHex(page.refill_public_key) !== bytesToHex(first.refill_public_key) || bytesToHex(page.redemption_commitment) !== bytesToHex(first.redemption_commitment) || page.shards.length !== Math.min(LP_SHARD_PAGE_SIZE, total - startIndex) || page.shards.length === 0)
681
879
  invalid();
682
880
  for (const [offset, shard] of page.shards.entries()) {
683
881
  const index = startIndex + offset;
@@ -703,48 +901,76 @@ function lpPositionPageRequestOptions(options) {
703
901
  timeoutMs: Math.min(options.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS, remainingMs)
704
902
  };
705
903
  }
706
- async function finalizeDkgBatch(session, input, auth) {
707
- const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_finalize", {
708
- case: "dkgFinalize",
709
- value: {
710
- start_index: input.startIndex,
711
- addresses: input.addresses,
712
- finalized_at_ms: input.finalizedAtMs
713
- }
714
- });
715
- return hydratePositionResponse(session, auth.lpPositionId, auth, response);
904
+ async function finalizeDkgBatch(session, input, auth, options = {}) {
905
+ const response = await requestForPosition(
906
+ session,
907
+ input.lpPositionId,
908
+ auth,
909
+ "dkg_finalize",
910
+ {
911
+ case: "dkgFinalize",
912
+ value: {
913
+ start_index: input.startIndex,
914
+ addresses: input.addresses,
915
+ finalized_at_ms: input.finalizedAtMs
916
+ }
917
+ },
918
+ options
919
+ );
920
+ return hydratePositionResponse(session, auth.lpPositionId, auth, response, options);
716
921
  }
717
- async function refillPosition(session, input, auth) {
718
- const response = await requestForPosition(session, input.lpPositionId, auth, "refill", {
719
- case: "refill",
720
- value: {
721
- shard_count: input.shardCount,
722
- shard_amount_lamports: input.shardAmountLamports,
723
- addresses: input.addresses,
724
- authorized_at_ms: input.authorizedAtMs
725
- }
726
- });
727
- return hydratePositionResponse(session, auth.lpPositionId, auth, response);
922
+ async function refillPosition(session, input, auth, options = {}) {
923
+ const response = await requestForPosition(
924
+ session,
925
+ input.lpPositionId,
926
+ auth,
927
+ "refill",
928
+ {
929
+ case: "refill",
930
+ value: {
931
+ shard_count: input.shardCount,
932
+ shard_amount_lamports: input.shardAmountLamports,
933
+ addresses: input.addresses,
934
+ authorized_at_ms: input.authorizedAtMs
935
+ }
936
+ },
937
+ options
938
+ );
939
+ return hydratePositionResponse(session, auth.lpPositionId, auth, response, options);
728
940
  }
729
- async function reconcileFunding(session, lpPositionId, auth) {
730
- const response = await requestForPosition(session, lpPositionId, auth, "reconcile_funding", {
731
- case: "reconcileFunding",
732
- value: {}
733
- });
734
- return hydratePositionResponse(session, auth.lpPositionId, auth, response);
941
+ async function reconcileFunding(session, lpPositionId, auth, options = {}) {
942
+ const response = await requestForPosition(
943
+ session,
944
+ lpPositionId,
945
+ auth,
946
+ "reconcile_funding",
947
+ {
948
+ case: "reconcileFunding",
949
+ value: {}
950
+ },
951
+ options
952
+ );
953
+ return hydratePositionResponse(session, auth.lpPositionId, auth, response, options);
735
954
  }
736
- async function planFunding(session, input, auth) {
737
- const response = await requestForPosition(session, input.lpPositionId, auth, "funding_plan", {
738
- case: "fundingPlan",
739
- value: {
740
- shard_count: input.shardCount,
741
- planned_at_ms: input.nowMs
742
- }
743
- });
955
+ async function planFunding(session, input, auth, options = {}) {
956
+ const response = await requestForPosition(
957
+ session,
958
+ input.lpPositionId,
959
+ auth,
960
+ "funding_plan",
961
+ {
962
+ case: "fundingPlan",
963
+ value: {
964
+ shard_count: input.shardCount,
965
+ planned_at_ms: input.nowMs
966
+ }
967
+ },
968
+ options
969
+ );
744
970
  if (response.case !== "shards") throw new Error("Unexpected LP funding plan response");
745
971
  return response.value.map(mapShard);
746
972
  }
747
- async function confirmFunding(session, input, auth) {
973
+ async function confirmFunding(session, input, auth, options = {}) {
748
974
  const response = await requestForPosition(
749
975
  session,
750
976
  input.lpPositionId,
@@ -757,38 +983,60 @@ async function confirmFunding(session, input, auth) {
757
983
  amount_lamports: input.amountLamports,
758
984
  funding_tx_signature: input.fundingTxSignature
759
985
  }
760
- }
986
+ },
987
+ options
761
988
  );
762
989
  if (response.case !== "shard") throw new Error("Unexpected LP funding confirmation response");
763
990
  return mapShard(response.value);
764
991
  }
765
- async function prepareTopUp(session, lpPositionId, auth) {
766
- const response = await requestForPosition(session, lpPositionId, auth, "prepare_top_up", {
767
- case: "prepareTopUp",
768
- value: {}
769
- });
992
+ async function prepareTopUp(session, lpPositionId, auth, options = {}) {
993
+ const response = await requestForPosition(
994
+ session,
995
+ lpPositionId,
996
+ auth,
997
+ "prepare_top_up",
998
+ {
999
+ case: "prepareTopUp",
1000
+ value: {}
1001
+ },
1002
+ options
1003
+ );
770
1004
  if (response.case !== "topUpRequest") throw new Error("Unexpected LP top-up response");
771
1005
  return mapTopUpRequest(response.value);
772
1006
  }
773
- async function getTopUpStatus(session, input, auth) {
774
- const response = await requestForPosition(session, input.lpPositionId, auth, "top_up_status", {
775
- case: "topUpStatus",
776
- value: { top_up_id: input.topUpId }
777
- });
1007
+ async function getTopUpStatus(session, input, auth, options = {}) {
1008
+ const response = await requestForPosition(
1009
+ session,
1010
+ input.lpPositionId,
1011
+ auth,
1012
+ "top_up_status",
1013
+ {
1014
+ case: "topUpStatus",
1015
+ value: { top_up_id: input.topUpId }
1016
+ },
1017
+ options
1018
+ );
778
1019
  if (response.case !== "topUpRequest") throw new Error("Unexpected LP top-up response");
779
1020
  return mapTopUpRequest(response.value);
780
1021
  }
781
- async function makeAvailable(session, input, auth) {
782
- const response = await requestForPosition(session, input.lpPositionId, auth, "make_available", {
783
- case: "makeAvailable",
784
- value: {
785
- shard_id: input.shardId
786
- }
787
- });
1022
+ async function makeAvailable(session, input, auth, options = {}) {
1023
+ const response = await requestForPosition(
1024
+ session,
1025
+ input.lpPositionId,
1026
+ auth,
1027
+ "make_available",
1028
+ {
1029
+ case: "makeAvailable",
1030
+ value: {
1031
+ shard_id: input.shardId
1032
+ }
1033
+ },
1034
+ options
1035
+ );
788
1036
  if (response.case !== "shard") throw new Error("Unexpected LP availability response");
789
1037
  return mapShard(response.value);
790
1038
  }
791
- async function executeWithdrawal(session, input, auth) {
1039
+ async function executeWithdrawal(session, input, auth, options = {}) {
792
1040
  validateLpWithdrawalPlan({
793
1041
  destinationAddresses: input.destinationAddresses,
794
1042
  depositPrincipalLamports: input.withdrawalMode === "max_available" ? 1 : input.depositPrincipalLamports,
@@ -798,20 +1046,35 @@ async function executeWithdrawal(session, input, auth) {
798
1046
  // a one-destination request based on an unknown current source count.
799
1047
  allowManyToOne: input.withdrawalMode === "max_available" || input.allowManyToOne
800
1048
  });
801
- const response = await requestForPosition(session, input.lpPositionId, auth, "withdraw_request", {
802
- case: "withdrawRequest",
803
- value: {
804
- destination_addresses: input.destinationAddresses,
805
- deposit_principal_lamports: input.withdrawalMode === "max_available" ? 0 : input.depositPrincipalLamports,
806
- earned_fee_lamports: input.withdrawalMode === "max_available" ? 0 : input.earnedFeeLamports,
807
- withdrawal_mode: input.withdrawalMode,
808
- requested_at_ms: input.requestedAtMs
809
- }
810
- });
1049
+ const response = await requestForPosition(
1050
+ session,
1051
+ input.lpPositionId,
1052
+ auth,
1053
+ "withdraw_request",
1054
+ {
1055
+ case: "withdrawRequest",
1056
+ value: {
1057
+ destination_addresses: input.destinationAddresses,
1058
+ deposit_principal_lamports: input.withdrawalMode === "max_available" ? 0 : input.depositPrincipalLamports,
1059
+ earned_fee_lamports: input.withdrawalMode === "max_available" ? 0 : input.earnedFeeLamports,
1060
+ withdrawal_mode: input.withdrawalMode,
1061
+ requested_at_ms: input.requestedAtMs,
1062
+ fee_policy_hash: input.feePolicyHash,
1063
+ fee_basis_revision: input.feeBasisRevision
1064
+ }
1065
+ },
1066
+ options
1067
+ );
811
1068
  if (response.case !== "withdrawalExecution") {
812
1069
  throw new Error("Unexpected LP withdrawal execution response");
813
1070
  }
814
1071
  const execution = response.value;
1072
+ if (execution.fee_policy_hash !== input.feePolicyHash || execution.fee_basis_revision !== input.feeBasisRevision) {
1073
+ throw new PolicyValidationError(
1074
+ "INVALID_WITHDRAWAL_PLAN",
1075
+ "Coordinator accepted a different LP fee policy or fee basis"
1076
+ );
1077
+ }
815
1078
  if (BigInt(execution.accepted_lamports) !== BigInt(execution.accepted_deposit_principal_lamports) + BigInt(execution.accepted_earned_fee_lamports)) {
816
1079
  throw new PolicyValidationError(
817
1080
  "INVALID_WITHDRAWAL_PLAN",
@@ -825,6 +1088,8 @@ async function executeWithdrawal(session, input, auth) {
825
1088
  );
826
1089
  }
827
1090
  return {
1091
+ feePolicyHash: execution.fee_policy_hash,
1092
+ feeBasisRevision: execution.fee_basis_revision,
828
1093
  withdrawalId: execution.withdrawal_id,
829
1094
  txSignatures: [...execution.tx_signatures],
830
1095
  acceptedLamports: execution.accepted_lamports,
@@ -832,13 +1097,15 @@ async function executeWithdrawal(session, input, auth) {
832
1097
  acceptedEarnedFeeLamports: execution.accepted_earned_fee_lamports
833
1098
  };
834
1099
  }
835
- function request(session, auth, action, payload) {
836
- return requestForPosition(session, auth.lpPositionId, auth, action, payload);
1100
+ function request(session, auth, action, payload, options = {}) {
1101
+ return requestForPosition(session, auth.lpPositionId, auth, action, payload, options);
837
1102
  }
838
1103
  async function requestForPosition(session, lpPositionId, auth, action, payload, options = {}) {
1104
+ options.signal?.throwIfAborted();
839
1105
  const timeoutMs = lpPositionPageRequestOptions(options).timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS;
840
1106
  assertAuthMatchesPosition(lpPositionId, auth);
841
1107
  const protocol = await loadProtocol();
1108
+ options.signal?.throwIfAborted();
842
1109
  const state = getSessionState(session);
843
1110
  const attestation = state.channel?.attestation;
844
1111
  if (!attestation) {
@@ -855,7 +1122,9 @@ async function requestForPosition(session, lpPositionId, auth, action, payload,
855
1122
  ),
856
1123
  payload
857
1124
  };
1125
+ options.signal?.throwIfAborted();
858
1126
  const signature = await signLpCommandBytes(auth, protocol.encodeLpCommandForSigning(command));
1127
+ options.signal?.throwIfAborted();
859
1128
  const { type, payload: envelopePayload } = protocol.lpSignedCommandEnvelope(command, signature);
860
1129
  const requestOptions = lpPositionPageRequestOptions(options);
861
1130
  const envelope = await requestEnvelope(session, type, envelopePayload, {
@@ -872,7 +1141,9 @@ function assertAuthMatchesPosition(lpPositionId, auth) {
872
1141
  );
873
1142
  }
874
1143
  function mapPosition(position) {
875
- return {
1144
+ const mapped = {
1145
+ feePolicy: position.fee_policy === void 0 ? void 0 : validateLpFeePolicy(position.fee_policy),
1146
+ lpFeePolicyVersion: position.lp_fee_policy_version,
876
1147
  id: position.lp_position_id,
877
1148
  status: position.status,
878
1149
  targetShardCount: position.target_shard_count,
@@ -887,6 +1158,8 @@ function mapPosition(position) {
887
1158
  redemptionPermission: mapLpRedemptionPermission(position.redemption_permission),
888
1159
  ...position.actor_sync === void 0 ? {} : { actorSync: mapLpActorSync(position.actor_sync) }
889
1160
  };
1161
+ validateLpPositionFeePolicy(mapped);
1162
+ return mapped;
890
1163
  }
891
1164
  function mapLpRedemptionPermission(permission) {
892
1165
  if (permission === void 0) {
@@ -916,11 +1189,22 @@ function mapLpActorSync(actorSync) {
916
1189
  );
917
1190
  }
918
1191
  const redeemableProjectionAvailable = redeemableBucketCount === redeemableBucketValues.length;
919
- return {
920
- ...actorSync.economic_return === void 0 ? {} : { economicReturn: mapLpEconomicReturn(actorSync.economic_return) },
1192
+ return normalizeLpActorSync({
1193
+ ...actorSync.economic_return === void 0 ? {} : {
1194
+ economicReturn: mapLpEconomicReturn(
1195
+ actorSync.economic_return,
1196
+ actorSync.protocol_commission_lamports ?? 0
1197
+ )
1198
+ },
1199
+ feePolicy: actorSync.fee_policy === void 0 ? void 0 : validateLpFeePolicy(actorSync.fee_policy),
1200
+ feeBasisRevision: actorSync.fee_basis_revision,
1201
+ grossFeeLamports: actorSync.gross_fee_lamports,
1202
+ protocolCommissionLamports: actorSync.protocol_commission_lamports,
1203
+ waivedProtocolCommissionLamports: actorSync.waived_protocol_commission_lamports,
921
1204
  lpPositionId: actorSync.lp_position_id,
922
1205
  status: actorSync.status,
923
1206
  sourceLiquidityUse: actorSync.source_liquidity_use,
1207
+ totalNetFundsLamports: actorSync.total_net_funds_lamports,
924
1208
  redeemableProjectionAvailable,
925
1209
  redeemablePrincipalLamports: actorSync.redeemable_principal_lamports ?? 0,
926
1210
  redeemableFeeLamports: actorSync.redeemable_fee_lamports ?? 0,
@@ -946,14 +1230,14 @@ function mapLpActorSync(actorSync) {
946
1230
  ...actorSync.redemption_history_next_cursor === void 0 ? {} : { redemptionHistoryNextCursor: actorSync.redemption_history_next_cursor },
947
1231
  stateVersion: actorSync.state_version,
948
1232
  updatedAtMs: actorSync.updated_at_ms
949
- };
1233
+ });
950
1234
  }
951
1235
  var LP_ECONOMIC_UNSIGNED_TOTAL_MAX = (1n << 128n) - 1n;
952
1236
  var LP_ECONOMIC_SIGNED_TOTAL_MIN = -(1n << 127n);
953
1237
  var LP_ECONOMIC_SIGNED_TOTAL_MAX = (1n << 127n) - 1n;
954
1238
  var LP_ECONOMIC_UNSIGNED_DECIMAL = /^(0|[1-9][0-9]*)$/;
955
1239
  var LP_ECONOMIC_SIGNED_DECIMAL = /^(0|-?[1-9][0-9]*)$/;
956
- function mapLpEconomicReturn(summary) {
1240
+ function mapLpEconomicReturn(summary, protocolCommissionLamports) {
957
1241
  const unsigned = [
958
1242
  summary.provider_reward_lamports,
959
1243
  summary.source_expense_compensation_lamports,
@@ -965,7 +1249,7 @@ function mapLpEconomicReturn(summary) {
965
1249
  if (!valid)
966
1250
  throw new CommandError("REJECTED_STATE", "coordinator LP economic return totals are invalid");
967
1251
  const net = BigInt(summary.net_economic_return_lamports);
968
- if (net < LP_ECONOMIC_SIGNED_TOTAL_MIN || net > LP_ECONOMIC_SIGNED_TOTAL_MAX || net !== BigInt(summary.provider_reward_lamports) - BigInt(summary.charged_reimbursement_loss_lamports)) {
1252
+ if (net < LP_ECONOMIC_SIGNED_TOTAL_MIN || net > LP_ECONOMIC_SIGNED_TOTAL_MAX || net !== BigInt(summary.provider_reward_lamports) - BigInt(summary.charged_reimbursement_loss_lamports) - BigInt(protocolCommissionLamports)) {
969
1253
  throw new CommandError("REJECTED_STATE", "coordinator LP economic return invariant is invalid");
970
1254
  }
971
1255
  return {
@@ -996,6 +1280,8 @@ function mergePositionHistoryPages(first, next) {
996
1280
  }
997
1281
  function mapLpRedemptionHistory(history) {
998
1282
  return {
1283
+ feePolicyHash: history.fee_policy_hash,
1284
+ feeBasisRevision: history.fee_basis_revision,
999
1285
  redemptionId: history.redemption_id,
1000
1286
  state: history.state,
1001
1287
  ...history.withdrawal_mode === void 0 ? {} : { withdrawalMode: history.withdrawal_mode },
@@ -1077,13 +1363,14 @@ function randomBytes(length) {
1077
1363
  return bytes;
1078
1364
  }
1079
1365
  function loadProtocol() {
1080
- protocolPromise ??= import('./coordinator-DHL3AP7V.js');
1366
+ protocolPromise ??= import('./coordinator-EQEL23A3.js');
1081
1367
  return protocolPromise;
1082
1368
  }
1083
1369
 
1084
1370
  // src/lp/dkgBatch.ts
1085
- var MAX_LP_DKG_CONCURRENCY = 8;
1371
+ var MAX_LP_DKG_CONCURRENCY = 16;
1086
1372
  var DEFAULT_LP_DKG_CONCURRENCY = MAX_LP_DKG_CONCURRENCY;
1373
+ var LP_DKG_WORKER_FAILURE_MESSAGE = "LP DKG worker failed";
1087
1374
  var LP_DKG_WORKER_REQUEST_TIMEOUT_MS = 3e4;
1088
1375
  var LP_DKG_WORKER_REQUEST_TIMEOUT_MESSAGE = "LP DKG worker request timed out";
1089
1376
  var LP_DKG_ABORT_CLEANUP_TIMEOUT_MS = 1e4;
@@ -1093,21 +1380,28 @@ var LP_DKG_COMPLETED_SHARD_POLL_TIMEOUT_MS = 5 * 6e4 + 15e3;
1093
1380
  var LP_DKG_ACTIVE_SESSION_RETRY_INTERVAL_MS = 2e3;
1094
1381
  var LP_DKG_ACTIVE_SESSION_RETRY_TIMEOUT_MS = LP_DKG_COMPLETED_SHARD_POLL_TIMEOUT_MS;
1095
1382
  async function createLpDkgBatch(input) {
1383
+ input.signal?.throwIfAborted();
1096
1384
  if (input.useDeterministicTestAddresses === true) {
1097
1385
  const addresses2 = Array.from(
1098
1386
  { length: input.count },
1099
1387
  (_, offset) => testAddress(input.lpPositionId, input.startIndex + offset)
1100
1388
  );
1101
1389
  for (const [offset, address] of addresses2.entries()) {
1390
+ input.signal?.throwIfAborted();
1102
1391
  input.onProgress?.({
1103
1392
  completed: offset + 1,
1104
1393
  total: input.count,
1105
1394
  address
1106
1395
  });
1107
1396
  }
1397
+ input.signal?.throwIfAborted();
1108
1398
  return addresses2;
1109
1399
  }
1110
- const workerCount = resolveConcurrency(input.count, input.concurrency);
1400
+ const workerCount = resolveLpDkgConcurrency(input.count, input.concurrency);
1401
+ const cancellation = new AbortController();
1402
+ const cancel = () => cancellation.abort(input.signal?.reason);
1403
+ input.signal?.addEventListener("abort", cancel, { once: true });
1404
+ if (input.signal?.aborted) cancel();
1111
1405
  const workers = [];
1112
1406
  let workersTerminated = false;
1113
1407
  const terminateWorkers = () => {
@@ -1120,16 +1414,19 @@ async function createLpDkgBatch(input) {
1120
1414
  let nextOffset = 0;
1121
1415
  try {
1122
1416
  for (let index = 0; index < workerCount; index += 1) {
1417
+ cancellation.signal.throwIfAborted();
1123
1418
  workers.push(createWorker(input.workerFactory));
1124
1419
  }
1125
1420
  await Promise.all(
1126
1421
  workers.map(async (worker) => {
1127
1422
  for (; ; ) {
1423
+ cancellation.signal.throwIfAborted();
1128
1424
  const offset = nextOffset;
1129
1425
  nextOffset += 1;
1130
1426
  if (offset >= input.count) return;
1131
1427
  const index = input.startIndex + offset;
1132
- const address = await createLpDkgShard(input, worker, index);
1428
+ const address = await createLpDkgShard(input, worker, index, cancellation.signal);
1429
+ cancellation.signal.throwIfAborted();
1133
1430
  addresses[offset] = address;
1134
1431
  completed += 1;
1135
1432
  input.onProgress?.({
@@ -1142,12 +1439,14 @@ async function createLpDkgBatch(input) {
1142
1439
  );
1143
1440
  return addresses;
1144
1441
  } catch (error) {
1442
+ cancellation.abort(error);
1145
1443
  terminateWorkers();
1146
1444
  if (!isLpDkgSessionCoordinationError(error)) {
1147
1445
  await abortDkgSessionsAfterFailure(input);
1148
1446
  }
1149
1447
  throw error;
1150
1448
  } finally {
1449
+ input.signal?.removeEventListener("abort", cancel);
1151
1450
  terminateWorkers();
1152
1451
  }
1153
1452
  }
@@ -1171,61 +1470,82 @@ async function abortDkgSessionsAfterFailure(input) {
1171
1470
  if (timeoutId !== void 0) globalThis.clearTimeout(timeoutId);
1172
1471
  }
1173
1472
  }
1174
- async function createLpDkgShard(input, worker, index) {
1473
+ async function createLpDkgShard(input, worker, index, signal) {
1175
1474
  const deadline = Date.now() + LP_DKG_ACTIVE_SESSION_RETRY_TIMEOUT_MS;
1176
1475
  let sawActiveSession = false;
1177
1476
  for (; ; ) {
1178
1477
  if (sawActiveSession) {
1179
- const completedAddress = await getCompletedShardAddress(input, index);
1478
+ const completedAddress = await getCompletedShardAddress(input, index, signal);
1180
1479
  if (completedAddress !== null) return completedAddress;
1181
1480
  }
1182
1481
  try {
1183
- return await runLpDkgShard(input, worker, index);
1482
+ return await runLpDkgShard(input, worker, index, signal);
1184
1483
  } catch (error) {
1185
1484
  if (isLpDkgCompletedOrVisibleError(error)) {
1186
- return waitForCompletedShardAddress(input, index);
1485
+ return waitForCompletedShardAddress(input, index, signal);
1187
1486
  }
1188
1487
  if (!isLpDkgActiveSessionError(error)) throw error;
1189
1488
  sawActiveSession = true;
1190
- const completedAddress = await getCompletedShardAddress(input, index);
1489
+ const completedAddress = await getCompletedShardAddress(input, index, signal);
1191
1490
  if (completedAddress !== null) return completedAddress;
1192
1491
  if (Date.now() >= deadline) throw error;
1193
- await sleep(LP_DKG_ACTIVE_SESSION_RETRY_INTERVAL_MS);
1492
+ await abortable(sleep(LP_DKG_ACTIVE_SESSION_RETRY_INTERVAL_MS), signal);
1194
1493
  }
1195
1494
  }
1196
1495
  }
1197
- async function runLpDkgShard(input, worker, index) {
1496
+ async function runLpDkgShard(input, worker, index, signal) {
1198
1497
  const roundId = `lp_dkg_${Date.now()}_${index}_${Math.random().toString(16).slice(2)}`;
1199
- const initResponse = await input.client.initDkg(
1200
- { lpPositionId: input.lpPositionId, index },
1201
- input.auth
1498
+ signal.throwIfAborted();
1499
+ const initResponse = await abortable(
1500
+ input.client.initDkg({ lpPositionId: input.lpPositionId, index }, input.auth, { signal }),
1501
+ signal
1202
1502
  );
1203
- const round1 = await workerRequest(worker, {
1204
- id: roundId,
1205
- type: "round1"
1206
- });
1207
- if (round1.type !== "round1") throw new Error("LP DKG worker returned wrong round1 type");
1208
- const round1Response = await input.client.dkgRound1(
1503
+ const round1 = await workerRequest(
1504
+ worker,
1209
1505
  {
1210
- sessionId: initResponse.sessionId,
1211
- package: round1.package
1506
+ id: roundId,
1507
+ type: "round1"
1212
1508
  },
1213
- input.auth
1509
+ signal
1214
1510
  );
1215
- const round2 = await workerRequest(worker, {
1216
- id: roundId,
1217
- type: "round2",
1218
- teeRound1: Array.from(round1Response.package),
1219
- teeRound2ForLp: Array.from(round1Response.round2ForLp)
1220
- });
1221
- if (round2.type !== "round2") throw new Error("LP DKG worker returned wrong round2 type");
1222
- const round2Response = await input.client.dkgRound2(
1511
+ if (round1.type !== "round1")
1512
+ throw lpDkgWorkerFailure(new Error("LP DKG worker returned wrong round1 type"));
1513
+ signal.throwIfAborted();
1514
+ const round1Response = await abortable(
1515
+ input.client.dkgRound1(
1516
+ {
1517
+ sessionId: initResponse.sessionId,
1518
+ package: round1.package
1519
+ },
1520
+ input.auth,
1521
+ { signal }
1522
+ ),
1523
+ signal
1524
+ );
1525
+ const round2 = await workerRequest(
1526
+ worker,
1223
1527
  {
1224
- sessionId: initResponse.sessionId,
1225
- package: round2.package,
1226
- delegatedShare: round2.delegatedShare
1528
+ id: roundId,
1529
+ type: "round2",
1530
+ teeRound1: Array.from(round1Response.package),
1531
+ teeRound2ForLp: Array.from(round1Response.round2ForLp)
1227
1532
  },
1228
- input.auth
1533
+ signal
1534
+ );
1535
+ if (round2.type !== "round2")
1536
+ throw lpDkgWorkerFailure(new Error("LP DKG worker returned wrong round2 type"));
1537
+ signal.throwIfAborted();
1538
+ const round2Response = await abortable(
1539
+ input.client.dkgRound2(
1540
+ {
1541
+ sessionId: initResponse.sessionId,
1542
+ package: round2.package,
1543
+ delegatedShare: round2.delegatedShare
1544
+ },
1545
+ input.auth,
1546
+ { signal }
1547
+ ),
1548
+ signal
1229
1549
  );
1230
1550
  if (!bytesEqual(Uint8Array.from(round2.jointPubkey), round2Response.jointPubkey)) {
1231
1551
  throw new Error(`LP DKG joint pubkey mismatch at index ${index}`);
@@ -1235,22 +1555,27 @@ async function runLpDkgShard(input, worker, index) {
1235
1555
  }
1236
1556
  return round2Response.address;
1237
1557
  }
1238
- async function waitForCompletedShardAddress(input, index) {
1558
+ async function waitForCompletedShardAddress(input, index, signal) {
1239
1559
  const deadline = Date.now() + LP_DKG_COMPLETED_SHARD_POLL_TIMEOUT_MS;
1240
1560
  for (; ; ) {
1241
- const address = await getCompletedShardAddress(input, index);
1561
+ const address = await getCompletedShardAddress(input, index, signal);
1242
1562
  if (address !== null) return address;
1243
1563
  if (Date.now() >= deadline) {
1244
1564
  throw new Error(`LP DKG completed shard ${index} did not become visible`);
1245
1565
  }
1246
- await sleep(LP_DKG_COMPLETED_SHARD_POLL_INTERVAL_MS);
1566
+ await abortable(sleep(LP_DKG_COMPLETED_SHARD_POLL_INTERVAL_MS), signal);
1247
1567
  }
1248
1568
  }
1249
- async function getCompletedShardAddress(input, index) {
1250
- const position = await input.client.getPosition(input.lpPositionId, input.auth);
1569
+ async function getCompletedShardAddress(input, index, signal) {
1570
+ signal.throwIfAborted();
1571
+ const position = await abortable(
1572
+ input.client.getPosition(input.lpPositionId, input.auth, { signal }),
1573
+ signal
1574
+ );
1251
1575
  return position.shards.find((candidate) => candidate.index === index)?.address ?? null;
1252
1576
  }
1253
- function workerRequest(worker, request2) {
1577
+ function workerRequest(worker, request2, signal) {
1578
+ signal.throwIfAborted();
1254
1579
  return new Promise((resolve, reject) => {
1255
1580
  let timeoutId;
1256
1581
  const onMessage = (event) => {
@@ -1258,37 +1583,54 @@ function workerRequest(worker, request2) {
1258
1583
  if (message.id !== request2.id) return;
1259
1584
  cleanup();
1260
1585
  if (message.type === "error") {
1261
- reject(new Error(message.error));
1586
+ reject(lpDkgWorkerFailure(message.error));
1262
1587
  } else {
1263
1588
  resolve(message);
1264
1589
  }
1265
1590
  };
1266
1591
  const onError = (event) => {
1267
1592
  cleanup();
1268
- reject(new Error(event.message));
1593
+ reject(lpDkgWorkerFailure(new Error(event.message)));
1594
+ };
1595
+ const onAbort = () => {
1596
+ cleanup();
1597
+ reject(signal.reason);
1269
1598
  };
1270
1599
  const cleanup = () => {
1600
+ signal.removeEventListener("abort", onAbort);
1271
1601
  if (timeoutId !== void 0) globalThis.clearTimeout(timeoutId);
1272
1602
  worker.removeEventListener("message", onMessage);
1273
1603
  worker.removeEventListener("error", onError);
1274
1604
  };
1605
+ signal.addEventListener("abort", onAbort, { once: true });
1275
1606
  worker.addEventListener("message", onMessage);
1276
1607
  worker.addEventListener("error", onError);
1277
1608
  timeoutId = globalThis.setTimeout(() => {
1278
1609
  cleanup();
1279
- reject(new Error(LP_DKG_WORKER_REQUEST_TIMEOUT_MESSAGE));
1610
+ reject(lpDkgWorkerFailure(new Error(LP_DKG_WORKER_REQUEST_TIMEOUT_MESSAGE)));
1280
1611
  }, LP_DKG_WORKER_REQUEST_TIMEOUT_MS);
1281
1612
  try {
1282
1613
  worker.postMessage(request2);
1283
1614
  } catch (error) {
1284
1615
  cleanup();
1285
- reject(error instanceof Error ? error : new Error(String(error)));
1616
+ reject(lpDkgWorkerFailure(error));
1286
1617
  }
1287
1618
  });
1288
1619
  }
1289
1620
  function createWorker(workerFactory) {
1290
- if (workerFactory !== void 0) return workerFactory();
1291
- return new Worker(new URL("./dkgWorker.js", import.meta.url), { type: "module" });
1621
+ try {
1622
+ if (workerFactory !== void 0) return workerFactory();
1623
+ return new Worker(new URL("./dkgWorker.js", import.meta.url), { type: "module" });
1624
+ } catch (error) {
1625
+ throw lpDkgWorkerFailure(error);
1626
+ }
1627
+ }
1628
+ function lpDkgWorkerFailure(cause) {
1629
+ return new LpLifecycleError(
1630
+ "LP_DKG_WORKER_FAILED",
1631
+ cause instanceof Error ? cause.message : typeof cause === "string" ? cause : LP_DKG_WORKER_FAILURE_MESSAGE,
1632
+ { cause }
1633
+ );
1292
1634
  }
1293
1635
  function bytesEqual(a, b) {
1294
1636
  if (a.byteLength !== b.byteLength) return false;
@@ -1312,12 +1654,20 @@ function isLpDkgIndexDriftError(error) {
1312
1654
  function isLpDkgSessionCoordinationError(error) {
1313
1655
  return isLpDkgActiveSessionError(error) || isLpDkgIndexDriftError(error);
1314
1656
  }
1315
- function resolveConcurrency(count, requested) {
1657
+ function resolveLpDkgConcurrency(count, requested) {
1316
1658
  if (count <= 0) return 0;
1317
1659
  const concurrency = requested ?? DEFAULT_LP_DKG_CONCURRENCY;
1318
1660
  if (!Number.isFinite(concurrency)) return Math.min(count, DEFAULT_LP_DKG_CONCURRENCY);
1319
1661
  return Math.max(1, Math.min(count, MAX_LP_DKG_CONCURRENCY, Math.floor(concurrency)));
1320
1662
  }
1663
+ function abortable(promise, signal) {
1664
+ signal.throwIfAborted();
1665
+ return new Promise((resolve, reject) => {
1666
+ const onAbort = () => reject(signal.reason);
1667
+ signal.addEventListener("abort", onAbort, { once: true });
1668
+ void promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
1669
+ });
1670
+ }
1321
1671
  function sleep(delayMs) {
1322
1672
  return new Promise((resolve) => {
1323
1673
  globalThis.setTimeout(resolve, delayMs);
@@ -1375,6 +1725,7 @@ function isLpPositionAuthMissingError(error) {
1375
1725
  return error instanceof LpPositionAuthMissingError || error instanceof Error && error.message === LP_MISSING_POSITION_CODE_MESSAGE;
1376
1726
  }
1377
1727
  function previewLpWithdrawalPlan(position, args = {}) {
1728
+ validateLpPositionFeePolicy(position);
1378
1729
  const claim = resolveWithdrawalClaim(position, args);
1379
1730
  return {
1380
1731
  ...claim,
@@ -1384,6 +1735,46 @@ function previewLpWithdrawalPlan(position, args = {}) {
1384
1735
  })
1385
1736
  };
1386
1737
  }
1738
+ var dkgAdmissions = /* @__PURE__ */ new WeakMap();
1739
+ var LP_DKG_BUSY_MESSAGE = "LP DKG session capacity is busy; wait for the active batch to finish";
1740
+ var LP_CLIENT_INVALIDATED_MESSAGE = "LP client invalidated by a reconnect capacity conflict; create a new LP lifecycle client and recover positions using their saved Position Codes";
1741
+ function getDkgAdmission(session) {
1742
+ let admission = dkgAdmissions.get(session);
1743
+ if (admission === void 0) {
1744
+ admission = { reservations: /* @__PURE__ */ new Set() };
1745
+ dkgAdmissions.set(session, admission);
1746
+ }
1747
+ while (admission.parent !== void 0) admission = admission.parent;
1748
+ dkgAdmissions.set(session, admission);
1749
+ return admission;
1750
+ }
1751
+ function assertDkgCapacity(reservations) {
1752
+ let workerCount = 0;
1753
+ const positions = /* @__PURE__ */ new Set();
1754
+ for (const reservation of reservations) {
1755
+ workerCount += reservation.workerCount;
1756
+ if (positions.has(reservation.positionId) || workerCount > MAX_LP_DKG_CONCURRENCY) {
1757
+ throw new LpLifecycleError("LP_DKG_BUSY", LP_DKG_BUSY_MESSAGE);
1758
+ }
1759
+ positions.add(reservation.positionId);
1760
+ }
1761
+ }
1762
+ function reserveDkg(session, positionId, workerCount) {
1763
+ const admission = getDkgAdmission(session);
1764
+ const reservation = { positionId, workerCount };
1765
+ assertDkgCapacity([...admission.reservations, reservation]);
1766
+ admission.reservations.add(reservation);
1767
+ return () => getDkgAdmission(session).reservations.delete(reservation);
1768
+ }
1769
+ function transferDkgAdmission(previousSession, nextSession) {
1770
+ const previous = getDkgAdmission(previousSession);
1771
+ const next = getDkgAdmission(nextSession);
1772
+ if (previous === next) return;
1773
+ assertDkgCapacity([...previous.reservations, ...next.reservations]);
1774
+ for (const reservation of previous.reservations) next.reservations.add(reservation);
1775
+ previous.reservations.clear();
1776
+ previous.parent = next;
1777
+ }
1387
1778
  var lifecycleClients = /* @__PURE__ */ new WeakMap();
1388
1779
  function createLpLifecycleClient(session, runtime = {}) {
1389
1780
  const state = {
@@ -1392,26 +1783,47 @@ function createLpLifecycleClient(session, runtime = {}) {
1392
1783
  redemptions: /* @__PURE__ */ new Map(),
1393
1784
  topUps: /* @__PURE__ */ new Map()
1394
1785
  };
1786
+ const cancellation = new AbortController();
1787
+ const assertUsable = () => cancellation.signal.throwIfAborted();
1395
1788
  let currentSession = session;
1396
1789
  const reconnectSession = runtime.reconnectSession === void 0 ? void 0 : async () => {
1397
1790
  const nextSession = await runtime.reconnectSession();
1791
+ assertUsable();
1792
+ try {
1793
+ transferDkgAdmission(currentSession, nextSession);
1794
+ } catch (error) {
1795
+ if (error instanceof LpLifecycleError && error.code === "LP_DKG_BUSY") {
1796
+ const invalidated = new LpLifecycleError(
1797
+ "LP_CLIENT_INVALIDATED",
1798
+ LP_CLIENT_INVALIDATED_MESSAGE,
1799
+ { cause: error }
1800
+ );
1801
+ cancellation.abort(invalidated);
1802
+ throw invalidated;
1803
+ }
1804
+ throw error;
1805
+ }
1398
1806
  currentSession = nextSession;
1399
1807
  return nextSession;
1400
1808
  };
1401
- const client = runtime.coordinatorClient ?? createLpCoordinatorClient(session, { reconnectSession });
1809
+ const client = runtime.coordinatorClient ?? createLpCoordinatorClient(session, { reconnectSession, signal: cancellation.signal });
1402
1810
  const nowMs = runtime.nowMs ?? Date.now;
1403
1811
  const sleep2 = runtime.sleep ?? defaultSleep;
1404
1812
  const requireAuth = (positionId) => {
1813
+ assertUsable();
1405
1814
  const auth = state.authByPosition.get(positionId);
1406
1815
  if (auth === void 0) throw new LpPositionAuthMissingError();
1407
1816
  return auth;
1408
1817
  };
1409
1818
  const rememberPosition = (position) => {
1819
+ assertUsable();
1820
+ validateLpPositionFeePolicy(position, state.positions.get(position.id));
1410
1821
  const snapshot = clonePosition(position);
1411
1822
  const previous = state.positions.get(snapshot.id);
1412
- const merged = previous === void 0 ? snapshot : {
1823
+ const merged = {
1413
1824
  ...snapshot,
1414
- redemptionPermission: mergeLpRedemptionPermission(
1825
+ actorSync: mergeLpActorSync(previous?.actorSync, snapshot.actorSync),
1826
+ redemptionPermission: previous === void 0 ? snapshot.redemptionPermission : mergeLpRedemptionPermission(
1415
1827
  previous.redemptionPermission,
1416
1828
  snapshot.redemptionPermission
1417
1829
  )
@@ -1421,6 +1833,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1421
1833
  return merged;
1422
1834
  };
1423
1835
  const rememberAuth = (auth) => {
1836
+ assertUsable();
1424
1837
  state.authByPosition.set(auth.lpPositionId, auth);
1425
1838
  };
1426
1839
  const refreshPosition2 = async (positionId) => {
@@ -1441,7 +1854,8 @@ function createLpLifecycleClient(session, runtime = {}) {
1441
1854
  }
1442
1855
  }
1443
1856
  };
1444
- const completeDkgBatch2 = async (positionId, options = {}) => {
1857
+ const completeDkgBatch2 = async (positionId, options = {}, hasReservation = false) => {
1858
+ assertUsable();
1445
1859
  assertAttested(currentSession);
1446
1860
  const auth = requireAuth(positionId);
1447
1861
  let activeSessionRetryStartedAtMs = null;
@@ -1451,10 +1865,16 @@ function createLpLifecycleClient(session, runtime = {}) {
1451
1865
  const startIndex = current.shards.length;
1452
1866
  const remaining = Math.min(current.targetShardCount - startIndex, LP_MAX_INITIAL_SHARDS);
1453
1867
  if (remaining <= 0) return current;
1454
- emitDkgProgress(options, current, startIndex, remaining, 0);
1868
+ const release = hasReservation ? () => void 0 : reserveDkg(
1869
+ currentSession,
1870
+ positionId,
1871
+ runtime.useDeterministicTestAddresses === true ? 0 : resolveLpDkgConcurrency(remaining, options.concurrency ?? runtime.dkgConcurrency)
1872
+ );
1455
1873
  let expectedAddresses = null;
1456
1874
  try {
1875
+ emitDkgProgress(options, current, startIndex, remaining, 0);
1457
1876
  const addresses = await createLpDkgBatch({
1877
+ signal: cancellation.signal,
1458
1878
  lpPositionId: positionId,
1459
1879
  startIndex,
1460
1880
  count: remaining,
@@ -1467,6 +1887,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1467
1887
  emitDkgProgress(options, current, startIndex, remaining, progress.completed);
1468
1888
  }
1469
1889
  });
1890
+ assertUsable();
1470
1891
  expectedAddresses = addresses;
1471
1892
  const position = await client.finalizeDkgBatch(
1472
1893
  {
@@ -1483,6 +1904,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1483
1904
  if (position.shards.length < position.targetShardCount) continue;
1484
1905
  return position;
1485
1906
  } catch (error) {
1907
+ assertUsable();
1486
1908
  const reconciled = await tryRefreshCompletedDkgWindow({
1487
1909
  client,
1488
1910
  positionId,
@@ -1504,11 +1926,14 @@ function createLpLifecycleClient(session, runtime = {}) {
1504
1926
  throw error;
1505
1927
  }
1506
1928
  await sleep2(LP_DKG_ACTIVE_SESSION_RETRY_DELAY_MS);
1929
+ } finally {
1930
+ release();
1507
1931
  }
1508
1932
  }
1509
1933
  };
1510
1934
  return {
1511
1935
  async createPosition(args) {
1936
+ assertUsable();
1512
1937
  assertNoUnsupportedFeePolicy(args.feePolicy);
1513
1938
  const shardCount = args.shardCount ?? LP_DEFAULT_TARGET_SHARDS;
1514
1939
  assertShardCount(shardCount, LP_MAX_INITIAL_SHARDS, "shardCount");
@@ -1532,6 +1957,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1532
1957
  {
1533
1958
  lpPositionId: positionId,
1534
1959
  creationAuthorizationId: creationAuthorization.authorizationId,
1960
+ feePolicy: creationAuthorization.feePolicy,
1535
1961
  authPublicKey: Array.from(auth.authPublicKey),
1536
1962
  refillPublicKey: Array.from(auth.refillPublicKey),
1537
1963
  withdrawalCommitment: Array.from(auth.withdrawalCommitment),
@@ -1549,6 +1975,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1549
1975
  return { positionId, lpPositionCode: auth.code, position };
1550
1976
  },
1551
1977
  async recoverPosition(args) {
1978
+ assertUsable();
1552
1979
  const auth = await deriveLpPositionCodeAuth(args.code);
1553
1980
  rememberAuth(auth);
1554
1981
  const position = rememberPosition(
@@ -1561,11 +1988,13 @@ function createLpLifecycleClient(session, runtime = {}) {
1561
1988
  return position;
1562
1989
  },
1563
1990
  confirmPositionCodeSaved(_positionId) {
1991
+ assertUsable();
1564
1992
  return;
1565
1993
  },
1566
1994
  completeDkgBatch: completeDkgBatch2,
1567
1995
  refreshPosition: refreshPosition2,
1568
1996
  async refill(positionId, args = {}) {
1997
+ assertUsable();
1569
1998
  const requestedShardCount = args.shardCount ?? LP_DEFAULT_REFILL_BATCH_SIZE;
1570
1999
  assertShardCount(requestedShardCount, LP_MAX_REFILL_BATCH_SIZE, "shardCount");
1571
2000
  assertAttested(currentSession);
@@ -1573,29 +2002,44 @@ function createLpLifecycleClient(session, runtime = {}) {
1573
2002
  const current = await refreshPosition2(positionId);
1574
2003
  args.onPosition?.(current);
1575
2004
  const refillShardCount = resolveLpRefillShardCount(current, requestedShardCount);
1576
- const authorizedPosition = rememberPosition(
1577
- await client.refillPosition(
2005
+ const release = reserveDkg(
2006
+ currentSession,
2007
+ positionId,
2008
+ runtime.useDeterministicTestAddresses === true ? 0 : resolveLpDkgConcurrency(refillShardCount, runtime.dkgConcurrency)
2009
+ );
2010
+ try {
2011
+ const authorizedPosition = rememberPosition(
2012
+ await client.refillPosition(
2013
+ {
2014
+ lpPositionId: positionId,
2015
+ shardCount: refillShardCount,
2016
+ shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
2017
+ addresses: [],
2018
+ authorizedAtMs: nowMs()
2019
+ },
2020
+ auth
2021
+ )
2022
+ );
2023
+ args.onPosition?.(authorizedPosition);
2024
+ if (authorizedPosition.shards.length >= authorizedPosition.targetShardCount) {
2025
+ return authorizedPosition;
2026
+ }
2027
+ return await completeDkgBatch2(
2028
+ positionId,
1578
2029
  {
1579
- lpPositionId: positionId,
1580
- shardCount: refillShardCount,
1581
- shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
1582
- addresses: [],
1583
- authorizedAtMs: nowMs()
2030
+ onProgress: args.onProgress,
2031
+ onPosition: args.onPosition,
2032
+ workerFactory: args.workerFactory,
2033
+ concurrency: resolveLpDkgConcurrency(refillShardCount, runtime.dkgConcurrency)
1584
2034
  },
1585
- auth
1586
- )
1587
- );
1588
- args.onPosition?.(authorizedPosition);
1589
- if (authorizedPosition.shards.length >= authorizedPosition.targetShardCount) {
1590
- return authorizedPosition;
2035
+ true
2036
+ );
2037
+ } finally {
2038
+ release();
1591
2039
  }
1592
- return completeDkgBatch2(positionId, {
1593
- onProgress: args.onProgress,
1594
- onPosition: args.onPosition,
1595
- workerFactory: args.workerFactory
1596
- });
1597
2040
  },
1598
2041
  async prepareInitialFunding(positionId) {
2042
+ assertUsable();
1599
2043
  assertAttested(currentSession);
1600
2044
  const auth = requireAuth(positionId);
1601
2045
  const currentPosition = rememberPosition(await client.getPosition(positionId, auth));
@@ -1625,6 +2069,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1625
2069
  return fundingPlanFromShard(position, plannedShard);
1626
2070
  },
1627
2071
  async prepareTopUp(positionId) {
2072
+ assertUsable();
1628
2073
  assertAttested(currentSession);
1629
2074
  const auth = requireAuth(positionId);
1630
2075
  const request2 = await client.prepareTopUp(positionId, auth);
@@ -1632,6 +2077,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1632
2077
  return request2;
1633
2078
  },
1634
2079
  async waitForTopUp(topUpId, options = {}) {
2080
+ assertUsable();
1635
2081
  const cached = state.topUps.get(topUpId);
1636
2082
  if (cached !== void 0 && options.positionId !== void 0 && cached.positionId !== options.positionId) {
1637
2083
  throw new PolicyValidationError(
@@ -1673,11 +2119,13 @@ function createLpLifecycleClient(session, runtime = {}) {
1673
2119
  }
1674
2120
  },
1675
2121
  async reconcileFunding(positionId) {
2122
+ assertUsable();
1676
2123
  assertAttested(currentSession);
1677
2124
  const auth = requireAuth(positionId);
1678
2125
  return rememberPosition(await client.reconcileFunding(positionId, auth));
1679
2126
  },
1680
2127
  async withdrawPosition(positionId, args) {
2128
+ assertUsable();
1681
2129
  validateExplicitWithdrawalInput(args);
1682
2130
  validateWithdrawalDestinations(args);
1683
2131
  assertAttested(currentSession);
@@ -1692,6 +2140,8 @@ function createLpLifecycleClient(session, runtime = {}) {
1692
2140
  const existingRedemption = findOpenRedemptionHistory(before, args);
1693
2141
  if (existingRedemption !== void 0) {
1694
2142
  const execution2 = {
2143
+ feePolicyHash: existingRedemption.feePolicyHash,
2144
+ feeBasisRevision: existingRedemption.feeBasisRevision,
1695
2145
  withdrawalId: existingRedemption.redemptionId,
1696
2146
  txSignatures: [...existingRedemption.txSignatures],
1697
2147
  acceptedLamports: existingRedemption.acceptedLamports,
@@ -1736,6 +2186,8 @@ function createLpLifecycleClient(session, runtime = {}) {
1736
2186
  earnedFeeLamports: args.withdrawalMode === "max_available" ? 0 : preview.earnedFeeLamports,
1737
2187
  withdrawalMode: args.withdrawalMode,
1738
2188
  requestedAtMs,
2189
+ feePolicyHash: before.feePolicy?.policy_hash,
2190
+ feeBasisRevision: before.actorSync?.feeBasisRevision,
1739
2191
  allowManyToOne: args.allowManyToOne === true || preview?.sourceCount === 1
1740
2192
  },
1741
2193
  auth
@@ -1766,6 +2218,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1766
2218
  return { execution, position, redemption };
1767
2219
  },
1768
2220
  async waitForWithdrawalConfirmation(positionId, execution, options = {}) {
2221
+ assertUsable();
1769
2222
  const auth = requireAuth(positionId);
1770
2223
  let record = resolveWithdrawalRecord({
1771
2224
  state,
@@ -1822,6 +2275,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1822
2275
  }
1823
2276
  },
1824
2277
  async withdrawFees(_positionId, args) {
2278
+ assertUsable();
1825
2279
  publicKey(args.destination);
1826
2280
  assertAttested(currentSession);
1827
2281
  throw new RoutingError(
@@ -1830,6 +2284,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1830
2284
  );
1831
2285
  },
1832
2286
  async listPositions() {
2287
+ assertUsable();
1833
2288
  return [...state.positions.values()].map(clonePosition);
1834
2289
  }
1835
2290
  };
@@ -1864,6 +2319,7 @@ async function tryRefreshCompletedDkgWindow({
1864
2319
  const position = await client.getPosition(positionId, auth).catch(() => null);
1865
2320
  if (position === null) return null;
1866
2321
  if (!hasCompletedDkgWindow(position, startIndex, total, expectedAddresses)) return null;
2322
+ validateLpPositionFeePolicy(position, state.positions.get(position.id));
1867
2323
  state.positions.set(position.id, clonePosition(position));
1868
2324
  return position;
1869
2325
  }
@@ -1962,7 +2418,9 @@ function hasActorSyncLpActivity(sync) {
1962
2418
  }
1963
2419
  function resolveRedeemablePrincipalLamports(sync, legacyAvailableLamports, actorRedeemablePrincipalLamports, actorRedeemableFeeLamports) {
1964
2420
  if (sync?.redeemableProjectionAvailable !== true) return legacyAvailableLamports;
1965
- if (legacyAvailableLamports <= 0) return actorRedeemablePrincipalLamports;
2421
+ if (sync.feePolicy !== void 0 || legacyAvailableLamports <= 0) {
2422
+ return actorRedeemablePrincipalLamports;
2423
+ }
1966
2424
  if (actorRedeemablePrincipalLamports >= legacyAvailableLamports || hasActorSyncLpActivity(sync)) {
1967
2425
  return actorRedeemablePrincipalLamports;
1968
2426
  }
@@ -2067,6 +2525,8 @@ function buildLpRedemptionRecord({
2067
2525
  nowMs
2068
2526
  }) {
2069
2527
  return {
2528
+ feePolicyHash: execution.feePolicyHash,
2529
+ feeBasisRevision: execution.feeBasisRevision,
2070
2530
  redemptionId: execution.withdrawalId,
2071
2531
  lpPositionId: position.id,
2072
2532
  requestedAt: nowMs,
@@ -2115,7 +2575,7 @@ function reconcileLpRedemptionFromActorSync(record, position) {
2115
2575
  function reconcileLpRedemptionFromHistory(record, history) {
2116
2576
  const observedStateVersion = record.coordinatorStateVersion ?? 0;
2117
2577
  if (history.stateVersion < observedStateVersion) return record;
2118
- if (record.acceptedLamports !== void 0 && history.acceptedLamports !== record.acceptedLamports || history.detailsTruncated !== true && !sameStringList(history.destinationAddresses, record.destinationAddresses)) {
2578
+ if (history.feePolicyHash !== record.feePolicyHash || history.feeBasisRevision !== record.feeBasisRevision || record.acceptedLamports !== void 0 && history.acceptedLamports !== record.acceptedLamports || history.detailsTruncated !== true && !sameStringList(history.destinationAddresses, record.destinationAddresses)) {
2119
2579
  return {
2120
2580
  ...record,
2121
2581
  status: "failed",
@@ -2165,6 +2625,8 @@ function hydrateLpRedemptionsFromPosition(state, position) {
2165
2625
  }
2166
2626
  function lpWithdrawalRecordFromHistory(position, history) {
2167
2627
  const record = {
2628
+ feePolicyHash: history.feePolicyHash,
2629
+ feeBasisRevision: history.feeBasisRevision,
2168
2630
  redemptionId: history.redemptionId,
2169
2631
  lpPositionId: position.id,
2170
2632
  requestedAt: history.createdAtMs,
@@ -2194,6 +2656,7 @@ function uniqueStrings(values) {
2194
2656
  function clonePosition(position) {
2195
2657
  return {
2196
2658
  ...position,
2659
+ feePolicy: position.feePolicy === void 0 ? void 0 : { ...position.feePolicy },
2197
2660
  shards: position.shards.map((shard) => ({ ...shard })),
2198
2661
  actorSync: position.actorSync === void 0 ? void 0 : {
2199
2662
  ...position.actorSync,
@@ -2306,6 +2769,6 @@ function listPositions(session) {
2306
2769
  return defaultLpLifecycleClient(session).listPositions();
2307
2770
  }
2308
2771
 
2309
- export { LP_COMMAND_REQUEST_TIMEOUT_MS, LP_DKG_ABORT_CLEANUP_TIMEOUT_MS, LP_DKG_ABORT_REQUEST_TIMEOUT_MS, LP_DKG_WORKER_REQUEST_TIMEOUT_MESSAGE, LP_DKG_WORKER_REQUEST_TIMEOUT_MS, LP_FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE, 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, previewLpWithdrawalPlan, reconcileFunding2 as reconcileFunding, recoverPosition, refill, refreshPosition, signLpCommandBytes, waitForTopUp, waitForWithdrawalConfirmation, withdrawFees, withdrawPosition };
2772
+ export { LP_COMMAND_REQUEST_TIMEOUT_MS, LP_DKG_ABORT_CLEANUP_TIMEOUT_MS, LP_DKG_ABORT_REQUEST_TIMEOUT_MS, LP_DKG_WORKER_REQUEST_TIMEOUT_MESSAGE, LP_DKG_WORKER_REQUEST_TIMEOUT_MS, LP_FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE, LP_MISSING_POSITION_CODE_MESSAGE, LP_REDEMPTION_RECONCILING_MESSAGE, bytesToHex, completeDkgBatch, confirmPositionCodeSaved, createLpCoordinatorClient, createLpDkgBatch, createLpLifecycleClient, createPosition, defaultLpLifecycleClient, deriveLpPositionCodeAuth, generateLpPositionCode, isLpPositionAuthMissingError, isLpRedemptionReconcilingError, listPositions, mergeLpActorSync, normalizeLpActorSync, normalizeLpPositionCode, prepareInitialFunding, prepareTopUp2 as prepareTopUp, previewLpWithdrawalPlan, reconcileFunding2 as reconcileFunding, recoverPosition, refill, refreshPosition, signLpCommandBytes, validateLpPositionFeePolicy, waitForTopUp, waitForWithdrawalConfirmation, withdrawFees, withdrawPosition };
2310
2773
  //# sourceMappingURL=lp.js.map
2311
2774
  //# sourceMappingURL=lp.js.map