@invisible-labs/sdk 0.6.0-devnet.2 → 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.
Files changed (45) hide show
  1. package/README.md +186 -70
  2. package/dist/.invisible-sdk-build-target.json +7 -0
  3. package/dist/{chunk-TQNNTV5F.js → chunk-4X5OCCQW.js} +3 -3
  4. package/dist/{chunk-TQNNTV5F.js.map → chunk-4X5OCCQW.js.map} +1 -1
  5. package/dist/{chunk-7DSVN3MV.js → chunk-5NNEO7IK.js} +41 -36
  6. package/dist/chunk-5NNEO7IK.js.map +1 -0
  7. package/dist/chunk-BRDLLGCZ.js +59 -0
  8. package/dist/chunk-BRDLLGCZ.js.map +1 -0
  9. package/dist/{chunk-NCY4FCYU.js → chunk-DIA6U2CV.js} +19 -3
  10. package/dist/chunk-DIA6U2CV.js.map +1 -0
  11. package/dist/{chunk-LBC7ALYO.js → chunk-E4DCEDAG.js} +177 -12
  12. package/dist/chunk-E4DCEDAG.js.map +1 -0
  13. package/dist/{chunk-OHIM2YWU.js → chunk-GHBQF3A7.js} +2 -2
  14. package/dist/chunk-GHBQF3A7.js.map +1 -0
  15. package/dist/{chunk-SZAYO2L5.js → chunk-NHEHCUS5.js} +3 -3
  16. package/dist/{chunk-SZAYO2L5.js.map → chunk-NHEHCUS5.js.map} +1 -1
  17. package/dist/{chunk-XUWBGET5.js → chunk-UADJCUPK.js} +125 -18
  18. package/dist/chunk-UADJCUPK.js.map +1 -0
  19. package/dist/{chunk-JRMKCE3S.js → chunk-V5HXJRBW.js} +3 -3
  20. package/dist/{chunk-JRMKCE3S.js.map → chunk-V5HXJRBW.js.map} +1 -1
  21. package/dist/{coordinator-7Y45MCCZ.js → coordinator-ZNLGAYXD.js} +3 -3
  22. package/dist/{coordinator-7Y45MCCZ.js.map → coordinator-ZNLGAYXD.js.map} +1 -1
  23. package/dist/events.js +3 -3
  24. package/dist/index.d.ts +62 -300
  25. package/dist/index.js +98 -43
  26. package/dist/index.js.map +1 -1
  27. package/dist/lp.d.ts +51 -10
  28. package/dist/lp.js +313 -86
  29. package/dist/lp.js.map +1 -1
  30. package/dist/presets.d.ts +5 -42
  31. package/dist/presets.js +11 -62
  32. package/dist/presets.js.map +1 -1
  33. package/dist/stats.js +1 -1
  34. package/dist/storage.js +1 -1
  35. package/dist/{createSession-D3ym-Ira.d.ts → types.generated-DJBTjLpP.d.ts} +37 -53
  36. package/dist/user.d.ts +94 -112
  37. package/dist/user.js +649 -248
  38. package/dist/user.js.map +1 -1
  39. package/package.json +2 -28
  40. package/dist/chunk-7DSVN3MV.js.map +0 -1
  41. package/dist/chunk-LBC7ALYO.js.map +0 -1
  42. package/dist/chunk-NCY4FCYU.js.map +0 -1
  43. package/dist/chunk-OHIM2YWU.js.map +0 -1
  44. package/dist/chunk-XUWBGET5.js.map +0 -1
  45. package/dist/types.generated-CHGSbmLp.d.ts +0 -67
package/dist/user.js CHANGED
@@ -1,12 +1,12 @@
1
- import { scheduledPayoutWindowSeconds } from './chunk-LBC7ALYO.js';
2
- import { createSession, createExponentialBackoff, validatePayoutPolicyShape, formatAjvErrors } from './chunk-7DSVN3MV.js';
3
- import { PRODUCT_MAX_ENTRY_LAMPORTS, PRODUCT_MIN_ENTRY_LAMPORTS } from './chunk-TQNNTV5F.js';
4
- import { solanaAddressFromPublicKeyBytes, publicKey, assertAttested } from './chunk-SZAYO2L5.js';
5
- import { CoordinatorMessageType, onEnvelope, closeProtocolRuntime, EnvelopeKind, requestEnvelope } from './chunk-XUWBGET5.js';
6
- import { getSessionState } from './chunk-JRMKCE3S.js';
7
- import './chunk-NCY4FCYU.js';
8
- import { TransportError, CommandError, InvalidDepositAmountError, normalizeError, PolicyValidationError, NotImplementedError, RoutingError, AttestationError } from './chunk-OHIM2YWU.js';
9
- export { InvalidDepositAmountError } from './chunk-OHIM2YWU.js';
1
+ import { scheduledPayoutWindowSeconds } from './chunk-E4DCEDAG.js';
2
+ import { validatePayoutPolicyShape, formatAjvErrors, createSession, createExponentialBackoff } from './chunk-5NNEO7IK.js';
3
+ import { PRODUCT_MAX_ENTRY_LAMPORTS, PRODUCT_MIN_ENTRY_LAMPORTS } from './chunk-4X5OCCQW.js';
4
+ import { assertAttested, publicKey, solanaAddressFromPublicKeyBytes } from './chunk-NHEHCUS5.js';
5
+ import { requestEnvelope, CoordinatorMessageType, onEnvelope, closeProtocolRuntime, EnvelopeKind } from './chunk-UADJCUPK.js';
6
+ import { getSessionState } from './chunk-V5HXJRBW.js';
7
+ import './chunk-DIA6U2CV.js';
8
+ import { PolicyValidationError, CommandError, TransportError, InvalidDepositAmountError, normalizeError, AttestationError, RoutingError } from './chunk-GHBQF3A7.js';
9
+ export { InvalidDepositAmountError } from './chunk-GHBQF3A7.js';
10
10
  import './chunk-VR6T6OJS.js';
11
11
 
12
12
  // src/validation/payoutPolicy.ts
