@arkade-os/boltz-swap 0.3.39 → 0.3.41

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.
@@ -245,6 +245,22 @@ var isSubmarineFinalStatus = (status) => {
245
245
  var isSubmarineRefundableStatus = (status) => {
246
246
  return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
247
247
  };
248
+ var SUBMARINE_STATUS_PROGRESSION = [
249
+ "swap.created",
250
+ "invoice.set",
251
+ "transaction.mempool",
252
+ "transaction.confirmed",
253
+ "invoice.pending",
254
+ "invoice.paid",
255
+ "transaction.claim.pending",
256
+ "transaction.claimed"
257
+ ];
258
+ var hasSubmarineStatusReached = (status, target) => {
259
+ const progression = SUBMARINE_STATUS_PROGRESSION;
260
+ const statusIndex = progression.indexOf(status);
261
+ const targetIndex = progression.indexOf(target);
262
+ return statusIndex >= 0 && targetIndex >= 0 && statusIndex >= targetIndex;
263
+ };
248
264
  var isSubmarineSuccessStatus = (status) => {
249
265
  return status === "transaction.claimed";
250
266
  };
@@ -1291,10 +1307,10 @@ var import_light_bolt11_decoder = __toESM(require("light-bolt11-decoder"), 1);
1291
1307
  var import_sdk2 = require("@arkade-os/sdk");
1292
1308
  var decodeInvoice = (invoice) => {
1293
1309
  const decoded = import_light_bolt11_decoder.default.decode(invoice);
1294
- const millisats = Number(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1310
+ const millisats = BigInt(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1295
1311
  return {
1296
1312
  expiry: decoded.expiry ?? 3600,
1297
- amountSats: Math.floor(millisats / 1e3),
1313
+ amountSats: Number(millisats / 1000n),
1298
1314
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1299
1315
  paymentHash: decoded.sections.find((s) => s.name === "payment_hash")?.value ?? ""
1300
1316
  };
@@ -2909,6 +2925,19 @@ var createVHTLCScript = (args) => {
2909
2925
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2910
2926
  return { vhtlcScript, vhtlcAddress };
2911
2927
  };
2928
+ var candidateServerPubkeys = (arkInfo) => {
2929
+ const current = import_base8.hex.encode(normalizeToXOnlyKey(import_base8.hex.decode(arkInfo.signerPubkey), "server"));
2930
+ const seen = /* @__PURE__ */ new Set([current]);
2931
+ const candidates = [current];
2932
+ for (const deprecated of arkInfo.deprecatedSigners ?? []) {
2933
+ if (!deprecated.pubkey) continue;
2934
+ const key = import_base8.hex.encode(normalizeToXOnlyKey(import_base8.hex.decode(deprecated.pubkey), "server"));
2935
+ if (seen.has(key)) continue;
2936
+ seen.add(key);
2937
+ candidates.push(key);
2938
+ }
2939
+ return candidates;
2940
+ };
2912
2941
  var joinBatch = async (arkProvider, identity, input, output, {
2913
2942
  forfeitPubkey,
2914
2943
  forfeitAddress,
@@ -3323,6 +3352,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
3323
3352
  ...args.description?.trim() ? { description: args.description.trim() } : {}
3324
3353
  };
3325
3354
  const swapResponse = await this.swapProvider.createReverseSwap(swapRequest);
3355
+ const decodedInvoice = decodeInvoice(swapResponse.invoice);
3356
+ if (decodedInvoice.paymentHash !== preimageHash) {
3357
+ throw new SwapError({ message: "Preimage hash does not match invoice payment hash" });
3358
+ }
3326
3359
  const pendingSwap = {
3327
3360
  id: swapResponse.id,
3328
3361
  type: "reverse",
@@ -3366,27 +3399,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
3366
3399
  "boltz",
3367
3400
  pendingSwap.id
3368
3401
  );
3369
- const serverXOnly = normalizeToXOnlyKey(
3370
- import_base9.hex.decode(arkInfo.signerPubkey),
3371
- "server",
3372
- pendingSwap.id
3373
- );
3374
- const { vhtlcScript, vhtlcAddress } = this.createVHTLCScript({
3375
- network: arkInfo.network,
3402
+ const { vhtlcScript, serverXOnlyPublicKey: serverXOnly } = this.resolveVHTLCForLockup({
3403
+ arkInfo,
3376
3404
  preimageHash: (0, import_sha23.sha256)(preimage),
3377
3405
  receiverPubkey: import_base9.hex.encode(receiverXOnly),
3378
3406
  senderPubkey: import_base9.hex.encode(senderXOnly),
3379
- serverPubkey: import_base9.hex.encode(serverXOnly),
3380
- timeoutBlockHeights: vhtlcTimeouts
3407
+ timeoutBlockHeights: vhtlcTimeouts,
3408
+ lockupAddress,
3409
+ swapId: pendingSwap.id
3381
3410
  });
3382
- if (!vhtlcScript.claimScript)
3383
- throw new Error(
3384
- `Swap ${pendingSwap.id}: failed to create VHTLC script for reverse swap`
3385
- );
3386
- if (vhtlcAddress !== lockupAddress)
3387
- throw new Error(
3388
- `Swap ${pendingSwap.id}: VHTLC address mismatch. Expected ${lockupAddress}, got ${vhtlcAddress}`
3389
- );
3390
3411
  let unspentVtxos = [];
3391
3412
  let rawVtxos = [];
3392
3413
  for (let attempt = 1; attempt <= CLAIM_VTXO_RETRY_ATTEMPTS; attempt++) {
@@ -3537,16 +3558,6 @@ var ArkadeSwaps = class _ArkadeSwaps {
3537
3558
  this.swapProvider.monitorSwap(pendingSwap.id, onStatusUpdate).catch(reject);
3538
3559
  });
3539
3560
  }
3540
- // =========================================================================
3541
- // Lightning: Submarine swaps (send Arkade -> Lightning)
3542
- // =========================================================================
3543
- /**
3544
- * Sends a Lightning payment via a submarine swap (Arkade → Lightning).
3545
- * Creates the swap, sends funds, and waits for settlement. Auto-refunds on failure.
3546
- * @param args.invoice - BOLT11 Lightning invoice to pay.
3547
- * @returns The amount paid, preimage (proof of payment), and transaction ID.
3548
- * @throws {TransactionFailedError} If the payment fails (auto-refunds if possible).
3549
- */
3550
3561
  async sendLightningPayment(args) {
3551
3562
  const pendingSwap = await this.createSubmarineSwap(args);
3552
3563
  if (!pendingSwap.response.address)
@@ -3557,6 +3568,18 @@ var ArkadeSwaps = class _ArkadeSwaps {
3557
3568
  amount: pendingSwap.response.expectedAmount
3558
3569
  });
3559
3570
  try {
3571
+ if (args.waitFor === "funded") {
3572
+ if (!this.swapManager) {
3573
+ logger.warn(
3574
+ `Swap ${pendingSwap.id}: sendLightningPayment with waitFor "funded" but SwapManager is disabled \u2014 a failure after this promise resolves is only persisted as refundable, not auto-refunded; recover via restoreSwaps/recoverSubmarineFunds`
3575
+ );
3576
+ }
3577
+ await this.waitForSwapFunded(pendingSwap);
3578
+ return {
3579
+ amount: pendingSwap.response.expectedAmount,
3580
+ txid
3581
+ };
3582
+ }
3560
3583
  const { preimage } = await this.waitForSwapSettlement(pendingSwap);
3561
3584
  return {
3562
3585
  amount: pendingSwap.response.expectedAmount,
@@ -3628,11 +3651,6 @@ var ArkadeSwaps = class _ArkadeSwaps {
3628
3651
  "our",
3629
3652
  swap.id
3630
3653
  );
3631
- const serverXOnlyPublicKey = normalizeToXOnlyKey(
3632
- import_base9.hex.decode(resolvedArkInfo.signerPubkey),
3633
- "server",
3634
- swap.id
3635
- );
3636
3654
  const { claimPublicKey, timeoutBlockHeights: vhtlcTimeouts } = swap.response;
3637
3655
  if (!claimPublicKey || !vhtlcTimeouts)
3638
3656
  throw new Error(`Swap ${swap.id}: incomplete submarine swap response`);
@@ -3641,20 +3659,19 @@ var ArkadeSwaps = class _ArkadeSwaps {
3641
3659
  "boltz",
3642
3660
  swap.id
3643
3661
  );
3644
- const { vhtlcScript, vhtlcAddress } = this.createVHTLCScript({
3645
- network: resolvedArkInfo.network,
3662
+ const lockupAddress = swap.response.address;
3663
+ if (!lockupAddress)
3664
+ throw new Error(`Swap ${swap.id}: missing lockup address in submarine swap response`);
3665
+ const { vhtlcScript, serverXOnlyPublicKey } = this.resolveVHTLCForLockup({
3666
+ arkInfo: resolvedArkInfo,
3646
3667
  preimageHash: import_base9.hex.decode(preimageHash),
3647
3668
  receiverPubkey: import_base9.hex.encode(boltzXOnlyPublicKey),
3648
3669
  senderPubkey: import_base9.hex.encode(ourXOnlyPublicKey),
3649
- serverPubkey: import_base9.hex.encode(serverXOnlyPublicKey),
3650
- timeoutBlockHeights: vhtlcTimeouts
3670
+ timeoutBlockHeights: vhtlcTimeouts,
3671
+ lockupAddress,
3672
+ swapId: swap.id
3651
3673
  });
3652
- if (!vhtlcScript.claimScript)
3653
- throw new Error(`Swap ${swap.id}: failed to create VHTLC script for submarine swap`);
3654
- if (vhtlcAddress !== swap.response.address)
3655
- throw new Error(
3656
- `VHTLC address mismatch for swap ${swap.id}: expected ${swap.response.address}, got ${vhtlcAddress}`
3657
- );
3674
+ const vhtlcAddress = lockupAddress;
3658
3675
  const vhtlcPkScriptHex = import_base9.hex.encode(vhtlcScript.pkScript);
3659
3676
  return {
3660
3677
  arkInfo: resolvedArkInfo,
@@ -4064,6 +4081,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4064
4081
  }
4065
4082
  /**
4066
4083
  * Waits for a submarine swap's Lightning payment to settle.
4084
+ * Resolves only at the terminal "transaction.claimed" status, once Boltz
4085
+ * has swept the HTLC. To resolve as soon as the payment is in flight, use
4086
+ * {@link waitForSwapFunded} instead.
4067
4087
  * @param pendingSwap - The submarine swap to monitor.
4068
4088
  * @returns The preimage from the settled Lightning payment (proof of payment).
4069
4089
  * @throws {SwapExpiredError} If the swap expires.
@@ -4071,19 +4091,59 @@ var ArkadeSwaps = class _ArkadeSwaps {
4071
4091
  * @throws {TransactionLockupFailedError} If the lockup transaction fails.
4072
4092
  */
4073
4093
  async waitForSwapSettlement(pendingSwap) {
4094
+ return this.waitForSubmarineSwap(pendingSwap);
4095
+ }
4096
+ /**
4097
+ * Waits until a submarine swap is funded: resolves as soon as the lockup
4098
+ * transaction is observed ("transaction.mempool" or any later status in
4099
+ * the lifecycle — statuses can be skipped since subscriptions report only
4100
+ * the current one). The sender's funds are committed and the swap is
4101
+ * refundable from this point, which is when most Lightning wallets show
4102
+ * a payment as "sent".
4103
+ *
4104
+ * Monitoring continues in the background until the swap reaches a
4105
+ * terminal status, persisting updates to the repository (the preimage on
4106
+ * claim, the refundable flag on failure), but this promise no longer
4107
+ * rejects once resolved — acting on a late failure is the caller's
4108
+ * responsibility (the SwapManager handles it automatically when enabled).
4109
+ * @param pendingSwap - The submarine swap to monitor.
4110
+ * @throws {SwapExpiredError} If the swap expires before funding.
4111
+ * @throws {InvoiceFailedToPayError} If Boltz fails to route the payment.
4112
+ * @throws {TransactionLockupFailedError} If the lockup transaction fails.
4113
+ */
4114
+ async waitForSwapFunded(pendingSwap) {
4115
+ await this.waitForSubmarineSwap(pendingSwap, "transaction.mempool");
4116
+ }
4117
+ /**
4118
+ * Shared wait machinery: monitors the swap and resolves at the terminal
4119
+ * "transaction.claimed" status (with the preimage) or, when `resolveAt`
4120
+ * is given, as soon as that status — or any later one in the successful
4121
+ * progression — is observed (without a preimage).
4122
+ */
4123
+ async waitForSubmarineSwap(pendingSwap, resolveAt) {
4074
4124
  return new Promise((resolve, reject) => {
4075
- let isResolved = false;
4125
+ let isFinal = false;
4126
+ let isSettled = false;
4076
4127
  const onStatusUpdate = async (status) => {
4077
- if (isResolved) return;
4078
- const saveStatus = (additionalFields) => updateSubmarineSwapStatus(
4079
- pendingSwap,
4080
- status,
4081
- this.savePendingSubmarineSwap.bind(this),
4082
- additionalFields
4083
- );
4128
+ if (isFinal) return;
4129
+ const saveStatus = async (additionalFields) => {
4130
+ try {
4131
+ await updateSubmarineSwapStatus(
4132
+ pendingSwap,
4133
+ status,
4134
+ this.savePendingSubmarineSwap.bind(this),
4135
+ additionalFields
4136
+ );
4137
+ } catch (error) {
4138
+ logger.error(
4139
+ `Swap ${pendingSwap.id}: failed to persist status "${status}": ${error}`
4140
+ );
4141
+ }
4142
+ };
4084
4143
  switch (status) {
4085
4144
  case "swap.expired":
4086
- isResolved = true;
4145
+ isFinal = true;
4146
+ isSettled = true;
4087
4147
  await saveStatus({ refundable: true });
4088
4148
  reject(
4089
4149
  new SwapExpiredError({
@@ -4093,7 +4153,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
4093
4153
  );
4094
4154
  break;
4095
4155
  case "invoice.failedToPay":
4096
- isResolved = true;
4156
+ isFinal = true;
4157
+ isSettled = true;
4097
4158
  await saveStatus({ refundable: true });
4098
4159
  reject(
4099
4160
  new InvoiceFailedToPayError({
@@ -4103,7 +4164,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
4103
4164
  );
4104
4165
  break;
4105
4166
  case "transaction.lockupFailed":
4106
- isResolved = true;
4167
+ isFinal = true;
4168
+ isSettled = true;
4107
4169
  await saveStatus({ refundable: true });
4108
4170
  reject(
4109
4171
  new TransactionLockupFailedError({
@@ -4113,23 +4175,47 @@ var ArkadeSwaps = class _ArkadeSwaps {
4113
4175
  );
4114
4176
  break;
4115
4177
  case "transaction.claimed": {
4116
- isResolved = true;
4117
- const { preimage } = await this.swapProvider.getSwapPreimage(
4118
- pendingSwap.id
4119
- );
4120
- await saveStatus({ preimage });
4121
- resolve({ preimage });
4178
+ isFinal = true;
4179
+ isSettled = true;
4180
+ try {
4181
+ const { preimage } = await this.swapProvider.getSwapPreimage(
4182
+ pendingSwap.id
4183
+ );
4184
+ await saveStatus({ preimage });
4185
+ resolve({ preimage });
4186
+ } catch (error) {
4187
+ logger.error(
4188
+ `Swap ${pendingSwap.id}: failed to fetch preimage on claim: ${error}`
4189
+ );
4190
+ reject(error);
4191
+ }
4122
4192
  break;
4123
4193
  }
4124
4194
  default:
4125
4195
  await saveStatus();
4196
+ if (resolveAt && hasSubmarineStatusReached(status, resolveAt)) {
4197
+ isSettled = true;
4198
+ resolve({ preimage: void 0 });
4199
+ }
4126
4200
  break;
4127
4201
  }
4128
4202
  };
4129
- this.swapProvider.monitorSwap(pendingSwap.id, onStatusUpdate).catch((error) => {
4130
- if (!isResolved) {
4131
- isResolved = true;
4203
+ this.swapProvider.monitorSwap(pendingSwap.id, (status) => {
4204
+ onStatusUpdate(status).catch(
4205
+ (error) => logger.error(
4206
+ `Swap ${pendingSwap.id}: error handling status "${status}": ${error}`
4207
+ )
4208
+ );
4209
+ }).catch((error) => {
4210
+ if (!isSettled) {
4211
+ isFinal = true;
4212
+ isSettled = true;
4132
4213
  reject(error);
4214
+ } else {
4215
+ isFinal = true;
4216
+ logger.warn(
4217
+ `Swap ${pendingSwap.id}: monitor failed after settlement: ${error}`
4218
+ );
4133
4219
  }
4134
4220
  });
4135
4221
  });
@@ -4367,29 +4453,26 @@ var ArkadeSwaps = class _ArkadeSwaps {
4367
4453
  "user",
4368
4454
  pendingSwap.id
4369
4455
  );
4370
- const serverXOnlyPublicKey = normalizeToXOnlyKey(
4371
- import_base9.hex.decode(arkInfo.signerPubkey),
4372
- "server",
4373
- pendingSwap.id
4374
- );
4375
4456
  const boltzXOnlyPublicKey = normalizeToXOnlyKey(
4376
4457
  import_base9.hex.decode(pendingSwap.response.lockupDetails.serverPublicKey),
4377
4458
  "boltz",
4378
4459
  pendingSwap.id
4379
4460
  );
4380
- const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
4381
- network: arkInfo.network,
4382
- preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
4383
- serverPubkey: import_base9.hex.encode(serverXOnlyPublicKey),
4384
- senderPubkey: import_base9.hex.encode(ourXOnlyPublicKey),
4385
- receiverPubkey: import_base9.hex.encode(boltzXOnlyPublicKey),
4386
- timeoutBlockHeights: pendingSwap.response.lockupDetails.timeouts
4387
- });
4388
- if (!vhtlcScript.refundScript)
4389
- throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
4390
- if (pendingSwap.response.lockupDetails.lockupAddress !== vhtlcAddress) {
4461
+ let vhtlcScript;
4462
+ let serverXOnlyPublicKey;
4463
+ try {
4464
+ ({ vhtlcScript, serverXOnlyPublicKey } = this.resolveVHTLCForLockup({
4465
+ arkInfo,
4466
+ preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
4467
+ senderPubkey: import_base9.hex.encode(ourXOnlyPublicKey),
4468
+ receiverPubkey: import_base9.hex.encode(boltzXOnlyPublicKey),
4469
+ timeoutBlockHeights: pendingSwap.response.lockupDetails.timeouts,
4470
+ lockupAddress: pendingSwap.response.lockupDetails.lockupAddress,
4471
+ swapId: pendingSwap.id
4472
+ }));
4473
+ } catch (error) {
4391
4474
  throw new SwapError({
4392
- message: "Unable to claim: invalid VHTLC address"
4475
+ message: error instanceof Error ? error.message : "Unable to refund: invalid VHTLC address"
4393
4476
  });
4394
4477
  }
4395
4478
  const { vtxos } = await this.indexerProvider.getVtxos({
@@ -4648,20 +4731,21 @@ var ArkadeSwaps = class _ArkadeSwaps {
4648
4731
  pendingSwap.response.claimDetails.serverPublicKey,
4649
4732
  "sender"
4650
4733
  );
4651
- const serverXOnlyPublicKey = normalizeToXOnlyKey(arkInfo.signerPubkey, "server");
4652
- const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
4653
- network: arkInfo.network,
4654
- preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
4655
- serverPubkey: import_base9.hex.encode(serverXOnlyPublicKey),
4656
- senderPubkey: import_base9.hex.encode(senderXOnlyPublicKey),
4657
- receiverPubkey: import_base9.hex.encode(receiverXOnlyPublicKey),
4658
- timeoutBlockHeights: pendingSwap.response.claimDetails.timeouts
4659
- });
4660
- if (!vhtlcScript.claimScript)
4661
- throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
4662
- if (pendingSwap.response.claimDetails.lockupAddress !== vhtlcAddress) {
4734
+ let vhtlcScript;
4735
+ let serverXOnlyPublicKey;
4736
+ try {
4737
+ ({ vhtlcScript, serverXOnlyPublicKey } = this.resolveVHTLCForLockup({
4738
+ arkInfo,
4739
+ preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
4740
+ senderPubkey: import_base9.hex.encode(senderXOnlyPublicKey),
4741
+ receiverPubkey: import_base9.hex.encode(receiverXOnlyPublicKey),
4742
+ timeoutBlockHeights: pendingSwap.response.claimDetails.timeouts,
4743
+ lockupAddress: pendingSwap.response.claimDetails.lockupAddress,
4744
+ swapId: pendingSwap.id
4745
+ }));
4746
+ } catch (error) {
4663
4747
  throw new SwapError({
4664
- message: "Unable to claim: invalid VHTLC address"
4748
+ message: error instanceof Error ? error.message : "Unable to claim: invalid VHTLC address"
4665
4749
  });
4666
4750
  }
4667
4751
  let vtxo;
@@ -5023,6 +5107,55 @@ var ArkadeSwaps = class _ArkadeSwaps {
5023
5107
  createVHTLCScript(args) {
5024
5108
  return createVHTLCScript(args);
5025
5109
  }
5110
+ /**
5111
+ * Reconstruct a swap's VHTLC by matching the persisted `lockupAddress`
5112
+ * against the current and deprecated server signers, returning the matched
5113
+ * script together with the server key it was minted under.
5114
+ *
5115
+ * Recovery paths (claim/refund/lookup) must use this instead of building the
5116
+ * VHTLC from the current signer alone: a swap created before a planned arkd
5117
+ * signer rotation is locked to a now-deprecated signer, so the current key
5118
+ * would yield the wrong address and strand the funds. The returned
5119
+ * `serverXOnlyPublicKey` is the original (possibly deprecated) key and MUST
5120
+ * be threaded into downstream signing/verification.
5121
+ *
5122
+ * Throws a descriptive mismatch error when no candidate reproduces the
5123
+ * lockup address (e.g. the swap predates an already-pruned deprecated
5124
+ * signer) — replacing the previous current-signer-only equality check.
5125
+ */
5126
+ resolveVHTLCForLockup(args) {
5127
+ const candidates = candidateServerPubkeys(args.arkInfo);
5128
+ for (const serverPubkey of candidates) {
5129
+ const { vhtlcScript, vhtlcAddress } = this.createVHTLCScript({
5130
+ network: args.arkInfo.network,
5131
+ preimageHash: args.preimageHash,
5132
+ receiverPubkey: args.receiverPubkey,
5133
+ senderPubkey: args.senderPubkey,
5134
+ serverPubkey,
5135
+ timeoutBlockHeights: args.timeoutBlockHeights
5136
+ });
5137
+ if (vhtlcAddress !== args.lockupAddress) continue;
5138
+ if (!vhtlcScript.claimScript && !vhtlcScript.refundScript) {
5139
+ throw new Error(
5140
+ `Swap ${args.swapId}: VHTLC address matched but claim/refund script leaves are empty`
5141
+ );
5142
+ }
5143
+ return {
5144
+ vhtlcScript,
5145
+ // The matched (possibly deprecated) key must flow into downstream
5146
+ // signing/verification: arkd signs a deprecated-signer input with
5147
+ // the deprecated key, so the claim/refund leaf checks use it.
5148
+ serverXOnlyPublicKey: normalizeToXOnlyKey(
5149
+ import_base9.hex.decode(serverPubkey),
5150
+ "server",
5151
+ args.swapId
5152
+ )
5153
+ };
5154
+ }
5155
+ throw new Error(
5156
+ `Swap ${args.swapId}: VHTLC address mismatch. Expected ${args.lockupAddress}; no current or deprecated server signer (${candidates.length} candidate(s) tried) reproduced it`
5157
+ );
5158
+ }
5026
5159
  async getFees(from, to) {
5027
5160
  if (from && to) {
5028
5161
  return this.swapProvider.getChainFees(from, to);
@@ -5106,13 +5239,28 @@ var ArkadeSwaps = class _ArkadeSwaps {
5106
5239
  for (const swap of await this.getPendingSubmarineSwapsFromStorage()) {
5107
5240
  if (isSubmarineFinalStatus(swap.status)) continue;
5108
5241
  promises.push(
5109
- this.getSwapStatus(swap.id).then(
5110
- ({ status }) => updateSubmarineSwapStatus(
5242
+ this.getSwapStatus(swap.id).then(async ({ status }) => {
5243
+ let additionalFields;
5244
+ if (isSubmarineSuccessStatus(status) && !swap.preimage) {
5245
+ try {
5246
+ const { preimage } = await this.swapProvider.getSwapPreimage(
5247
+ swap.id
5248
+ );
5249
+ additionalFields = { preimage };
5250
+ } catch (error) {
5251
+ logger.warn(
5252
+ `Failed to fetch preimage for settled swap ${swap.id}:`,
5253
+ error
5254
+ );
5255
+ }
5256
+ }
5257
+ await updateSubmarineSwapStatus(
5111
5258
  swap,
5112
5259
  status,
5113
- this.savePendingSubmarineSwap.bind(this)
5114
- )
5115
- ).catch((error) => {
5260
+ this.savePendingSubmarineSwap.bind(this),
5261
+ additionalFields
5262
+ );
5263
+ }).catch((error) => {
5116
5264
  logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
5117
5265
  })
5118
5266
  );
@@ -1,8 +1,8 @@
1
- import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-BlyUrhtO.cjs';
2
- export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-BlyUrhtO.cjs';
1
+ import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-CGMXUKPe.cjs';
2
+ export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-CGMXUKPe.cjs';
3
3
  import '@arkade-os/sdk/worker/expo';
4
4
  import '@arkade-os/sdk';
5
- import '../types-D97i1LFu.cjs';
5
+ import '../types-8NrCdOpS.cjs';
6
6
 
7
7
  /**
8
8
  * Define the Expo background task handler for swap polling.
@@ -1,8 +1,8 @@
1
- import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-Bv4Z2R7g.js';
2
- export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-Bv4Z2R7g.js';
1
+ import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-CuDM6sxV.js';
2
+ export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-CuDM6sxV.js';
3
3
  import '@arkade-os/sdk/worker/expo';
4
4
  import '@arkade-os/sdk';
5
- import '../types-D97i1LFu.js';
5
+ import '../types-8NrCdOpS.js';
6
6
 
7
7
  /**
8
8
  * Define the Expo background task handler for swap polling.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  SWAP_POLL_TASK_TYPE,
3
3
  swapsPollProcessor
4
- } from "../chunk-DNCIVDU5.js";
4
+ } from "../chunk-CWY37W4B.js";
5
5
  import {
6
6
  BoltzSwapProvider
7
- } from "../chunk-CFB2NNGT.js";
7
+ } from "../chunk-UXYHW7KV.js";
8
8
  import "../chunk-SJQJQO7P.js";
9
9
 
10
10
  // src/expo/background.ts