@flashnet/sdk 0.4.1 → 0.4.3

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.
@@ -4,6 +4,7 @@ import { getClientEnvironmentName, resolveClientNetworkConfig, getClientNetworkC
4
4
  import { getSparkNetworkFromLegacy, getClientEnvironmentFromLegacy, Network } from '../types/index.js';
5
5
  import { generateNonce, compareDecimalStrings } from '../utils/index.js';
6
6
  import { AuthManager } from '../utils/auth.js';
7
+ import sha256 from 'fast-sha256';
7
8
  import { getHexFromUint8Array } from '../utils/hex.js';
8
9
  import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage, generateCreateEscrowIntentMessage, generateFundEscrowIntentMessage, generateClaimEscrowIntentMessage, generateClawbackIntentMessage } from '../utils/intents.js';
9
10
  import { getSparkNetworkFromAddress, encodeSparkAddressNew } from '../utils/spark-address.js';
@@ -364,7 +365,7 @@ class FlashnetClient {
364
365
  nonce,
365
366
  });
366
367
  // Sign intent
367
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
368
+ const messageHash = sha256(intentMessage);
368
369
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
369
370
  // Create pool
370
371
  const request = {
@@ -495,7 +496,7 @@ class FlashnetClient {
495
496
  totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
496
497
  nonce,
497
498
  });
498
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
499
+ const messageHash = sha256(intentMessage);
499
500
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
500
501
  // Create pool
501
502
  const request = {
@@ -551,7 +552,7 @@ class FlashnetClient {
551
552
  assetASparkTransferId,
552
553
  nonce,
553
554
  });
554
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
555
+ const messageHash = sha256(intentMessage);
555
556
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
556
557
  const request = {
557
558
  poolId,
@@ -635,7 +636,7 @@ class FlashnetClient {
635
636
  nonce,
636
637
  });
637
638
  // Sign intent
638
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
639
+ const messageHash = sha256(intentMessage);
639
640
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
640
641
  const request = {
641
642
  userPublicKey: this.publicKey,
@@ -771,7 +772,7 @@ class FlashnetClient {
771
772
  defaultIntegratorFeeRateBps: params.integratorFeeRateBps?.toString(),
772
773
  });
773
774
  // Sign intent
774
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
775
+ const messageHash = sha256(intentMessage);
775
776
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
776
777
  const request = {
777
778
  userPublicKey: this.publicKey,
@@ -870,7 +871,7 @@ class FlashnetClient {
870
871
  nonce,
871
872
  });
872
873
  // Sign intent
873
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
874
+ const messageHash = sha256(intentMessage);
874
875
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
875
876
  const request = {
876
877
  userPublicKey: this.publicKey,
@@ -945,7 +946,7 @@ class FlashnetClient {
945
946
  nonce,
946
947
  });
947
948
  // Sign intent
948
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
949
+ const messageHash = sha256(intentMessage);
949
950
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
950
951
  const request = {
951
952
  userPublicKey: this.publicKey,
@@ -979,7 +980,7 @@ class FlashnetClient {
979
980
  nonce,
980
981
  });
981
982
  // Sign intent
982
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
983
+ const messageHash = sha256(intentMessage);
983
984
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
984
985
  const request = {
985
986
  namespace: params.namespace,
@@ -1027,7 +1028,7 @@ class FlashnetClient {
1027
1028
  nonce,
1028
1029
  });
1029
1030
  // Sign intent
1030
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1031
+ const messageHash = sha256(intentMessage);
1031
1032
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1032
1033
  const request = {
1033
1034
  lpIdentityPublicKey: params.lpIdentityPublicKey,
@@ -1084,7 +1085,7 @@ class FlashnetClient {
1084
1085
  nonce,
1085
1086
  });
1086
1087
  // Sign intent
1087
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1088
+ const messageHash = sha256(intentMessage);
1088
1089
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1089
1090
  const request = {
1090
1091
  integratorPublicKey: this.publicKey,
@@ -1135,7 +1136,7 @@ class FlashnetClient {
1135
1136
  abandonConditions: params.abandonConditions || undefined,
1136
1137
  nonce,
1137
1138
  });
