@flashnet/sdk 0.4.4 → 0.5.0

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 (61) hide show
  1. package/dist/cjs/index.d.ts +2 -1
  2. package/dist/cjs/index.d.ts.map +1 -1
  3. package/dist/cjs/index.js +17 -0
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/src/api/typed-endpoints.d.ts +70 -0
  6. package/dist/cjs/src/api/typed-endpoints.d.ts.map +1 -1
  7. package/dist/cjs/src/api/typed-endpoints.js +106 -11
  8. package/dist/cjs/src/api/typed-endpoints.js.map +1 -1
  9. package/dist/cjs/src/client/FlashnetClient.d.ts +181 -1
  10. package/dist/cjs/src/client/FlashnetClient.d.ts.map +1 -1
  11. package/dist/cjs/src/client/FlashnetClient.js +478 -14
  12. package/dist/cjs/src/client/FlashnetClient.js.map +1 -1
  13. package/dist/cjs/src/config/index.js +1 -1
  14. package/dist/cjs/src/types/errors.js +11 -11
  15. package/dist/cjs/src/types/index.d.ts +414 -0
  16. package/dist/cjs/src/types/index.d.ts.map +1 -1
  17. package/dist/cjs/src/types/index.js +1 -1
  18. package/dist/cjs/src/utils/index.d.ts +1 -0
  19. package/dist/cjs/src/utils/index.d.ts.map +1 -1
  20. package/dist/cjs/src/utils/index.js.map +1 -1
  21. package/dist/cjs/src/utils/intents.d.ts +91 -0
  22. package/dist/cjs/src/utils/intents.d.ts.map +1 -1
  23. package/dist/cjs/src/utils/intents.js +117 -0
  24. package/dist/cjs/src/utils/intents.js.map +1 -1
  25. package/dist/cjs/src/utils/spark-address.js +2 -2
  26. package/dist/cjs/src/utils/tick-math.d.ts +240 -0
  27. package/dist/cjs/src/utils/tick-math.d.ts.map +1 -0
  28. package/dist/cjs/src/utils/tick-math.js +299 -0
  29. package/dist/cjs/src/utils/tick-math.js.map +1 -0
  30. package/dist/cjs/src/utils/tokenAddress.js +1 -1
  31. package/dist/esm/index.d.ts +2 -1
  32. package/dist/esm/index.d.ts.map +1 -1
  33. package/dist/esm/index.js +2 -1
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/src/api/typed-endpoints.d.ts +70 -0
  36. package/dist/esm/src/api/typed-endpoints.d.ts.map +1 -1
  37. package/dist/esm/src/api/typed-endpoints.js +106 -11
  38. package/dist/esm/src/api/typed-endpoints.js.map +1 -1
  39. package/dist/esm/src/client/FlashnetClient.d.ts +181 -1
  40. package/dist/esm/src/client/FlashnetClient.d.ts.map +1 -1
  41. package/dist/esm/src/client/FlashnetClient.js +479 -15
  42. package/dist/esm/src/client/FlashnetClient.js.map +1 -1
  43. package/dist/esm/src/config/index.js +1 -1
  44. package/dist/esm/src/types/errors.js +11 -11
  45. package/dist/esm/src/types/index.d.ts +414 -0
  46. package/dist/esm/src/types/index.d.ts.map +1 -1
  47. package/dist/esm/src/types/index.js +1 -1
  48. package/dist/esm/src/utils/index.d.ts +1 -0
  49. package/dist/esm/src/utils/index.d.ts.map +1 -1
  50. package/dist/esm/src/utils/index.js.map +1 -1
  51. package/dist/esm/src/utils/intents.d.ts +91 -0
  52. package/dist/esm/src/utils/intents.d.ts.map +1 -1
  53. package/dist/esm/src/utils/intents.js +112 -1
  54. package/dist/esm/src/utils/intents.js.map +1 -1
  55. package/dist/esm/src/utils/spark-address.js +2 -2
  56. package/dist/esm/src/utils/tick-math.d.ts +240 -0
  57. package/dist/esm/src/utils/tick-math.d.ts.map +1 -0
  58. package/dist/esm/src/utils/tick-math.js +287 -0
  59. package/dist/esm/src/utils/tick-math.js.map +1 -0
  60. package/dist/esm/src/utils/tokenAddress.js +1 -1
  61. package/package.json +6 -3