@@ -171,7 +171,7 @@ function recoveryCodeFromUserInput(input) {
171
171
  var protocolPromise = null;
172
172
  var frostRuntimePromise = null;
173
173
  function loadProtocol() {
174
- protocolPromise ??= import('./coordinator-7Y45MCCZ.js');
174
+ protocolPromise ??= import('./coordinator-ZNLGAYXD.js');
175
175
  return protocolPromise;
176
176
  }
177
177
  function loadFrostRuntime() {
@@ -185,22 +185,22 @@ var BENIGN_TRANSPORT_DROP = /* @__PURE__ */ Symbol("benignTransportDrop");
185
185
  var ALICE_ID = 1;
186
186
  var COORDINATOR_ID = 2;
187
187
  var SYNC_SECRET_BYTE_LENGTH = 32;
188
- var ACTIVE_CONTRACT_REQUEST_MESSAGE = "coordinator session already has an active contract";
188
+ var RESUME_SECRET_BYTE_LENGTH = SYNC_SECRET_BYTE_LENGTH;
189
+ var SYNC_CONTRACT_ID_MISMATCH_MESSAGE = "sync response contract id does not match requested contract";
189
190
  var DEFAULT_SWAP_STATUS_RECONCILE_INTERVAL_MS = 5e3;
190
191
  var MAX_SWAP_STATUS_RETRY_DELAY_MS = 3e4;
191
192
  var SWAP_STATUS_RETRY_MULTIPLIER = 2;
193
+ var MAX_EVENT_DEDUP_ENTRIES_PER_KIND = 1024;
192
194
  var SWAP_STATUS_RETRY_JITTER_RATIO = 0.2;
193
195
  var DEFAULT_REFUND_OUTCOME_POLL_BUDGET_MS = 5 * 60 * 1e3;
194
196
  var REFUND_OUTCOME_POLL_INTERVAL_MS = 3e3;
195
197
  var REFUND_OUTCOME_PER_POLL_TIMEOUT_MS = 5e3;
198
+ var UNBOUND_RECONCILIATION_KEY = "__unbound__";
196
199
  var REQUEST_TIMEOUT_MESSAGE = "coordinator request timed out";
200
+ var REFUND_PENDING_FINALIZATION_MESSAGE = "refund accepted and pending finalization; check status again shortly";
197
201
  var CURRENT_SCHEDULED_PAYOUT_WINDOW_SECONDS = new Set(scheduledPayoutWindowSeconds());
198
202
  var CURRENT_SCHEDULED_PAYOUT_WINDOW_MESSAGE = `scheduled payout_spec.window_seconds must be one of [${scheduledPayoutWindowSeconds().join(", ")}]`;
199
203
  var TERMINAL_SWAP_STATES = /* @__PURE__ */ new Set(["completed", "refunded", "failed"]);
200
- var activeFunctionalContractRequests = /* @__PURE__ */ new WeakSet();
201
- function createCoordinatorSession(options) {
202
- return new UserCoordinatorSession(options);
203
- }
204
204
  function isBenignTransportDrop(err) {
205
205
  return typeof err === "object" && err !== null && err[BENIGN_TRANSPORT_DROP] === true;
206
206
  }
@@ -229,8 +229,12 @@ var UserCoordinatorSession = class {
229
229
  this.sessionStatus = "idle";
230
230
  this.swapId = null;
231
231
  this.syncSecret = null;
232
+ this.resumeSecret = null;
232
233
  this.attested = false;
233
234
  this.pushListeners = /* @__PURE__ */ new Map();
235
+ this.swapContexts = /* @__PURE__ */ new Map();
236
+ this.openingSession = null;
237
+ this.activeOperations = 0;
234
238
  this.stateListeners = /* @__PURE__ */ new Set();
235
239
  this.logListeners = /* @__PURE__ */ new Set();
236
240
  this.payoutExecutedListeners = /* @__PURE__ */ new Set();
@@ -239,12 +243,10 @@ var UserCoordinatorSession = class {
239
243
  this.coordinatorErrorListeners = /* @__PURE__ */ new Set();
240
244
  this.emittedPayoutKeys = /* @__PURE__ */ new Set();
241
245
  this.emittedRefundKeys = /* @__PURE__ */ new Set();
242
- this.stopStatusReconciliation = null;
243
- this.bufferDepositConfirmedUntilDelegateAck = false;
244
- this.bufferedDepositConfirmed = null;
246
+ this.statusReconciliationStops = /* @__PURE__ */ new Map();
245
247
  this.coordinator = options.coordinator;
246
248
  this.createSessionImpl = options.createSession ?? createSession;
247
- this.closeSessionAfterRefund = closeSessionAfterRefund;
249
+ this.closeSessionAfterOperationFailure = closeSessionAfterRefund;
248
250
  }
249
251
  onStateChange(listener) {
250
252
  this.stateListeners.add(listener);
@@ -280,51 +282,226 @@ var UserCoordinatorSession = class {
280
282
  get currentSwapId() {
281
283
  return this.swapId;
282
284
  }
285
+ beginOperation() {
286
+ this.activeOperations += 1;
287
+ if (this.activeOperations === 1) this.setSessionStatus("connecting");
288
+ }
289
+ endOperation() {
290
+ this.activeOperations = Math.max(0, this.activeOperations - 1);
291
+ if (this.activeOperations === 0 && this.sessionStatus === "connecting") {
292
+ this.setSessionStatus("idle");
293
+ }
294
+ }
295
+ ensureSwapContext(swapId, syncSecret, resumeSecret) {
296
+ let context = this.swapContexts.get(swapId);
297
+ if (!context) {
298
+ context = {
299
+ swapId,
300
+ swapState: this.swapId === swapId ? this.swapState : "new",
301
+ syncSecret: this.swapId === swapId && this.syncSecret ? new Uint8Array(this.syncSecret) : null,
302
+ resumeSecret: this.swapId === swapId && this.resumeSecret ? new Uint8Array(this.resumeSecret) : null,
303
+ bufferDepositConfirmedUntilDelegateAck: false,
304
+ bufferedDepositConfirmed: null
305
+ };
306
+ this.swapContexts.set(swapId, context);
307
+ }
308
+ if (syncSecret !== void 0) context.syncSecret = new Uint8Array(syncSecret);
309
+ if (resumeSecret !== void 0) context.resumeSecret = new Uint8Array(resumeSecret);
310
+ this.setCurrentContext(context);
311
+ return context;
312
+ }
313
+ setCurrentContext(context) {
314
+ this.swapId = context.swapId;
315
+ this.swapState = context.swapState;
316
+ this.syncSecret = context.syncSecret ? new Uint8Array(context.syncSecret) : null;
317
+ this.resumeSecret = context.resumeSecret ? new Uint8Array(context.resumeSecret) : null;
318
+ }
319
+ updateContextState(context, state) {
320
+ context.swapState = state;
321
+ if (this.swapId === context.swapId) {
322
+ this.swapState = state;
323
+ }
324
+ this.emitStateChange(context.swapId, state);
325
+ if (TERMINAL_SWAP_STATES.has(state)) {
326
+ this.evictSwapContext(context.swapId);
327
+ }
328
+ }
329
+ evictSwapContext(swapId) {
330
+ this.statusReconciliationStops.get(swapId)?.();
331
+ this.statusReconciliationStops.delete(swapId);
332
+ const context = this.swapContexts.get(swapId);
333
+ if (context) {
334
+ context.syncSecret?.fill(0);
335
+ context.resumeSecret?.fill(0);
336
+ context.bufferedDepositConfirmed = null;
337
+ context.bufferDepositConfirmedUntilDelegateAck = false;
338
+ this.swapContexts.delete(swapId);
339
+ }
340
+ if (this.swapId === swapId) {
341
+ this.syncSecret?.fill(0);
342
+ this.resumeSecret?.fill(0);
343
+ this.syncSecret = null;
344
+ this.resumeSecret = null;
345
+ }
346
+ }
347
+ clearSwapContexts() {
348
+ for (const swapId of this.swapContexts.keys()) this.evictSwapContext(swapId);
349
+ this.swapContexts.clear();
350
+ this.emittedPayoutKeys.clear();
351
+ this.emittedRefundKeys.clear();
352
+ }
283
353
  async runSwap(params) {
284
354
  assertCurrentUserPayoutSpecRuntimeLimits(params.payoutSpec);
285
- this.assertNoActiveContract();
286
- this.setSessionStatus("connecting");
355
+ this.beginOperation();
287
356
  let completed = false;
357
+ let terminalStatus = null;
288
358
  try {
289
359
  await ensureFrostWasm();
290
360
  await this.openSession();
291
361
  this.setSessionStatus("running");
292
362
  const result = await this.runProtocol(params);
293
363
  completed = true;
294
- this.setSessionStatus("done");
364
+ terminalStatus = "done";
295
365
  return result;
296
366
  } catch (err) {
297
367
  const error = this.normalizeSessionError(err);
298
368
  if (!isBenignTransportDrop(error)) {
299
- this.setSessionStatus("failed");
369
+ terminalStatus = "failed";
300
370
  }
301
371
  throw error;
302
372
  } finally {
303
- if (!completed) {
373
+ this.endOperation();
374
+ if (this.activeOperations === 0 && terminalStatus !== null) {
375
+ this.setSessionStatus(terminalStatus);
376
+ }
377
+ if (!completed && this.activeOperations === 0 && this.closeSessionAfterOperationFailure) {
304
378
  this.close();
305
379
  }
306
380
  }
307
381
  }
308
382
  async contractRequest(params) {
309
383
  assertCurrentUserPayoutSpecRuntimeLimits(params.payoutSpec);
310
- this.assertNoActiveContract();
311
- this.setSessionStatus("connecting");
384
+ this.beginOperation();
385
+ let completed = false;
386
+ let terminalStatus = null;
312
387
  try {
313
388
  await ensureFrostWasm();
314
389
  await this.openSession();
315
390
  this.setSessionStatus("running");
316
391
  const prepared = await this.prepareContract(params);
392
+ completed = true;
393
+ terminalStatus = "done";
317
394
  return toContractRequestResult(prepared);
318
395
  } catch (err) {
319
396
  const error = this.normalizeSessionError(err);
320
- this.setSessionStatus("failed");
397
+ terminalStatus = "failed";
321
398
  throw error;
399
+ } finally {
400
+ this.endOperation();
401
+ if (this.activeOperations === 0 && terminalStatus !== null) {
402
+ this.setSessionStatus(terminalStatus);
403
+ }
404
+ if (!completed && this.activeOperations === 0 && this.closeSessionAfterOperationFailure) {
405
+ this.close();
406
+ }
407
+ }
408
+ }
409
+ async resumePreDelegation(params) {
410
+ if (!params.policySnapshot) {
411
+ throw new CommandError(
412
+ "REJECTED_GUARD",
413
+ "policySnapshot is required to resume pre-delegation"
414
+ );
415
+ }
416
+ if (params.recoveryCommitment.length !== SYNC_SECRET_BYTE_LENGTH) {
417
+ throw new CommandError(
418
+ "REJECTED_GUARD",
419
+ `recoveryCommitment must be ${SYNC_SECRET_BYTE_LENGTH} bytes`
420
+ );
421
+ }
422
+ this.beginOperation();
423
+ let completed = false;
424
+ let terminalStatus = null;
425
+ try {
426
+ await ensureFrostWasm();
427
+ this.restoreSwapContext({
428
+ swapId: params.swapId,
429
+ syncSecret: params.syncSecret,
430
+ resumeSecret: params.resumeSecret
431
+ });
432
+ await this.openSession();
433
+ this.setSessionStatus("running");
434
+ const context = this.ensureSwapContext(params.swapId);
435
+ context.swapState = params.delegateShare ? "awaiting_deposit" : "awaiting_dkg";
436
+ this.setCurrentContext(context);
437
+ let jointPublicKey;
438
+ let depositExpiresAtMs;
439
+ let coordinatorTimestampMs;
440
+ let delegateShare = params.delegateShare ? new Uint8Array(params.delegateShare) : void 0;
441
+ if (!delegateShare) {
442
+ const dkg = await this.runDkg(context, params.onDelegateShareReady);
443
+ jointPublicKey = dkg.jointPublicKey;
444
+ depositExpiresAtMs = dkg.depositExpiresAtMs;
445
+ coordinatorTimestampMs = dkg.coordinatorTimestampMs;
446
+ delegateShare = dkg.keyHandle.secretShareBytes();
447
+ this.updateContextState(context, "awaiting_deposit");
448
+ }
449
+ const delegateAckPromise = this.awaitPush(CoordinatorMessageType.DelegateAck, context.swapId);
450
+ let delegateResp;
451
+ try {
452
+ delegateResp = await this.request(
453
+ CoordinatorMessageType.DelegateShare,
454
+ {
455
+ share: delegateShare,
456
+ recovery_commitment: new Uint8Array(params.recoveryCommitment),
457
+ resume_secret: context.resumeSecret
458
+ },
459
+ { swapId: context.swapId }
460
+ );
461
+ } catch (err) {
462
+ void delegateAckPromise.catch(() => void 0);
463
+ throw err;
464
+ }
465
+ const delegatedAtMs = this.requireProtocol().asDelegateShareResponsePayload(delegateResp).delegated_at_ms;
466
+ const delegateAck = this.requireProtocol().asDelegateAckPayload(await delegateAckPromise);
467
+ if (delegateAck.delegated_at_ms !== delegatedAtMs) {
468
+ throw new CommandError(
469
+ "REJECTED_GUARD",
470
+ "DelegateAck delegated_at_ms does not match DelegateShare response"
471
+ );
472
+ }
473
+ if (!spendingPoliciesEqual(delegateAck.spending_policy, params.policySnapshot)) {
474
+ throw new CommandError(
475
+ "REJECTED_GUARD",
476
+ "DelegateAck spending_policy does not match persisted policy_snapshot"
477
+ );
478
+ }
479
+ context.bufferDepositConfirmedUntilDelegateAck = false;
480
+ this.updateContextState(context, "awaiting_deposit");
481
+ completed = true;
482
+ terminalStatus = "done";
483
+ return {
484
+ swapId: context.swapId,
485
+ jointPublicKey,
486
+ depositExpiresAtMs,
487
+ coordinatorTimestampMs,
488
+ delegatedAtMs
489
+ };
490
+ } catch (err) {
491
+ terminalStatus = "failed";
492
+ throw this.normalizeSessionError(err);
493
+ } finally {
494
+ if (!completed) this.cancelPushWaitersForSwap(params.swapId);
495
+ this.endOperation();
496
+ if (this.activeOperations === 0 && terminalStatus !== null) {
497
+ this.setSessionStatus(terminalStatus);
498
+ }
322
499
  }
323
500
  }
324
501
  /** @internal Used by the functional Session API without closing its caller-owned session. */
325
502
  releaseWithoutClosing() {
326
- this.stopStatusReconciliation?.();
327
- this.stopStatusReconciliation = null;
503
+ this.stopAllStatusReconciliations();
504
+ this.clearSwapContexts();
328
505
  this.detachSession(
329
506
  new TransportError("CONNECTION_LOST", "coordinator session operation released"),
330
507
  false,
@@ -332,12 +509,15 @@ var UserCoordinatorSession = class {
332
509
  );
333
510
  }
334
511
  close() {
335
- this.stopStatusReconciliation?.();
336
- this.stopStatusReconciliation = null;
512
+ this.stopAllStatusReconciliations();
513
+ this.clearSwapContexts();
337
514
  this.detachSession(new TransportError("CONNECTION_LOST", "coordinator session closed"), true);
338
515
  }
516
+ stopAllStatusReconciliations() {
517
+ for (const stop of [...this.statusReconciliationStops.values()]) stop();
518
+ this.statusReconciliationStops.clear();
519
+ }
339
520
  restoreSwapContext(params) {
340
- this.swapId = params.swapId;
341
521
  if (params.syncSecret !== void 0) {
342
522
  if (params.syncSecret.length !== SYNC_SECRET_BYTE_LENGTH) {
343
523
  throw new CommandError(
@@ -345,28 +525,46 @@ var UserCoordinatorSession = class {
345
525
  `syncSecret must be ${SYNC_SECRET_BYTE_LENGTH} bytes`
346
526
  );
347
527
  }
348
- this.syncSecret = new Uint8Array(params.syncSecret);
349
528
  }
529
+ if (params.resumeSecret !== void 0) {
530
+ if (params.resumeSecret.length !== RESUME_SECRET_BYTE_LENGTH) {
531
+ throw new CommandError(
532
+ "REJECTED_GUARD",
533
+ `resumeSecret must be ${RESUME_SECRET_BYTE_LENGTH} bytes`
534
+ );
535
+ }
536
+ }
537
+ this.ensureSwapContext(params.swapId, params.syncSecret, params.resumeSecret);
350
538
  }
351
539
  async requestSwapStatus(timeoutMs) {
352
540
  if (!this.swapId) {
353
541
  throw new CommandError("REJECTED_STATE", "Sync requires swapId");
354
542
  }
543
+ const context = this.ensureSwapContext(this.swapId, this.syncSecret ?? void 0);
544
+ return this.requestSwapStatusForContext(context, timeoutMs);
545
+ }
546
+ async requestSwapStatusForContext(context, timeoutMs) {
355
547
  await this.openSession();
356
548
  const env = await this.request(
357
549
  CoordinatorMessageType.Sync,
358
550
  {
359
551
  actor: "user",
360
- ...this.syncSecret ? { sync_secret: this.syncSecret } : {}
552
+ ...context.syncSecret ? { sync_secret: context.syncSecret } : {}
361
553
  },
362
- timeoutMs
554
+ {
555
+ swapId: context.swapId,
556
+ timeoutMs,
557
+ rejectMatchingOutOfBandErrors: true
558
+ }
363
559
  );
364
560
  const status = this.requireProtocol().asSyncUserResponsePayload(env);
365
- this.applySwapStatus(status, env.timestamp_ms);
561
+ this.applySwapStatus(context, status, env.timestamp_ms);
366
562
  return status;
367
563
  }
368
564
  startSwapStatusReconciliation(intervalMs = DEFAULT_SWAP_STATUS_RECONCILE_INTERVAL_MS) {
369
- this.stopStatusReconciliation?.();
565
+ const reconciliationContext = this.swapId ? this.ensureSwapContext(this.swapId, this.syncSecret ?? void 0) : null;
566
+ const reconciliationKey = reconciliationContext?.swapId ?? UNBOUND_RECONCILIATION_KEY;
567
+ this.statusReconciliationStops.get(reconciliationKey)?.();
370
568
  let stopped = false;
371
569
  let reconciling = false;
372
570
  let timer = null;
@@ -391,20 +589,18 @@ var UserCoordinatorSession = class {
391
589
  if (stopped || reconciling || !isVisible()) return;
392
590
  reconciling = true;
393
591
  try {
394
- const status = await this.requestSwapStatus();
592
+ const status = reconciliationContext ? await this.requestSwapStatusForContext(reconciliationContext) : await this.requestSwapStatus();
395
593
  retryBackoff.reset();
396
594
  if (status.state === "completed" || status.state === "refunded" || status.state === "failed") {
397
- stopped = true;
398
- if (timer) clearTimeout(timer);
595
+ stopReconciliation();
399
596
  } else {
400
597
  schedule(intervalMs);
401
598
  }
402
599
  } catch (err) {
403
600
  this.log(`SwapStatus reconcile failed: ${normalizeError(err)}`, "info");
404
601
  if (!isRetryableSwapStatusReconciliationError(err)) {
405
- stopped = true;
406
- if (timer) clearTimeout(timer);
407
- this.close();
602
+ stopReconciliation();
603
+ if (err instanceof AttestationError) this.close();
408
604
  return;
409
605
  }
410
606
  schedule(retryBackoff.nextDelayMs());
@@ -440,7 +636,8 @@ var UserCoordinatorSession = class {
440
636
  browserDocument?.addEventListener("freeze", handleFreeze);
441
637
  browserDocument?.addEventListener("resume", reconcileWhenVisible);
442
638
  reconcileWhenVisible();
443
- this.stopStatusReconciliation = () => {
639
+ const stopReconciliation = () => {
640
+ if (stopped) return;
444
641
  stopped = true;
445
642
  if (timer) clearTimeout(timer);
446
643
  browserWindow?.removeEventListener("focus", reconcileWhenVisible);
@@ -449,8 +646,12 @@ var UserCoordinatorSession = class {
449
646
  browserDocument?.removeEventListener("visibilitychange", handleVisibilityChange);
450
647
  browserDocument?.removeEventListener("freeze", handleFreeze);
451
648
  browserDocument?.removeEventListener("resume", reconcileWhenVisible);
649
+ if (this.statusReconciliationStops.get(reconciliationKey) === stopReconciliation) {
650
+ this.statusReconciliationStops.delete(reconciliationKey);
651
+ }
452
652
  };
453
- return this.stopStatusReconciliation;
653
+ this.statusReconciliationStops.set(reconciliationKey, stopReconciliation);
654
+ return stopReconciliation;
454
655
  }
455
656
  async requestRefund(params) {
456
657
  const result = await this.requestRefundIntentInternal(params, { waitForFinality: true });
@@ -462,9 +663,10 @@ var UserCoordinatorSession = class {
462
663
  }
463
664
  async requestRefundIntentInternal(params, options) {
464
665
  assertRecoveryCode(params.recoveryCode);
666
+ this.beginOperation();
465
667
  const recoveryCode = new Uint8Array(params.recoveryCode);
466
668
  const ackTimeout = params.timeoutMs ?? 3e4;
467
- let ackTimer;
669
+ let requestSwapId = params.swapId ?? null;
468
670
  let openedForRefund = false;
469
671
  try {
470
672
  if (params.syncSecret !== void 0) {
@@ -473,33 +675,36 @@ var UserCoordinatorSession = class {
473
675
  throw new CommandError("REJECTED_STATE", "syncSecret requires swapId");
474
676
  }
475
677
  this.restoreSwapContext({ swapId, syncSecret: params.syncSecret });
678
+ requestSwapId = swapId;
476
679
  } else if (params.swapId) {
477
680
  const swapId = params.swapId;
478
681
  this.restoreSwapContext({ swapId });
682
+ requestSwapId = swapId;
479
683
  }
480
684
  openedForRefund = !this.hasUsableSession();
481
685
  await this.openSession();
482
- const env = await Promise.race([
483
- this.request(CoordinatorMessageType.WithdrawRequest, {
484
- recovery_code: recoveryCode
485
- }),
486
- new Promise((_, reject) => {
487
- ackTimer = setTimeout(
488
- () => reject(
489
- new CommandError(
490
- "REJECTED_STALE_JOB",
491
- `WithdrawRequest acknowledgement timed out after ${ackTimeout}ms`
492
- )
493
- ),
494
- ackTimeout
686
+ let env;
687
+ try {
688
+ env = await this.request(
689
+ CoordinatorMessageType.WithdrawRequest,
690
+ {
691
+ recovery_code: recoveryCode
692
+ },
693
+ { swapId: requestSwapId, timeoutMs: ackTimeout }
694
+ );
695
+ } catch (err) {
696
+ if (isRequestTimeoutError(err)) {
697
+ throw new CommandError(
698
+ "REJECTED_STALE_JOB",
699
+ `WithdrawRequest acknowledgement timed out after ${ackTimeout}ms`
495
700
  );
496
- })
497
- ]);
498
- if (ackTimer) clearTimeout(ackTimer);
499
- if (env.type === CoordinatorMessageType.WithdrawExecuted) {
500
- if (env.swap_id && !this.swapId) {
501
- this.swapId = env.swap_id;
502
701
  }
702
+ throw err;
703
+ }
704
+ if (env.type === CoordinatorMessageType.WithdrawExecuted) {
705
+ const resolvedSwapId2 = env.swap_id ?? requestSwapId;
706
+ const resolvedContext2 = resolvedSwapId2 ? this.ensureSwapContext(resolvedSwapId2) : null;
707
+ if (resolvedContext2) this.setCurrentContext(resolvedContext2);
503
708
  return { kind: "executed", refund: this.requireProtocol().asWithdrawExecutedPayload(env) };
504
709
  }
505
710
  if (env.type !== CoordinatorMessageType.WithdrawAccepted) {
@@ -508,29 +713,32 @@ var UserCoordinatorSession = class {
508
713
  `Expected WithdrawAccepted or WithdrawExecuted response, got ${env.type}`
509
714
  );
510
715
  }
511
- if (env.swap_id && !this.swapId) {
512
- this.swapId = env.swap_id;
513
- }
514
716
  const accepted = this.requireProtocol().asWithdrawAcceptedPayload(env);
717
+ const resolvedSwapId = env.swap_id ?? requestSwapId;
718
+ const resolvedContext = resolvedSwapId ? this.ensureSwapContext(resolvedSwapId) : null;
515
719
  if (!options.waitForFinality) {
516
720
  return { kind: "accepted", accepted };
517
721
  }
518
- if (!this.swapId) {
519
- throw new CommandError(
520
- "REJECTED_STALE_JOB",
521
- "refund accepted and pending finalization; check status again shortly"
522
- );
722
+ if (!resolvedContext) {
723
+ throw new CommandError("REJECTED_STALE_JOB", REFUND_PENDING_FINALIZATION_MESSAGE);
724
+ }
725
+ if (!resolvedContext.syncSecret) {
726
+ throw new CommandError("REJECTED_STALE_JOB", REFUND_PENDING_FINALIZATION_MESSAGE);
523
727
  }
524
- return { kind: "executed", refund: await this.awaitRefundOutcomeFromSwapStatus() };
728
+ this.setCurrentContext(resolvedContext);
729
+ return {
730
+ kind: "executed",
731
+ refund: await this.awaitRefundOutcomeFromSwapStatus(resolvedContext)
732
+ };
525
733
  } finally {
526
- if (ackTimer) clearTimeout(ackTimer);
527
734
  recoveryCode.fill(0);
528
- if (openedForRefund && this.closeSessionAfterRefund) {
735
+ this.endOperation();
736
+ if (openedForRefund && this.closeSessionAfterOperationFailure && this.activeOperations === 0) {
529
737
  this.close();
530
738
  }
531
739
  }
532
740
  }
533
- async awaitRefundOutcomeFromSwapStatus() {
741
+ async awaitRefundOutcomeFromSwapStatus(context) {
534
742
  const deadline = Date.now() + DEFAULT_REFUND_OUTCOME_POLL_BUDGET_MS;
535
743
  for (; ; ) {
536
744
  const perPollTimeout = Math.min(
@@ -538,7 +746,7 @@ var UserCoordinatorSession = class {
538
746
  Math.max(0, deadline - Date.now())
539
747
  );
540
748
  try {
541
- const status = await this.requestSwapStatus(perPollTimeout);
749
+ const status = await this.requestSwapStatusForContext(context, perPollTimeout);
542
750
  if (status.withdraw_execution) {
543
751
  return status.withdraw_execution;
544
752
  }
@@ -548,10 +756,7 @@ var UserCoordinatorSession = class {
548
756
  }
549
757
  }
550
758
  if (Date.now() >= deadline) {
551
- throw new CommandError(
552
- "REJECTED_STALE_JOB",
553
- "refund accepted and pending finalization; check status again shortly"
554
- );
759
+ throw new CommandError("REJECTED_STALE_JOB", REFUND_PENDING_FINALIZATION_MESSAGE);
555
760
  }
556
761
  await delay(REFUND_OUTCOME_POLL_INTERVAL_MS);
557
762
  }
@@ -572,13 +777,19 @@ var UserCoordinatorSession = class {
572
777
  "in"
573
778
  );
574
779
  await params.onDepositConfirmed?.({ originAddress: depositPayload.origin_address });
575
- await this.requestSwapStatus();
780
+ const context = this.swapContexts.get(prepared.swapId);
781
+ if (!context) {
782
+ throw new CommandError("REJECTED_STATE", `missing client context for ${prepared.swapId}`);
783
+ }
784
+ await this.requestSwapStatusForContext(context);
576
785
  return {
577
786
  swapId: prepared.swapId,
578
787
  jointPublicKey: prepared.jointPublicKey,
579
788
  depositTxSignature: depositPayload.tx_signature,
580
789
  depositObservedAtMs: depositPayload.observed_at_ms,
581
790
  delegatedAtMs: prepared.delegatedAtMs,
791
+ resumeSecret: new Uint8Array(prepared.resumeSecret),
792
+ resumeSecretHex: prepared.resumeSecretHex,
582
793
  recoveryCodeHex: prepared.recoveryCodeHex,
583
794
  policySnapshot: prepared.policySnapshot,
584
795
  delegatePolicySnapshot: prepared.delegatePolicySnapshot
@@ -589,6 +800,7 @@ var UserCoordinatorSession = class {
589
800
  const requestedPayoutWindowMs = params.payoutWindowMs ?? payoutWindowMsFromSpec(params.payoutSpec);
590
801
  const payoutDeadlineMs = requestedPayoutWindowMs !== void 0 ? Date.now() + requestedPayoutWindowMs : void 0;
591
802
  let generatedSyncSecret = null;
803
+ let generatedResumeSecret = null;
592
804
  const syncSecret = params.syncSecret ? new Uint8Array(params.syncSecret) : (() => {
593
805
  generatedSyncSecret = randomSecret32();
594
806
  return generatedSyncSecret;
@@ -599,105 +811,165 @@ var UserCoordinatorSession = class {
599
811
  `syncSecret must be ${SYNC_SECRET_BYTE_LENGTH} bytes`
600
812
  );
601
813
  }
602
- this.syncSecret = new Uint8Array(syncSecret);
603
814
  const syncSecretHex = bytesToHex(syncSecret);
604
815
  const syncCommitment = await sha256Commitment(syncSecret);
605
816
  if (generatedSyncSecret) {
606
817
  await params.onSyncSecretReady?.({ secretHex: syncSecretHex });
607
818
  }
608
- const swapResp = await this.request(CoordinatorMessageType.ContractRequest, {
609
- actor: "user",
610
- amount_lamports: params.amountLamports,
611
- payout_spec: params.payoutSpec,
612
- sync_commitment: syncCommitment,
613
- ...(params.payoutDeadlineMs !== void 0 || payoutDeadlineMs !== void 0) && {
614
- payout_deadline_ms: params.payoutDeadlineMs ?? payoutDeadlineMs
615
- }
616
- });
617
- const swapPayload = protocol.asSwapRequestResponsePayload(swapResp);
618
- const swapId = swapPayload.swap_id;
619
- this.swapId = swapId;
620
- params.onPolicySnapshot?.(swapPayload.policy_snapshot);
621
- this.log(`swap accepted - id=${swapId}`, "in");
622
- this.bufferDepositConfirmedUntilDelegateAck = true;
623
- this.bufferedDepositConfirmed = null;
624
- const depositConfirmedPromise = this.awaitPush(CoordinatorMessageType.DepositConfirmed, null);
625
- void depositConfirmedPromise.catch(() => void 0);
626
- const { jointPublicKey, keyHandle, depositExpiresAtMs, coordinatorTimestampMs } = await this.runDkg();
627
- let recoveryCommitment = params.recoveryCommitment;
628
- let recoveryCodeHex;
629
- if (!recoveryCommitment) {
630
- const generated = await generateRecoveryCode();
631
- recoveryCommitment = generated.commitment;
632
- try {
633
- recoveryCodeHex = recoveryCodeToHex(generated.code);
634
- await params.onRecoveryCodeReady?.({ codeHex: recoveryCodeHex });
635
- } finally {
636
- generated.code.fill(0);
637
- }
638
- }
639
- const delegateAckPromise = this.awaitPush(CoordinatorMessageType.DelegateAck);
640
- const share = params.delegateShare ?? keyHandle.secretShareBytes();
641
- let delegateResp;
642
- try {
643
- delegateResp = await this.request(CoordinatorMessageType.DelegateShare, {
644
- share,
645
- recovery_commitment: recoveryCommitment
646
- });
647
- } catch (err) {
648
- void delegateAckPromise.catch(() => void 0);
649
- throw err;
650
- }
651
- const delegatePayload = protocol.asDelegateShareResponsePayload(delegateResp);
652
- const delegateAck = protocol.asDelegateAckPayload(await delegateAckPromise);
653
- if (delegateAck.delegated_at_ms !== delegatePayload.delegated_at_ms) {
819
+ const resumeSecret = params.resumeSecret ? new Uint8Array(params.resumeSecret) : (() => {
820
+ generatedResumeSecret = randomSecret32();
821
+ return generatedResumeSecret;
822
+ })();
823
+ if (resumeSecret.length !== RESUME_SECRET_BYTE_LENGTH) {
654
824
  throw new CommandError(
655
825
  "REJECTED_GUARD",
656
- "DelegateAck delegated_at_ms does not match DelegateShare response"
826
+ `resumeSecret must be ${RESUME_SECRET_BYTE_LENGTH} bytes`
657
827
  );
658
828
  }
659
- if (!spendingPoliciesEqual(delegateAck.spending_policy, swapPayload.policy_snapshot)) {
660
- throw new CommandError(
661
- "REJECTED_GUARD",
662
- "DelegateAck spending_policy does not match ContractRequest policy_snapshot"
663
- );
829
+ const resumeSecretHex = bytesToHex(resumeSecret);
830
+ const resumeCommitment = await sha256Commitment(resumeSecret);
831
+ if (generatedResumeSecret) {
832
+ await params.onResumeSecretReady?.({ secretHex: resumeSecretHex });
664
833
  }
665
- this.log(`delegated at ${delegateAck.delegated_at_ms}`, "in");
666
- this.releaseDepositConfirmedGate();
667
- await params.onDepositAddressReady?.({
834
+ const swapResp = await this.request(
835
+ CoordinatorMessageType.ContractRequest,
836
+ {
837
+ actor: "user",
838
+ amount_lamports: params.amountLamports,
839
+ payout_spec: params.payoutSpec,
840
+ sync_commitment: syncCommitment,
841
+ resume_commitment: resumeCommitment,
842
+ ...(params.payoutDeadlineMs !== void 0 || payoutDeadlineMs !== void 0) && {
843
+ payout_deadline_ms: params.payoutDeadlineMs ?? payoutDeadlineMs
844
+ }
845
+ },
846
+ { swapId: null, requireNonNullResponseSwapId: true }
847
+ );
848
+ const swapPayload = protocol.asSwapRequestResponsePayload(swapResp);
849
+ const swapId = swapPayload.swap_id;
850
+ const context = {
668
851
  swapId,
669
- jointPublicKey,
670
- amountLamports: params.amountLamports,
671
- depositExpiresAtMs,
672
- coordinatorTimestampMs
673
- });
674
- return {
675
- requestId: swapId,
852
+ swapState: "awaiting_dkg",
853
+ syncSecret: new Uint8Array(syncSecret),
854
+ resumeSecret: new Uint8Array(resumeSecret),
855
+ bufferDepositConfirmedUntilDelegateAck: true,
856
+ bufferedDepositConfirmed: null
857
+ };
858
+ this.swapContexts.set(swapId, context);
859
+ this.setCurrentContext(context);
860
+ await params.onSwapAllocated?.({
676
861
  swapId,
677
- amountLamports: params.amountLamports,
678
- jointPublicKey,
679
- depositExpiresAtMs,
680
- coordinatorTimestampMs,
681
- delegatedAtMs: delegateAck.delegated_at_ms,
682
- syncSecret,
683
862
  syncSecretHex,
684
- recoveryCodeHex,
685
- policySnapshot: swapPayload.policy_snapshot,
686
- delegatePolicySnapshot: delegateAck.spending_policy,
687
- depositConfirmedPromise
688
- };
863
+ resumeSecretHex
864
+ });
865
+ params.onPolicySnapshot?.(swapPayload.policy_snapshot);
866
+ this.log(`swap accepted - id=${swapId}`, "in");
867
+ let preparedSuccessfully = false;
868
+ try {
869
+ const depositConfirmedPromise = this.awaitPush(
870
+ CoordinatorMessageType.DepositConfirmed,
871
+ context.swapId,
872
+ null
873
+ );
874
+ void depositConfirmedPromise.catch(() => void 0);
875
+ const { jointPublicKey, keyHandle, depositExpiresAtMs, coordinatorTimestampMs } = await this.runDkg(context, params.delegateShare ? void 0 : params.onDelegateShareReady);
876
+ this.updateContextState(context, "awaiting_deposit");
877
+ let recoveryCommitment = params.recoveryCommitment;
878
+ let recoveryCodeHex;
879
+ if (!recoveryCommitment) {
880
+ const generated = await generateRecoveryCode();
881
+ recoveryCommitment = generated.commitment;
882
+ try {
883
+ recoveryCodeHex = recoveryCodeToHex(generated.code);
884
+ await params.onRecoveryCodeReady?.({ codeHex: recoveryCodeHex });
885
+ } finally {
886
+ generated.code.fill(0);
887
+ }
888
+ }
889
+ const delegateAckPromise = this.awaitPush(CoordinatorMessageType.DelegateAck, context.swapId);
890
+ const share = params.delegateShare ?? keyHandle.secretShareBytes();
891
+ let delegateResp;
892
+ try {
893
+ delegateResp = await this.request(
894
+ CoordinatorMessageType.DelegateShare,
895
+ {
896
+ share,
897
+ recovery_commitment: recoveryCommitment,
898
+ resume_secret: context.resumeSecret
899
+ },
900
+ { swapId: context.swapId }
901
+ );
902
+ } catch (err) {
903
+ void delegateAckPromise.catch(() => void 0);
904
+ throw err;
905
+ }
906
+ const delegatePayload = protocol.asDelegateShareResponsePayload(delegateResp);
907
+ const delegateAck = protocol.asDelegateAckPayload(await delegateAckPromise);
908
+ if (delegateAck.delegated_at_ms !== delegatePayload.delegated_at_ms) {
909
+ throw new CommandError(
910
+ "REJECTED_GUARD",
911
+ "DelegateAck delegated_at_ms does not match DelegateShare response"
912
+ );
913
+ }
914
+ if (!spendingPoliciesEqual(delegateAck.spending_policy, swapPayload.policy_snapshot)) {
915
+ throw new CommandError(
916
+ "REJECTED_GUARD",
917
+ "DelegateAck spending_policy does not match ContractRequest policy_snapshot"
918
+ );
919
+ }
920
+ this.log(`delegated at ${delegateAck.delegated_at_ms}`, "in");
921
+ this.releaseDepositConfirmedGate(context);
922
+ await params.onDepositAddressReady?.({
923
+ swapId,
924
+ jointPublicKey,
925
+ amountLamports: params.amountLamports,
926
+ depositExpiresAtMs,
927
+ coordinatorTimestampMs
928
+ });
929
+ preparedSuccessfully = true;
930
+ return {
931
+ requestId: swapId,
932
+ swapId,
933
+ amountLamports: params.amountLamports,
934
+ jointPublicKey,
935
+ depositExpiresAtMs,
936
+ coordinatorTimestampMs,
937
+ delegatedAtMs: delegateAck.delegated_at_ms,
938
+ syncSecret,
939
+ syncSecretHex,
940
+ resumeSecret,
941
+ resumeSecretHex,
942
+ recoveryCodeHex,
943
+ policySnapshot: swapPayload.policy_snapshot,
944
+ delegatePolicySnapshot: delegateAck.spending_policy,
945
+ depositConfirmedPromise
946
+ };
947
+ } finally {
948
+ if (!preparedSuccessfully) {
949
+ this.cancelPushWaitersForSwap(context.swapId);
950
+ }
951
+ }
689
952
  }
690
- async runDkg() {
953
+ async runDkg(context, onDelegateShareReady) {
691
954
  await ensureFrostWasm();
692
955
  const protocol = this.requireProtocol();
693
956
  const { dkgRound1 } = await loadFrost();
694
- await this.request(CoordinatorMessageType.DkgInit, {});
957
+ await this.request(
958
+ CoordinatorMessageType.DkgInit,
959
+ { resume_secret: context.resumeSecret },
960
+ { swapId: context.swapId }
961
+ );
695
962
  this.log("DKG initialised", "info");
696
963
  const aliceRound1State = dkgRound1(ALICE_ID);
697
964
  const aliceRound1Bytes = aliceRound1State.round1PackageBytes();
698
- const round1Resp = await this.request(CoordinatorMessageType.DkgRound1, {
699
- package: aliceRound1Bytes
700
- });
965
+ const round1Resp = await this.request(
966
+ CoordinatorMessageType.DkgRound1,
967
+ {
968
+ package: aliceRound1Bytes,
969
+ resume_secret: context.resumeSecret
970
+ },
971
+ { swapId: context.swapId }
972
+ );
701
973
  const round1Payload = protocol.asDkgRound1ResponsePayload(round1Resp);
702
974
  this.log(`DKG round1 complete - coord pkg ${round1Payload.package.byteLength}B`, "in");
703
975
  const aliceRound2State = aliceRound1State.intoRound2(COORDINATOR_ID, round1Payload.package);
@@ -712,10 +984,20 @@ var UserCoordinatorSession = class {
712
984
  } catch (err) {
713
985
  throw new CommandError("REJECTED_GUARD", `Alice DKG finalize failed: ${normalizeError(err)}`);
714
986
  }
715
- const dkgCompletePromise = this.awaitPush(CoordinatorMessageType.DkgComplete);
716
- await this.request(CoordinatorMessageType.DkgRound2, {
717
- package: aliceRound2Bytes
987
+ await onDelegateShareReady?.({
988
+ swapId: context.swapId,
989
+ delegateShare: new Uint8Array(aliceKeyHandle.secretShareBytes())
718
990
  });
991
+ const dkgCompletePromise = this.awaitPush(CoordinatorMessageType.DkgComplete, context.swapId);
992
+ void dkgCompletePromise.catch(() => void 0);
993
+ await this.request(
994
+ CoordinatorMessageType.DkgRound2,
995
+ {
996
+ package: aliceRound2Bytes,
997
+ resume_secret: context.resumeSecret
998
+ },
999
+ { swapId: context.swapId }
1000
+ );
719
1001
  const completeEnvelope = await dkgCompletePromise;
720
1002
  const complete = readDkgComplete(protocol, completeEnvelope);
721
1003
  const alicePubkey = aliceKeyHandle.groupPublicKeyBytes();
@@ -735,6 +1017,20 @@ var UserCoordinatorSession = class {
735
1017
  };
736
1018
  }
737
1019
  async openSession() {
1020
+ if (this.hasUsableSession()) return;
1021
+ if (this.openingSession !== null) {
1022
+ await this.openingSession;
1023
+ return;
1024
+ }
1025
+ const opening = this.openSessionOnce();
1026
+ this.openingSession = opening;
1027
+ try {
1028
+ await opening;
1029
+ } finally {
1030
+ if (this.openingSession === opening) this.openingSession = null;
1031
+ }
1032
+ }
1033
+ async openSessionOnce() {
738
1034
  if (this.hasUsableSession()) return;
739
1035
  if (this.session !== null) {
740
1036
  this.detachSession(
@@ -760,11 +1056,6 @@ var UserCoordinatorSession = class {
760
1056
  this.log(`connected to ${this.coordinator.endpoints[0]?.wsUrl ?? "coordinator"}`, "info");
761
1057
  this.log("attestation verified", "info");
762
1058
  }
763
- assertNoActiveContract() {
764
- if (this.swapId !== null || this.sessionStatus === "connecting" || this.sessionStatus === "running") {
765
- throw new CommandError("REJECTED_STATE", ACTIVE_CONTRACT_REQUEST_MESSAGE);
766
- }
767
- }
768
1059
  hasUsableSession() {
769
1060
  if (this.session === null) return false;
770
1061
  const state = getSessionState(this.session);
@@ -790,30 +1081,43 @@ var UserCoordinatorSession = class {
790
1081
  handleEnvelope(env) {
791
1082
  const protocol = this.requireProtocol();
792
1083
  this.log(`<- ${env.type} kind=${env.kind}`, "in");
1084
+ const context = env.swap_id ? this.swapContexts.get(env.swap_id) ?? null : null;
1085
+ if (env.swap_id === null && env.kind !== EnvelopeKind.ERROR) {
1086
+ this.log(`ignored ${env.type} without an explicit swap_id`, "info");
1087
+ return;
1088
+ }
1089
+ if (env.swap_id !== null && context === null) {
1090
+ this.log(`ignored ${env.type} for unknown swap ${env.swap_id}`, "info");
1091
+ return;
1092
+ }
793
1093
  const payoutExecuted = env.kind === EnvelopeKind.PUSH && env.type === CoordinatorMessageType.PayoutExecuted ? protocol.asPayoutExecutedPayload(env) : null;
794
- const payoutMatchesCurrentSwap = payoutExecuted !== null && (env.swap_id === null || env.swap_id === this.swapId);
1094
+ const payoutMatchesCurrentSwap = payoutExecuted !== null && context !== null;
795
1095
  const refundExecuted = env.kind === EnvelopeKind.PUSH && env.type === CoordinatorMessageType.WithdrawExecuted ? protocol.asWithdrawExecutedPayload(env) : null;
796
1096
  if (this.shouldBufferDepositConfirmed(env)) {
797
1097
  this.bufferDepositConfirmed(env);
798
1098
  return;
799
1099
  }
800
- const foreignPayout = payoutExecuted !== null && !payoutMatchesCurrentSwap;
801
- const next = foreignPayout ? null : protocol.nextState(
802
- this.swapState,
1100
+ const stateForEnvelope = context?.swapState ?? this.swapState;
1101
+ const next = payoutExecuted !== null && !payoutMatchesCurrentSwap ? null : protocol.nextState(
1102
+ stateForEnvelope,
803
1103
  env.kind,
804
1104
  env.type,
805
1105
  payoutExecuted ? { payoutFinal: payoutExecuted.final } : void 0
806
1106
  );
807
1107
  if (env.kind === EnvelopeKind.PUSH && next === null) {
808
1108
  this.log(
809
- foreignPayout ? `ignored ${env.type} for swap ${env.swap_id ?? "null"} while current swap is ${this.swapId ?? "none"}` : `ignored invalid push ${env.type} in state ${this.swapState}`,
1109
+ `ignored ${env.type} for swap ${env.swap_id ?? "null"} while client context is ${context?.swapId ?? "none"} in state ${stateForEnvelope}`,
810
1110
  "info"
811
1111
  );
812
1112
  return;
813
1113
  }
814
1114
  if (next !== null) {
815
- this.swapState = next;
816
- this.emitStateChange();
1115
+ if (context) {
1116
+ this.updateContextState(context, next);
1117
+ } else {
1118
+ this.swapState = next;
1119
+ this.emitStateChange(this.swapId, next);
1120
+ }
817
1121
  }
818
1122
  if (env.kind === EnvelopeKind.ERROR) {
819
1123
  const coordinatorError = coordinatorErrorFromEnvelope(protocol, env);
@@ -825,26 +1129,25 @@ var UserCoordinatorSession = class {
825
1129
  return;
826
1130
  }
827
1131
  if (env.kind === EnvelopeKind.PUSH) {
828
- if (payoutExecuted && payoutMatchesCurrentSwap) {
829
- this.emitPayoutExecuted(payoutExecuted, env.swap_id ?? this.swapId);
1132
+ if (payoutExecuted && payoutMatchesCurrentSwap && context) {
1133
+ this.emitPayoutExecuted(payoutExecuted, context.swapId);
830
1134
  }
831
- if (refundExecuted) {
832
- this.emitRefundExecuted(refundExecuted, env.swap_id ?? this.swapId);
1135
+ if (refundExecuted && context) {
1136
+ this.emitRefundExecuted(refundExecuted, context.swapId);
833
1137
  }
834
1138
  this.resolvePushListener(env);
835
1139
  }
836
1140
  }
837
- applySwapStatus(status, coordinatorTimestampMs) {
838
- const keepTerminalState = TERMINAL_SWAP_STATES.has(this.swapState) && status.state !== this.swapState;
839
- if (!keepTerminalState && this.swapState !== status.state) {
840
- this.swapState = status.state;
841
- this.emitStateChange();
1141
+ applySwapStatus(context, status, coordinatorTimestampMs) {
1142
+ const keepTerminalState = TERMINAL_SWAP_STATES.has(context.swapState) && status.state !== context.swapState;
1143
+ if (!keepTerminalState && context.swapState !== status.state) {
1144
+ this.updateContextState(context, status.state);
842
1145
  }
843
1146
  for (const fragment of status.fragments_executed) {
844
- this.emitPayoutExecuted(fragment, this.swapId);
1147
+ this.emitPayoutExecuted(fragment, context.swapId);
845
1148
  }
846
1149
  if (status.withdraw_execution) {
847
- this.emitRefundExecuted(status.withdraw_execution, this.swapId);
1150
+ this.emitRefundExecuted(status.withdraw_execution, context.swapId);
848
1151
  }
849
1152
  if (status.actor_sync !== void 0) {
850
1153
  for (const listener of this.normalUserActorSyncListeners) {
@@ -855,8 +1158,7 @@ var UserCoordinatorSession = class {
855
1158
  emitPayoutExecuted(payload, swapId) {
856
1159
  if (!swapId) return;
857
1160
  const key = `${swapId}:${payload.index}:${payload.tx_signature}`;
858
- if (this.emittedPayoutKeys.has(key)) return;
859
- this.emittedPayoutKeys.add(key);
1161
+ if (!this.rememberEventKey(this.emittedPayoutKeys, key)) return;
860
1162
  this.log(
861
1163
  `payout executed tx=${payload.tx_signature} amount=${payload.amount_lamports} final=${payload.final}`,
862
1164
  "in"
@@ -868,88 +1170,133 @@ var UserCoordinatorSession = class {
868
1170
  emitRefundExecuted(payload, swapId) {
869
1171
  if (!swapId) return;
870
1172
  const key = `${swapId}:${payload.tx_signature}`;
871
- if (this.emittedRefundKeys.has(key)) return;
872
- this.emittedRefundKeys.add(key);
1173
+ if (!this.rememberEventKey(this.emittedRefundKeys, key)) return;
873
1174
  this.log(`refund executed tx=${payload.tx_signature} amount=${payload.amount_lamports}`, "in");
874
1175
  for (const listener of this.refundExecutedListeners) {
875
1176
  listener(payload, swapId);
876
1177
  }
877
1178
  }
878
- request(type, payload, timeoutMs) {
1179
+ rememberEventKey(keys, key) {
1180
+ if (keys.has(key)) return false;
1181
+ keys.add(key);
1182
+ if (keys.size > MAX_EVENT_DEDUP_ENTRIES_PER_KIND) {
1183
+ const oldest = keys.values().next().value;
1184
+ if (oldest !== void 0) keys.delete(oldest);
1185
+ }
1186
+ return true;
1187
+ }
1188
+ request(type, payload, options = {}) {
879
1189
  if (this.session === null) {
880
1190
  throw new TransportError("CONNECTION_LOST", "coordinator session is not connected");
881
1191
  }
882
1192
  return requestEnvelope(this.session, type, payload, {
883
- swapId: this.swapId,
884
- timeoutMs,
1193
+ swapId: options.swapId === void 0 ? this.swapId : options.swapId,
1194
+ timeoutMs: options.timeoutMs,
1195
+ rejectMatchingOutOfBandErrors: options.rejectMatchingOutOfBandErrors,
1196
+ requireNonNullResponseSwapId: options.requireNonNullResponseSwapId,
885
1197
  onLog: (message, direction) => this.log(message, direction)
886
1198
  });
887
1199
  }
888
- awaitPush(type, timeoutMs = 3e4) {
1200
+ awaitPush(type, swapId, timeoutMs = 3e4) {
889
1201
  return new Promise((resolve, reject) => {
890
1202
  const timer = timeoutMs === null ? null : setTimeout(() => {
891
- this.pushListeners.delete(type);
1203
+ this.removePushListener(type, listener);
892
1204
  reject(new CommandError("REJECTED_STALE_JOB", `timed out waiting for ${type} push`));
893
1205
  }, timeoutMs);
894
- this.pushListeners.set(type, { resolve, reject, timer });
1206
+ const listener = { swapId, resolve, reject, timer };
1207
+ const listeners = this.pushListeners.get(type) ?? [];
1208
+ listeners.push(listener);
1209
+ this.pushListeners.set(type, listeners);
895
1210
  });
896
1211
  }
897
1212
  rejectAllPushListeners(err) {
898
- for (const listener of this.pushListeners.values()) {
899
- if (listener.timer) clearTimeout(listener.timer);
900
- listener.reject(err);
1213
+ for (const listeners of this.pushListeners.values()) {
1214
+ for (const listener of listeners) {
1215
+ if (listener.timer) clearTimeout(listener.timer);
1216
+ listener.reject(err);
1217
+ }
901
1218
  }
902
1219
  this.pushListeners.clear();
903
- this.bufferDepositConfirmedUntilDelegateAck = false;
904
- this.bufferedDepositConfirmed = null;
1220
+ for (const context of this.swapContexts.values()) {
1221
+ context.bufferDepositConfirmedUntilDelegateAck = false;
1222
+ context.bufferedDepositConfirmed = null;
1223
+ }
905
1224
  }
906
1225
  shouldBufferDepositConfirmed(env) {
907
- return env.kind === EnvelopeKind.PUSH && env.type === CoordinatorMessageType.DepositConfirmed && this.bufferDepositConfirmedUntilDelegateAck;
1226
+ const context = env.swap_id ? this.swapContexts.get(env.swap_id) : null;
1227
+ return env.kind === EnvelopeKind.PUSH && env.type === CoordinatorMessageType.DepositConfirmed && context?.bufferDepositConfirmedUntilDelegateAck === true;
908
1228
  }
909
1229
  bufferDepositConfirmed(env) {
910
- if (!this.bufferedDepositConfirmed) {
911
- this.bufferedDepositConfirmed = env;
912
- this.clearPushListenerTimer(CoordinatorMessageType.DepositConfirmed);
1230
+ const context = env.swap_id ? this.swapContexts.get(env.swap_id) : null;
1231
+ if (!context) return;
1232
+ if (!context.bufferedDepositConfirmed) {
1233
+ context.bufferedDepositConfirmed = env;
1234
+ this.clearPushListenerTimer(CoordinatorMessageType.DepositConfirmed, context.swapId);
913
1235
  this.log("buffered DepositConfirmed until DelegateAck is verified", "info");
914
1236
  return;
915
1237
  }
916
1238
  this.log("ignored duplicate DepositConfirmed while waiting for DelegateAck", "info");
917
1239
  }
918
- releaseDepositConfirmedGate() {
1240
+ releaseDepositConfirmedGate(context) {
919
1241
  const protocol = this.requireProtocol();
920
- this.bufferDepositConfirmedUntilDelegateAck = false;
921
- const buffered = this.bufferedDepositConfirmed;
1242
+ context.bufferDepositConfirmedUntilDelegateAck = false;
1243
+ const buffered = context.bufferedDepositConfirmed;
922
1244
  if (!buffered) return;
923
- this.bufferedDepositConfirmed = null;
924
- const next = protocol.nextState(this.swapState, buffered.kind, buffered.type);
1245
+ context.bufferedDepositConfirmed = null;
1246
+ const next = protocol.nextState(context.swapState, buffered.kind, buffered.type);
925
1247
  if (next === null) {
926
- this.log(`ignored buffered DepositConfirmed in state ${this.swapState}`, "info");
1248
+ this.log(`ignored buffered DepositConfirmed in state ${context.swapState}`, "info");
927
1249
  return;
928
1250
  }
929
- this.swapState = next;
930
- this.emitStateChange();
1251
+ this.updateContextState(context, next);
931
1252
  this.resolvePushListener(buffered);
932
1253
  }
933
1254
  resolvePushListener(env) {
934
- const listener = this.pushListeners.get(env.type);
935
- if (!listener) return;
936
- this.pushListeners.delete(env.type);
1255
+ const listeners = this.pushListeners.get(env.type);
1256
+ if (!listeners) return;
1257
+ const index = listeners.findIndex(
1258
+ (candidate) => candidate.swapId === void 0 || candidate.swapId === env.swap_id
1259
+ );
1260
+ if (index < 0) return;
1261
+ const listener = listeners.splice(index, 1)[0];
1262
+ if (listeners.length === 0) this.pushListeners.delete(env.type);
937
1263
  if (listener.timer) clearTimeout(listener.timer);
938
1264
  listener.resolve(env);
939
1265
  }
940
- clearPushListenerTimer(type) {
941
- const listener = this.pushListeners.get(type);
1266
+ clearPushListenerTimer(type, swapId) {
1267
+ const listener = this.pushListeners.get(type)?.find((candidate) => candidate.swapId === void 0 || candidate.swapId === swapId);
942
1268
  if (!listener?.timer) return;
943
1269
  clearTimeout(listener.timer);
944
1270
  listener.timer = null;
945
1271
  }
1272
+ removePushListener(type, listener) {
1273
+ const listeners = this.pushListeners.get(type);
1274
+ if (!listeners) return;
1275
+ const index = listeners.indexOf(listener);
1276
+ if (index >= 0) listeners.splice(index, 1);
1277
+ if (listeners.length === 0) this.pushListeners.delete(type);
1278
+ }
1279
+ cancelPushWaitersForSwap(swapId) {
1280
+ for (const [type, listeners] of this.pushListeners.entries()) {
1281
+ const remaining = listeners.filter((listener) => {
1282
+ if (listener.swapId !== swapId) return true;
1283
+ if (listener.timer) clearTimeout(listener.timer);
1284
+ return false;
1285
+ });
1286
+ if (remaining.length === 0) {
1287
+ this.pushListeners.delete(type);
1288
+ } else if (remaining.length !== listeners.length) {
1289
+ this.pushListeners.set(type, remaining);
1290
+ }
1291
+ }
1292
+ }
946
1293
  setSessionStatus(status) {
947
1294
  this.sessionStatus = status;
948
1295
  this.emitStateChange();
949
1296
  }
950
- emitStateChange() {
1297
+ emitStateChange(swapId = this.swapId, swapState = this.swapState) {
951
1298
  for (const listener of this.stateListeners) {
952
- listener(this.swapState, this.sessionStatus);
1299
+ listener(swapState, this.sessionStatus, swapId);
953
1300
  }
954
1301
  }
955
1302
  log(msg, direction) {
@@ -985,28 +1332,42 @@ function toContractRequestResult(prepared) {
985
1332
  delegatedAtMs: prepared.delegatedAtMs,
986
1333
  syncSecret: new Uint8Array(prepared.syncSecret),
987
1334
  syncSecretHex: prepared.syncSecretHex,
1335
+ resumeSecret: new Uint8Array(prepared.resumeSecret),
1336
+ resumeSecretHex: prepared.resumeSecretHex,
988
1337
  recoveryCodeHex: prepared.recoveryCodeHex,
989
1338
  policySnapshot: prepared.policySnapshot,
990
1339
  delegatePolicySnapshot: prepared.delegatePolicySnapshot
991
1340
  };
992
1341
  }
993
1342
  async function contractRequestOnSession(session, params) {
994
- if (activeFunctionalContractRequests.has(session)) {
995
- throw new CommandError("REJECTED_STATE", ACTIVE_CONTRACT_REQUEST_MESSAGE);
996
- }
997
- activeFunctionalContractRequests.add(session);
998
1343
  let client = null;
999
1344
  try {
1000
- client = new UserCoordinatorSession({
1001
- coordinator: getSessionState(session).pool,
1002
- createSession: async () => session
1003
- });
1345
+ client = new UserCoordinatorSession(
1346
+ {
1347
+ coordinator: getSessionState(session).pool,
1348
+ createSession: async () => session
1349
+ },
1350
+ false
1351
+ );
1004
1352
  return await client.contractRequest(params);
1005
1353
  } finally {
1006
- activeFunctionalContractRequests.delete(session);
1007
1354
  client?.releaseWithoutClosing();
1008
1355
  }
1009
1356
  }
1357
+ async function resumePreDelegationOnSession(session, params) {
1358
+ const client = new UserCoordinatorSession(
1359
+ {
1360
+ coordinator: getSessionState(session).pool,
1361
+ createSession: async () => session
1362
+ },
1363
+ false
1364
+ );
1365
+ try {
1366
+ return await client.resumePreDelegation(params);
1367
+ } finally {
1368
+ client.releaseWithoutClosing();
1369
+ }
1370
+ }
1010
1371
  async function requestRefundOnSession(session, params) {
1011
1372
  const client = new UserCoordinatorSession(
1012
1373
  {
@@ -1016,7 +1377,7 @@ async function requestRefundOnSession(session, params) {
1016
1377
  false
1017
1378
  );
1018
1379
  try {
1019
- await client.requestRefundIntent(params);
1380
+ const result = await client.requestRefundIntent(params);
1020
1381
  const requestId = client.currentSwapId;
1021
1382
  if (!requestId) {
1022
1383
  throw new CommandError(
@@ -1024,7 +1385,19 @@ async function requestRefundOnSession(session, params) {
1024
1385
  "refund response did not include the resolved contract id"
1025
1386
  );
1026
1387
  }
1027
- return { accepted: true, requestId };
1388
+ if (result.kind === "accepted") {
1389
+ return {
1390
+ accepted: true,
1391
+ requestId,
1392
+ amountLamports: result.accepted.amount_lamports,
1393
+ acceptedAtMs: result.accepted.accepted_at_ms
1394
+ };
1395
+ }
1396
+ return {
1397
+ accepted: true,
1398
+ requestId,
1399
+ amountLamports: result.refund.amount_lamports
1400
+ };
1028
1401
  } finally {
1029
1402
  client.releaseWithoutClosing();
1030
1403
  }
@@ -1043,9 +1416,20 @@ async function requestUserSync(session, swapId, syncSecret, timeoutMs) {
1043
1416
  actor: "user",
1044
1417
  sync_secret: new Uint8Array(syncSecret)
1045
1418
  },
1046
- { swapId, timeoutMs }
1419
+ { swapId, timeoutMs, rejectMatchingOutOfBandErrors: true }
1047
1420
  );
1048
- return (await loadProtocol()).asSyncUserResponsePayload(env);
1421
+ const payload = (await loadProtocol()).asSyncUserResponsePayload(env);
1422
+ assertSyncResponseContractId(env, payload, swapId);
1423
+ return {
1424
+ ...payload,
1425
+ coordinatorTimestampMs: env.timestamp_ms
1426
+ };
1427
+ }
1428
+ function assertSyncResponseContractId(envelope, payload, requestedSwapId) {
1429
+ const actorSyncContractId = payload.actor_sync?.snapshot.contract_id;
1430
+ if (envelope.swap_id !== requestedSwapId || actorSyncContractId !== void 0 && actorSyncContractId !== requestedSwapId) {
1431
+ throw new CommandError("REJECTED_STATE", SYNC_CONTRACT_ID_MISMATCH_MESSAGE);
1432
+ }
1049
1433
  }
1050
1434
  async function ensureFrostWasm() {
1051
1435
  await (await loadFrostRuntime()).ensureFrostWasm();
@@ -1150,10 +1534,10 @@ async function contractRequest(session, args) {
1150
1534
  assertCurrentUserContractPolicy(payoutPolicy);
1151
1535
  assertAttested(session);
1152
1536
  const destination = payoutPolicy.destinations[0];
1153
- const payoutSpec = payoutSpecFromPolicy(payoutPolicy, destination.address);
1537
+ const wirePayoutSpec = wirePayoutSpecFromPolicy(payoutPolicy, destination.address);
1154
1538
  return contractRequestOnSession(session, {
1155
1539
  amountLamports: args.amountLamports,
1156
- payoutSpec,
1540
+ payoutSpec: wirePayoutSpec,
1157
1541
  ...args.syncSecret !== void 0 && { syncSecret: args.syncSecret },
1158
1542
  ...args.recoveryCommitment !== void 0 && {
1159
1543
  recoveryCommitment: args.recoveryCommitment
@@ -1161,11 +1545,35 @@ async function contractRequest(session, args) {
1161
1545
  ...args.onSyncSecretReady !== void 0 && {
1162
1546
  onSyncSecretReady: args.onSyncSecretReady
1163
1547
  },
1548
+ ...args.resumeSecret !== void 0 && { resumeSecret: args.resumeSecret },
1549
+ ...args.onResumeSecretReady !== void 0 && {
1550
+ onResumeSecretReady: args.onResumeSecretReady
1551
+ },
1552
+ ...args.onSwapAllocated !== void 0 && {
1553
+ onSwapAllocated: args.onSwapAllocated
1554
+ },
1164
1555
  ...args.onRecoveryCodeReady !== void 0 && {
1165
1556
  onRecoveryCodeReady: args.onRecoveryCodeReady
1557
+ },
1558
+ ...args.onDelegateShareReady !== void 0 && {
1559
+ onDelegateShareReady: args.onDelegateShareReady
1166
1560
  }
1167
1561
  });
1168
1562
  }
1563
+ async function resumePreDelegation(session, args) {
1564
+ assertAttested(session);
1565
+ return resumePreDelegationOnSession(session, {
1566
+ swapId: args.swapId,
1567
+ resumeSecret: new Uint8Array(args.resumeSecret),
1568
+ ...args.syncSecret !== void 0 && { syncSecret: new Uint8Array(args.syncSecret) },
1569
+ recoveryCommitment: new Uint8Array(args.recoveryCommitment),
1570
+ policySnapshot: args.policySnapshot,
1571
+ ...args.onDelegateShareReady !== void 0 && {
1572
+ onDelegateShareReady: args.onDelegateShareReady
1573
+ },
1574
+ ...args.delegateShare !== void 0 && { delegateShare: new Uint8Array(args.delegateShare) }
1575
+ });
1576
+ }
1169
1577
  async function sync(session, contractId, syncSecret, timeoutMs) {
1170
1578
  assertAttested(session);
1171
1579
  return requestUserSync(session, contractId, syncSecret, timeoutMs);
@@ -1188,7 +1596,7 @@ function assertCurrentUserContractPolicy(policy) {
1188
1596
  }
1189
1597
  }
1190
1598
  }
1191
- function payoutSpecFromPolicy(policy, destinationAddress) {
1599
+ function wirePayoutSpecFromPolicy(policy, destinationAddress) {
1192
1600
  const totalDeadlineMs = policy.totalDeadlineMs;
1193
1601
  if (totalDeadlineMs === void 0 || totalDeadlineMs === INSTANT_PAYOUT_WINDOW_MS) {
1194
1602
  return {
@@ -1207,19 +1615,12 @@ async function requestRefund(session, args) {
1207
1615
  assertAttested(session);
1208
1616
  assertRecoveryCode(args.recoveryCode);
1209
1617
  return requestRefundOnSession(session, {
1618
+ ...args.swapId !== void 0 && { swapId: args.swapId },
1619
+ ...args.syncSecret !== void 0 && { syncSecret: new Uint8Array(args.syncSecret) },
1210
1620
  recoveryCode: new Uint8Array(args.recoveryCode)
1211
1621
  });
1212
1622
  }
1213
- async function getDepositAddress(_session, _requestId, _syncSecret) {
1214
- throw new NotImplementedError("user.getDepositAddress");
1215
- }
1216
- function subscribe(_session, _requestId, _syncSecret, _handler) {
1217
- throw new NotImplementedError("user.subscribe");
1218
- }
1219
- async function replayFromStorage(_session, _storage) {
1220
- throw new NotImplementedError("user.replayFromStorage");
1221
- }
1222
1623
 
1223
- export { INSTANT_PAYOUT_WINDOW_MS, MAX_ENTRY_AMOUNT_LAMPORTS, MIN_ENTRY_AMOUNT_LAMPORTS, PAYOUT_WINDOW_MILLISECONDS_PER_SECOND, RECOVERY_CODE_BYTE_LENGTH, SUPPORTED_TOTAL_DEADLINE_MS, commitmentFromCode, contractRequest, createCoordinatorSession, generateRecoveryCode, getDepositAddress, isBenignTransportDrop, recoveryCodeFromHex, recoveryCodeFromUserInput, recoveryCodeToHex, replayFromStorage, requestRefund, subscribe, sync };
1624
+ export { INSTANT_PAYOUT_WINDOW_MS, MAX_ENTRY_AMOUNT_LAMPORTS, MIN_ENTRY_AMOUNT_LAMPORTS, PAYOUT_WINDOW_MILLISECONDS_PER_SECOND, RECOVERY_CODE_BYTE_LENGTH, SUPPORTED_TOTAL_DEADLINE_MS, commitmentFromCode, contractRequest, generateRecoveryCode, recoveryCodeFromHex, recoveryCodeFromUserInput, recoveryCodeToHex, requestRefund, resumePreDelegation, sync };
1224
1625
  //# sourceMappingURL=user.js.map
1225
1626
  //# sourceMappingURL=user.js.map