1138
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1139
+ const messageHash = sha256(intentMessage);
1139
1140
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1140
1141
  const request = {
1141
1142
  creatorPublicKey: this.publicKey,
@@ -1203,7 +1204,7 @@ class FlashnetClient {
1203
1204
  nonce,
1204
1205
  });
1205
1206
  // Sign
1206
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1207
+ const messageHash = sha256(intentMessage);
1207
1208
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1208
1209
  // Call API
1209
1210
  const request = {
@@ -1228,7 +1229,7 @@ class FlashnetClient {
1228
1229
  recipientPublicKey: this.publicKey,
1229
1230
  nonce,
1230
1231
  });
1231
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1232
+ const messageHash = sha256(intentMessage);
1232
1233
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1233
1234
  const request = {
1234
1235
  escrowId: params.escrowId,
@@ -1284,7 +1285,7 @@ class FlashnetClient {
1284
1285
  lpIdentityPublicKey: params.lpIdentityPublicKey,
1285
1286
  nonce,
1286
1287
  });
1287
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1288
+ const messageHash = sha256(intentMessage);
1288
1289
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1289
1290
  const request = {
1290
1291
  senderPublicKey: this.publicKey,
@@ -1765,7 +1766,7 @@ class FlashnetClient {
1765
1766
  assetBMinAmountIn: assetBMinAmountIn.toString(),
1766
1767
  nonce,
1767
1768
  });