@@ -6,7 +6,7 @@ import { getSparkNetworkFromLegacy, getClientEnvironmentFromLegacy, Network } fr
6
6
  import { generateNonce, compareDecimalStrings } from '../utils/index.js';
7
7
  import { AuthManager } from '../utils/auth.js';
8
8
  import { getHexFromUint8Array } from '../utils/hex.js';
9
- import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage, generateCreateEscrowIntentMessage, generateFundEscrowIntentMessage, generateClaimEscrowIntentMessage, generateClawbackIntentMessage } from '../utils/intents.js';
9
+ import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage, generateCreateEscrowIntentMessage, generateFundEscrowIntentMessage, generateClaimEscrowIntentMessage, generateClawbackIntentMessage, generateCreateConcentratedPoolIntentMessage, generateDecreaseLiquidityIntentMessage, generateCollectFeesIntentMessage, generateWithdrawBalanceIntentMessage, generateIncreaseLiquidityIntentMessage, generateRebalancePositionIntentMessage } from '../utils/intents.js';
10
10
  import { getSparkNetworkFromAddress, encodeSparkAddressNew } from '../utils/spark-address.js';
11
11
  import { encodeSparkHumanReadableTokenIdentifier, decodeSparkHumanReadableTokenIdentifier } from '../utils/tokenAddress.js';
12
12
  import { FlashnetError } from '../types/errors.js';
@@ -299,7 +299,7 @@ class FlashnetClient {
299
299
  }
300
300
  }
301
301
  }
302
- // ===== Pool Operations =====
302
+ // Pool Operations
303
303
  /**
304
304
  * List pools with optional filters
305
305
  */
@@ -562,7 +562,7 @@ class FlashnetClient {
562
562
  };
563
563
  return this.typedApi.confirmInitialDeposit(request);
564
564
  }
565
- // ===== Swap Operations =====
565
+ // Swap Operations
566
566
  /**
567
567
  * Simulate a swap without executing it
568
568
  */
@@ -811,7 +811,7 @@ class FlashnetClient {
811
811
  return response;
812
812
  }, [initialTransferId], firstPoolId);
813
813
  }
814
- // ===== Liquidity Operations =====
814
+ // Liquidity Operations
815
815
  /**
816
816
  * Simulate adding liquidity
817
817
  */
@@ -962,7 +962,7 @@ class FlashnetClient {
962
962
  }
963
963
  return response;
964
964
  }
965
- // ===== Host Operations =====
965
+ // Host Operations
966
966
  /**
967
967
  * Register as a host
968
968
  */
@@ -1108,7 +1108,7 @@ class FlashnetClient {
1108
1108
  await this.ensureInitialized();
1109
1109
  return this.typedApi.getIntegratorFees();
1110
1110
  }
1111
- // ===== Escrow Operations =====
1111
+ // Escrow Operations
1112
1112
  /**
1113
1113
  * Creates a new escrow contract.
1114
1114
  * This is the first step in a two-step process: create, then fund.
@@ -1247,7 +1247,7 @@ class FlashnetClient {
1247
1247
  await this.ensureInitialized();
1248
1248
  return this.typedApi.getEscrow(escrowId);
1249
1249
  }
1250
- // ===== Swap History =====
1250
+ // Swap History
1251
1251
  /**
1252
1252
  * Get swaps for a specific pool
1253
1253
  */
@@ -1270,7 +1270,7 @@ class FlashnetClient {
1270
1270
  const user = userPublicKey || this.publicKey;
1271
1271
  return this.typedApi.getUserSwaps(user, query);
1272
1272
  }
1273
- // ===== Clawback =====
1273
+ // Clawback
1274
1274
  /**
1275
1275
  * Request clawback of a stuck inbound transfer to an LP wallet
1276
1276
  */
@@ -1455,7 +1455,7 @@ class FlashnetClient {
1455
1455
  throw flashnetError;
1456
1456
  }
1457
1457
  }
