@invisible-labs/sdk 0.6.0-devnet.3 → 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.
- package/README.md +129 -15
- package/dist/.invisible-sdk-build-target.json +7 -0
- package/dist/{chunk-QQSI3ZD7.js → chunk-3MDHAS4D.js} +109 -17
- package/dist/chunk-3MDHAS4D.js.map +1 -0
- package/dist/{chunk-NKRVQXRP.js → chunk-3TGQ4CZ3.js} +297 -11
- package/dist/chunk-3TGQ4CZ3.js.map +1 -0
- package/dist/chunk-DCTYBUY3.js +258 -0
- package/dist/chunk-DCTYBUY3.js.map +1 -0
- package/dist/{chunk-NHEHCUS5.js → chunk-EBWTAXNT.js} +3 -3
- package/dist/{chunk-NHEHCUS5.js.map → chunk-EBWTAXNT.js.map} +1 -1
- package/dist/{chunk-V5HXJRBW.js → chunk-HJFMDMPY.js} +3 -3
- package/dist/chunk-HJFMDMPY.js.map +1 -0
- package/dist/{chunk-GHBQF3A7.js → chunk-QRN46R3F.js} +5 -2
- package/dist/chunk-QRN46R3F.js.map +1 -0
- package/dist/chunk-Y7AK6RBV.js +4189 -0
- package/dist/chunk-Y7AK6RBV.js.map +1 -0
- package/dist/{coordinator-VVJ33WF2.js → coordinator-PEWHNZ7I.js} +3 -3
- package/dist/{coordinator-VVJ33WF2.js.map → coordinator-PEWHNZ7I.js.map} +1 -1
- package/dist/events.js +3 -3
- package/dist/index.d.ts +69 -353
- package/dist/index.js +161 -43
- package/dist/index.js.map +1 -1
- package/dist/lp.d.ts +76 -18
- package/dist/lp.js +511 -111
- package/dist/lp.js.map +1 -1
- package/dist/presets.d.ts +7 -40
- package/dist/presets.js +10 -68
- package/dist/presets.js.map +1 -1
- package/dist/stats.js +1 -1
- package/dist/storage.js +2 -243
- package/dist/storage.js.map +1 -1
- package/dist/{errors-N2touVm4.d.ts → types.generated-YhFu9ZBR.d.ts} +65 -4
- package/dist/user.d.ts +134 -8
- package/dist/user.js +991 -250
- package/dist/user.js.map +1 -1
- package/package.json +5 -31
- package/dist/chunk-BWCGNTN5.js +0 -1153
- package/dist/chunk-BWCGNTN5.js.map +0 -1
- package/dist/chunk-DIA6U2CV.js +0 -1927
- package/dist/chunk-DIA6U2CV.js.map +0 -1
- package/dist/chunk-GHBQF3A7.js.map +0 -1
- package/dist/chunk-NKRVQXRP.js.map +0 -1
- package/dist/chunk-QQSI3ZD7.js.map +0 -1
- package/dist/chunk-V5HXJRBW.js.map +0 -1
- package/dist/types.generated-BSEBz4jR.d.ts +0 -68
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-
|
|
3
|
-
import { requestEnvelope } from './chunk-
|
|
4
|
-
import { getSessionState } from './chunk-
|
|
5
|
-
import { CommandError, PolicyValidationError, RoutingError, LpLifecycleError } from './chunk-
|
|
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
|
|
@@ -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
|
|
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);
|
|
@@ -224,59 +228,206 @@ function validateDestinationAddresses(destinationAddresses) {
|
|
|
224
228
|
|
|
225
229
|
// src/lp/coordinatorClient.ts
|
|
226
230
|
var COMMAND_TTL_MS = 3e4;
|
|
227
|
-
var
|
|
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;
|
|
228
240
|
var LP_DKG_ABORT_REQUEST_TIMEOUT_MS = 1e4;
|
|
229
241
|
var encoder2 = new TextEncoder();
|
|
230
242
|
var protocolPromise = null;
|
|
231
|
-
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
|
+
};
|
|
232
250
|
return {
|
|
251
|
+
authorizePositionCreation(intent, signer) {
|
|
252
|
+
return authorizePositionCreation(sessionRef.current, intent, signer);
|
|
253
|
+
},
|
|
233
254
|
initDkg(input, auth) {
|
|
234
|
-
return initDkg(
|
|
255
|
+
return initDkg(sessionRef.current, input, auth);
|
|
235
256
|
},
|
|
236
257
|
abortDkgSessions(lpPositionId, auth) {
|
|
237
|
-
return abortDkgSessions(
|
|
258
|
+
return abortDkgSessions(sessionRef.current, lpPositionId, auth);
|
|
238
259
|
},
|
|
239
260
|
dkgRound1(input, auth) {
|
|
240
|
-
return dkgRound1(
|
|
261
|
+
return dkgRound1(sessionRef.current, input, auth);
|
|
241
262
|
},
|
|
242
263
|
dkgRound2(input, auth) {
|
|
243
|
-
return dkgRound2(
|
|
264
|
+
return dkgRound2(sessionRef.current, input, auth);
|
|
244
265
|
},
|
|
245
266
|
openPosition(input, auth) {
|
|
246
|
-
return openPosition(
|
|
267
|
+
return openPosition(sessionRef.current, input, auth, reconnectSession);
|
|
247
268
|
},
|
|
248
|
-
getPosition(lpPositionId, auth) {
|
|
249
|
-
return getPosition(
|
|
269
|
+
getPosition(lpPositionId, auth, options2) {
|
|
270
|
+
return getPosition(sessionRef.current, lpPositionId, auth, options2);
|
|
250
271
|
},
|
|
251
272
|
finalizeDkgBatch(input, auth) {
|
|
252
|
-
return finalizeDkgBatch(
|
|
273
|
+
return finalizeDkgBatch(sessionRef.current, input, auth);
|
|
253
274
|
},
|
|
254
275
|
refillPosition(input, auth) {
|
|
255
|
-
return refillPosition(
|
|
276
|
+
return refillPosition(sessionRef.current, input, auth);
|
|
256
277
|
},
|
|
257
278
|
reconcileFunding(lpPositionId, auth) {
|
|
258
|
-
return reconcileFunding(
|
|
279
|
+
return reconcileFunding(sessionRef.current, lpPositionId, auth);
|
|
259
280
|
},
|
|
260
281
|
planFunding(input, auth) {
|
|
261
|
-
return planFunding(
|
|
282
|
+
return planFunding(sessionRef.current, input, auth);
|
|
262
283
|
},
|
|
263
284
|
confirmFunding(input, auth) {
|
|
264
|
-
return confirmFunding(
|
|
285
|
+
return confirmFunding(sessionRef.current, input, auth);
|
|
265
286
|
},
|
|
266
287
|
prepareTopUp(lpPositionId, auth) {
|
|
267
|
-
return prepareTopUp(
|
|
288
|
+
return prepareTopUp(sessionRef.current, lpPositionId, auth);
|
|
268
289
|
},
|
|
269
290
|
getTopUpStatus(input, auth) {
|
|
270
|
-
return getTopUpStatus(
|
|
291
|
+
return getTopUpStatus(sessionRef.current, input, auth);
|
|
271
292
|
},
|
|
272
293
|
makeAvailable(input, auth) {
|
|
273
|
-
return makeAvailable(
|
|
294
|
+
return makeAvailable(sessionRef.current, input, auth);
|
|
274
295
|
},
|
|
275
296
|
executeWithdrawal(input, auth) {
|
|
276
|
-
return executeWithdrawal(
|
|
297
|
+
return executeWithdrawal(sessionRef.current, input, auth);
|
|
277
298
|
}
|
|
278
299
|
};
|
|
279
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
|
+
}
|
|
280
431
|
async function initDkg(session, input, auth) {
|
|
281
432
|
const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_init", {
|
|
282
433
|
case: "dkgInit",
|
|
@@ -295,7 +446,7 @@ async function abortDkgSessions(session, lpPositionId, auth) {
|
|
|
295
446
|
case: "dkgAbort",
|
|
296
447
|
value: {}
|
|
297
448
|
},
|
|
298
|
-
LP_DKG_ABORT_REQUEST_TIMEOUT_MS
|
|
449
|
+
{ timeoutMs: LP_DKG_ABORT_REQUEST_TIMEOUT_MS }
|
|
299
450
|
);
|
|
300
451
|
if (response.case !== "dkgAbort") throw new Error("Unexpected LP DKG abort response");
|
|
301
452
|
return { abortedSessionCount: response.value.aborted_session_count };
|
|
@@ -331,10 +482,11 @@ async function dkgRound2(session, input, auth) {
|
|
|
331
482
|
index: response.value.index
|
|
332
483
|
};
|
|
333
484
|
}
|
|
334
|
-
async function openPosition(session, input, auth) {
|
|
335
|
-
const
|
|
485
|
+
async function openPosition(session, input, auth, reconnectSession) {
|
|
486
|
+
const payload = {
|
|
336
487
|
case: "positionOpen",
|
|
337
488
|
value: {
|
|
489
|
+
creation_authorization_id: input.creationAuthorizationId,
|
|
338
490
|
auth_public_key: Uint8Array.from(input.authPublicKey),
|
|
339
491
|
refill_public_key: Uint8Array.from(input.refillPublicKey),
|
|
340
492
|
redemption_commitment: Uint8Array.from(input.withdrawalCommitment),
|
|
@@ -345,18 +497,86 @@ async function openPosition(session, input, auth) {
|
|
|
345
497
|
addresses: input.addresses,
|
|
346
498
|
created_at_ms: input.createdAtMs
|
|
347
499
|
}
|
|
348
|
-
}
|
|
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
|
+
}
|
|
349
530
|
return expectPosition(response);
|
|
350
531
|
}
|
|
351
|
-
async function
|
|
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
|
+
}
|
|
563
|
+
async function getPosition(session, lpPositionId, auth, options = {}) {
|
|
352
564
|
let cursor;
|
|
353
565
|
let position;
|
|
354
566
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
355
567
|
for (; ; ) {
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
568
|
+
const pageOptions = lpPositionPageRequestOptions(options);
|
|
569
|
+
const response = await requestForPosition(
|
|
570
|
+
session,
|
|
571
|
+
lpPositionId,
|
|
572
|
+
auth,
|
|
573
|
+
"position_get",
|
|
574
|
+
{
|
|
575
|
+
case: "positionGet",
|
|
576
|
+
value: cursor === void 0 ? {} : { redemption_history_cursor: cursor }
|
|
577
|
+
},
|
|
578
|
+
pageOptions
|
|
579
|
+
);
|
|
360
580
|
const page = expectPosition(response);
|
|
361
581
|
const mergedPosition = position === void 0 ? page : mergePositionHistoryPages(position, page);
|
|
362
582
|
position = mergedPosition;
|
|
@@ -373,6 +593,18 @@ async function getPosition(session, lpPositionId, auth) {
|
|
|
373
593
|
cursor = nextCursor;
|
|
374
594
|
}
|
|
375
595
|
}
|
|
596
|
+
function lpPositionPageRequestOptions(options) {
|
|
597
|
+
if (options.deadlineMs === void 0) return options;
|
|
598
|
+
const remainingMs = options.deadlineMs - Date.now();
|
|
599
|
+
if (remainingMs <= 0) {
|
|
600
|
+
throw new CommandError("REJECTED_STALE_JOB", "LP position observation deadline elapsed");
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
deadlineMs: options.deadlineMs,
|
|
604
|
+
signal: options.signal,
|
|
605
|
+
timeoutMs: Math.min(options.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS, remainingMs)
|
|
606
|
+
};
|
|
607
|
+
}
|
|
376
608
|
async function finalizeDkgBatch(session, input, auth) {
|
|
377
609
|
const response = await requestForPosition(session, input.lpPositionId, auth, "dkg_finalize", {
|
|
378
610
|
case: "dkgFinalize",
|
|
@@ -461,41 +693,64 @@ async function makeAvailable(session, input, auth) {
|
|
|
461
693
|
async function executeWithdrawal(session, input, auth) {
|
|
462
694
|
validateLpWithdrawalPlan({
|
|
463
695
|
destinationAddresses: input.destinationAddresses,
|
|
464
|
-
depositPrincipalLamports: input.depositPrincipalLamports,
|
|
465
|
-
earnedFeeLamports: input.earnedFeeLamports,
|
|
466
|
-
|
|
696
|
+
depositPrincipalLamports: input.withdrawalMode === "max_available" ? 1 : input.depositPrincipalLamports,
|
|
697
|
+
earnedFeeLamports: input.withdrawalMode === "max_available" ? 0 : input.earnedFeeLamports,
|
|
698
|
+
// Max mode deliberately does not preview source count. The coordinator
|
|
699
|
+
// owns the accepted split, so local destination validation must not reject
|
|
700
|
+
// a one-destination request based on an unknown current source count.
|
|
701
|
+
allowManyToOne: input.withdrawalMode === "max_available" || input.allowManyToOne
|
|
467
702
|
});
|
|
468
703
|
const response = await requestForPosition(session, input.lpPositionId, auth, "withdraw_request", {
|
|
469
704
|
case: "withdrawRequest",
|
|
470
705
|
value: {
|
|
471
706
|
destination_addresses: input.destinationAddresses,
|
|
472
|
-
deposit_principal_lamports: input.depositPrincipalLamports,
|
|
473
|
-
earned_fee_lamports: input.earnedFeeLamports,
|
|
707
|
+
deposit_principal_lamports: input.withdrawalMode === "max_available" ? 0 : input.depositPrincipalLamports,
|
|
708
|
+
earned_fee_lamports: input.withdrawalMode === "max_available" ? 0 : input.earnedFeeLamports,
|
|
709
|
+
withdrawal_mode: input.withdrawalMode,
|
|
474
710
|
requested_at_ms: input.requestedAtMs
|
|
475
711
|
}
|
|
476
712
|
});
|
|
477
713
|
if (response.case !== "withdrawalExecution") {
|
|
478
714
|
throw new Error("Unexpected LP withdrawal execution response");
|
|
479
715
|
}
|
|
716
|
+
const execution = response.value;
|
|
717
|
+
if (BigInt(execution.accepted_lamports) !== BigInt(execution.accepted_deposit_principal_lamports) + BigInt(execution.accepted_earned_fee_lamports)) {
|
|
718
|
+
throw new PolicyValidationError(
|
|
719
|
+
"INVALID_WITHDRAWAL_PLAN",
|
|
720
|
+
"Coordinator accepted withdrawal total does not match its principal and fee split"
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
if (input.withdrawalMode === "exact" && (execution.accepted_deposit_principal_lamports !== input.depositPrincipalLamports || execution.accepted_earned_fee_lamports !== input.earnedFeeLamports)) {
|
|
724
|
+
throw new PolicyValidationError(
|
|
725
|
+
"INVALID_WITHDRAWAL_PLAN",
|
|
726
|
+
"Coordinator accepted withdrawal split does not match the signed exact claim"
|
|
727
|
+
);
|
|
728
|
+
}
|
|
480
729
|
return {
|
|
481
|
-
withdrawalId:
|
|
482
|
-
txSignatures: [...
|
|
730
|
+
withdrawalId: execution.withdrawal_id,
|
|
731
|
+
txSignatures: [...execution.tx_signatures],
|
|
732
|
+
acceptedLamports: execution.accepted_lamports,
|
|
733
|
+
acceptedDepositPrincipalLamports: execution.accepted_deposit_principal_lamports,
|
|
734
|
+
acceptedEarnedFeeLamports: execution.accepted_earned_fee_lamports
|
|
483
735
|
};
|
|
484
736
|
}
|
|
485
737
|
function request(session, auth, action, payload) {
|
|
486
738
|
return requestForPosition(session, auth.lpPositionId, auth, action, payload);
|
|
487
739
|
}
|
|
488
|
-
async function requestForPosition(session, lpPositionId, auth, action, payload,
|
|
740
|
+
async function requestForPosition(session, lpPositionId, auth, action, payload, options = {}) {
|
|
741
|
+
const timeoutMs = options.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS;
|
|
489
742
|
assertAuthMatchesPosition(lpPositionId, auth);
|
|
490
743
|
const protocol = await loadProtocol();
|
|
491
744
|
const state = getSessionState(session);
|
|
492
745
|
const attestation = state.channel?.attestation;
|
|
493
|
-
if (!attestation)
|
|
746
|
+
if (!attestation) {
|
|
747
|
+
throw new TransportError("CONNECTION_LOST", "LP coordinator attestation is not available");
|
|
748
|
+
}
|
|
494
749
|
const command = {
|
|
495
750
|
lp_position_id: lpPositionId,
|
|
496
751
|
action,
|
|
497
|
-
nonce: randomBytes(32),
|
|
498
|
-
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),
|
|
499
754
|
coordinator_attestation_hash: await coordinatorAttestationHash(
|
|
500
755
|
attestation.binaryHash,
|
|
501
756
|
attestation.pubkeyHex
|
|
@@ -504,8 +759,10 @@ async function requestForPosition(session, lpPositionId, auth, action, payload,
|
|
|
504
759
|
};
|
|
505
760
|
const signature = await signLpCommandBytes(auth, protocol.encodeLpCommandForSigning(command));
|
|
506
761
|
const { type, payload: envelopePayload } = protocol.lpSignedCommandEnvelope(command, signature);
|
|
762
|
+
const requestOptions = lpPositionPageRequestOptions(options);
|
|
507
763
|
const envelope = await requestEnvelope(session, type, envelopePayload, {
|
|
508
|
-
timeoutMs
|
|
764
|
+
timeoutMs: requestOptions.timeoutMs ?? LP_COMMAND_REQUEST_TIMEOUT_MS,
|
|
765
|
+
signal: requestOptions.signal
|
|
509
766
|
});
|
|
510
767
|
return protocol.asLpCommandResponsePayload(envelope);
|
|
511
768
|
}
|
|
@@ -533,9 +790,20 @@ function mapPosition(position) {
|
|
|
533
790
|
refillThreshold: position.refill_threshold,
|
|
534
791
|
shards: position.shards.map(mapShard),
|
|
535
792
|
createdAt: position.created_at_ms,
|
|
793
|
+
redemptionPermission: mapLpRedemptionPermission(position.redemption_permission),
|
|
536
794
|
...position.actor_sync === void 0 ? {} : { actorSync: mapLpActorSync(position.actor_sync) }
|
|
537
795
|
};
|
|
538
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
|
+
}
|
|
539
807
|
function mapLpActorSync(actorSync) {
|
|
540
808
|
const redeemableBucketValues = [
|
|
541
809
|
actorSync.redeemable_principal_lamports,
|
|
@@ -564,6 +832,7 @@ function mapLpActorSync(actorSync) {
|
|
|
564
832
|
lifetimeEarnedLamports: actorSync.lifetime_earned_lamports ?? 0,
|
|
565
833
|
redeemablePrincipalSourceCount: actorSync.redeemable_principal_source_count ?? 0,
|
|
566
834
|
redeemableFeeSourceCount: actorSync.redeemable_fee_source_count ?? 0,
|
|
835
|
+
redeemableSourceCount: actorSync.redeemable_source_count,
|
|
567
836
|
pendingReservationCount: actorSync.pending_reservation_count,
|
|
568
837
|
reservedPayoutLamports: actorSync.reserved_payout_lamports,
|
|
569
838
|
estimatedNetworkFeeLamports: actorSync.estimated_network_fee_lamports,
|
|
@@ -607,6 +876,7 @@ function mapLpRedemptionHistory(history) {
|
|
|
607
876
|
return {
|
|
608
877
|
redemptionId: history.redemption_id,
|
|
609
878
|
state: history.state,
|
|
879
|
+
...history.withdrawal_mode === void 0 ? {} : { withdrawalMode: history.withdrawal_mode },
|
|
610
880
|
acceptedLamports: history.accepted_lamports,
|
|
611
881
|
acceptedDepositPrincipalLamports: history.accepted_deposit_principal_lamports,
|
|
612
882
|
acceptedEarnedFeeLamports: history.accepted_earned_fee_lamports,
|
|
@@ -685,7 +955,7 @@ function randomBytes(length) {
|
|
|
685
955
|
return bytes;
|
|
686
956
|
}
|
|
687
957
|
function loadProtocol() {
|
|
688
|
-
protocolPromise ??= import('./coordinator-
|
|
958
|
+
protocolPromise ??= import('./coordinator-PEWHNZ7I.js');
|
|
689
959
|
return protocolPromise;
|
|
690
960
|
}
|
|
691
961
|
|
|
@@ -960,9 +1230,10 @@ var WITHDRAWAL_MISSING_RECORD_MESSAGE = "LP withdrawal record is missing";
|
|
|
960
1230
|
var WITHDRAWAL_RECORD_MISMATCH_MESSAGE = "LP withdrawal record does not match this confirmation request";
|
|
961
1231
|
var WITHDRAWAL_NO_LAMPORTS_MESSAGE = "LP redemption did not land any lamports on-chain";
|
|
962
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";
|
|
963
1234
|
var LP_WITHDRAWAL_UNRESOLVED_FUNDING_MESSAGE = "LP position has unresolved funding shards before redemption";
|
|
964
1235
|
var UNSUPPORTED_FEE_POLICY_MESSAGE = "LP fee policy overrides are not advertised by the coordinator";
|
|
965
|
-
var
|
|
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";
|
|
966
1237
|
var LpRedemptionReconcilingError = class extends LpLifecycleError {
|
|
967
1238
|
constructor() {
|
|
968
1239
|
super("LP_REDEMPTION_RECONCILING", LP_REDEMPTION_RECONCILING_MESSAGE);
|
|
@@ -981,6 +1252,16 @@ function isLpRedemptionReconcilingError(error) {
|
|
|
981
1252
|
function isLpPositionAuthMissingError(error) {
|
|
982
1253
|
return error instanceof LpPositionAuthMissingError || error instanceof Error && error.message === LP_MISSING_POSITION_CODE_MESSAGE;
|
|
983
1254
|
}
|
|
1255
|
+
function previewLpWithdrawalPlan(position, args = {}) {
|
|
1256
|
+
const claim = resolveWithdrawalClaim(position, args);
|
|
1257
|
+
return {
|
|
1258
|
+
...claim,
|
|
1259
|
+
sourceCount: lpWithdrawalSourceCountForClaim(position, {
|
|
1260
|
+
...claim,
|
|
1261
|
+
allowManyToOne: args.allowManyToOne
|
|
1262
|
+
})
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
984
1265
|
var lifecycleClients = /* @__PURE__ */ new WeakMap();
|
|
985
1266
|
function createLpLifecycleClient(session, runtime = {}) {
|
|
986
1267
|
const state = {
|
|
@@ -989,7 +1270,13 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
989
1270
|
redemptions: /* @__PURE__ */ new Map(),
|
|
990
1271
|
topUps: /* @__PURE__ */ new Map()
|
|
991
1272
|
};
|
|
992
|
-
|
|
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 });
|
|
993
1280
|
const nowMs = runtime.nowMs ?? Date.now;
|
|
994
1281
|
const sleep2 = runtime.sleep ?? defaultSleep;
|
|
995
1282
|
const requireAuth = (positionId) => {
|
|
@@ -999,9 +1286,17 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
999
1286
|
};
|
|
1000
1287
|
const rememberPosition = (position) => {
|
|
1001
1288
|
const snapshot = clonePosition(position);
|
|
1002
|
-
state.positions.
|
|
1003
|
-
|
|
1004
|
-
|
|
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;
|
|
1005
1300
|
};
|
|
1006
1301
|
const rememberAuth = (auth) => {
|
|
1007
1302
|
state.authByPosition.set(auth.lpPositionId, auth);
|
|
@@ -1025,7 +1320,7 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1025
1320
|
}
|
|
1026
1321
|
};
|
|
1027
1322
|
const completeDkgBatch2 = async (positionId, options = {}) => {
|
|
1028
|
-
assertAttested(
|
|
1323
|
+
assertAttested(currentSession);
|
|
1029
1324
|
const auth = requireAuth(positionId);
|
|
1030
1325
|
let activeSessionRetryStartedAtMs = null;
|
|
1031
1326
|
for (; ; ) {
|
|
@@ -1089,39 +1384,55 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1089
1384
|
}
|
|
1090
1385
|
};
|
|
1091
1386
|
return {
|
|
1092
|
-
async createPosition(args
|
|
1387
|
+
async createPosition(args) {
|
|
1093
1388
|
assertNoUnsupportedFeePolicy(args.feePolicy);
|
|
1094
1389
|
const shardCount = args.shardCount ?? LP_DEFAULT_TARGET_SHARDS;
|
|
1095
1390
|
assertShardCount(shardCount, LP_MAX_TARGET_SHARDS, "shardCount");
|
|
1096
|
-
assertAttested(
|
|
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
|
+
);
|
|
1097
1403
|
const positionId = runtime.idFactory?.() ?? generateId(LP_ID_PREFIX);
|
|
1098
1404
|
const auth = await generateLpPositionCode(positionId);
|
|
1099
1405
|
rememberAuth(auth);
|
|
1100
|
-
args.onPositionCode?.({ positionId, lpPositionCode: auth.code });
|
|
1101
|
-
const committedLamports = args.committedLamports ?? DEFAULT_SHARD_FUNDING_LAMPORTS;
|
|
1102
1406
|
const position = rememberPosition(
|
|
1103
1407
|
await client.openPosition(
|
|
1104
1408
|
{
|
|
1105
1409
|
lpPositionId: positionId,
|
|
1410
|
+
creationAuthorizationId: creationAuthorization.authorizationId,
|
|
1106
1411
|
authPublicKey: Array.from(auth.authPublicKey),
|
|
1107
1412
|
refillPublicKey: Array.from(auth.refillPublicKey),
|
|
1108
1413
|
withdrawalCommitment: Array.from(auth.withdrawalCommitment),
|
|
1109
1414
|
committedLamports,
|
|
1110
1415
|
shardCount,
|
|
1111
1416
|
shardAmountLamports: DEFAULT_SHARD_FUNDING_LAMPORTS,
|
|
1112
|
-
refillThreshold
|
|
1417
|
+
refillThreshold,
|
|
1113
1418
|
addresses: [],
|
|
1114
1419
|
createdAtMs: nowMs()
|
|
1115
1420
|
},
|
|
1116
1421
|
auth
|
|
1117
1422
|
)
|
|
1118
1423
|
);
|
|
1424
|
+
args.onPositionCode?.({ positionId, lpPositionCode: auth.code });
|
|
1119
1425
|
return { positionId, lpPositionCode: auth.code, position };
|
|
1120
1426
|
},
|
|
1121
1427
|
async recoverPosition(args) {
|
|
1122
1428
|
const auth = await deriveLpPositionCodeAuth(args.code);
|
|
1123
1429
|
rememberAuth(auth);
|
|
1124
|
-
const position = rememberPosition(
|
|
1430
|
+
const position = rememberPosition(
|
|
1431
|
+
await client.getPosition(auth.lpPositionId, auth, {
|
|
1432
|
+
deadlineMs: args.deadlineMs,
|
|
1433
|
+
signal: args.signal
|
|
1434
|
+
})
|
|
1435
|
+
);
|
|
1125
1436
|
assertPositionMatchesAuth(position, auth);
|
|
1126
1437
|
return position;
|
|
1127
1438
|
},
|
|
@@ -1133,7 +1444,7 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1133
1444
|
async refill(positionId, args = {}) {
|
|
1134
1445
|
const requestedShardCount = args.shardCount ?? LP_DEFAULT_REFILL_BATCH_SIZE;
|
|
1135
1446
|
assertShardCount(requestedShardCount, LP_MAX_REFILL_BATCH_SIZE, "shardCount");
|
|
1136
|
-
assertAttested(
|
|
1447
|
+
assertAttested(currentSession);
|
|
1137
1448
|
const auth = requireAuth(positionId);
|
|
1138
1449
|
const current = await refreshPosition2(positionId);
|
|
1139
1450
|
args.onPosition?.(current);
|
|
@@ -1165,7 +1476,7 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1165
1476
|
});
|
|
1166
1477
|
},
|
|
1167
1478
|
async prepareInitialFunding(positionId) {
|
|
1168
|
-
assertAttested(
|
|
1479
|
+
assertAttested(currentSession);
|
|
1169
1480
|
const auth = requireAuth(positionId);
|
|
1170
1481
|
const currentPosition = rememberPosition(await client.getPosition(positionId, auth));
|
|
1171
1482
|
if (!isLpPositionReadyForInitialFunding(currentPosition)) return null;
|
|
@@ -1194,7 +1505,7 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1194
1505
|
return fundingPlanFromShard(position, plannedShard);
|
|
1195
1506
|
},
|
|
1196
1507
|
async prepareTopUp(positionId) {
|
|
1197
|
-
assertAttested(
|
|
1508
|
+
assertAttested(currentSession);
|
|
1198
1509
|
const auth = requireAuth(positionId);
|
|
1199
1510
|
const request2 = await client.prepareTopUp(positionId, auth);
|
|
1200
1511
|
state.topUps.set(request2.topUpId, request2);
|
|
@@ -1242,21 +1553,30 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1242
1553
|
}
|
|
1243
1554
|
},
|
|
1244
1555
|
async reconcileFunding(positionId) {
|
|
1245
|
-
assertAttested(
|
|
1556
|
+
assertAttested(currentSession);
|
|
1246
1557
|
const auth = requireAuth(positionId);
|
|
1247
1558
|
return rememberPosition(await client.reconcileFunding(positionId, auth));
|
|
1248
1559
|
},
|
|
1249
1560
|
async withdrawPosition(positionId, args) {
|
|
1250
1561
|
validateExplicitWithdrawalInput(args);
|
|
1251
1562
|
validateWithdrawalDestinations(args);
|
|
1252
|
-
assertAttested(
|
|
1563
|
+
assertAttested(currentSession);
|
|
1253
1564
|
const auth = requireAuth(positionId);
|
|
1254
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
|
+
}
|
|
1255
1572
|
const existingRedemption = findOpenRedemptionHistory(before, args);
|
|
1256
1573
|
if (existingRedemption !== void 0) {
|
|
1257
1574
|
const execution2 = {
|
|
1258
1575
|
withdrawalId: existingRedemption.redemptionId,
|
|
1259
|
-
txSignatures: [...existingRedemption.txSignatures]
|
|
1576
|
+
txSignatures: [...existingRedemption.txSignatures],
|
|
1577
|
+
acceptedLamports: existingRedemption.acceptedLamports,
|
|
1578
|
+
acceptedDepositPrincipalLamports: existingRedemption.acceptedDepositPrincipalLamports,
|
|
1579
|
+
acceptedEarnedFeeLamports: existingRedemption.acceptedEarnedFeeLamports
|
|
1260
1580
|
};
|
|
1261
1581
|
const redemption2 = {
|
|
1262
1582
|
...buildLpRedemptionRecord({
|
|
@@ -1276,39 +1596,44 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1276
1596
|
state.redemptions.set(execution2.withdrawalId, redemption2);
|
|
1277
1597
|
return { execution: execution2, position: before, redemption: redemption2 };
|
|
1278
1598
|
}
|
|
1279
|
-
const
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
allowManyToOne: args.allowManyToOne ?? false,
|
|
1291
|
-
sourceCount
|
|
1292
|
-
};
|
|
1293
|
-
validateLpWithdrawalPlan(validationInput);
|
|
1599
|
+
const preview = args.withdrawalMode === "exact" ? previewLpWithdrawalPlan(before, args) : void 0;
|
|
1600
|
+
const requestedAtMs = nowMs();
|
|
1601
|
+
if (args.withdrawalMode === "exact") {
|
|
1602
|
+
validateLpWithdrawalPlan({
|
|
1603
|
+
destinationAddresses: args.destinationAddresses,
|
|
1604
|
+
depositPrincipalLamports: preview.depositPrincipalLamports,
|
|
1605
|
+
earnedFeeLamports: preview.earnedFeeLamports,
|
|
1606
|
+
allowManyToOne: args.allowManyToOne ?? false,
|
|
1607
|
+
sourceCount: preview.sourceCount
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1294
1610
|
const execution = await executeWithdrawalAfterFundingReadiness(
|
|
1295
1611
|
positionId,
|
|
1296
1612
|
{
|
|
1297
|
-
lpPositionId:
|
|
1298
|
-
destinationAddresses:
|
|
1299
|
-
depositPrincipalLamports:
|
|
1300
|
-
earnedFeeLamports:
|
|
1301
|
-
|
|
1302
|
-
|
|
1613
|
+
lpPositionId: positionId,
|
|
1614
|
+
destinationAddresses: args.destinationAddresses,
|
|
1615
|
+
depositPrincipalLamports: args.withdrawalMode === "max_available" ? 0 : preview.depositPrincipalLamports,
|
|
1616
|
+
earnedFeeLamports: args.withdrawalMode === "max_available" ? 0 : preview.earnedFeeLamports,
|
|
1617
|
+
withdrawalMode: args.withdrawalMode,
|
|
1618
|
+
requestedAtMs,
|
|
1619
|
+
allowManyToOne: args.allowManyToOne === true || preview?.sourceCount === 1
|
|
1303
1620
|
},
|
|
1304
1621
|
auth
|
|
1305
1622
|
);
|
|
1623
|
+
const acceptedLamports = execution.acceptedLamports;
|
|
1624
|
+
if (args.withdrawalMode === "max_available" && acceptedLamports === void 0) {
|
|
1625
|
+
throw new PolicyValidationError(
|
|
1626
|
+
"INVALID_WITHDRAWAL_PLAN",
|
|
1627
|
+
"LP redemption response did not include the sealed accepted amount"
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
const acceptedAmount = acceptedLamports ?? preview.depositPrincipalLamports + preview.earnedFeeLamports;
|
|
1306
1631
|
const pendingRedemption = buildLpRedemptionRecord({
|
|
1307
1632
|
execution,
|
|
1308
1633
|
position: before,
|
|
1309
1634
|
destinationAddresses: args.destinationAddresses,
|
|
1310
|
-
acceptedLamports:
|
|
1311
|
-
requestedTargetLamports: (before.actorSync?.withdrawalRequestedLamports ?? 0) +
|
|
1635
|
+
acceptedLamports: acceptedAmount,
|
|
1636
|
+
requestedTargetLamports: (before.actorSync?.withdrawalRequestedLamports ?? 0) + acceptedAmount,
|
|
1312
1637
|
landedBaselineLamports: before.actorSync?.withdrawalLandedLamports ?? 0,
|
|
1313
1638
|
landedFeeBaselineLamports: before.actorSync?.withdrawalLandedFeeLamports ?? 0,
|
|
1314
1639
|
txSignatureBaseline: before.actorSync?.withdrawalTxHashes ?? [],
|
|
@@ -1329,10 +1654,32 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1329
1654
|
options
|
|
1330
1655
|
});
|
|
1331
1656
|
state.redemptions.set(record.redemptionId, record);
|
|
1332
|
-
const deadlineMs = nowMs() + (runtime.withdrawalConfirmationTimeoutMs ?? LP_WITHDRAWAL_CONFIRMATION_TIMEOUT_MS);
|
|
1657
|
+
const deadlineMs = options.deadlineMs ?? nowMs() + (runtime.withdrawalConfirmationTimeoutMs ?? LP_WITHDRAWAL_CONFIRMATION_TIMEOUT_MS);
|
|
1333
1658
|
const pollIntervalMs = runtime.withdrawalConfirmationPollIntervalMs ?? LP_WITHDRAWAL_CONFIRMATION_POLL_INTERVAL_MS;
|
|
1334
1659
|
for (; ; ) {
|
|
1335
|
-
|
|
1660
|
+
throwIfWithdrawalConfirmationAborted(options.signal);
|
|
1661
|
+
if (nowMs() >= deadlineMs) {
|
|
1662
|
+
markLpRedemptionReconciling(state, record, options);
|
|
1663
|
+
}
|
|
1664
|
+
const remainingMs = deadlineMs - nowMs();
|
|
1665
|
+
if (remainingMs <= 0) {
|
|
1666
|
+
markLpRedemptionReconciling(state, record, options);
|
|
1667
|
+
}
|
|
1668
|
+
let position;
|
|
1669
|
+
try {
|
|
1670
|
+
position = rememberPosition(
|
|
1671
|
+
await client.getPosition(positionId, auth, {
|
|
1672
|
+
deadlineMs,
|
|
1673
|
+
signal: options.signal
|
|
1674
|
+
})
|
|
1675
|
+
);
|
|
1676
|
+
} catch (error) {
|
|
1677
|
+
if (isLpPositionObservationDeadlineError(error, deadlineMs, nowMs)) {
|
|
1678
|
+
markLpRedemptionReconciling(state, record, options);
|
|
1679
|
+
}
|
|
1680
|
+
throw error;
|
|
1681
|
+
}
|
|
1682
|
+
throwIfWithdrawalConfirmationAborted(options.signal);
|
|
1336
1683
|
options.onPosition?.(position);
|
|
1337
1684
|
const reconciled = reconcileLpRedemptionFromActorSync(record, position);
|
|
1338
1685
|
record = reconciled;
|
|
@@ -1348,17 +1695,15 @@ function createLpLifecycleClient(session, runtime = {}) {
|
|
|
1348
1695
|
if (reconciled.status === "failed") {
|
|
1349
1696
|
throw new Error(reconciled.error ?? WITHDRAWAL_NO_LAMPORTS_MESSAGE);
|
|
1350
1697
|
}
|
|
1351
|
-
if (nowMs()
|
|
1352
|
-
state
|
|
1353
|
-
options.onRedemptionUpdate?.({ ...reconciled, status: "reconciling" });
|
|
1354
|
-
throw new LpRedemptionReconcilingError();
|
|
1698
|
+
if (nowMs() >= deadlineMs) {
|
|
1699
|
+
markLpRedemptionReconciling(state, reconciled, options);
|
|
1355
1700
|
}
|
|
1356
|
-
await sleep2
|
|
1701
|
+
await sleepWithAbort(sleep2, pollIntervalMs, options.signal);
|
|
1357
1702
|
}
|
|
1358
1703
|
},
|
|
1359
1704
|
async withdrawFees(_positionId, args) {
|
|
1360
1705
|
publicKey(args.destination);
|
|
1361
|
-
assertAttested(
|
|
1706
|
+
assertAttested(currentSession);
|
|
1362
1707
|
throw new RoutingError(
|
|
1363
1708
|
"VERSION_NOT_SUPPORTED",
|
|
1364
1709
|
"LP fee withdrawal is not advertised by the coordinator"
|
|
@@ -1462,10 +1807,12 @@ function isLpPositionReadyForInitialFunding(position) {
|
|
|
1462
1807
|
}
|
|
1463
1808
|
function resolveWithdrawalClaim(position, args) {
|
|
1464
1809
|
if (args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0) {
|
|
1465
|
-
|
|
1810
|
+
const claim = {
|
|
1466
1811
|
depositPrincipalLamports: args.depositPrincipalLamports ?? 0,
|
|
1467
1812
|
earnedFeeLamports: args.earnedFeeLamports ?? 0
|
|
1468
1813
|
};
|
|
1814
|
+
validateLpWithdrawalClaim(claim);
|
|
1815
|
+
return claim;
|
|
1469
1816
|
}
|
|
1470
1817
|
const legacyDepositPrincipalLamports = position.shards.filter((shard) => shard.status === "AVAILABLE" && shard.amountLamports > 0).reduce((sum, shard) => sum + shard.amountLamports, 0);
|
|
1471
1818
|
const actorRedeemablePrincipalLamports = Math.max(
|
|
@@ -1491,11 +1838,11 @@ function resolveWithdrawalClaim(position, args) {
|
|
|
1491
1838
|
return { depositPrincipalLamports, earnedFeeLamports };
|
|
1492
1839
|
}
|
|
1493
1840
|
function findOpenRedemptionHistory(position, args) {
|
|
1494
|
-
const hasExplicitClaim = args.
|
|
1841
|
+
const hasExplicitClaim = args.withdrawalMode === "exact";
|
|
1495
1842
|
const requestedPrincipalLamports = args.depositPrincipalLamports ?? 0;
|
|
1496
1843
|
const requestedFeeLamports = args.earnedFeeLamports ?? 0;
|
|
1497
1844
|
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)
|
|
1845
|
+
(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
1846
|
);
|
|
1500
1847
|
}
|
|
1501
1848
|
function hasActorSyncLpActivity(sync) {
|
|
@@ -1517,11 +1864,25 @@ function hasActorRedeemableProjection(sync) {
|
|
|
1517
1864
|
return sync?.redeemableProjectionAvailable === true;
|
|
1518
1865
|
}
|
|
1519
1866
|
function validateExplicitWithdrawalInput(args) {
|
|
1520
|
-
if (args.
|
|
1867
|
+
if (args.withdrawalMode === "max_available") {
|
|
1868
|
+
if (args.depositPrincipalLamports !== void 0 || args.earnedFeeLamports !== void 0) {
|
|
1869
|
+
throw new PolicyValidationError(
|
|
1870
|
+
"INVALID_WITHDRAWAL_PLAN",
|
|
1871
|
+
"max_available withdrawal must not carry exact bucket claims"
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
if (args.depositPrincipalLamports === void 0 || args.earnedFeeLamports === void 0) {
|
|
1877
|
+
throw new PolicyValidationError(
|
|
1878
|
+
"INVALID_WITHDRAWAL_PLAN",
|
|
1879
|
+
"exact withdrawal requires both bucket claims"
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1521
1882
|
validateLpWithdrawalPlan({
|
|
1522
1883
|
destinationAddresses: args.destinationAddresses,
|
|
1523
|
-
depositPrincipalLamports: args.depositPrincipalLamports
|
|
1524
|
-
earnedFeeLamports: args.earnedFeeLamports
|
|
1884
|
+
depositPrincipalLamports: args.depositPrincipalLamports,
|
|
1885
|
+
earnedFeeLamports: args.earnedFeeLamports,
|
|
1525
1886
|
allowManyToOne: args.allowManyToOne,
|
|
1526
1887
|
sourceCount: args.destinationAddresses.length
|
|
1527
1888
|
});
|
|
@@ -1554,6 +1915,13 @@ function resolveWithdrawalRecord({
|
|
|
1554
1915
|
};
|
|
1555
1916
|
}
|
|
1556
1917
|
function lpWithdrawalSourceCountForClaim(position, claim) {
|
|
1918
|
+
const actorSync = position.actorSync;
|
|
1919
|
+
const actorRedeemablePrincipalLamports = actorSync?.redeemablePrincipalLamports ?? 0;
|
|
1920
|
+
const actorRedeemableFeeLamports = actorSync?.redeemableFeeLamports ?? 0;
|
|
1921
|
+
const actorRedeemableSourceCount = actorSync?.redeemableSourceCount;
|
|
1922
|
+
if (actorSync?.redeemableProjectionAvailable === true && claim.depositPrincipalLamports === actorRedeemablePrincipalLamports && claim.earnedFeeLamports === actorRedeemableFeeLamports && actorRedeemableSourceCount !== void 0) {
|
|
1923
|
+
return actorRedeemableSourceCount;
|
|
1924
|
+
}
|
|
1557
1925
|
let remainingPrincipalLamports = claim.depositPrincipalLamports;
|
|
1558
1926
|
let principalSourceCount = 0;
|
|
1559
1927
|
const principalCandidates = [...position.shards].filter((shard) => shard.status === "AVAILABLE" && shard.amountLamports > 0).sort((left, right) => left.shardId.localeCompare(right.shardId));
|
|
@@ -1562,7 +1930,6 @@ function lpWithdrawalSourceCountForClaim(position, claim) {
|
|
|
1562
1930
|
principalSourceCount += 1;
|
|
1563
1931
|
remainingPrincipalLamports -= shard.amountLamports;
|
|
1564
1932
|
}
|
|
1565
|
-
const actorRedeemablePrincipalLamports = position.actorSync?.redeemablePrincipalLamports ?? 0;
|
|
1566
1933
|
const actorRedeemablePrincipalSourceCount = position.actorSync?.redeemablePrincipalSourceCount ?? 0;
|
|
1567
1934
|
if (claim.depositPrincipalLamports > 0 && position.actorSync?.redeemableProjectionAvailable === true && actorRedeemablePrincipalLamports >= claim.depositPrincipalLamports && actorRedeemablePrincipalSourceCount > 0) {
|
|
1568
1935
|
principalSourceCount = actorRedeemablePrincipalSourceCount;
|
|
@@ -1571,7 +1938,7 @@ function lpWithdrawalSourceCountForClaim(position, claim) {
|
|
|
1571
1938
|
if (claim.earnedFeeLamports > 0 && observedEarnedFeeSourceCount < 1 && claim.allowManyToOne !== true) {
|
|
1572
1939
|
throw new PolicyValidationError(
|
|
1573
1940
|
"INVALID_WITHDRAWAL_PLAN",
|
|
1574
|
-
|
|
1941
|
+
LP_FEE_SOURCE_COUNT_UNAVAILABLE_MESSAGE
|
|
1575
1942
|
);
|
|
1576
1943
|
}
|
|
1577
1944
|
const earnedFeeSourceCount = claim.earnedFeeLamports > 0 ? Math.max(1, observedEarnedFeeSourceCount) : 0;
|
|
@@ -1725,6 +2092,15 @@ function clonePosition(position) {
|
|
|
1725
2092
|
}
|
|
1726
2093
|
};
|
|
1727
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
|
+
}
|
|
1728
2104
|
function generateId(prefix) {
|
|
1729
2105
|
return `${prefix}_${Array.from(randomBytes2(8)).map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
1730
2106
|
}
|
|
@@ -1737,6 +2113,30 @@ function throwIfTopUpWaitAborted(signal) {
|
|
|
1737
2113
|
if (signal?.aborted !== true) return;
|
|
1738
2114
|
throw new DOMException("LP top-up wait aborted", "AbortError");
|
|
1739
2115
|
}
|
|
2116
|
+
function throwIfWithdrawalConfirmationAborted(signal) {
|
|
2117
|
+
if (signal?.aborted !== true) return;
|
|
2118
|
+
throw new DOMException("LP withdrawal confirmation wait aborted", "AbortError");
|
|
2119
|
+
}
|
|
2120
|
+
function markLpRedemptionReconciling(state, record, options) {
|
|
2121
|
+
const reconciling = { ...record, status: "reconciling" };
|
|
2122
|
+
state.redemptions.set(record.redemptionId, reconciling);
|
|
2123
|
+
options.onRedemptionUpdate?.(reconciling);
|
|
2124
|
+
throw new LpRedemptionReconcilingError();
|
|
2125
|
+
}
|
|
2126
|
+
function isLpPositionObservationDeadlineError(error, deadlineMs, nowMs) {
|
|
2127
|
+
return error instanceof CommandError && error.code === "REJECTED_STALE_JOB" && nowMs() >= deadlineMs;
|
|
2128
|
+
}
|
|
2129
|
+
function sleepWithAbort(sleep2, delayMs, signal) {
|
|
2130
|
+
if (signal === void 0) return sleep2(delayMs);
|
|
2131
|
+
throwIfWithdrawalConfirmationAborted(signal);
|
|
2132
|
+
return new Promise((resolve, reject) => {
|
|
2133
|
+
const onAbort = () => reject(new DOMException("LP withdrawal confirmation wait aborted", "AbortError"));
|
|
2134
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2135
|
+
void sleep2(delayMs).then(resolve, reject).finally(() => {
|
|
2136
|
+
signal.removeEventListener("abort", onAbort);
|
|
2137
|
+
});
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
1740
2140
|
function defaultSleep(delayMs) {
|
|
1741
2141
|
return new Promise((resolve) => {
|
|
1742
2142
|
globalThis.setTimeout(resolve, delayMs);
|
|
@@ -1744,7 +2144,7 @@ function defaultSleep(delayMs) {
|
|
|
1744
2144
|
}
|
|
1745
2145
|
|
|
1746
2146
|
// src/lp/index.ts
|
|
1747
|
-
function createPosition(session, args
|
|
2147
|
+
function createPosition(session, args) {
|
|
1748
2148
|
return defaultLpLifecycleClient(session).createPosition(args);
|
|
1749
2149
|
}
|
|
1750
2150
|
function recoverPosition(session, args) {
|
|
@@ -1793,6 +2193,6 @@ function listPositions(session) {
|
|
|
1793
2193
|
return defaultLpLifecycleClient(session).listPositions();
|
|
1794
2194
|
}
|
|
1795
2195
|
|
|
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 };
|
|
2196
|
+
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
2197
|
//# sourceMappingURL=lp.js.map
|
|
1798
2198
|
//# sourceMappingURL=lp.js.map
|