1768
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1769
+ const messageHash = sha256(intentMessage);
1769
1770
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1770
1771
  const request = {
1771
1772
  userPublicKey: this.publicKey,
@@ -1863,15 +1864,16 @@ class FlashnetClient {
1863
1864
  */
1864
1865
  async payLightningWithToken(options) {
1865
1866
  await this.ensureInitialized();
1866
- const { invoice, tokenAddress, maxSlippageBps = 100, // 1% default
1867
- maxLightningFeeSats, preferSpark = true, integratorFeeRateBps, integratorPublicKey, transferTimeoutMs = 10000, } = options;
1867
+ const { invoice, tokenAddress, maxSlippageBps = 500, // 5% default
1868
+ maxLightningFeeSats, preferSpark = true, integratorFeeRateBps, integratorPublicKey, transferTimeoutMs = 30000, // 30s default
1869
+ rollbackOnFailure = false, useExistingBtcBalance = false, } = options;
1868
1870
  try {
1869
1871
  // Step 1: Get a quote for the payment
1870
1872
  const quote = await this.getPayLightningWithTokenQuote(invoice, tokenAddress, {
1871
1873
  maxSlippageBps,
1872
1874
  integratorFeeRateBps,
1873
1875
  });
1874
- // Step 2: Check balance
1876
+ // Step 2: Check token balance (always required)
1875
1877
  await this.checkBalance({
1876
1878
  balancesToCheck: [
1877
1879
  {
@@ -1881,6 +1883,16 @@ class FlashnetClient {
1881
1883
  ],
1882
1884
  errorPrefix: "Insufficient token balance for Lightning payment: ",
1883
1885
  });
1886
+ // Determine if we can pay immediately using existing BTC balance
1887
+ let canPayImmediately = false;
1888
+ if (useExistingBtcBalance) {
1889
+ const invoiceAmountSats = await this.decodeInvoiceAmount(invoice);
1890
+ const effectiveMaxLightningFee = maxLightningFeeSats ?? quote.estimatedLightningFee;
1891
+ const btcNeededForPayment = invoiceAmountSats + effectiveMaxLightningFee;
1892
+ // Check if we have enough BTC (don't throw if not, just fall back to waiting)
1893
+ const balance = await this.getBalance();
1894
+ canPayImmediately = balance.balance >= BigInt(btcNeededForPayment);
1895
+ }
1884
1896
  // Step 3: Get pool details
1885
1897
  const pool = await this.getPool(quote.poolId);
1886
1898
  // Step 4: Determine swap direction and execute
@@ -1914,39 +1926,88 @@ class FlashnetClient {
1914
1926
  error: swapResponse.error || "Swap was not accepted",
1915
1927
  };
1916
1928
  }
1917
- // Step 5: Wait for the transfer to complete
1918
- const transferComplete = await this.waitForTransferCompletion(swapResponse.outboundTransferId, transferTimeoutMs);
1919
- if (!transferComplete) {
1929
+ // Step 5: Wait for the transfer to complete (unless paying immediately with existing BTC)
1930
+ if (!canPayImmediately) {
1931
+ const transferComplete = await this.waitForTransferCompletion(swapResponse.outboundTransferId, transferTimeoutMs);
1932
+ if (!transferComplete) {
1933
+ return {
1934
+ success: false,
1935
+ poolId: quote.poolId,
1936
+ tokenAmountSpent: quote.tokenAmountRequired,
1937
+ btcAmountReceived: swapResponse.amountOut || "0",
1938
+ swapTransferId: swapResponse.outboundTransferId,
1939
+ ammFeePaid: quote.estimatedAmmFee,
1940
+ error: "Transfer did not complete within timeout",
1941
+ };
1942
+ }
1943
+ }
1944
+ // Step 6: Calculate Lightning fee limit - use the quoted estimate, not a recalculation
1945
+ const effectiveMaxLightningFee = maxLightningFeeSats ?? quote.estimatedLightningFee;
1946
+ // Step 7: Pay the Lightning invoice
1947
+ const btcReceived = swapResponse.amountOut || quote.btcAmountRequired;
1948
+ try {
1949
+ const lightningPayment = await this._wallet.payLightningInvoice({
1950
+ invoice,
1951
+ maxFeeSats: effectiveMaxLightningFee,
1952
+ preferSpark,
1953
+ });
1954
+ return {
1955
+ success: true,
1956
+ poolId: quote.poolId,
1957
+ tokenAmountSpent: quote.tokenAmountRequired,
1958
+ btcAmountReceived: btcReceived,
1959
+ swapTransferId: swapResponse.outboundTransferId,
1960
+ lightningPaymentId: lightningPayment.id,
1961
+ ammFeePaid: quote.estimatedAmmFee,
1962
+ lightningFeePaid: effectiveMaxLightningFee,
1963
+ };
1964
+ }
1965
+ catch (lightningError) {
1966
+ // Lightning payment failed after swap succeeded
1967
+ const lightningErrorMessage = lightningError instanceof Error
1968
+ ? lightningError.message
1969
+ : String(lightningError);
1970
+ // Attempt rollback if requested
1971
+ if (rollbackOnFailure) {
1972
+ try {
1973
+ const rollbackResult = await this.rollbackSwap(quote.poolId, btcReceived, tokenAddress, maxSlippageBps);
1974
+ if (rollbackResult.success) {
1975
+ return {
1976
+ success: false,
1977
+ poolId: quote.poolId,
1978
+ tokenAmountSpent: "0", // Rolled back
1979
+ btcAmountReceived: "0",
1980
+ swapTransferId: swapResponse.outboundTransferId,
1981
+ ammFeePaid: quote.estimatedAmmFee,
1982
+ error: `Lightning payment failed: ${lightningErrorMessage}. Funds rolled back to ${rollbackResult.tokenAmount} tokens.`,
1983
+ };
1984
+ }
1985
+ }
1986
+ catch (rollbackError) {
1987
+ const rollbackErrorMessage = rollbackError instanceof Error
1988
+ ? rollbackError.message
1989
+ : String(rollbackError);
1990
+ return {
1991
+ success: false,
1992
+ poolId: quote.poolId,
1993
+ tokenAmountSpent: quote.tokenAmountRequired,
1994
+ btcAmountReceived: btcReceived,
1995
+ swapTransferId: swapResponse.outboundTransferId,
1996
+ ammFeePaid: quote.estimatedAmmFee,
1997
+ error: `Lightning payment failed: ${lightningErrorMessage}. Rollback also failed: ${rollbackErrorMessage}. BTC remains in wallet.`,
1998
+ };
1999
+ }
2000
+ }
1920
2001
  return {
1921
2002
  success: false,
1922
2003
  poolId: quote.poolId,
1923
2004
  tokenAmountSpent: quote.tokenAmountRequired,
1924
- btcAmountReceived: swapResponse.amountOut || "0",
2005
+ btcAmountReceived: btcReceived,
1925
2006
  swapTransferId: swapResponse.outboundTransferId,
1926
2007
  ammFeePaid: quote.estimatedAmmFee,
1927
- error: "Transfer did not complete within timeout",
2008
+ error: `Lightning payment failed: ${lightningErrorMessage}. BTC (${btcReceived} sats) remains in wallet.`,
1928
2009
  };
1929
2010
  }
1930
- // Step 6: Calculate Lightning fee limit
1931
- const invoiceAmountSats = await this.decodeInvoiceAmount(invoice);
1932
- const effectiveMaxLightningFee = maxLightningFeeSats ??
1933
- Math.max(5, Math.ceil(invoiceAmountSats * 0.0017)); // 17 bps or 5 sats minimum
1934
- // Step 7: Pay the Lightning invoice
1935
- const lightningPayment = await this._wallet.payLightningInvoice({
1936
- invoice,
1937
- maxFeeSats: effectiveMaxLightningFee,
1938
- preferSpark,
1939
- });
1940
- return {
1941
- success: true,
1942
- poolId: quote.poolId,
1943
- tokenAmountSpent: quote.tokenAmountRequired,
1944
- btcAmountReceived: swapResponse.amountOut || quote.btcAmountRequired,
1945
- swapTransferId: swapResponse.outboundTransferId,
1946
- lightningPaymentId: lightningPayment.id,
1947
- ammFeePaid: quote.estimatedAmmFee,
1948
- lightningFeePaid: effectiveMaxLightningFee,
1949
- };
1950
2011
  }
1951
2012
  catch (error) {
1952
2013
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -1961,6 +2022,45 @@ class FlashnetClient {
1961
2022
  };
1962
2023
  }
1963
2024
  }
2025
+ /**
2026
+ * Attempt to rollback a swap by swapping BTC back to the original token
2027
+ * @private
2028
+ */
2029
+ async rollbackSwap(poolId, btcAmount, tokenAddress, maxSlippageBps) {
2030
+ const pool = await this.getPool(poolId);
2031
+ const tokenHex = this.toHexTokenIdentifier(tokenAddress);
2032
+ // Determine swap direction (BTC -> Token)
2033
+ const tokenIsAssetA = pool.assetAAddress === tokenHex;
2034
+ const assetInAddress = tokenIsAssetA
2035
+ ? pool.assetBAddress
2036
+ : pool.assetAAddress; // BTC
2037
+ const assetOutAddress = tokenIsAssetA
2038
+ ? pool.assetAAddress
2039
+ : pool.assetBAddress; // Token
2040
+ // Calculate expected token output and min amount with slippage
2041
+ // For rollback, we accept more slippage since we're recovering from failure
2042
+ const minAmountOut = "0"; // Accept any amount to ensure rollback succeeds
2043
+ // Execute reverse swap
2044
+ const swapResponse = await this.executeSwap({
2045
+ poolId,
2046
+ assetInAddress,
2047
+ assetOutAddress,
2048
+ amountIn: btcAmount,
2049
+ maxSlippageBps: maxSlippageBps * 2, // Double slippage for rollback
2050
+ minAmountOut,
2051
+ });
2052
+ if (!swapResponse.accepted) {
2053
+ throw new Error(swapResponse.error || "Rollback swap not accepted");
2054
+ }
2055
+ // Wait for the rollback transfer
2056
+ if (swapResponse.outboundTransferId) {
2057
+ await this.waitForTransferCompletion(swapResponse.outboundTransferId, 30000);
2058
+ }
2059
+ return {
2060
+ success: true,
2061
+ tokenAmount: swapResponse.amountOut,
2062
+ };
2063
+ }
1964
2064
  /**
1965
2065
  * Find the best pool for swapping a token to BTC
1966
2066
  * @private