1458
- // ===== Clawback Monitor =====
1458
+ // Clawback Monitor
1459
1459
  /**
1460
1460
  * Start a background job that periodically polls for clawbackable transfers
1461
1461
  * and automatically claws them back.
@@ -1589,7 +1589,7 @@ class FlashnetClient {
1589
1589
  },
1590
1590
  };
1591
1591
  }
1592
- // ===== Token Address Operations =====
1592
+ // Token Address Operations
1593
1593
  /**
1594
1594
  * Encode a token identifier into a human-readable token address using the client's Spark network
1595
1595
  * @param tokenIdentifier - Token identifier as hex string or Uint8Array
@@ -1624,8 +1624,8 @@ class FlashnetClient {
1624
1624
  decodeLegacyTokenAddress(address) {
1625
1625
  return decodeSparkHumanReadableTokenIdentifier(address, this.sparkNetwork);
1626
1626
  }
1627
- // ===== Status =====
1628
- // ===== Config Inspection =====
1627
+ // Status
1628
+ // Config Inspection
1629
1629
  /**
1630
1630
  * Get raw feature status list (cached briefly)
1631
1631
  */
@@ -1686,7 +1686,7 @@ class FlashnetClient {
1686
1686
  await this.ensureInitialized();
1687
1687
  return this.typedApi.ping();
1688
1688
  }
1689
- // ===== Helper Methods =====
1689
+ // Helper Methods
1690
1690
  /**
1691
1691
  * Performs asset transfer using generalized asset address for both BTC and tokens.
1692
1692
  */
@@ -1786,7 +1786,7 @@ class FlashnetClient {
1786
1786
  throw new Error(errorMessage);
1787
1787
  }
1788
1788
  }
