@flashnet/sdk 0.5.4 → 0.5.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.
@@ -11,6 +11,7 @@ var hex = require('../utils/hex.js');
11
11
  var intents = require('../utils/intents.js');
12
12
  var sparkAddress = require('../utils/spark-address.js');
13
13
  var tokenAddress = require('../utils/tokenAddress.js');
14
+ var bigint = require('../utils/bigint.js');
14
15
  var errors = require('../types/errors.js');
15
16
 
16
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -245,7 +246,8 @@ class FlashnetClient {
245
246
  const tokenIdentifierHex = hex.getHexFromUint8Array(info.rawTokenIdentifier);
246
247
  const tokenAddress$1 = tokenAddress.encodeSparkHumanReadableTokenIdentifier(info.rawTokenIdentifier, this.sparkNetwork);
247
248
  tokenBalances.set(tokenPubkey, {
248
- balance: BigInt(tokenData.balance),
249
+ balance: FlashnetClient.safeBigInt(tokenData.ownedBalance),
250
+ availableToSendBalance: FlashnetClient.safeBigInt(tokenData.availableToSendBalance),
249
251
  tokenInfo: {
250
252
  tokenIdentifier: tokenIdentifierHex,
251
253
  tokenAddress: tokenAddress$1,
@@ -258,7 +260,7 @@ class FlashnetClient {
258
260
  }
259
261
  }
260
262
  return {
261
- balance: BigInt(balance.balance),
263
+ balance: FlashnetClient.safeBigInt(balance.balance),
262
264
  tokenBalances,
263
265
  };
264
266
  }
@@ -266,17 +268,17 @@ class FlashnetClient {
266
268
  * Check if wallet has sufficient balance for an operation
267
269
  */
268
270
  async checkBalance(params) {
269
- const balance = await this.getBalance();
271
+ const balance = params.walletBalance ?? (await this.getBalance());
270
272
  // Check balance
271
273
  const requirements = {
272
274
  tokens: new Map(),
273
275
  };
274
276
  for (const balance of params.balancesToCheck) {
275
277
  if (balance.assetAddress === index$1.BTC_ASSET_PUBKEY) {
276
- requirements.btc = BigInt(balance.amount);
278
+ requirements.btc = FlashnetClient.safeBigInt(balance.amount);
277
279
  }
278
280
  else {
279
- requirements.tokens?.set(balance.assetAddress, BigInt(balance.amount));
281
+ requirements.tokens?.set(balance.assetAddress, FlashnetClient.safeBigInt(balance.amount));
280
282
  }
281
283
  }
282
284
  // Check BTC balance
@@ -294,7 +296,9 @@ class FlashnetClient {
294
296
  const hrKey = this.toHumanReadableTokenIdentifier(tokenPubkey);
295
297
  const effectiveTokenBalance = balance.tokenBalances.get(tokenPubkey) ??
296
298
  balance.tokenBalances.get(hrKey);
297
- const available = effectiveTokenBalance?.balance ?? 0n;
299
+ const available = params.useAvailableBalance
300
+ ? (effectiveTokenBalance?.availableToSendBalance ?? 0n)
301
+ : (effectiveTokenBalance?.balance ?? 0n);
298
302
  if (available < requiredAmount) {
299
303
  throw new Error([
300
304
  params.errorPrefix ?? "",
@@ -356,6 +360,7 @@ class FlashnetClient {
356
360
  },
357
361
  ],
358
362
  errorPrefix: "Insufficient balance for initial liquidity: ",
363
+ useAvailableBalance: params.useAvailableBalance,
359
364
  });
360
365
  }
361
366
  const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
@@ -415,6 +420,12 @@ class FlashnetClient {
415
420
  throw new Error(`${name} must be positive integer`);
416
421
  }
417
422
  }
423
+ /**
424
+ * Safely convert a value to BigInt. Delegates to the shared `safeBigInt` utility.
425
+ */
426
+ static safeBigInt(value, fallback = 0n) {
427
+ return bigint.safeBigInt(value, fallback);
428
+ }
418
429
  /**
419
430
  * Calculates virtual reserves for a bonding curve AMM.
420
431
  *
@@ -435,7 +446,7 @@ class FlashnetClient {
435
446
  }
436
447
  const supply = FlashnetClient.parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
437
448
  const targetB = FlashnetClient.parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
438
- const graduationThresholdPct = BigInt(params.graduationThresholdPct);
449
+ const graduationThresholdPct = FlashnetClient.safeBigInt(params.graduationThresholdPct);
439
450
  // Align bounds with Rust AMM (20%..95%), then check feasibility for g=1 (requires >50%).
440
451
  const MIN_PCT = 20n;
441
452
  const MAX_PCT = 95n;
@@ -485,6 +496,7 @@ class FlashnetClient {
485
496
  },
486
497
  ],
487
498
  errorPrefix: "Insufficient balance for pool creation: ",
499
+ useAvailableBalance: params.useAvailableBalance,
488
500
  });
489
501
  const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
490
502
  // Generate intent
@@ -605,10 +617,14 @@ class FlashnetClient {
605
617
  });
606
618
  // If using free balance (V3 pools only), skip the Spark transfer
607
619
  if (params.useFreeBalance) {
608
- return this.executeSwapIntent({
620
+ const swapResponse = await this.executeSwapIntent({
609
621
  ...params,
610
622
  // No transferId - triggers free balance mode
611
623
  });
624
+ return {
625
+ ...swapResponse,
626
+ inboundSparkTransferId: swapResponse.requestId,
627
+ };
612
628
  }
613
629
  // Transfer assets to pool using new address encoding
614
630
  const lpSparkAddress = sparkAddress.encodeSparkAddressNew({
@@ -619,12 +635,13 @@ class FlashnetClient {
619
635
  receiverSparkAddress: lpSparkAddress,
620
636
  assetAddress: params.assetInAddress,
621
637
  amount: params.amountIn,
622
- }, "Insufficient balance for swap: ");
638
+ }, "Insufficient balance for swap: ", params.useAvailableBalance);
623
639
  // Execute with auto-clawback on failure
624
- return this.executeWithAutoClawback(() => this.executeSwapIntent({
640
+ const swapResponse = await this.executeWithAutoClawback(() => this.executeSwapIntent({
625
641
  ...params,
626
642
  transferId,
627
643
  }), [transferId], params.poolId);
644
+ return { ...swapResponse, inboundSparkTransferId: transferId };
628
645
  }
629
646
  /**
630
647
  * Execute a swap with a pre-created transfer or using free balance.
@@ -754,7 +771,7 @@ class FlashnetClient {
754
771
  receiverSparkAddress: lpSparkAddress,
755
772
  assetAddress: params.initialAssetAddress,
756
773
  amount: params.inputAmount,
757
- }, "Insufficient balance for route swap: ");
774
+ }, "Insufficient balance for route swap: ", params.useAvailableBalance);
758
775
  // Execute with auto-clawback on failure
759
776
  return this.executeWithAutoClawback(async () => {
760
777
  // Prepare hops for validation
@@ -877,7 +894,7 @@ class FlashnetClient {
877
894
  assetAddress: pool.assetBAddress,
878
895
  amount: params.assetBAmount,
879
896
  },
880
- ], "Insufficient balance for adding liquidity: ");
897
+ ], "Insufficient balance for adding liquidity: ", params.useAvailableBalance);
881
898
  // Execute with auto-clawback on failure
882
899
  return this.executeWithAutoClawback(async () => {
883
900
  // Generate add liquidity intent
@@ -1183,6 +1200,7 @@ class FlashnetClient {
1183
1200
  depositAddress: createResponse.depositAddress,
1184
1201
  assetId: params.assetId,
1185
1202
  assetAmount: params.assetAmount,
1203
+ useAvailableBalance: params.useAvailableBalance,
1186
1204
  });
1187
1205
  }
1188
1206
  /**
@@ -1200,6 +1218,7 @@ class FlashnetClient {
1200
1218
  { assetAddress: params.assetId, amount: params.assetAmount },
1201
1219
  ],
1202
1220
  errorPrefix: "Insufficient balance to fund escrow: ",
1221
+ useAvailableBalance: params.useAvailableBalance,
1203
1222
  });
1204
1223
  // 2. Perform transfer
1205
1224
  const escrowSparkAddress = sparkAddress.encodeSparkAddressNew({
@@ -1714,19 +1733,20 @@ class FlashnetClient {
1714
1733
  /**
1715
1734
  * Performs asset transfer using generalized asset address for both BTC and tokens.
1716
1735
  */
1717
- async transferAsset(recipient, checkBalanceErrorPrefix) {
1718
- const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix);
1736
+ async transferAsset(recipient, checkBalanceErrorPrefix, useAvailableBalance) {
1737
+ const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix, useAvailableBalance);
1719
1738
  return transferIds[0];
1720
1739
  }
1721
1740
  /**
1722
1741
  * Performs asset transfers using generalized asset addresses for both BTC and tokens.
1723
1742
  * Supports optional generic to hardcode recipients length so output list can be typed with same length.
1724
1743
  */
1725
- async transferAssets(recipients, checkBalanceErrorPrefix) {
1744
+ async transferAssets(recipients, checkBalanceErrorPrefix, useAvailableBalance) {
1726
1745
  if (checkBalanceErrorPrefix) {
1727
1746
  await this.checkBalance({
1728
1747
  balancesToCheck: recipients,
1729
1748
  errorPrefix: checkBalanceErrorPrefix,
1749
+ useAvailableBalance,
1730
1750
  });
1731
1751
  }
1732
1752
  const transferIds = [];
@@ -1741,7 +1761,7 @@ class FlashnetClient {
1741
1761
  else {
1742
1762
  const transferId = await this._wallet.transferTokens({
1743
1763
  tokenIdentifier: this.toHumanReadableTokenIdentifier(recipient.assetAddress),
1744
- tokenAmount: BigInt(recipient.amount),
1764
+ tokenAmount: FlashnetClient.safeBigInt(recipient.amount),
1745
1765
  receiverSparkAddress: recipient.receiverSparkAddress,
1746
1766
  });
1747
1767
  transferIds.push(transferId);
@@ -1848,7 +1868,8 @@ class FlashnetClient {
1848
1868
  const lightningFeeEstimate = await this.getLightningFeeEstimate(invoice);
1849
1869
  // Total BTC needed = invoice amount + lightning fee (unmasked).
1850
1870
  // Bitmasking for V2 pools is handled inside findBestPoolForTokenToBtc.
1851
- const baseBtcNeeded = BigInt(invoiceAmountSats) + BigInt(lightningFeeEstimate);
1871
+ const baseBtcNeeded = FlashnetClient.safeBigInt(invoiceAmountSats) +
1872
+ FlashnetClient.safeBigInt(lightningFeeEstimate);
1852
1873
  // Check Flashnet minimum amounts early to provide clear error messages
1853
1874
  const minAmounts = await this.getEnabledMinAmountsMap();
1854
1875
  // Check BTC minimum (output from swap)
@@ -1875,7 +1896,7 @@ class FlashnetClient {
1875
1896
  const tokenHex = this.toHexTokenIdentifier(tokenAddress).toLowerCase();
1876
1897
  const tokenMinAmount = minAmounts.get(tokenHex);
1877
1898
  if (tokenMinAmount &&
1878
- BigInt(poolQuote.tokenAmountRequired) < tokenMinAmount) {
1899
+ FlashnetClient.safeBigInt(poolQuote.tokenAmountRequired) < tokenMinAmount) {
1879
1900
  const msg = `Token amount too small. Minimum input is ${tokenMinAmount} units, but calculated amount is only ${poolQuote.tokenAmountRequired} units.`;
1880
1901
  throw new errors.FlashnetError(msg, {
1881
1902
  response: {
@@ -1892,7 +1913,7 @@ class FlashnetClient {
1892
1913
  }
1893
1914
  // BTC variable fee adjustment: difference between what the pool targets and unmasked base.
1894
1915
  // For V3 pools this is 0 (no masking). For V2 it's the rounding overhead.
1895
- const btcVariableFeeAdjustment = Number(BigInt(poolQuote.btcAmountUsed) - baseBtcNeeded);
1916
+ const btcVariableFeeAdjustment = Number(FlashnetClient.safeBigInt(poolQuote.btcAmountUsed) - baseBtcNeeded);
1896
1917
  return {
1897
1918
  poolId: poolQuote.poolId,
1898
1919
  tokenAddress: this.toHexTokenIdentifier(tokenAddress),
@@ -1964,7 +1985,7 @@ class FlashnetClient {
1964
1985
  amountIn: tokenAmount,
1965
1986
  integratorBps: options?.integratorFeeRateBps,
1966
1987
  });
1967
- const btcOut = BigInt(simulation.amountOut);
1988
+ const btcOut = FlashnetClient.safeBigInt(simulation.amountOut);
1968
1989
  if (btcOut > bestBtcOut) {
1969
1990
  bestBtcOut = btcOut;
1970
1991
  bestResult = {
@@ -2052,7 +2073,7 @@ class FlashnetClient {
2052
2073
  await this.ensureInitialized();
2053
2074
  const { invoice, tokenAddress, tokenAmount, maxSlippageBps = 500, // 5% default
2054
2075
  maxLightningFeeSats, preferSpark = true, integratorFeeRateBps, integratorPublicKey, transferTimeoutMs = 30000, // 30s default
2055
- rollbackOnFailure = false, useExistingBtcBalance = false, } = options;
2076
+ rollbackOnFailure = false, useExistingBtcBalance = false, useAvailableBalance = false, } = options;
2056
2077
  try {
2057
2078
  // Step 1: Get a quote for the payment
2058
2079
  const quote = await this.getPayLightningWithTokenQuote(invoice, tokenAddress, {
@@ -2069,6 +2090,7 @@ class FlashnetClient {
2069
2090
  },
2070
2091
  ],
2071
2092
  errorPrefix: "Insufficient token balance for Lightning payment: ",
2093
+ useAvailableBalance,
2072
2094
  });
2073
2095
  // Step 3: Get pool details
2074
2096
  const pool = await this.getPool(quote.poolId);
@@ -2091,6 +2113,7 @@ class FlashnetClient {
2091
2113
  minAmountOut: minBtcOut,
2092
2114
  integratorFeeRateBps,
2093
2115
  integratorPublicKey,
2116
+ useAvailableBalance,
2094
2117
  });
2095
2118
  if (!swapResponse.accepted || !swapResponse.outboundTransferId) {
2096
2119
  return {
@@ -2100,6 +2123,7 @@ class FlashnetClient {
2100
2123
  btcAmountReceived: "0",
2101
2124
  swapTransferId: swapResponse.outboundTransferId || "",
2102
2125
  ammFeePaid: quote.estimatedAmmFee,
2126
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2103
2127
  error: swapResponse.error || "Swap was not accepted",
2104
2128
  };
2105
2129
  }
@@ -2110,7 +2134,8 @@ class FlashnetClient {
2110
2134
  const effectiveMaxLightningFee = maxLightningFeeSats ?? quote.estimatedLightningFee;
2111
2135
  const btcNeededForPayment = invoiceAmountSats + effectiveMaxLightningFee;
2112
2136
  const balance = await this.getBalance();
2113
- canPayImmediately = balance.balance >= BigInt(btcNeededForPayment);
2137
+ canPayImmediately =
2138
+ balance.balance >= FlashnetClient.safeBigInt(btcNeededForPayment);
2114
2139
  }
2115
2140
  if (!canPayImmediately) {
2116
2141
  const transferComplete = await this.waitForTransferCompletion(swapResponse.outboundTransferId, transferTimeoutMs);
@@ -2122,6 +2147,7 @@ class FlashnetClient {
2122
2147
  btcAmountReceived: swapResponse.amountOut || "0",
2123
2148
  swapTransferId: swapResponse.outboundTransferId,
2124
2149
  ammFeePaid: quote.estimatedAmmFee,
2150
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2125
2151
  error: "Transfer did not complete within timeout",
2126
2152
  };
2127
2153
  }
@@ -2135,8 +2161,8 @@ class FlashnetClient {
2135
2161
  let invoiceAmountPaid;
2136
2162
  if (quote.isZeroAmountInvoice) {
2137
2163
  // Zero-amount invoice: pay whatever BTC we received minus lightning fee
2138
- const actualBtc = BigInt(btcReceived);
2139
- const lnFee = BigInt(effectiveMaxLightningFee);
2164
+ const actualBtc = FlashnetClient.safeBigInt(btcReceived);
2165
+ const lnFee = FlashnetClient.safeBigInt(effectiveMaxLightningFee);
2140
2166
  const amountToPay = actualBtc - lnFee;
2141
2167
  if (amountToPay <= 0n) {
2142
2168
  return {
@@ -2146,6 +2172,7 @@ class FlashnetClient {
2146
2172
  btcAmountReceived: btcReceived,
2147
2173
  swapTransferId: swapResponse.outboundTransferId,
2148
2174
  ammFeePaid: quote.estimatedAmmFee,
2175
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2149
2176
  error: `BTC received (${btcReceived} sats) is not enough to cover lightning fee (${effectiveMaxLightningFee} sats).`,
2150
2177
  };
2151
2178
  }
@@ -2165,6 +2192,12 @@ class FlashnetClient {
2165
2192
  preferSpark,
2166
2193
  });
2167
2194
  }
2195
+ // Extract the Spark transfer ID from the lightning payment result.
2196
+ // payLightningInvoice returns LightningSendRequest | WalletTransfer:
2197
+ // - LightningSendRequest has .transfer?.sparkId (the Sparkscan-visible transfer ID)
2198
+ // - WalletTransfer (Spark-to-Spark) has .id directly as the transfer ID
2199
+ // Note: lightningPayment.id (the SSP request ID) is already returned as lightningPaymentId
2200
+ const sparkLightningTransferId = lightningPayment.transfer?.sparkId;
2168
2201
  return {
2169
2202
  success: true,
2170
2203
  poolId: quote.poolId,
@@ -2175,6 +2208,8 @@ class FlashnetClient {
2175
2208
  ammFeePaid: quote.estimatedAmmFee,
2176
2209
  lightningFeePaid: effectiveMaxLightningFee,
2177
2210
  invoiceAmountPaid,
2211
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2212
+ sparkLightningTransferId,
2178
2213
  };
2179
2214
  }
2180
2215
  catch (lightningError) {
@@ -2194,6 +2229,7 @@ class FlashnetClient {
2194
2229
  btcAmountReceived: "0",
2195
2230
  swapTransferId: swapResponse.outboundTransferId,
2196
2231
  ammFeePaid: quote.estimatedAmmFee,
2232
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2197
2233
  error: `Lightning payment failed: ${lightningErrorMessage}. Funds rolled back to ${rollbackResult.tokenAmount} tokens.`,
2198
2234
  };
2199
2235
  }
@@ -2209,6 +2245,7 @@ class FlashnetClient {
2209
2245
  btcAmountReceived: btcReceived,
2210
2246
  swapTransferId: swapResponse.outboundTransferId,
2211
2247
  ammFeePaid: quote.estimatedAmmFee,
2248
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2212
2249
  error: `Lightning payment failed: ${lightningErrorMessage}. Rollback also failed: ${rollbackErrorMessage}. BTC remains in wallet.`,
2213
2250
  };
2214
2251
  }
@@ -2220,6 +2257,7 @@ class FlashnetClient {
2220
2257
  btcAmountReceived: btcReceived,
2221
2258
  swapTransferId: swapResponse.outboundTransferId,
2222
2259
  ammFeePaid: quote.estimatedAmmFee,
2260
+ sparkTokenTransferId: swapResponse.inboundSparkTransferId,
2223
2261
  error: `Lightning payment failed: ${lightningErrorMessage}. BTC (${btcReceived} sats) remains in wallet.`,
2224
2262
  };
2225
2263
  }
@@ -2382,7 +2420,7 @@ class FlashnetClient {
2382
2420
  tokenIsAssetA: pool.tokenIsAssetA,
2383
2421
  integratorBps: integratorFeeRateBps,
2384
2422
  });
2385
- tokenAmount = BigInt(v3Result.amountIn);
2423
+ tokenAmount = FlashnetClient.safeBigInt(v3Result.amountIn);
2386
2424
  fee = v3Result.totalFee;
2387
2425
  executionPrice = v3Result.simulation.executionPrice || "0";
2388
2426
  priceImpactPct = v3Result.simulation.priceImpactPct || "0";
@@ -2391,7 +2429,7 @@ class FlashnetClient {
2391
2429
  else {
2392
2430
  // V2: constant product math + simulation verification
2393
2431
  const calculation = this.calculateTokenAmountForBtcOutput(btcTarget.toString(), poolDetails.assetAReserve, poolDetails.assetBReserve, poolDetails.lpFeeBps, poolDetails.hostFeeBps, pool.tokenIsAssetA, integratorFeeRateBps);
2394
- tokenAmount = BigInt(calculation.amountIn);
2432
+ tokenAmount = FlashnetClient.safeBigInt(calculation.amountIn);
2395
2433
  // Verify with simulation
2396
2434
  const simulation = await this.simulateSwap({
2397
2435
  poolId: pool.lpPublicKey,
@@ -2400,7 +2438,7 @@ class FlashnetClient {
2400
2438
  amountIn: calculation.amountIn,
2401
2439
  integratorBps: integratorFeeRateBps,
2402
2440
  });
2403
- if (BigInt(simulation.amountOut) < btcTarget) {
2441
+ if (FlashnetClient.safeBigInt(simulation.amountOut) < btcTarget) {
2404
2442
  const btcReserve = pool.tokenIsAssetA
2405
2443
  ? poolDetails.assetBReserve
2406
2444
  : poolDetails.assetAReserve;
@@ -2488,9 +2526,9 @@ class FlashnetClient {
2488
2526
  * @private
2489
2527
  */
2490
2528
  calculateTokenAmountForBtcOutput(btcAmountOut, reserveA, reserveB, lpFeeBps, hostFeeBps, tokenIsAssetA, integratorFeeBps) {
2491
- const amountOut = BigInt(btcAmountOut);
2492
- const resA = BigInt(reserveA);
2493
- const resB = BigInt(reserveB);
2529
+ const amountOut = FlashnetClient.safeBigInt(btcAmountOut);
2530
+ const resA = FlashnetClient.safeBigInt(reserveA);
2531
+ const resB = FlashnetClient.safeBigInt(reserveB);
2494
2532
  const totalFeeBps = lpFeeBps + hostFeeBps + (integratorFeeBps || 0);
2495
2533
  const feeRate = Number(totalFeeBps) / 10000; // Convert bps to decimal
2496
2534
  // Token is the input asset
@@ -2588,7 +2626,7 @@ class FlashnetClient {
2588
2626
  amountIn: upperBound.toString(),
2589
2627
  integratorBps,
2590
2628
  });
2591
- if (BigInt(sim.amountOut) >= desiredBtcOut) {
2629
+ if (FlashnetClient.safeBigInt(sim.amountOut) >= desiredBtcOut) {
2592
2630
  upperSim = sim;
2593
2631
  break;
2594
2632
  }
@@ -2599,7 +2637,7 @@ class FlashnetClient {
2599
2637
  throw new Error(`V3 pool ${poolId} has insufficient liquidity for ${desiredBtcOut} sats`);
2600
2638
  }
2601
2639
  // Step 3: Refine estimate via linear interpolation
2602
- const upperOut = BigInt(upperSim.amountOut);
2640
+ const upperOut = FlashnetClient.safeBigInt(upperSim.amountOut);
2603
2641
  // Scale proportionally: if upperBound produced upperOut, we need roughly
2604
2642
  // (upperBound * desiredBtcOut / upperOut). Add +1 to avoid undershoot from truncation.
2605
2643
  let refined = (upperBound * desiredBtcOut) / upperOut + 1n;
@@ -2617,7 +2655,7 @@ class FlashnetClient {
2617
2655
  amountIn: refined.toString(),
2618
2656
  integratorBps,
2619
2657
  });
2620
- if (BigInt(refinedSim.amountOut) >= desiredBtcOut) {
2658
+ if (FlashnetClient.safeBigInt(refinedSim.amountOut) >= desiredBtcOut) {
2621
2659
  bestAmountIn = refined;
2622
2660
  bestSim = refinedSim;
2623
2661
  }
@@ -2651,7 +2689,7 @@ class FlashnetClient {
2651
2689
  amountIn: mid.toString(),
2652
2690
  integratorBps,
2653
2691
  });
2654
- if (BigInt(midSim.amountOut) >= desiredBtcOut) {
2692
+ if (FlashnetClient.safeBigInt(midSim.amountOut) >= desiredBtcOut) {
2655
2693
  hi = mid;
2656
2694
  bestAmountIn = mid;
2657
2695
  bestSim = midSim;
@@ -2673,7 +2711,7 @@ class FlashnetClient {
2673
2711
  * @private
2674
2712
  */
2675
2713
  calculateMinAmountOut(expectedAmount, slippageBps) {
2676
- const amount = BigInt(expectedAmount);
2714
+ const amount = FlashnetClient.safeBigInt(expectedAmount);
2677
2715
  const slippageFactor = BigInt(10000 - slippageBps);
2678
2716
  const minAmount = (amount * slippageFactor) / 10000n;
2679
2717
  return minAmount.toString();
@@ -2892,8 +2930,11 @@ class FlashnetClient {
2892
2930
  const map = new Map();
2893
2931
  for (const item of config) {
2894
2932
  if (item.enabled) {
2933
+ if (item.min_amount == null) {
2934
+ continue;
2935
+ }
2895
2936
  const key = item.asset_identifier.toLowerCase();
2896
- const value = BigInt(String(item.min_amount));
2937
+ const value = FlashnetClient.safeBigInt(item.min_amount);
2897
2938
  map.set(key, value);
2898
2939
  }
2899
2940
  }
@@ -2915,8 +2956,8 @@ class FlashnetClient {
2915
2956
  const outHex = this.getHexAddress(params.assetOutAddress);
2916
2957
  const minIn = minMap.get(inHex);
2917
2958
  const minOut = minMap.get(outHex);
2918
- const amountIn = BigInt(String(params.amountIn));
2919
- const minAmountOut = BigInt(String(params.minAmountOut));
2959
+ const amountIn = FlashnetClient.safeBigInt(params.amountIn);
2960
+ const minAmountOut = FlashnetClient.safeBigInt(params.minAmountOut);
2920
2961
  if (minIn && minOut) {
2921
2962
  if (amountIn < minIn) {
2922
2963
  throw new Error(`Minimum amount not met for input asset. Required \
@@ -2950,13 +2991,13 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
2950
2991
  const aMin = minMap.get(aHex);
2951
2992
  const bMin = minMap.get(bHex);
2952
2993
  if (aMin) {
2953
- const aAmt = BigInt(String(params.assetAAmount));
2994
+ const aAmt = FlashnetClient.safeBigInt(params.assetAAmount);
2954
2995
  if (aAmt < aMin) {
2955
2996
  throw new Error(`Minimum amount not met for Asset A. Required ${aMin.toString()}, provided ${aAmt.toString()}`);
2956
2997
  }
2957
2998
  }
2958
2999
  if (bMin) {
2959
- const bAmt = BigInt(String(params.assetBAmount));
3000
+ const bAmt = FlashnetClient.safeBigInt(params.assetBAmount);
2960
3001
  if (bAmt < bMin) {
2961
3002
  throw new Error(`Minimum amount not met for Asset B. Required ${bMin.toString()}, provided ${bAmt.toString()}`);
2962
3003
  }
@@ -2978,14 +3019,14 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
2978
3019
  const aMin = minMap.get(aHex);
2979
3020
  const bMin = minMap.get(bHex);
2980
3021
  if (aMin) {
2981
- const predictedAOut = BigInt(String(simulation.assetAAmount));
3022
+ const predictedAOut = FlashnetClient.safeBigInt(simulation.assetAAmount);
2982
3023
  const relaxedA = aMin / 2n; // apply 50% relaxation for outputs
2983
3024
  if (predictedAOut < relaxedA) {
2984
3025
  throw new Error(`Minimum amount not met for Asset A on withdrawal. Required at least ${relaxedA.toString()} (50% relaxed), predicted ${predictedAOut.toString()}`);
2985
3026
  }
2986
3027
  }
2987
3028
  if (bMin) {
2988
- const predictedBOut = BigInt(String(simulation.assetBAmount));
3029
+ const predictedBOut = FlashnetClient.safeBigInt(simulation.assetBAmount);
2989
3030
  const relaxedB = bMin / 2n;
2990
3031
  if (predictedBOut < relaxedB) {
2991
3032
  throw new Error(`Minimum amount not met for Asset B on withdrawal. Required at least ${relaxedB.toString()} (50% relaxed), predicted ${predictedBOut.toString()}`);
@@ -3098,20 +3139,20 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
3098
3139
  const transferIds = [];
3099
3140
  // Transfer assets if not using free balance
3100
3141
  if (!params.useFreeBalance) {
3101
- if (BigInt(params.amountADesired) > 0n) {
3142
+ if (FlashnetClient.safeBigInt(params.amountADesired) > 0n) {
3102
3143
  assetATransferId = await this.transferAsset({
3103
3144
  receiverSparkAddress: lpSparkAddress,
3104
3145
  assetAddress: pool.assetAAddress,
3105
3146
  amount: params.amountADesired,
3106
- }, "Insufficient balance for adding V3 liquidity (Asset A): ");
3147
+ }, "Insufficient balance for adding V3 liquidity (Asset A): ", params.useAvailableBalance);
3107
3148
  transferIds.push(assetATransferId);
3108
3149
  }
3109
- if (BigInt(params.amountBDesired) > 0n) {
3150
+ if (FlashnetClient.safeBigInt(params.amountBDesired) > 0n) {
3110
3151
  assetBTransferId = await this.transferAsset({
3111
3152
  receiverSparkAddress: lpSparkAddress,
3112
3153
  assetAddress: pool.assetBAddress,
3113
3154
  amount: params.amountBDesired,
3114
- }, "Insufficient balance for adding V3 liquidity (Asset B): ");
3155
+ }, "Insufficient balance for adding V3 liquidity (Asset B): ", params.useAvailableBalance);
3115
3156
  transferIds.push(assetBTransferId);
3116
3157
  }
3117
3158
  }
@@ -3304,14 +3345,14 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
3304
3345
  receiverSparkAddress: lpSparkAddress,
3305
3346
  assetAddress: pool.assetAAddress,
3306
3347
  amount: params.additionalAmountA,
3307
- }, "Insufficient balance for rebalance (Asset A): ");
3348
+ }, "Insufficient balance for rebalance (Asset A): ", params.useAvailableBalance);
3308
3349
  }
3309
3350
  if (params.additionalAmountB && BigInt(params.additionalAmountB) > 0n) {
3310
3351
  assetBTransferId = await this.transferAsset({
3311
3352
  receiverSparkAddress: lpSparkAddress,
3312
3353
  assetAddress: pool.assetBAddress,
3313
3354
  amount: params.additionalAmountB,
3314
- }, "Insufficient balance for rebalance (Asset B): ");
3355
+ }, "Insufficient balance for rebalance (Asset B): ", params.useAvailableBalance);
3315
3356
  }
3316
3357
  // Collect transfer IDs for potential clawback
3317
3358
  const transferIds = [];
@@ -3503,20 +3544,20 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
3503
3544
  let assetBTransferId = "";
3504
3545
  const transferIds = [];
3505
3546
  // Transfer assets to pool
3506
- if (BigInt(params.amountA) > 0n) {
3547
+ if (FlashnetClient.safeBigInt(params.amountA) > 0n) {
3507
3548
  assetATransferId = await this.transferAsset({
3508
3549
  receiverSparkAddress: lpSparkAddress,
3509
3550
  assetAddress: pool.assetAAddress,
3510
3551
  amount: params.amountA,
3511
- }, "Insufficient balance for depositing to V3 pool (Asset A): ");
3552
+ }, "Insufficient balance for depositing to V3 pool (Asset A): ", params.useAvailableBalance);
3512
3553
  transferIds.push(assetATransferId);
3513
3554
  }
3514
- if (BigInt(params.amountB) > 0n) {
3555
+ if (FlashnetClient.safeBigInt(params.amountB) > 0n) {
3515
3556
  assetBTransferId = await this.transferAsset({
3516
3557
  receiverSparkAddress: lpSparkAddress,
3517
3558
  assetAddress: pool.assetBAddress,
3518
3559
  amount: params.amountB,
3519
- }, "Insufficient balance for depositing to V3 pool (Asset B): ");
3560
+ }, "Insufficient balance for depositing to V3 pool (Asset B): ", params.useAvailableBalance);
3520
3561
  transferIds.push(assetBTransferId);
3521
3562
  }
3522
3563
  const executeDeposit = async () => {