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

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 (46) hide show
  1. package/README.md +70 -13
  2. package/dist/.invisible-sdk-build-target.json +2 -2
  3. package/dist/{chunk-UADJCUPK.js → chunk-3MDHAS4D.js} +6 -5
  4. package/dist/chunk-3MDHAS4D.js.map +1 -0
  5. package/dist/{chunk-E4DCEDAG.js → chunk-3TGQ4CZ3.js} +178 -2
  6. package/dist/chunk-3TGQ4CZ3.js.map +1 -0
  7. package/dist/chunk-DCTYBUY3.js +258 -0
  8. package/dist/chunk-DCTYBUY3.js.map +1 -0
  9. package/dist/{chunk-NHEHCUS5.js → chunk-EBWTAXNT.js} +3 -3
  10. package/dist/{chunk-NHEHCUS5.js.map → chunk-EBWTAXNT.js.map} +1 -1
  11. package/dist/{chunk-V5HXJRBW.js → chunk-HJFMDMPY.js} +3 -3
  12. package/dist/chunk-HJFMDMPY.js.map +1 -0
  13. package/dist/{chunk-GHBQF3A7.js → chunk-QRN46R3F.js} +5 -2
  14. package/dist/chunk-QRN46R3F.js.map +1 -0
  15. package/dist/chunk-Y7AK6RBV.js +4189 -0
  16. package/dist/chunk-Y7AK6RBV.js.map +1 -0
  17. package/dist/{coordinator-ZNLGAYXD.js → coordinator-PEWHNZ7I.js} +3 -3
  18. package/dist/{coordinator-ZNLGAYXD.js.map → coordinator-PEWHNZ7I.js.map} +1 -1
  19. package/dist/events.js +3 -3
  20. package/dist/index.d.ts +14 -4
  21. package/dist/index.js +74 -11
  22. package/dist/index.js.map +1 -1
  23. package/dist/lp.d.ts +33 -9
  24. package/dist/lp.js +305 -45
  25. package/dist/lp.js.map +1 -1
  26. package/dist/presets.d.ts +6 -2
  27. package/dist/presets.js +8 -8
  28. package/dist/presets.js.map +1 -1
  29. package/dist/stats.js +1 -1
  30. package/dist/storage.js +2 -243
  31. package/dist/storage.js.map +1 -1
  32. package/dist/{types.generated-DJBTjLpP.d.ts → types.generated-YhFu9ZBR.d.ts} +29 -4
  33. package/dist/user.d.ts +31 -4
  34. package/dist/user.js +409 -57
  35. package/dist/user.js.map +1 -1
  36. package/package.json +4 -1
  37. package/dist/chunk-5NNEO7IK.js +0 -1150
  38. package/dist/chunk-5NNEO7IK.js.map +0 -1
  39. package/dist/chunk-BRDLLGCZ.js +0 -59
  40. package/dist/chunk-BRDLLGCZ.js.map +0 -1
  41. package/dist/chunk-DIA6U2CV.js +0 -1927
  42. package/dist/chunk-DIA6U2CV.js.map +0 -1
  43. package/dist/chunk-E4DCEDAG.js.map +0 -1
  44. package/dist/chunk-GHBQF3A7.js.map +0 -1
  45. package/dist/chunk-UADJCUPK.js.map +0 -1
  46. package/dist/chunk-V5HXJRBW.js.map +0 -1
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
- import { publicKey, assertAttested, lamports } from './chunk-NHEHCUS5.js';
3
- import { requestEnvelope } from './chunk-UADJCUPK.js';
4
- import { getSessionState } from './chunk-V5HXJRBW.js';
5
- import { CommandError, PolicyValidationError, TransportError, RoutingError, LpLifecycleError } from './chunk-GHBQF3A7.js';
2
+ import { publicKey, assertAttested, lamports } from './chunk-EBWTAXNT.js';
3
+ import { requestEnvelope, CoordinatorMessageType } from './chunk-3MDHAS4D.js';
4
+ import { getSessionState } from './chunk-HJFMDMPY.js';
5
+ import { CommandError, WalletError, TransportError, PolicyValidationError, RoutingError, LpLifecycleError } from './chunk-QRN46R3F.js';
6
6
  import './chunk-VR6T6OJS.js';