1789
- // ===== Lightning Payment with Token =====
1789
+ // Lightning Payment with Token
1790
1790
  /**
1791
1791
  * Get a quote for paying a Lightning invoice with a token.
1792
1792
  * This calculates the optimal pool and token amount needed.
@@ -2488,7 +2488,7 @@ class FlashnetClient {
2488
2488
  async cleanup() {
2489
2489
  await this._wallet.cleanupConnections();
2490
2490
  }
2491
- // ===== Config and Policy Enforcement Helpers =====
2491
+ // Config and Policy Enforcement Helpers
2492
2492
  async ensureAmmOperationAllowed(requiredFeature) {
2493
2493
  await this.ensurePingOk();
2494
2494
  const featureMap = await this.getFeatureStatusMap();
@@ -2668,6 +2668,470 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
2668
2668
  throw new Error(`Asset B is not allowed for pool creation: ${assetBHex}`);
2669
2669
  }
2670
2670
  }
2671
+ // V3 Concentrated Liquidity Operations
2672
+ /**
2673
+ * Create a V3 concentrated liquidity pool
2674
+ *
2675
+ * Concentrated liquidity pools allow LPs to provide liquidity within specific
2676
+ * price ranges (tick ranges) for higher capital efficiency.
2677
+ *
2678
+ * @param params Pool creation parameters
2679
+ * @param params.assetAAddress - Address of asset A (base asset)
2680
+ * @param params.assetBAddress - Address of asset B (quote asset)
2681
+ * @param params.tickSpacing - Tick spacing (common values: 10, 60, 200)
2682
+ * @param params.initialPrice - Initial price of asset A in terms of asset B
2683
+ * @param params.lpFeeRateBps - LP fee rate in basis points
2684
+ * @param params.hostFeeRateBps - Host fee rate in basis points
2685
+ * @param params.hostNamespace - Optional host namespace
2686
+ * @param params.poolOwnerPublicKey - Optional pool owner (defaults to wallet pubkey)
2687
+ */
2688
+ async createConcentratedPool(params) {
2689
+ await this.ensureInitialized();
2690
+ await this.ensureAmmOperationAllowed("allow_pool_creation");
2691
+ await this.assertAllowedAssetBForPoolCreation(this.toHexTokenIdentifier(params.assetBAddress));
2692
+ const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
2693
+ // Generate intent
2694
+ const nonce = generateNonce();
2695
+ const intentMessage = generateCreateConcentratedPoolIntentMessage({
2696
+ poolOwnerPublicKey,
2697
+ assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
2698
+ assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
2699
+ tickSpacing: params.tickSpacing,
2700
+ initialPrice: params.initialPrice,
2701
+ lpFeeRateBps: params.lpFeeRateBps.toString(),
2702
+ hostFeeRateBps: params.hostFeeRateBps.toString(),
2703
+ nonce,
2704
+ });
2705
+ // Sign intent
2706
+ const messageHash = sha256(intentMessage);
2707
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2708
+ const request = {
2709
+ poolOwnerPublicKey,
2710
+ assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
2711
+ assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
2712
+ tickSpacing: params.tickSpacing,
2713
+ initialPrice: params.initialPrice,
2714
+ lpFeeRateBps: params.lpFeeRateBps.toString(),
2715
+ hostFeeRateBps: params.hostFeeRateBps.toString(),
2716
+ hostNamespace: params.hostNamespace,
2717
+ nonce,
2718
+ signature: getHexFromUint8Array(signature),
2719
+ };
2720
+ return this.typedApi.createConcentratedPool(request);
2721
+ }
2722
+ /**
2723
+ * Add liquidity to a V3 concentrated position
2724
+ *
2725
+ * Increases liquidity within a specific tick range. If the position doesn't exist,
2726
+ * a new position is created.
2727
+ *
2728
+ * @param params Position parameters
2729
+ * @param params.poolId - Pool ID (LP identity public key)
2730
+ * @param params.tickLower - Lower tick of the position
2731
+ * @param params.tickUpper - Upper tick of the position
2732
+ * @param params.amountADesired - Desired amount of asset A to add
2733
+ * @param params.amountBDesired - Desired amount of asset B to add
2734
+ * @param params.amountAMin - Minimum amount of asset A (slippage protection)
2735
+ * @param params.amountBMin - Minimum amount of asset B (slippage protection)
2736
+ * @param params.useFreeBalanceA - If true, use free balance from pool for asset A instead of Spark transfer
2737
+ * @param params.useFreeBalanceB - If true, use free balance from pool for asset B instead of Spark transfer
2738
+ * @param params.retainExcessInBalance - If true, retain any excess amounts in pool free balance instead of refunding via Spark
2739
+ */
2740
+ async increaseLiquidity(params) {
2741
+ await this.ensureInitialized();
2742
+ await this.ensureAmmOperationAllowed("allow_add_liquidity");
2743
+ // Get pool details to know asset addresses
2744
+ const pool = await this.getPool(params.poolId);
2745
+ // Transfer assets to pool (unless using free balance)
2746
+ const lpSparkAddress = encodeSparkAddressNew({
2747
+ identityPublicKey: params.poolId,
2748
+ network: this.sparkNetwork,
2749
+ });
2750
+ let assetATransferId = "";
2751
+ let assetBTransferId = "";
2752
+ const transferIds = [];
2753
+ // Transfer asset A if not using free balance
2754
+ if (!params.useFreeBalanceA && BigInt(params.amountADesired) > 0n) {
2755
+ assetATransferId = await this.transferAsset({
2756
+ receiverSparkAddress: lpSparkAddress,
2757
+ assetAddress: pool.assetAAddress,
2758
+ amount: params.amountADesired,
2759
+ }, "Insufficient balance for adding V3 liquidity (Asset A): ");
2760
+ transferIds.push(assetATransferId);
2761
+ }
2762
+ // Transfer asset B if not using free balance
2763
+ if (!params.useFreeBalanceB && BigInt(params.amountBDesired) > 0n) {
2764
+ assetBTransferId = await this.transferAsset({
2765
+ receiverSparkAddress: lpSparkAddress,
2766
+ assetAddress: pool.assetBAddress,
2767
+ amount: params.amountBDesired,
2768
+ }, "Insufficient balance for adding V3 liquidity (Asset B): ");
2769
+ transferIds.push(assetBTransferId);
2770
+ }
2771
+ const executeIncrease = async () => {
2772
+ // Generate intent
2773
+ const nonce = generateNonce();
2774
+ const intentMessage = generateIncreaseLiquidityIntentMessage({
2775
+ userPublicKey: this.publicKey,
2776
+ lpIdentityPublicKey: params.poolId,
2777
+ tickLower: params.tickLower,
2778
+ tickUpper: params.tickUpper,
2779
+ assetASparkTransferId: assetATransferId,
2780
+ assetBSparkTransferId: assetBTransferId,
2781
+ amountADesired: params.amountADesired,
2782
+ amountBDesired: params.amountBDesired,
2783
+ amountAMin: params.amountAMin,
2784
+ amountBMin: params.amountBMin,
2785
+ nonce,
2786
+ });
2787
+ // Sign intent
2788
+ const messageHash = sha256(intentMessage);
2789
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2790
+ const request = {
2791
+ poolId: params.poolId,
2792
+ tickLower: params.tickLower,
2793
+ tickUpper: params.tickUpper,
2794
+ assetASparkTransferId: assetATransferId,
2795
+ assetBSparkTransferId: assetBTransferId,
2796
+ amountADesired: params.amountADesired,
2797
+ amountBDesired: params.amountBDesired,
2798
+ amountAMin: params.amountAMin,
2799
+ amountBMin: params.amountBMin,
2800
+ useFreeBalanceA: params.useFreeBalanceA,
2801
+ useFreeBalanceB: params.useFreeBalanceB,
2802
+ retainExcessInBalance: params.retainExcessInBalance,
2803
+ nonce,
2804
+ signature: getHexFromUint8Array(signature),
2805
+ };
2806
+ const response = await this.typedApi.increaseLiquidity(request);
2807
+ if (!response.accepted) {
2808
+ const errorMessage = response.error || "Increase liquidity rejected by the AMM";
2809
+ const hasRefund = !!(response.amountARefund || response.amountBRefund);
2810
+ const refundInfo = hasRefund
2811
+ ? ` Refunds: Asset A: ${response.amountARefund || "0"}, Asset B: ${response.amountBRefund || "0"}`
2812
+ : "";
2813
+ throw new FlashnetError(`${errorMessage}.${refundInfo}`, {
2814
+ response: {
2815
+ errorCode: hasRefund ? "FSAG-4203" : "UNKNOWN",
2816
+ errorCategory: hasRefund ? "Business" : "System",
2817
+ message: `${errorMessage}.${refundInfo}`,
2818
+ requestId: response.requestId || "",
2819
+ timestamp: new Date().toISOString(),
2820
+ service: "amm-gateway",
2821
+ severity: "Error",
2822
+ },
2823
+ httpStatus: 400,
2824
+ transferIds: hasRefund ? [] : transferIds,
2825
+ lpIdentityPublicKey: params.poolId,
2826
+ });
2827
+ }
2828
+ return response;
2829
+ };
2830
+ // Execute with auto-clawback if we made transfers
2831
+ if (transferIds.length > 0) {
2832
+ return this.executeWithAutoClawback(executeIncrease, transferIds, params.poolId);
2833
+ }
2834
+ return executeIncrease();
2835
+ }
2836
+ /**
2837
+ * Remove liquidity from a V3 concentrated position
2838
+ *
2839
+ * Decreases liquidity from a specific tick range position.
2840
+ *
2841
+ * @param params Position parameters
2842
+ * @param params.poolId - Pool ID (LP identity public key)
2843
+ * @param params.tickLower - Lower tick of the position
2844
+ * @param params.tickUpper - Upper tick of the position
2845
+ * @param params.liquidityToRemove - Amount of liquidity to remove (use "0" to remove all)
2846
+ * @param params.amountAMin - Minimum amount of asset A to receive (slippage protection)
2847
+ * @param params.amountBMin - Minimum amount of asset B to receive (slippage protection)
2848
+ * @param params.retainInBalance - If true, retain withdrawn assets in pool free balance instead of sending via Spark
2849
+ */
2850
+ async decreaseLiquidity(params) {
2851
+ await this.ensureInitialized();
2852
+ await this.ensureAmmOperationAllowed("allow_withdraw_liquidity");
2853
+ // Generate intent
2854
+ const nonce = generateNonce();
2855
+ const intentMessage = generateDecreaseLiquidityIntentMessage({
2856
+ userPublicKey: this.publicKey,
2857
+ lpIdentityPublicKey: params.poolId,
2858
+ tickLower: params.tickLower,
2859
+ tickUpper: params.tickUpper,
2860
+ liquidityToRemove: params.liquidityToRemove,
2861
+ amountAMin: params.amountAMin,
2862
+ amountBMin: params.amountBMin,
2863
+ nonce,
2864
+ });
2865
+ // Sign intent
2866
+ const messageHash = sha256(intentMessage);
2867
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2868
+ const request = {
2869
+ poolId: params.poolId,
2870
+ tickLower: params.tickLower,
2871
+ tickUpper: params.tickUpper,
2872
+ liquidityToRemove: params.liquidityToRemove,
2873
+ amountAMin: params.amountAMin,
2874
+ amountBMin: params.amountBMin,
2875
+ retainInBalance: params.retainInBalance,
2876
+ nonce,
2877
+ signature: getHexFromUint8Array(signature),
2878
+ };
2879
+ const response = await this.typedApi.decreaseLiquidity(request);
2880
+ if (!response.accepted) {
2881
+ const errorMessage = response.error || "Decrease liquidity rejected by the AMM";
2882
+ throw new Error(errorMessage);
2883
+ }
2884
+ return response;
2885
+ }
2886
+ /**
2887
+ * Collect accumulated fees from a V3 position
2888
+ *
2889
+ * Collects fees earned from trading activity without removing liquidity.
2890
+ *
2891
+ * @param params Position parameters
2892
+ * @param params.poolId - Pool ID (LP identity public key)
2893
+ * @param params.tickLower - Lower tick of the position
2894
+ * @param params.tickUpper - Upper tick of the position
2895
+ * @param params.retainInBalance - If true, retain collected fees in pool free balance instead of sending via Spark
2896
+ */
2897
+ async collectFees(params) {
2898
+ await this.ensureInitialized();
2899
+ await this.ensureAmmOperationAllowed("allow_withdraw_fees");
2900
+ // Generate intent
2901
+ const nonce = generateNonce();
2902
+ const intentMessage = generateCollectFeesIntentMessage({
2903
+ userPublicKey: this.publicKey,
2904
+ lpIdentityPublicKey: params.poolId,
2905
+ tickLower: params.tickLower,
2906
+ tickUpper: params.tickUpper,
2907
+ nonce,
2908
+ });
2909
+ // Sign intent
2910
+ const messageHash = sha256(intentMessage);
2911
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2912
+ const request = {
2913
+ poolId: params.poolId,
2914
+ tickLower: params.tickLower,
2915
+ tickUpper: params.tickUpper,
2916
+ retainInBalance: params.retainInBalance,
2917
+ nonce,
2918
+ signature: getHexFromUint8Array(signature),
2919
+ };
2920
+ const response = await this.typedApi.collectFees(request);
2921
+ if (!response.accepted) {
2922
+ const errorMessage = response.error || "Collect fees rejected by the AMM";
2923
+ throw new Error(errorMessage);
2924
+ }
2925
+ return response;
2926
+ }
2927
+ /**
2928
+ * Rebalance a V3 position to a new tick range
2929
+ *
2930
+ * Atomically moves liquidity from an old position to a new tick range.
2931
+ * Optionally can add additional funds during rebalancing.
2932
+ *
2933
+ * @param params Rebalance parameters
2934
+ * @param params.poolId - Pool ID (LP identity public key)
2935
+ * @param params.oldTickLower - Lower tick of the current position
2936
+ * @param params.oldTickUpper - Upper tick of the current position
2937
+ * @param params.newTickLower - Lower tick for the new position
2938
+ * @param params.newTickUpper - Upper tick for the new position
2939
+ * @param params.liquidityToMove - Amount of liquidity to move (use "0" to move all)
2940
+ * @param params.additionalAmountA - Optional additional asset A to add
2941
+ * @param params.additionalAmountB - Optional additional asset B to add
2942
+ * @param params.retainInBalance - If true, retain any excess amounts in pool free balance instead of sending via Spark
2943
+ */
2944
+ async rebalancePosition(params) {
2945
+ await this.ensureInitialized();
2946
+ await this.ensureAmmOperationAllowed("allow_add_liquidity");
2947
+ // Get pool details
2948
+ const pool = await this.getPool(params.poolId);
2949
+ // Transfer additional assets if provided
2950
+ let assetATransferId;
2951
+ let assetBTransferId;
2952
+ const lpSparkAddress = encodeSparkAddressNew({
2953
+ identityPublicKey: params.poolId,
2954
+ network: this.sparkNetwork,
2955
+ });
2956
+ if (params.additionalAmountA && BigInt(params.additionalAmountA) > 0n) {
2957
+ assetATransferId = await this.transferAsset({
2958
+ receiverSparkAddress: lpSparkAddress,
2959
+ assetAddress: pool.assetAAddress,
2960
+ amount: params.additionalAmountA,
2961
+ }, "Insufficient balance for rebalance (Asset A): ");
2962
+ }
2963
+ if (params.additionalAmountB && BigInt(params.additionalAmountB) > 0n) {
2964
+ assetBTransferId = await this.transferAsset({
2965
+ receiverSparkAddress: lpSparkAddress,
2966
+ assetAddress: pool.assetBAddress,
2967
+ amount: params.additionalAmountB,
2968
+ }, "Insufficient balance for rebalance (Asset B): ");
2969
+ }
2970
+ // Collect transfer IDs for potential clawback
2971
+ const transferIds = [];
2972
+ if (assetATransferId) {
2973
+ transferIds.push(assetATransferId);
2974
+ }
2975
+ if (assetBTransferId) {
2976
+ transferIds.push(assetBTransferId);
2977
+ }
2978
+ // Execute (with auto-clawback if we have transfers)
2979
+ const executeRebalance = async () => {
2980
+ // Generate intent
2981
+ const nonce = generateNonce();
2982
+ const intentMessage = generateRebalancePositionIntentMessage({
2983
+ userPublicKey: this.publicKey,
2984
+ lpIdentityPublicKey: params.poolId,
2985
+ oldTickLower: params.oldTickLower,
2986
+ oldTickUpper: params.oldTickUpper,
2987
+ newTickLower: params.newTickLower,
2988
+ newTickUpper: params.newTickUpper,
2989
+ liquidityToMove: params.liquidityToMove,
2990
+ assetASparkTransferId: assetATransferId,
2991
+ assetBSparkTransferId: assetBTransferId,
2992
+ additionalAmountA: params.additionalAmountA,
2993
+ additionalAmountB: params.additionalAmountB,
2994
+ nonce,
2995
+ });
2996
+ // Sign intent
2997
+ const messageHash = sha256(intentMessage);
2998
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2999
+ const request = {
3000
+ poolId: params.poolId,
3001
+ oldTickLower: params.oldTickLower,
3002
+ oldTickUpper: params.oldTickUpper,
3003
+ newTickLower: params.newTickLower,
3004
+ newTickUpper: params.newTickUpper,
3005
+ liquidityToMove: params.liquidityToMove,
3006
+ assetASparkTransferId: assetATransferId,
3007
+ assetBSparkTransferId: assetBTransferId,
3008
+ additionalAmountA: params.additionalAmountA,
3009
+ additionalAmountB: params.additionalAmountB,
3010
+ retainInBalance: params.retainInBalance,
3011
+ nonce,
3012
+ signature: getHexFromUint8Array(signature),
3013
+ };
3014
+ const response = await this.typedApi.rebalancePosition(request);
3015
+ if (!response.accepted) {
3016
+ const errorMessage = response.error || "Rebalance position rejected by the AMM";
3017
+ throw new FlashnetError(errorMessage, {
3018
+ response: {
3019
+ errorCode: "UNKNOWN",
3020
+ errorCategory: "System",
3021
+ message: errorMessage,
3022
+ requestId: response.requestId || "",
3023
+ timestamp: new Date().toISOString(),
3024
+ service: "amm-gateway",
3025
+ severity: "Error",
3026
+ },
3027
+ httpStatus: 400,
3028
+ transferIds,
3029
+ lpIdentityPublicKey: params.poolId,
3030
+ });
3031
+ }
3032
+ return response;
3033
+ };
3034
+ // Use auto-clawback if we made transfers
3035
+ if (transferIds.length > 0) {
3036
+ return this.executeWithAutoClawback(executeRebalance, transferIds, params.poolId);
3037
+ }
3038
+ return executeRebalance();
3039
+ }
3040
+ /**
3041
+ * List V3 concentrated liquidity positions
3042
+ *
3043
+ * @param query Optional query parameters
3044
+ * @param query.poolId - Filter by pool ID
3045
+ * @param query.page - Page number (default: 1)
3046
+ * @param query.pageSize - Page size (default: 20, max: 100)
3047
+ */
3048
+ async listConcentratedPositions(query) {
3049
+ await this.ensureInitialized();
3050
+ return this.typedApi.listConcentratedPositions(query);
3051
+ }
3052
+ /**
3053
+ * Get pool liquidity distribution for visualization
3054
+ *
3055
+ * Returns aggregated liquidity ranges for visualizing the liquidity distribution.
3056
+ *
3057
+ * @param poolId - Pool ID (LP identity public key)
3058
+ */
3059
+ async getPoolLiquidity(poolId) {
3060
+ await this.ensureInitialized();
3061
+ return this.typedApi.getPoolLiquidity(poolId);
3062
+ }
3063
+ /**
3064
+ * Get pool ticks for simulation
3065
+ *
3066
+ * Returns all initialized ticks with their liquidity deltas for swap simulation.
3067
+ *
3068
+ * @param poolId - Pool ID (LP identity public key)
3069
+ */
3070
+ async getPoolTicks(poolId) {
3071
+ await this.ensureInitialized();
3072
+ return this.typedApi.getPoolTicks(poolId);
3073
+ }
3074
+ // V3 Free Balance Methods
3075
+ /**
3076
+ * Get user's free balance for a specific V3 pool
3077
+ *
3078
+ * Returns the user's current free balance in the pool, which can be used for
3079
+ * liquidity operations without needing to transfer from the wallet.
3080
+ *
3081
+ * @param poolId - Pool ID (LP identity public key)
3082
+ */
3083
+ async getConcentratedBalance(poolId) {
3084
+ await this.ensureInitialized();
3085
+ return this.typedApi.getConcentratedBalance(poolId);
3086
+ }
3087
+ /**
3088
+ * Get user's free balances across all V3 pools
3089
+ *
3090
+ * Returns all free balances for the authenticated user across all V3 pools.
3091
+ */
3092
+ async getConcentratedBalances() {
3093
+ await this.ensureInitialized();
3094
+ return this.typedApi.getConcentratedBalances();
3095
+ }
3096
+ /**
3097
+ * Withdraw free balance from a V3 pool to user's Spark wallet
3098
+ *
3099
+ * Withdraws accumulated free balance from a pool. Use "0" to skip an asset,
3100
+ * or "max" to withdraw all available balance of that asset.
3101
+ *
3102
+ * @param params Withdrawal parameters
3103
+ * @param params.poolId - Pool ID (LP identity public key)
3104
+ * @param params.amountA - Amount of asset A to withdraw ("0" to skip, "max" to withdraw all)
3105
+ * @param params.amountB - Amount of asset B to withdraw ("0" to skip, "max" to withdraw all)
3106
+ */
3107
+ async withdrawConcentratedBalance(params) {
3108
+ await this.ensureInitialized();
3109
+ // Generate intent
3110
+ const nonce = generateNonce();
3111
+ const intentMessage = generateWithdrawBalanceIntentMessage({
3112
+ userPublicKey: this.publicKey,
3113
+ lpIdentityPublicKey: params.poolId,
3114
+ amountA: params.amountA,
3115
+ amountB: params.amountB,
3116
+ nonce,
3117
+ });
3118
+ // Sign intent
3119
+ const messageHash = sha256(intentMessage);
3120
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
3121
+ const request = {
3122
+ poolId: params.poolId,
3123
+ amountA: params.amountA,
3124
+ amountB: params.amountB,
3125
+ nonce,
3126
+ signature: getHexFromUint8Array(signature),
3127
+ };
3128
+ const response = await this.typedApi.withdrawConcentratedBalance(request);
3129
+ if (!response.accepted) {
3130
+ const errorMessage = response.error || "Withdraw balance rejected by the AMM";
3131
+ throw new Error(errorMessage);
3132
+ }
3133
+ return response;
3134
+ }
2671
3135
  }
2672
3136
 
2673
3137
  export { FlashnetClient };