@invisible-labs/sdk 0.6.0-devnet.3 → 0.6.0-devnet.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lp.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { isValidLpPositionId, LP_DEFAULT_REFILL_BATCH_SIZE, LP_DEFAULT_TARGET_SHARDS, LP_MAX_TARGET_SHARDS, LP_MAX_REFILL_BATCH_SIZE } from './chunk-4X5OCCQW.js';
2
2
  import { publicKey, assertAttested, lamports } from './chunk-NHEHCUS5.js';
3
- import { requestEnvelope } from './chunk-QQSI3ZD7.js';
3
+ import { requestEnvelope } from './chunk-UADJCUPK.js';
4
4
  import { getSessionState } from './chunk-V5HXJRBW.js';
5
- import { CommandError, PolicyValidationError, RoutingError, LpLifecycleError } from './chunk-GHBQF3A7.js';
5
+ import { CommandError, PolicyValidationError, TransportError, RoutingError, LpLifecycleError } from './chunk-GHBQF3A7.js';
6
6
  import './chunk-VR6T6OJS.js';
7
7
 
8
8
  // src/lp/positionCode.ts
@@ -172,14 +172,8 @@ function getCrypto() {
172
172
  var SINGLE_DESTINATION_WITHDRAWAL_MESSAGE = "single-destination withdrawal is privacy-degrading; pass allowManyToOne: true to acknowledge";
173
173
  var DESTINATION_SOURCE_COUNT_MESSAGE = "LP withdrawal requires one destination per withdrawable source unless allowManyToOne is true";
174
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;
175
+ function validateLpWithdrawalClaim(input) {
176
+ const { depositPrincipalLamports, earnedFeeLamports } = input;
183
177
  lamports(depositPrincipalLamports);
184
178
  lamports(earnedFeeLamports);
185
179
  if (depositPrincipalLamports === 0 && earnedFeeLamports === 0) {
@@ -188,6 +182,16 @@ function validateLpWithdrawalPlan(input) {
188
182
  "LP withdrawal requires a non-zero principal or earned-fee claim"
189
183
  );
190
184
  }
185
+ }
186
+ function validateLpWithdrawalPlan(input) {
187
+ const {
188
+ depositPrincipalLamports,
189
+ earnedFeeLamports,
190
+ destinationAddresses,
191
+ allowManyToOne,
192
+ sourceCount
193
+ } = input;
194
+ validateLpWithdrawalClaim({ depositPrincipalLamports, earnedFeeLamports });
191
195
  validateDestinationAddresses(destinationAddresses);
192
196
  if (sourceCount !== void 0 && (!Number.isInteger(sourceCount) || sourceCount < 1)) {
193
197
  throw new PolicyValidationError("INVALID_WITHDRAWAL_PLAN", SOURCE_COUNT_MESSAGE);
@@ -245,8 +249,8 @@ function createLpCoordinatorClient(session) {
245
249
  openPosition(input, auth) {
246
250
  return openPosition(session, input, auth);
247
251
  },
248
- getPosition(lpPositionId, auth) {
249
- return getPosition(session, lpPositionId, auth);
252
+ getPosition(lpPositionId, auth, options) {
253
+ return getPosition(session, lpPositionId, auth, options);
250
254
  },
251
255
  finalizeDkgBatch(input, auth) {
252
256
  return finalizeDkgBatch(session, input, auth);
@@ -295,7 +299,7 @@ async function abortDkgSessions(session, lpPositionId, auth) {
295
299
  case: "dkgAbort",
296
300
  value: {}
297
301
  },
298
- LP_DKG_ABORT_REQUEST_TIMEOUT_MS
302
+ { timeoutMs: LP_DKG_ABORT_REQUEST_TIMEOUT_MS }
299
303
  );
300
304
  if (response.case !== "dkgAbort") throw new Error("Unexpected LP DKG abort response");
301
305
  return { abortedSessionCount: response.value.aborted_session_count };
@@ -348,15 +352,23 @@ async function openPosition(session, input, auth) {
348
352
  });
349
353
  return expectPosition(response);
350
354
  }
351
- async function getPosition(session, lpPositionId, auth) {
355
+ async function getPosition(session, lpPositionId, auth, options = {}) {
352
356
  let cursor;
353
357
  let position;
354
358
  const seenCursors = /* @__PURE__ */ new Set();
355
359
  for (; ; ) {
356
- const response = await requestForPosition(session, lpPositionId, auth, "position_get", {
357
- case: "positionGet",
358
- value: cursor === void 0 ? {} : { redemption_history_cursor: cursor }
359
- });
360
+ const pageOptions = lpPositionPageRequestOptions(options);
361
+ const response = await requestForPosition(
362
+ session,
363
+ lpPositionId,
364
+ auth,
365
+ "position_get",
366
+ {
367
+ case: "positionGet",
368
+ value: cursor === void 0 ? {} : { redemption_history_cursor: cursor }
369
+ },
370
+ pageOptions
371
+ );
360
372
  const page = expectPosition(response);
361
373
  const mergedPosition = position === void 0 ? page : mergePositionHistoryPages(position, page);
362
374
  position = mergedPosition;
@@ -373,6 +385,18 @@ async function getPosition(session, lpPositionId, auth) {
373
385
  cursor = nextCursor;
374
386
  }
375
387
  }
388
+ function lpPositionPageRequestOptions(options) {
389
+ if (options.deadlineMs === void 0) return options;
390
+ const remainingMs = options.deadlineMs - Date.now();
391
+ if (remainingMs <= 0) {
392
+ throw new CommandError("REJECTED_STALE_JOB", "LP position observation deadline elapsed");
393
+ }
394
+ return {
395
+ deadlineMs: options.deadlineMs,
396
+ signal: options.signal,
397
+ timeoutMs: Math.min(options.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS, remainingMs)
398
+ };
399
+ }
376
400
  async function finalizeDkgBatch(session, input, auth) {
377
401
  const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_finalize", {
378
402
  case: "dkgFinalize",
@@ -461,36 +485,59 @@ async function makeAvailable(session, input, auth) {
461
485
  async function executeWithdrawal(session, input, auth) {
462
486
  validateLpWithdrawalPlan({
463
487
  destinationAddresses: input.destinationAddresses,
464
- depositPrincipalLamports: input.depositPrincipalLamports,
465
- earnedFeeLamports: input.earnedFeeLamports,
466
- allowManyToOne: input.allowManyToOne
488
+ depositPrincipalLamports: input.withdrawalMode === "max_available" ? 1 : input.depositPrincipalLamports,
489
+ earnedFeeLamports: input.withdrawalMode === "max_available" ? 0 : input.earnedFeeLamports,
490
+ // Max mode deliberately does not preview source count. The coordinator
491
+ // owns the accepted split, so local destination validation must not reject
492
+ // a one-destination request based on an unknown current source count.
493
+ allowManyToOne: input.withdrawalMode === "max_available" || input.allowManyToOne
467
494
  });
468
495
  const response = await requestForPosition(session, input.lpPositionId, auth, "withdraw_request", {
469
496
  case: "withdrawRequest",
470
497
  value: {
471
498
  destination_addresses: input.destinationAddresses,
472
- deposit_principal_lamports: input.depositPrincipalLamports,
473
- earned_fee_lamports: input.earnedFeeLamports,
499
+ deposit_principal_lamports: input.withdrawalMode === "max_available" ? 0 : input.depositPrincipalLamports,
500
+ earned_fee_lamports: input.withdrawalMode === "max_available" ? 0 : input.earnedFeeLamports,
501
+ withdrawal_mode: input.withdrawalMode,
474
502
  requested_at_ms: input.requestedAtMs
475
503
  }
476
504
  });
477
505
  if (response.case !== "withdrawalExecution") {
478
506
  throw new Error("Unexpected LP withdrawal execution response");
479
507
  }
508
+ const execution = response.value;
509
+ if (BigInt(execution.accepted_lamports) !== BigInt(execution.accepted_deposit_principal_lamports) + BigInt(execution.accepted_earned_fee_lamports)) {
510
+ throw new PolicyValidationError(
511
+ "INVALID_WITHDRAWAL_PLAN",
512
+ "Coordinator accepted withdrawal total does not match its principal and fee split"
513
+ );
514
+ }
515
+ if (input.withdrawalMode === "exact" && (execution.accepted_deposit_principal_lamports !== input.depositPrincipalLamports || execution.accepted_earned_fee_lamports !== input.earnedFeeLamports)) {
516
+ throw new PolicyValidationError(
517
+ "INVALID_WITHDRAWAL_PLAN",
518
+ "Coordinator accepted withdrawal split does not match the signed exact claim"
519
+ );
520
+ }
480
521
  return {
481
- withdrawalId: response.value.withdrawal_id,
482
- txSignatures: [...response.value.tx_signatures]
522
+ withdrawalId: execution.withdrawal_id,
523
+ txSignatures: [...execution.tx_signatures],
524
+ acceptedLamports: execution.accepted_lamports,
525
+ acceptedDepositPrincipalLamports: execution.accepted_deposit_principal_lamports,
526
+ acceptedEarnedFeeLamports: execution.accepted_earned_fee_lamports
483
527
  };
484
528
  }
485
529
  function request(session, auth, action, payload) {
486
530
  return requestForPosition(session, auth.lpPositionId, auth, action, payload);
487
531
  }
488
- async function requestForPosition(session, lpPositionId, auth, action, payload, timeoutMs = LP_COMMAND_REQUEST_TIMEOUT_MS) {
532
+ async function requestForPosition(session, lpPositionId, auth, action, payload, options = {}) {
533
+ const timeoutMs = options.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS;
489
534
  assertAuthMatchesPosition(lpPositionId, auth);
490
535
  const protocol = await loadProtocol();
491
536
  const state = getSessionState(session);
492
537
  const attestation = state.channel?.attestation;
493
- if (!attestation) throw new Error("LP coordinator attestation is not available");
538
+ if (!attestation) {
539
+ throw new TransportError("CONNECTION_LOST", "LP coordinator attestation is not available");
540
+ }
494
541
  const command = {
495
542
  lp_position_id: lpPositionId,
496
543
  action,
@@ -504,8 +551,10 @@ async function requestForPosition(session, lpPositionId, auth, action, payload,
504
551
  };
505
552
  const signature = await signLpCommandBytes(auth, protocol.encodeLpCommandForSigning(command));
506
553
  const { type, payload: envelopePayload } = protocol.lpSignedCommandEnvelope(command, signature);
554
+ const requestOptions = lpPositionPageRequestOptions(options);
507
555
  const envelope = await requestEnvelope(session, type, envelopePayload, {
508
- timeoutMs
556
+ timeoutMs: requestOptions.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS,
557
+ signal: requestOptions.signal
509
558
  });
510
559
  return protocol.asLpCommandResponsePayload(envelope);
511
560
  }
@@ -564,6 +613,7 @@ function mapLpActorSync(actorSync) {
564
613
  lifetimeEarnedLamports: actorSync.lifetime_earned_lamports ?? 0,
565
614
  redeemablePrincipalSourceCount: actorSync.redeemable_principal_source_count ?? 0,
566
615
  redeemableFeeSourceCount: actorSync.redeemable_fee_source_count ?? 0,
616
+ redeemableSourceCount: actorSync.redeemable_source_count,
567
617
  pendingReservationCount: actorSync.pending_reservation_count,
568
618
  reservedPayoutLamports: actorSync.reserved_payout_lamports,
569
619
  estimatedNetworkFeeLamports: actorSync.estimated_network_fee_lamports,
@@ -607,6 +657,7 @@ function mapLpRedemptionHistory(history) {
607
657
  return {
608
658
  redemptionId: history.redemption_id,
609
659
  state: history.state,
660
+ ...history.withdrawal_mode === void 0 ? {} : { withdrawalMode: history.withdrawal_mode },
610
661
  acceptedLamports: history.accepted_lamports,
611
662
  acceptedDepositPrincipalLamports: history.accepted_deposit_principal_lamports,
612
663
  acceptedEarnedFeeLamports: history.accepted_earned_fee_lamports,
@@ -685,7 +736,7 @@ function randomBytes(length) {
685
736
  return bytes;
686
737
  }
687
738
  function loadProtocol() {
688
- protocolPromise ??= import('./coordinator-VVJ33WF2.js');
739
+ protocolPromise ??= import('./coordinator-ZNLGAYXD.js');
689
740
  return protocolPromise;
690
741
  }
691
742
 
@@ -962,7 +1013,7 @@ var WITHDRAWAL_NO_LAMPORTS_MESSAGE = "LP redemption did not land any lamports on
962
1013
  var WITHDRAWAL_HISTORY_MISMATCH_MESSAGE = "LP redemption history does not match this request";
963
1014
  var LP_WITHDRAWAL_UNRESOLVED_FUNDING_MESSAGE = "LP position has unresolved funding shards before redemption";
964
1015
  var UNSUPPORTED_FEE_POLICY_MESSAGE = "LP fee policy overrides are not advertised by the coordinator";
965
- var FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE = "LP earned-fee source count is not available; pass allowManyToOne: true to acknowledge many-to-one withdrawal risk";
1016
+ var LP_FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE = "LP earned-fee source count is not available; pass allowManyToOne: true to acknowledge many-to-one withdrawal risk";
966
1017
  var LpRedemptionReconcilingError = class extends LpLifecycleError {
967
1018
  constructor() {
968
1019
  super("LP_REDEMPTION_RECONCILING", LP_REDEMPTION_RECONCILING_MESSAGE);
@@ -981,6 +1032,16 @@ function isLpRedemptionReconcilingError(error) {
981
1032
  function isLpPositionAuthMissingError(error) {
982
1033
  return error instanceof LpPositionAuthMissingError || error instanceof Error && error.message === LP_MISSING_POSITION_CODE_MESSAGE;
983
1034
  }
1035
+ function previewLpWithdrawalPlan(position, args = {}) {
1036
+ const claim = resolveWithdrawalClaim(position, args);
1037
+ return {
1038
+ ...claim,
1039
+ sourceCount: lpWithdrawalSourceCountForClaim(position, {
1040
+ ...claim,
1041
+ allowManyToOne: args.allowManyToOne
1042
+ })
1043
+ };
1044
+ }
984
1045
  var lifecycleClients = /* @__PURE__ */ new WeakMap();
985
1046
  function createLpLifecycleClient(session, runtime = {}) {
986
1047
  const state = {
@@ -1121,7 +1182,12 @@ function createLpLifecycleClient(session, runtime = {}) {
1121
1182
  async recoverPosition(args) {
1122
1183
  const auth = await deriveLpPositionCodeAuth(args.code);
1123
1184
  rememberAuth(auth);
1124
- const position = rememberPosition(await client.getPosition(auth.lpPositionId, auth));
1185
+ const position = rememberPosition(
1186
+ await client.getPosition(auth.lpPositionId, auth, {
1187
+ deadlineMs: args.deadlineMs,
1188
+ signal: args.signal
1189
+ })
1190
+ );
1125
1191
  assertPositionMatchesAuth(position, auth);
1126
1192
  return position;
1127
1193
  },
@@ -1256,7 +1322,10 @@ function createLpLifecycleClient(session, runtime = {}) {
1256
1322
  if (existingRedemption !== void 0) {
1257
1323
  const execution2 = {
1258
1324
  withdrawalId: existingRedemption.redemptionId,
1259
- txSignatures: [...existingRedemption.txSignatures]
1325
+ txSignatures: [...existingRedemption.txSignatures],
1326
+ acceptedLamports: existingRedemption.acceptedLamports,
1327
+ acceptedDepositPrincipalLamports: existingRedemption.acceptedDepositPrincipalLamports,
1328
+ acceptedEarnedFeeLamports: existingRedemption.acceptedEarnedFeeLamports
1260
1329
  };
1261
1330
  const redemption2 = {
1262
1331
  ...buildLpRedemptionRecord({
@@ -1276,39 +1345,44 @@ function createLpLifecycleClient(session, runtime = {}) {
1276
1345
  state.redemptions.set(execution2.withdrawalId, redemption2);
1277
1346
  return { execution: execution2, position: before, redemption: redemption2 };
1278
1347
  }
1279
- const claim = resolveWithdrawalClaim(before, args);
1280
- const sourceCount = lpWithdrawalSourceCountForClaim(before, {
1281
- ...claim,
1282
- allowManyToOne: args.allowManyToOne
1283
- });
1284
- const validationInput = {
1285
- lpPositionId: positionId,
1286
- destinationAddresses: args.destinationAddresses,
1287
- depositPrincipalLamports: claim.depositPrincipalLamports,
1288
- earnedFeeLamports: claim.earnedFeeLamports,
1289
- requestedAtMs: nowMs(),
1290
- allowManyToOne: args.allowManyToOne ?? false,
1291
- sourceCount
1292
- };
1293
- validateLpWithdrawalPlan(validationInput);
1348
+ const preview = args.withdrawalMode === "exact" ? previewLpWithdrawalPlan(before, args) : void 0;
1349
+ const requestedAtMs = nowMs();
1350
+ if (args.withdrawalMode === "exact") {
1351
+ validateLpWithdrawalPlan({
1352
+ destinationAddresses: args.destinationAddresses,
1353
+ depositPrincipalLamports: preview.depositPrincipalLamports,
1354
+ earnedFeeLamports: preview.earnedFeeLamports,
1355
+ allowManyToOne: args.allowManyToOne ?? false,
1356
+ sourceCount: preview.sourceCount
1357
+ });
1358
+ }
1294
1359
  const execution = await executeWithdrawalAfterFundingReadiness(
1295
1360
  positionId,
1296
1361
  {
1297
- lpPositionId: validationInput.lpPositionId,
1298
- destinationAddresses: validationInput.destinationAddresses,
1299
- depositPrincipalLamports: validationInput.depositPrincipalLamports,
1300
- earnedFeeLamports: validationInput.earnedFeeLamports,
1301
- requestedAtMs: validationInput.requestedAtMs,
1302
- allowManyToOne: args.allowManyToOne === true || sourceCount === 1
1362
+ lpPositionId: positionId,
1363
+ destinationAddresses: args.destinationAddresses,
1364
+ depositPrincipalLamports: args.withdrawalMode === "max_available" ? 0 : preview.depositPrincipalLamports,
1365
+ earnedFeeLamports: args.withdrawalMode === "max_available" ? 0 : preview.earnedFeeLamports,
1366
+ withdrawalMode: args.withdrawalMode,
1367
+ requestedAtMs,
1368
+ allowManyToOne: args.allowManyToOne === true || preview?.sourceCount === 1
1303
1369
  },
1304
1370
  auth
1305
1371
  );
1372
+ const acceptedLamports = execution.acceptedLamports;
1373
+ if (args.withdrawalMode === "max_available" && acceptedLamports === void 0) {
1374
+ throw new PolicyValidationError(
1375
+ "INVALID_WITHDRAWAL_PLAN",
1376
+ "LP redemption response did not include the sealed accepted amount"
1377
+ );
1378
+ }
1379
+ const acceptedAmount = acceptedLamports ?? preview.depositPrincipalLamports + preview.earnedFeeLamports;
1306
1380
  const pendingRedemption = buildLpRedemptionRecord({
1307
1381
  execution,
1308
1382
  position: before,
1309
1383
  destinationAddresses: args.destinationAddresses,
1310
- acceptedLamports: claim.depositPrincipalLamports + claim.earnedFeeLamports,
1311
- requestedTargetLamports: (before.actorSync?.withdrawalRequestedLamports ?? 0) + claim.depositPrincipalLamports + claim.earnedFeeLamports,
1384
+ acceptedLamports: acceptedAmount,
1385
+ requestedTargetLamports: (before.actorSync?.withdrawalRequestedLamports ?? 0) + acceptedAmount,
1312
1386
  landedBaselineLamports: before.actorSync?.withdrawalLandedLamports ?? 0,
1313
1387
  landedFeeBaselineLamports: before.actorSync?.withdrawalLandedFeeLamports ?? 0,
1314
1388
  txSignatureBaseline: before.actorSync?.withdrawalTxHashes ?? [],
@@ -1329,10 +1403,32 @@ function createLpLifecycleClient(session, runtime = {}) {
1329
1403
  options
1330
1404
  });
1331
1405
  state.redemptions.set(record.redemptionId, record);
1332
- const deadlineMs = nowMs() + (runtime.withdrawalConfirmationTimeoutMs ?? LP_WITHDRAWAL_CONFIRMATION_TIMEOUT_MS);
1406
+ const deadlineMs = options.deadlineMs ?? nowMs() + (runtime.withdrawalConfirmationTimeoutMs ?? LP_WITHDRAWAL_CONFIRMATION_TIMEOUT_MS);
1333
1407
  const pollIntervalMs = runtime.withdrawalConfirmationPollIntervalMs ?? LP_WITHDRAWAL_CONFIRMATION_POLL_INTERVAL_MS;
1334
1408
  for (; ; ) {
1335
- const position = rememberPosition(await client.getPosition(positionId, auth));
1409
+ throwIfWithdrawalConfirmationAborted(options.signal);
1410
+ if (nowMs() >= deadlineMs) {
1411
+ markLpRedemptionReconciling(state, record, options);
1412
+ }
1413
+ const remainingMs = deadlineMs - nowMs();
1414
+ if (remainingMs <= 0) {
1415
+ markLpRedemptionReconciling(state, record, options);
1416
+ }
1417
+ let position;
1418
+ try {
1419
+ position = rememberPosition(
1420
+ await client.getPosition(positionId, auth, {
1421
+ deadlineMs,
1422
+ signal: options.signal
1423
+ })
1424
+ );
1425
+ } catch (error) {
1426
+ if (isLpPositionObservationDeadlineError(error, deadlineMs, nowMs)) {
1427
+ markLpRedemptionReconciling(state, record, options);
1428
+ }
1429
+ throw error;
1430
+ }
1431
+ throwIfWithdrawalConfirmationAborted(options.signal);
1336
1432
  options.onPosition?.(position);
1337
1433
  const reconciled = reconcileLpRedemptionFromActorSync(record, position);
1338
1434
  record = reconciled;
@@ -1348,12 +1444,10 @@ function createLpLifecycleClient(session, runtime = {}) {
1348
1444
  if (reconciled.status === "failed") {
1349
1445
  throw new Error(reconciled.error ?? WITHDRAWAL_NO_LAMPORTS_MESSAGE);
1350
1446
  }
1351
- if (nowMs() > deadlineMs) {
1352
- state.redemptions.set(record.redemptionId, { ...reconciled, status: "reconciling" });
1353
- options.onRedemptionUpdate?.({ ...reconciled, status: "reconciling" });
1354
- throw new LpRedemptionReconcilingError();
1447
+ if (nowMs() >= deadlineMs) {
1448
+ markLpRedemptionReconciling(state, reconciled, options);
1355
1449
  }
1356
- await sleep2(pollIntervalMs);
1450
+ await sleepWithAbort(sleep2, pollIntervalMs, options.signal);
1357
1451
  }
1358
1452
  },
1359
1453
  async withdrawFees(_positionId, args) {
@@ -1462,10 +1556,12 @@ function isLpPositionReadyForInitialFunding(position) {
1462
1556
  }
1463
1557
  function resolveWithdrawalClaim(position, args) {
1464
1558
  if (args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0) {
1465
- return {
1559
+ const claim = {
1466
1560
  depositPrincipalLamports: args.depositPrincipalLamports ?? 0,
1467
1561
  earnedFeeLamports: args.earnedFeeLamports ?? 0
1468
1562
  };
1563
+ validateLpWithdrawalClaim(claim);
1564
+ return claim;
1469
1565
  }
1470
1566
  const legacyDepositPrincipalLamports = position.shards.filter((shard) => shard.status === "AVAILABLE" && shard.amountLamports > 0).reduce((sum, shard) => sum + shard.amountLamports, 0);
1471
1567
  const actorRedeemablePrincipalLamports = Math.max(
@@ -1491,11 +1587,11 @@ function resolveWithdrawalClaim(position, args) {
1491
1587
  return { depositPrincipalLamports, earnedFeeLamports };
1492
1588
  }
1493
1589
  function findOpenRedemptionHistory(position, args) {
1494
- const hasExplicitClaim = args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0;
1590
+ const hasExplicitClaim = args.withdrawalMode === "exact";
1495
1591
  const requestedPrincipalLamports = args.depositPrincipalLamports ?? 0;
1496
1592
  const requestedFeeLamports = args.earnedFeeLamports ?? 0;
1497
1593
  return position.actorSync?.redemptionHistory?.find(
1498
- (history) => LP_REDEMPTION_HISTORY_OPEN_STATES.has(history.state) && history.detailsTruncated !== true && sameStringList(history.destinationAddresses, args.destinationAddresses) && (!hasExplicitClaim || history.acceptedDepositPrincipalLamports === requestedPrincipalLamports && history.acceptedEarnedFeeLamports === requestedFeeLamports)
1594
+ (history) => LP_REDEMPTION_HISTORY_OPEN_STATES.has(history.state) && history.detailsTruncated !== true && (history.withdrawalMode ?? "exact") === args.withdrawalMode && sameStringList(history.destinationAddresses, args.destinationAddresses) && (!hasExplicitClaim || history.acceptedDepositPrincipalLamports === requestedPrincipalLamports && history.acceptedEarnedFeeLamports === requestedFeeLamports)
1499
1595
  );
1500
1596
  }
1501
1597
  function hasActorSyncLpActivity(sync) {
@@ -1517,11 +1613,25 @@ function hasActorRedeemableProjection(sync) {
1517
1613
  return sync?.redeemableProjectionAvailable === true;
1518
1614
  }
1519
1615
  function validateExplicitWithdrawalInput(args) {
1520
- if (args.depositPrincipalLamports === void 0 && args.earnedFeeLamports === void 0) return;
1616
+ if (args.withdrawalMode === "max_available") {
1617
+ if (args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0) {
1618
+ throw new PolicyValidationError(
1619
+ "INVALID_WITHDRAWAL_PLAN",
1620
+ "max_available withdrawal must not carry exact bucket claims"
1621
+ );
1622
+ }
1623
+ return;
1624
+ }
1625
+ if (args.depositPrincipalLamports === void 0 || args.earnedFeeLamports === void 0) {
1626
+ throw new PolicyValidationError(
1627
+ "INVALID_WITHDRAWAL_PLAN",
1628
+ "exact withdrawal requires both bucket claims"
1629
+ );
1630
+ }
1521
1631
  validateLpWithdrawalPlan({
1522
1632
  destinationAddresses: args.destinationAddresses,
1523
- depositPrincipalLamports: args.depositPrincipalLamports ?? 0,
1524
- earnedFeeLamports: args.earnedFeeLamports ?? 0,
1633
+ depositPrincipalLamports: args.depositPrincipalLamports,
1634
+ earnedFeeLamports: args.earnedFeeLamports,
1525
1635
  allowManyToOne: args.allowManyToOne,
1526
1636
  sourceCount: args.destinationAddresses.length
1527
1637
  });
@@ -1554,6 +1664,13 @@ function resolveWithdrawalRecord({
1554
1664
  };
1555
1665
  }
1556
1666
  function lpWithdrawalSourceCountForClaim(position, claim) {
1667
+ const actorSync = position.actorSync;
1668
+ const actorRedeemablePrincipalLamports = actorSync?.redeemablePrincipalLamports ?? 0;
1669
+ const actorRedeemableFeeLamports = actorSync?.redeemableFeeLamports ?? 0;
1670
+ const actorRedeemableSourceCount = actorSync?.redeemableSourceCount;
1671
+ if (actorSync?.redeemableProjectionAvailable === true && claim.depositPrincipalLamports === actorRedeemablePrincipalLamports && claim.earnedFeeLamports === actorRedeemableFeeLamports && actorRedeemableSourceCount !== void 0) {
1672
+ return actorRedeemableSourceCount;
1673
+ }
1557
1674
  let remainingPrincipalLamports = claim.depositPrincipalLamports;
1558
1675
  let principalSourceCount = 0;
1559
1676
  const principalCandidates = [...position.shards].filter((shard) => shard.status === "AVAILABLE" && shard.amountLamports > 0).sort((left, right) => left.shardId.localeCompare(right.shardId));
@@ -1562,7 +1679,6 @@ function lpWithdrawalSourceCountForClaim(position, claim) {
1562
1679
  principalSourceCount += 1;
1563
1680
  remainingPrincipalLamports -= shard.amountLamports;
1564
1681
  }
1565
- const actorRedeemablePrincipalLamports = position.actorSync?.redeemablePrincipalLamports ?? 0;
1566
1682
  const actorRedeemablePrincipalSourceCount = position.actorSync?.redeemablePrincipalSourceCount ?? 0;
1567
1683
  if (claim.depositPrincipalLamports > 0 && position.actorSync?.redeemableProjectionAvailable === true && actorRedeemablePrincipalLamports >= claim.depositPrincipalLamports && actorRedeemablePrincipalSourceCount > 0) {
1568
1684
  principalSourceCount = actorRedeemablePrincipalSourceCount;
@@ -1571,7 +1687,7 @@ function lpWithdrawalSourceCountForClaim(position, claim) {
1571
1687
  if (claim.earnedFeeLamports > 0 && observedEarnedFeeSourceCount < 1 && claim.allowManyToOne !== true) {
1572
1688
  throw new PolicyValidationError(
1573
1689
  "INVALID_WITHDRAWAL_PLAN",
1574
- FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE
1690
+ LP_FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE
1575
1691
  );
1576
1692
  }
1577
1693
  const earnedFeeSourceCount = claim.earnedFeeLamports > 0 ? Math.max(1, observedEarnedFeeSourceCount) : 0;
@@ -1737,6 +1853,30 @@ function throwIfTopUpWaitAborted(signal) {
1737
1853
  if (signal?.aborted !== true) return;
1738
1854
  throw new DOMException("LP top-up wait aborted", "AbortError");
1739
1855
  }
1856
+ function throwIfWithdrawalConfirmationAborted(signal) {
1857
+ if (signal?.aborted !== true) return;
1858
+ throw new DOMException("LP withdrawal confirmation wait aborted", "AbortError");
1859
+ }
1860
+ function markLpRedemptionReconciling(state, record, options) {
1861
+ const reconciling = { ...record, status: "reconciling" };
1862
+ state.redemptions.set(record.redemptionId, reconciling);
1863
+ options.onRedemptionUpdate?.(reconciling);
1864
+ throw new LpRedemptionReconcilingError();
1865
+ }
1866
+ function isLpPositionObservationDeadlineError(error, deadlineMs, nowMs) {
1867
+ return error instanceof CommandError && error.code === "REJECTED_STALE_JOB" && nowMs() >= deadlineMs;
1868
+ }
1869
+ function sleepWithAbort(sleep2, delayMs, signal) {
1870
+ if (signal === void 0) return sleep2(delayMs);
1871
+ throwIfWithdrawalConfirmationAborted(signal);
1872
+ return new Promise((resolve, reject) => {
1873
+ const onAbort = () => reject(new DOMException("LP withdrawal confirmation wait aborted", "AbortError"));
1874
+ signal.addEventListener("abort", onAbort, { once: true });
1875
+ void sleep2(delayMs).then(resolve, reject).finally(() => {
1876
+ signal.removeEventListener("abort", onAbort);
1877
+ });
1878
+ });
1879
+ }
1740
1880
  function defaultSleep(delayMs) {
1741
1881
  return new Promise((resolve) => {
1742
1882
  globalThis.setTimeout(resolve, delayMs);
@@ -1793,6 +1933,6 @@ function listPositions(session) {
1793
1933
  return defaultLpLifecycleClient(session).listPositions();
1794
1934
  }
1795
1935
 
1796
- 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_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 };
1936
+ 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 };
1797
1937
  //# sourceMappingURL=lp.js.map
1798
1938
  //# sourceMappingURL=lp.js.map