7
7
 
8
8
  // src/lp/positionCode.ts
@@ -228,59 +228,206 @@ function validateDestinationAddresses(destinationAddresses) {
228
228
 
229
229
  // src/lp/coordinatorClient.ts
230
230
  var COMMAND_TTL_MS = 3e4;
231
- var LP_COMMAND_REQUEST_TIMEOUT_MS = 12e4;
231
+ var LP_POSITION_OPEN_COMMAND_TTL_MS = 55e3;
232
+ var LP_POSITION_OPEN_REQUEST_TIMEOUT_MS = 2e4;
233
+ var LP_COMMAND_REQUEST_TIMEOUT_MS = COMMAND_TTL_MS;
234
+ var LP_CREATION_CHALLENGE_REQUEST_TIMEOUT_MS = 3e4;
235
+ var LP_CREATION_WALLET_SIGNATURE_TIMEOUT_MS = 45e3;
236
+ var LP_CREATION_PROOF_REQUEST_TIMEOUT_MS = 15e3;
237
+ var LP_CREATION_PROOF_RETRY_TIMEOUT_MS = 15e3;
238
+ var LP_CREATION_AUTHORIZATION_MARGIN_MS = 15e3;
239
+ var LP_CREATION_MIN_STEP_TIMEOUT_MS = 1;
232
240
  var LP_DKG_ABORT_REQUEST_TIMEOUT_MS = 1e4;
233
241
  var encoder2 = new TextEncoder();
234
242
  var protocolPromise = null;
235
- function createLpCoordinatorClient(session) {
243
+ function createLpCoordinatorClient(session, options = {}) {
244
+ const sessionRef = { current: session };
245
+ const reconnectSession = options.reconnectSession === void 0 ? void 0 : async () => {
246
+ const nextSession = await options.reconnectSession();
247
+ sessionRef.current = nextSession;
248
+ return nextSession;
249
+ };
236
250
  return {
251
+ authorizePositionCreation(intent, signer) {
252
+ return authorizePositionCreation(sessionRef.current, intent, signer);
253
+ },
237
254
  initDkg(input, auth) {
238
- return initDkg(session, input, auth);
255
+ return initDkg(sessionRef.current, input, auth);
239
256
  },
240
257
  abortDkgSessions(lpPositionId, auth) {
241
- return abortDkgSessions(session, lpPositionId, auth);
258
+ return abortDkgSessions(sessionRef.current, lpPositionId, auth);
242
259
  },
243
260
  dkgRound1(input, auth) {
244
- return dkgRound1(session, input, auth);
261
+ return dkgRound1(sessionRef.current, input, auth);
245
262
  },
246
263
  dkgRound2(input, auth) {
247
- return dkgRound2(session, input, auth);
264
+ return dkgRound2(sessionRef.current, input, auth);
248
265
  },
249
266
  openPosition(input, auth) {
250
- return openPosition(session, input, auth);
267
+ return openPosition(sessionRef.current, input, auth, reconnectSession);
251
268
  },
252
- getPosition(lpPositionId, auth, options) {
253
- return getPosition(session, lpPositionId, auth, options);
269
+ getPosition(lpPositionId, auth, options2) {
270
+ return getPosition(sessionRef.current, lpPositionId, auth, options2);
254
271
  },
255
272
  finalizeDkgBatch(input, auth) {
256
- return finalizeDkgBatch(session, input, auth);
273
+ return finalizeDkgBatch(sessionRef.current, input, auth);
257
274
  },
258
275
  refillPosition(input, auth) {
259
- return refillPosition(session, input, auth);
276
+ return refillPosition(sessionRef.current, input, auth);
260
277
  },
261
278
  reconcileFunding(lpPositionId, auth) {
262
- return reconcileFunding(session, lpPositionId, auth);
279
+ return reconcileFunding(sessionRef.current, lpPositionId, auth);
263
280
  },
264
281
  planFunding(input, auth) {
265
- return planFunding(session, input, auth);
282
+ return planFunding(sessionRef.current, input, auth);
266
283
  },
267
284
  confirmFunding(input, auth) {
268
- return confirmFunding(session, input, auth);
285
+ return confirmFunding(sessionRef.current, input, auth);
269
286
  },
270
287
  prepareTopUp(lpPositionId, auth) {
271
- return prepareTopUp(session, lpPositionId, auth);
288
+ return prepareTopUp(sessionRef.current, lpPositionId, auth);
272
289
  },
273
290
  getTopUpStatus(input, auth) {
274
- return getTopUpStatus(session, input, auth);
291
+ return getTopUpStatus(sessionRef.current, input, auth);
275
292
  },
276
293
  makeAvailable(input, auth) {
277
- return makeAvailable(session, input, auth);
294
+ return makeAvailable(sessionRef.current, input, auth);
278
295
  },
279
296
  executeWithdrawal(input, auth) {
280
- return executeWithdrawal(session, input, auth);
297
+ return executeWithdrawal(sessionRef.current, input, auth);
281
298
  }
282
299
  };
283
300
  }
301
+ async function authorizePositionCreation(session, intent, signer) {
302
+ const signerPublicKey = publicKey(signer.publicKey);
303
+ const challengeRequestStartedAtMs = Date.now();
304
+ const challengeEnvelope = await requestEnvelope(
305
+ session,
306
+ CoordinatorMessageType.LpCreateAuthorization,
307
+ {
308
+ case: "challenge",
309
+ value: {
310
+ public_key: signerPublicKey,
311
+ intent: {
312
+ committed_lamports: intent.committedLamports,
313
+ shard_count: intent.shardCount,
314
+ shard_amount_lamports: intent.shardAmountLamports,
315
+ refill_threshold: intent.refillThreshold
316
+ }
317
+ }
318
+ },
319
+ { timeoutMs: LP_CREATION_CHALLENGE_REQUEST_TIMEOUT_MS }
320
+ );
321
+ const challenge = challengeEnvelope.payload;
322
+ if (challenge.case !== "challenge") {
323
+ throw new CommandError(
324
+ "REJECTED_STATE",
325
+ "Coordinator returned an unexpected LP creation authorization response"
326
+ );
327
+ }
328
+ const localAuthorizationDeadlineMs = challengeRequestStartedAtMs + challenge.value.valid_for_ms;
329
+ const walletTimeoutMs = lpCreationAuthorizationStepTimeout(
330
+ localAuthorizationDeadlineMs,
331
+ LP_CREATION_PROOF_REQUEST_TIMEOUT_MS + LP_CREATION_PROOF_RETRY_TIMEOUT_MS + LP_CREATION_AUTHORIZATION_MARGIN_MS,
332
+ LP_CREATION_WALLET_SIGNATURE_TIMEOUT_MS
333
+ );
334
+ const signature = await signLpCreationMessageWithTimeout(
335
+ signer,
336
+ encoder2.encode(challenge.value.message),
337
+ walletTimeoutMs
338
+ );
339
+ if (!(signature instanceof Uint8Array) || signature.length !== 64) {
340
+ throw new WalletError(
341
+ "WALLET_INVALID_SIGNATURE",
342
+ "Solana wallet returned an invalid LP creation signature"
343
+ );
344
+ }
345
+ const proofPayload = {
346
+ case: "proof",
347
+ value: {
348
+ authorization_id: challenge.value.authorization_id,
349
+ public_key: signerPublicKey,
350
+ message: challenge.value.message,
351
+ signature
352
+ }
353
+ };
354
+ let proofEnvelope;
355
+ try {
356
+ const proofTimeoutMs = lpCreationAuthorizationStepTimeout(
357
+ localAuthorizationDeadlineMs,
358
+ LP_CREATION_PROOF_RETRY_TIMEOUT_MS + LP_CREATION_AUTHORIZATION_MARGIN_MS,
359
+ LP_CREATION_PROOF_REQUEST_TIMEOUT_MS
360
+ );
361
+ proofEnvelope = await requestEnvelope(
362
+ session,
363
+ CoordinatorMessageType.LpCreateAuthorization,
364
+ proofPayload,
365
+ { timeoutMs: proofTimeoutMs }
366
+ );
367
+ } catch (error) {
368
+ if (!isAmbiguousCoordinatorRequestFailure(error)) throw error;
369
+ const proofRetryTimeoutMs = lpCreationAuthorizationStepTimeout(
370
+ localAuthorizationDeadlineMs,
371
+ LP_CREATION_AUTHORIZATION_MARGIN_MS,
372
+ LP_CREATION_PROOF_RETRY_TIMEOUT_MS
373
+ );
374
+ proofEnvelope = await requestEnvelope(
375
+ session,
376
+ CoordinatorMessageType.LpCreateAuthorization,
377
+ proofPayload,
378
+ { timeoutMs: proofRetryTimeoutMs }
379
+ );
380
+ }
381
+ const authorization = proofEnvelope.payload;
382
+ if (authorization.case !== "authorized") {
383
+ throw new CommandError(
384
+ "REJECTED_STATE",
385
+ "Coordinator returned an unexpected LP creation authorization proof response"
386
+ );
387
+ }
388
+ if (authorization.value.authorization_id !== challenge.value.authorization_id) {
389
+ throw new CommandError(
390
+ "REJECTED_STATE",
391
+ "Coordinator returned an LP creation authorization for a different challenge"
392
+ );
393
+ }
394
+ lpCreationAuthorizationStepTimeout(
395
+ localAuthorizationDeadlineMs,
396
+ LP_CREATION_AUTHORIZATION_MARGIN_MS,
397
+ LP_CREATION_MIN_STEP_TIMEOUT_MS
398
+ );
399
+ return { authorizationId: authorization.value.authorization_id };
400
+ }
401
+ function lpCreationAuthorizationStepTimeout(localDeadlineMs, reservedMs, maximumTimeoutMs) {
402
+ const availableMs = localDeadlineMs - Date.now() - reservedMs;
403
+ if (availableMs < LP_CREATION_MIN_STEP_TIMEOUT_MS) {
404
+ throw new CommandError(
405
+ "REJECTED_EXPIRED",
406
+ "LP creation authorization expired; request a fresh coordinator challenge"
407
+ );
408
+ }
409
+ return Math.min(availableMs, maximumTimeoutMs);
410
+ }
411
+ async function signLpCreationMessageWithTimeout(signer, message, timeoutMs) {
412
+ let timeoutId;
413
+ try {
414
+ return await Promise.race([
415
+ signer.signMessage(message),
416
+ new Promise((_resolve, reject) => {
417
+ timeoutId = setTimeout(() => {
418
+ reject(
419
+ new WalletError(
420
+ "WALLET_USER_REJECTED",
421
+ "Solana wallet did not sign the LP creation challenge before it expired"
422
+ )
423
+ );
424
+ }, timeoutMs);
425
+ })
426
+ ]);
427
+ } finally {
428
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
429
+ }
430
+ }
284
431
  async function initDkg(session, input, auth) {
285
432
  const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_init", {
286
433
  case: "dkgInit",
@@ -335,10 +482,11 @@ async function dkgRound2(session, input, auth) {
335
482
  index: response.value.index
336
483
  };
337
484
  }
338
- async function openPosition(session, input, auth) {
339
- const response = await requestForPosition(session, input.lpPositionId, auth, "position_open", {
485
+ async function openPosition(session, input, auth, reconnectSession) {
486
+ const payload = {
340
487
  case: "positionOpen",
341
488
  value: {
489
+ creation_authorization_id: input.creationAuthorizationId,
342
490
  auth_public_key: Uint8Array.from(input.authPublicKey),
343
491
  refill_public_key: Uint8Array.from(input.refillPublicKey),
344
492
  redemption_commitment: Uint8Array.from(input.withdrawalCommitment),
@@ -349,9 +497,69 @@ async function openPosition(session, input, auth) {
349
497
  addresses: input.addresses,
350
498
  created_at_ms: input.createdAtMs
351
499
  }
352
- });
500
+ };
501
+ const commandDeadlineMs = Date.now() + LP_POSITION_OPEN_COMMAND_TTL_MS;
502
+ const commandOptions = {
503
+ commandNonce: randomBytes(32),
504
+ commandExpiresAtMs: commandDeadlineMs,
505
+ deadlineMs: commandDeadlineMs,
506
+ timeoutMs: LP_POSITION_OPEN_REQUEST_TIMEOUT_MS
507
+ };
508
+ let response;
509
+ try {
510
+ response = await requestForPosition(
511
+ session,
512
+ input.lpPositionId,
513
+ auth,
514
+ "position_open",
515
+ payload,
516
+ commandOptions
517
+ );
518
+ } catch (error) {
519
+ if (!isAmbiguousLpPositionOpenFailure(error)) throw error;
520
+ const replaySession = reconnectSession === void 0 ? session : await reconnectSessionWithinDeadline(reconnectSession, commandDeadlineMs);
521
+ response = await requestForPosition(
522
+ replaySession,
523
+ input.lpPositionId,
524
+ auth,
525
+ "position_open",
526
+ payload,
527
+ commandOptions
528
+ );
529
+ }
353
530
  return expectPosition(response);
354
531
  }
532
+ async function reconnectSessionWithinDeadline(reconnectSession, deadlineMs) {
533
+ const remainingMs = deadlineMs - Date.now();
534
+ if (remainingMs <= 0) {
535
+ throw new CommandError(
536
+ "REJECTED_EXPIRED",
537
+ "LP position-open command expired before coordinator reconnect"
538
+ );
539
+ }
540
+ let timer;
541
+ try {
542
+ return await new Promise((resolve, reject) => {
543
+ timer = setTimeout(() => {
544
+ reject(
545
+ new CommandError(
546
+ "REJECTED_EXPIRED",
547
+ "LP position-open command expired during coordinator reconnect"
548
+ )
549
+ );
550
+ }, remainingMs);
551
+ Promise.resolve().then(reconnectSession).then(resolve, reject);
552
+ });
553
+ } finally {
554
+ if (timer !== void 0) clearTimeout(timer);
555
+ }
556
+ }
557
+ function isAmbiguousLpPositionOpenFailure(error) {
558
+ return isAmbiguousCoordinatorRequestFailure(error);
559
+ }
560
+ function isAmbiguousCoordinatorRequestFailure(error) {
561
+ return error instanceof TransportError || error instanceof CommandError && error.code === "REJECTED_STALE_JOB";
562
+ }
355
563
  async function getPosition(session, lpPositionId, auth, options = {}) {
356
564
  let cursor;
357
565
  let position;
@@ -541,8 +749,8 @@ async function requestForPosition(session, lpPositionId, auth, action, payload,
541
749
  const command = {
542
750
  lp_position_id: lpPositionId,
543
751
  action,
544
- nonce: randomBytes(32),
545
- expires_at_ms: Date.now() + Math.min(COMMAND_TTL_MS, timeoutMs),
752
+ nonce: options.commandNonce === void 0 ? randomBytes(32) : new Uint8Array(options.commandNonce),
753
+ expires_at_ms: options.commandExpiresAtMs ?? Date.now() + Math.min(COMMAND_TTL_MS, timeoutMs),
546
754
  coordinator_attestation_hash: await coordinatorAttestationHash(
547
755
  attestation.binaryHash,
548
756
  attestation.pubkeyHex
@@ -582,9 +790,20 @@ function mapPosition(position) {
582
790
  refillThreshold: position.refill_threshold,
583
791
  shards: position.shards.map(mapShard),
584
792
  createdAt: position.created_at_ms,
793
+ redemptionPermission: mapLpRedemptionPermission(position.redemption_permission),
585
794
  ...position.actor_sync === void 0 ? {} : { actorSync: mapLpActorSync(position.actor_sync) }
586
795
  };
587
796
  }
797
+ function mapLpRedemptionPermission(permission) {
798
+ if (permission === void 0) {
799
+ return { state: "unknown", grantRevision: 0, observedAtMs: 0 };
800
+ }
801
+ return {
802
+ state: permission.state,
803
+ grantRevision: permission.grant_revision,
804
+ observedAtMs: permission.observed_at_ms
805
+ };
806
+ }
588
807
  function mapLpActorSync(actorSync) {
589
808
  const redeemableBucketValues = [
590
809
  actorSync.redeemable_principal_lamports,
@@ -736,7 +955,7 @@ function randomBytes(length) {
736
955
  return bytes;
737
956
  }
738
957
  function loadProtocol() {
739
- protocolPromise ??= import('./coordinator-ZNLGAYXD.js');
958
+ protocolPromise ??= import('./coordinator-PEWHNZ7I.js');
740
959
  return protocolPromise;
741
960
  }
742
961
 
@@ -1011,6 +1230,7 @@ var WITHDRAWAL_MISSING_RECORD_MESSAGE = "LP withdrawal record is missing";
1011
1230
  var WITHDRAWAL_RECORD_MISMATCH_MESSAGE = "LP withdrawal record does not match this confirmation request";
1012
1231
  var WITHDRAWAL_NO_LAMPORTS_MESSAGE = "LP redemption did not land any lamports on-chain";
1013
1232
  var WITHDRAWAL_HISTORY_MISMATCH_MESSAGE = "LP redemption history does not match this request";
1233
+ var LP_REDEMPTION_PERMISSION_UNAVAILABLE_MESSAGE = "LP redemption is unavailable until the coordinator confirms permission";
1014
1234
  var LP_WITHDRAWAL_UNRESOLVED_FUNDING_MESSAGE = "LP position has unresolved funding shards before redemption";
1015
1235
  var UNSUPPORTED_FEE_POLICY_MESSAGE = "LP fee policy overrides are not advertised by the coordinator";
1016
1236
  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";
@@ -1050,7 +1270,13 @@ function createLpLifecycleClient(session, runtime = {}) {
1050
1270
  redemptions: /* @__PURE__ */ new Map(),
1051
1271
  topUps: /* @__PURE__ */ new Map()
1052
1272
  };
1053
- const client = runtime.coordinatorClient ?? createLpCoordinatorClient(session);
1273
+ let currentSession = session;
1274
+ const reconnectSession = runtime.reconnectSession === void 0 ? void 0 : async () => {
1275
+ const nextSession = await runtime.reconnectSession();
1276
+ currentSession = nextSession;
1277
+ return nextSession;
1278
+ };
1279
+ const client = runtime.coordinatorClient ?? createLpCoordinatorClient(session, { reconnectSession });
1054
1280
  const nowMs = runtime.nowMs ?? Date.now;
1055
1281
  const sleep2 = runtime.sleep ?? defaultSleep;
1056
1282
  const requireAuth = (positionId) => {
@@ -1060,9 +1286,17 @@ function createLpLifecycleClient(session, runtime = {}) {
1060
1286
  };
1061
1287
  const rememberPosition = (position) => {
1062
1288
  const snapshot = clonePosition(position);
1063
- state.positions.set(snapshot.id, snapshot);
1064
- hydrateLpRedemptionsFromPosition(state, snapshot);
1065
- return position;
1289
+ const previous = state.positions.get(snapshot.id);
1290
+ const merged = previous === void 0 ? snapshot : {
1291
+ ...snapshot,
1292
+ redemptionPermission: mergeLpRedemptionPermission(
1293
+ previous.redemptionPermission,
1294
+ snapshot.redemptionPermission
1295
+ )
1296
+ };
1297
+ state.positions.set(merged.id, merged);
1298
+ hydrateLpRedemptionsFromPosition(state, merged);
1299
+ return merged;
1066
1300
  };
1067
1301
  const rememberAuth = (auth) => {
1068
1302
  state.authByPosition.set(auth.lpPositionId, auth);
@@ -1086,7 +1320,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1086
1320
  }
1087
1321
  };
1088
1322
  const completeDkgBatch2 = async (positionId, options = {}) => {
1089
- assertAttested(session);
1323
+ assertAttested(currentSession);
1090
1324
  const auth = requireAuth(positionId);
1091
1325
  let activeSessionRetryStartedAtMs = null;
1092
1326
  for (; ; ) {
@@ -1150,33 +1384,44 @@ function createLpLifecycleClient(session, runtime = {}) {
1150
1384
  }
1151
1385
  };
1152
1386
  return {
1153
- async createPosition(args = {}) {
1387
+ async createPosition(args) {
1154
1388
  assertNoUnsupportedFeePolicy(args.feePolicy);
1155
1389
  const shardCount = args.shardCount ?? LP_DEFAULT_TARGET_SHARDS;
1156
1390
  assertShardCount(shardCount, LP_MAX_TARGET_SHARDS, "shardCount");
1157
- assertAttested(session);
1391
+ assertAttested(currentSession);
1392
+ const committedLamports = args.committedLamports ?? DEFAULT_SHARD_FUNDING_LAMPORTS;
1393
+ const refillThreshold = args.refillThreshold ?? Math.ceil(shardCount * DEFAULT_REFILL_THRESHOLD_RATIO);
1394
+ const creationAuthorization = await client.authorizePositionCreation(
1395
+ {
1396
+ committedLamports,
1397
+ shardCount,
1398
+ shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
1399
+ refillThreshold
1400
+ },
1401
+ args.wallet
1402
+ );
1158
1403
  const positionId = runtime.idFactory?.() ?? generateId(LP_ID_PREFIX);
1159
1404
  const auth = await generateLpPositionCode(positionId);
1160
1405
  rememberAuth(auth);
1161
- args.onPositionCode?.({ positionId, lpPositionCode: auth.code });
1162
- const committedLamports = args.committedLamports ?? DEFAULT_SHARD_FUNDING_LAMPORTS;
1163
1406
  const position = rememberPosition(
1164
1407
  await client.openPosition(
1165
1408
  {
1166
1409
  lpPositionId: positionId,
1410
+ creationAuthorizationId: creationAuthorization.authorizationId,
1167
1411
  authPublicKey: Array.from(auth.authPublicKey),
1168
1412
  refillPublicKey: Array.from(auth.refillPublicKey),
1169
1413
  withdrawalCommitment: Array.from(auth.withdrawalCommitment),
1170
1414
  committedLamports,
1171
1415
  shardCount,
1172
1416
  shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
1173
- refillThreshold: args.refillThreshold ?? Math.ceil(shardCount * DEFAULT_REFILL_THRESHOLD_RATIO),
1417
+ refillThreshold,
1174
1418
  addresses: [],
1175
1419
  createdAtMs: nowMs()
1176
1420
  },
1177
1421
  auth
1178
1422
  )
1179
1423
  );
1424
+ args.onPositionCode?.({ positionId, lpPositionCode: auth.code });
1180
1425
  return { positionId, lpPositionCode: auth.code, position };
1181
1426
  },
1182
1427
  async recoverPosition(args) {
@@ -1199,7 +1444,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1199
1444
  async refill(positionId, args = {}) {
1200
1445
  const requestedShardCount = args.shardCount ?? LP_DEFAULT_REFILL_BATCH_SIZE;
1201
1446
  assertShardCount(requestedShardCount, LP_MAX_REFILL_BATCH_SIZE, "shardCount");
1202
- assertAttested(session);
1447
+ assertAttested(currentSession);
1203
1448
  const auth = requireAuth(positionId);
1204
1449
  const current = await refreshPosition2(positionId);
1205
1450
  args.onPosition?.(current);
@@ -1231,7 +1476,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1231
1476
  });
1232
1477
  },
1233
1478
  async prepareInitialFunding(positionId) {
1234
- assertAttested(session);
1479
+ assertAttested(currentSession);
1235
1480
  const auth = requireAuth(positionId);
1236
1481
  const currentPosition = rememberPosition(await client.getPosition(positionId, auth));
1237
1482
  if (!isLpPositionReadyForInitialFunding(currentPosition)) return null;
@@ -1260,7 +1505,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1260
1505
  return fundingPlanFromShard(position, plannedShard);
1261
1506
  },
1262
1507
  async prepareTopUp(positionId) {
1263
- assertAttested(session);
1508
+ assertAttested(currentSession);
1264
1509
  const auth = requireAuth(positionId);
1265
1510
  const request2 = await client.prepareTopUp(positionId, auth);
1266
1511
  state.topUps.set(request2.topUpId, request2);
@@ -1308,16 +1553,22 @@ function createLpLifecycleClient(session, runtime = {}) {
1308
1553
  }
1309
1554
  },
1310
1555
  async reconcileFunding(positionId) {
1311
- assertAttested(session);
1556
+ assertAttested(currentSession);
1312
1557
  const auth = requireAuth(positionId);
1313
1558
  return rememberPosition(await client.reconcileFunding(positionId, auth));
1314
1559
  },
1315
1560
  async withdrawPosition(positionId, args) {
1316
1561
  validateExplicitWithdrawalInput(args);
1317
1562
  validateWithdrawalDestinations(args);
1318
- assertAttested(session);
1563
+ assertAttested(currentSession);
1319
1564
  const auth = requireAuth(positionId);
1320
1565
  const before = rememberPosition(await client.reconcileFunding(positionId, auth));
1566
+ if (before.redemptionPermission?.state !== "enabled") {
1567
+ throw new PolicyValidationError(
1568
+ "INVALID_REDEMPTION_PLAN",
1569
+ LP_REDEMPTION_PERMISSION_UNAVAILABLE_MESSAGE
1570
+ );
1571
+ }
1321
1572
  const existingRedemption = findOpenRedemptionHistory(before, args);
1322
1573
  if (existingRedemption !== void 0) {
1323
1574
  const execution2 = {
@@ -1452,7 +1703,7 @@ function createLpLifecycleClient(session, runtime = {}) {
1452
1703
  },
1453
1704
  async withdrawFees(_positionId, args) {
1454
1705
  publicKey(args.destination);
1455
- assertAttested(session);
1706
+ assertAttested(currentSession);
1456
1707
  throw new RoutingError(
1457
1708
  "VERSION_NOT_SUPPORTED",
1458
1709
  "LP fee withdrawal is not advertised by the coordinator"
@@ -1841,6 +2092,15 @@ function clonePosition(position) {
1841
2092
  }
1842
2093
  };
1843
2094
  }
2095
+ function mergeLpRedemptionPermission(existing, next) {
2096
+ const current = existing ?? { state: "unknown", grantRevision: 0, observedAtMs: 0 };
2097
+ const incoming = next ?? { state: "unknown", grantRevision: 0, observedAtMs: 0 };
2098
+ if (incoming.state === "unknown") return incoming;
2099
+ if (current.state === "enabled" && incoming.state !== "enabled") return current;
2100
+ if (incoming.grantRevision > current.grantRevision) return incoming;
2101
+ if (incoming.grantRevision < current.grantRevision) return current;
2102
+ return incoming.observedAtMs > current.observedAtMs ? incoming : current;
2103
+ }
1844
2104
  function generateId(prefix) {
1845
2105
  return `${prefix}_${Array.from(randomBytes2(8)).map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
1846
2106
  }
@@ -1884,7 +2144,7 @@ function defaultSleep(delayMs) {
1884
2144
  }
1885
2145
 
1886
2146
  // src/lp/index.ts
1887
- function createPosition(session, args = {}) {
2147
+ function createPosition(session, args) {
1888
2148
  return defaultLpLifecycleClient(session).createPosition(args);
1889
2149
  }
1890
2150
  function recoverPosition(session, args) {