@flashnet/sdk 0.4.4 → 0.5.1

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 +179 -1
  10. package/dist/cjs/src/client/FlashnetClient.d.ts.map +1 -1
  11. package/dist/cjs/src/client/FlashnetClient.js +477 -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 +412 -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 +179 -1
  40. package/dist/esm/src/client/FlashnetClient.d.ts.map +1 -1
  41. package/dist/esm/src/client/FlashnetClient.js +478 -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 +412 -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,469 @@ ${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.useFreeBalance - If true, use free balance from pool instead of Spark transfers
2737
+ * @param params.retainExcessInBalance - If true, retain any excess amounts in pool free balance instead of refunding via Spark
2738
+ */
2739
+ async increaseLiquidity(params) {
2740
+ await this.ensureInitialized();
2741
+ await this.ensureAmmOperationAllowed("allow_add_liquidity");
2742
+ // Get pool details to know asset addresses
2743
+ const pool = await this.getPool(params.poolId);
2744
+ // Transfer assets to pool (unless using free balance)
2745
+ const lpSparkAddress = encodeSparkAddressNew({
2746
+ identityPublicKey: params.poolId,
2747
+ network: this.sparkNetwork,
2748
+ });
2749
+ let assetATransferId = "";
2750
+ let assetBTransferId = "";
2751
+ const transferIds = [];
2752
+ // Transfer assets if not using free balance
2753
+ if (!params.useFreeBalance) {
2754
+ if (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
+ if (BigInt(params.amountBDesired) > 0n) {
2763
+ assetBTransferId = await this.transferAsset({
2764
+ receiverSparkAddress: lpSparkAddress,
2765
+ assetAddress: pool.assetBAddress,
2766
+ amount: params.amountBDesired,
2767
+ }, "Insufficient balance for adding V3 liquidity (Asset B): ");
2768
+ transferIds.push(assetBTransferId);
2769
+ }
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
+ useFreeBalance: params.useFreeBalance,
2801
+ retainExcessInBalance: params.retainExcessInBalance,
2802
+ nonce,
2803
+ signature: getHexFromUint8Array(signature),
2804
+ };
2805
+ const response = await this.typedApi.increaseLiquidity(request);
2806
+ if (!response.accepted) {
2807
+ const errorMessage = response.error || "Increase liquidity rejected by the AMM";
2808
+ const hasRefund = !!(response.amountARefund || response.amountBRefund);
2809
+ const refundInfo = hasRefund
2810
+ ? ` Refunds: Asset A: ${response.amountARefund || "0"}, Asset B: ${response.amountBRefund || "0"}`
2811
+ : "";
2812
+ throw new FlashnetError(`${errorMessage}.${refundInfo}`, {
2813
+ response: {
2814
+ errorCode: hasRefund ? "FSAG-4203" : "UNKNOWN",
2815
+ errorCategory: hasRefund ? "Business" : "System",
2816
+ message: `${errorMessage}.${refundInfo}`,
2817
+ requestId: response.requestId || "",
2818
+ timestamp: new Date().toISOString(),
2819
+ service: "amm-gateway",
2820
+ severity: "Error",
2821
+ },
2822
+ httpStatus: 400,
2823
+ transferIds: hasRefund ? [] : transferIds,
2824
+ lpIdentityPublicKey: params.poolId,
2825
+ });
2826
+ }
2827
+ return response;
2828
+ };
2829
+ // Execute with auto-clawback if we made transfers
2830
+ if (transferIds.length > 0) {
2831
+ return this.executeWithAutoClawback(executeIncrease, transferIds, params.poolId);
2832
+ }
2833
+ return executeIncrease();
2834
+ }
2835
+ /**
2836
+ * Remove liquidity from a V3 concentrated position
2837
+ *
2838
+ * Decreases liquidity from a specific tick range position.
2839
+ *
2840
+ * @param params Position parameters
2841
+ * @param params.poolId - Pool ID (LP identity public key)
2842
+ * @param params.tickLower - Lower tick of the position
2843
+ * @param params.tickUpper - Upper tick of the position
2844
+ * @param params.liquidityToRemove - Amount of liquidity to remove (use "0" to remove all)
2845
+ * @param params.amountAMin - Minimum amount of asset A to receive (slippage protection)
2846
+ * @param params.amountBMin - Minimum amount of asset B to receive (slippage protection)
2847
+ * @param params.retainInBalance - If true, retain withdrawn assets in pool free balance instead of sending via Spark
2848
+ */
2849
+ async decreaseLiquidity(params) {
2850
+ await this.ensureInitialized();
2851
+ await this.ensureAmmOperationAllowed("allow_withdraw_liquidity");
2852
+ // Generate intent
2853
+ const nonce = generateNonce();
2854
+ const intentMessage = generateDecreaseLiquidityIntentMessage({
2855
+ userPublicKey: this.publicKey,
2856
+ lpIdentityPublicKey: params.poolId,
2857
+ tickLower: params.tickLower,
2858
+ tickUpper: params.tickUpper,
2859
+ liquidityToRemove: params.liquidityToRemove,
2860
+ amountAMin: params.amountAMin,
2861
+ amountBMin: params.amountBMin,
2862
+ nonce,
2863
+ });
2864
+ // Sign intent
2865
+ const messageHash = sha256(intentMessage);
2866
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2867
+ const request = {
2868
+ poolId: params.poolId,
2869
+ tickLower: params.tickLower,
2870
+ tickUpper: params.tickUpper,
2871
+ liquidityToRemove: params.liquidityToRemove,
2872
+ amountAMin: params.amountAMin,
2873
+ amountBMin: params.amountBMin,
2874
+ retainInBalance: params.retainInBalance,
2875
+ nonce,
2876
+ signature: getHexFromUint8Array(signature),
2877
+ };
2878
+ const response = await this.typedApi.decreaseLiquidity(request);
2879
+ if (!response.accepted) {
2880
+ const errorMessage = response.error || "Decrease liquidity rejected by the AMM";
2881
+ throw new Error(errorMessage);
2882
+ }
2883
+ return response;
2884
+ }
2885
+ /**
2886
+ * Collect accumulated fees from a V3 position
2887
+ *
2888
+ * Collects fees earned from trading activity without removing liquidity.
2889
+ *
2890
+ * @param params Position parameters
2891
+ * @param params.poolId - Pool ID (LP identity public key)
2892
+ * @param params.tickLower - Lower tick of the position
2893
+ * @param params.tickUpper - Upper tick of the position
2894
+ * @param params.retainInBalance - If true, retain collected fees in pool free balance instead of sending via Spark
2895
+ */
2896
+ async collectFees(params) {
2897
+ await this.ensureInitialized();
2898
+ await this.ensureAmmOperationAllowed("allow_withdraw_fees");
2899
+ // Generate intent
2900
+ const nonce = generateNonce();
2901
+ const intentMessage = generateCollectFeesIntentMessage({
2902
+ userPublicKey: this.publicKey,
2903
+ lpIdentityPublicKey: params.poolId,
2904
+ tickLower: params.tickLower,
2905
+ tickUpper: params.tickUpper,
2906
+ nonce,
2907
+ });
2908
+ // Sign intent
2909
+ const messageHash = sha256(intentMessage);
2910
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2911
+ const request = {
2912
+ poolId: params.poolId,
2913
+ tickLower: params.tickLower,
2914
+ tickUpper: params.tickUpper,
2915
+ retainInBalance: params.retainInBalance,
2916
+ nonce,
2917
+ signature: getHexFromUint8Array(signature),
2918
+ };
2919
+ const response = await this.typedApi.collectFees(request);
2920
+ if (!response.accepted) {
2921
+ const errorMessage = response.error || "Collect fees rejected by the AMM";
2922
+ throw new Error(errorMessage);
2923
+ }
2924
+ return response;
2925
+ }
2926
+ /**
2927
+ * Rebalance a V3 position to a new tick range
2928
+ *
2929
+ * Atomically moves liquidity from an old position to a new tick range.
2930
+ * Optionally can add additional funds during rebalancing.
2931
+ *
2932
+ * @param params Rebalance parameters
2933
+ * @param params.poolId - Pool ID (LP identity public key)
2934
+ * @param params.oldTickLower - Lower tick of the current position
2935
+ * @param params.oldTickUpper - Upper tick of the current position
2936
+ * @param params.newTickLower - Lower tick for the new position
2937
+ * @param params.newTickUpper - Upper tick for the new position
2938
+ * @param params.liquidityToMove - Amount of liquidity to move (use "0" to move all)
2939
+ * @param params.additionalAmountA - Optional additional asset A to add
2940
+ * @param params.additionalAmountB - Optional additional asset B to add
2941
+ * @param params.retainInBalance - If true, retain any excess amounts in pool free balance instead of sending via Spark
2942
+ */
2943
+ async rebalancePosition(params) {
2944
+ await this.ensureInitialized();
2945
+ await this.ensureAmmOperationAllowed("allow_add_liquidity");
2946
+ // Get pool details
2947
+ const pool = await this.getPool(params.poolId);
2948
+ // Transfer additional assets if provided
2949
+ let assetATransferId;
2950
+ let assetBTransferId;
2951
+ const lpSparkAddress = encodeSparkAddressNew({
2952
+ identityPublicKey: params.poolId,
2953
+ network: this.sparkNetwork,
2954
+ });
2955
+ if (params.additionalAmountA && BigInt(params.additionalAmountA) > 0n) {
2956
+ assetATransferId = await this.transferAsset({
2957
+ receiverSparkAddress: lpSparkAddress,
2958
+ assetAddress: pool.assetAAddress,
2959
+ amount: params.additionalAmountA,
2960
+ }, "Insufficient balance for rebalance (Asset A): ");
2961
+ }
2962
+ if (params.additionalAmountB && BigInt(params.additionalAmountB) > 0n) {
2963
+ assetBTransferId = await this.transferAsset({
2964
+ receiverSparkAddress: lpSparkAddress,
2965
+ assetAddress: pool.assetBAddress,
2966
+ amount: params.additionalAmountB,
2967
+ }, "Insufficient balance for rebalance (Asset B): ");
2968
+ }
2969
+ // Collect transfer IDs for potential clawback
2970
+ const transferIds = [];
2971
+ if (assetATransferId) {
2972
+ transferIds.push(assetATransferId);
2973
+ }
2974
+ if (assetBTransferId) {
2975
+ transferIds.push(assetBTransferId);
2976
+ }
2977
+ // Execute (with auto-clawback if we have transfers)
2978
+ const executeRebalance = async () => {
2979
+ // Generate intent
2980
+ const nonce = generateNonce();
2981
+ const intentMessage = generateRebalancePositionIntentMessage({
2982
+ userPublicKey: this.publicKey,
2983
+ lpIdentityPublicKey: params.poolId,
2984
+ oldTickLower: params.oldTickLower,
2985
+ oldTickUpper: params.oldTickUpper,
2986
+ newTickLower: params.newTickLower,
2987
+ newTickUpper: params.newTickUpper,
2988
+ liquidityToMove: params.liquidityToMove,
2989
+ assetASparkTransferId: assetATransferId,
2990
+ assetBSparkTransferId: assetBTransferId,
2991
+ additionalAmountA: params.additionalAmountA,
2992
+ additionalAmountB: params.additionalAmountB,
2993
+ nonce,
2994
+ });
2995
+ // Sign intent
2996
+ const messageHash = sha256(intentMessage);
2997
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
2998
+ const request = {
2999
+ poolId: params.poolId,
3000
+ oldTickLower: params.oldTickLower,
3001
+ oldTickUpper: params.oldTickUpper,
3002
+ newTickLower: params.newTickLower,
3003
+ newTickUpper: params.newTickUpper,
3004
+ liquidityToMove: params.liquidityToMove,
3005
+ assetASparkTransferId: assetATransferId,
3006
+ assetBSparkTransferId: assetBTransferId,
3007
+ additionalAmountA: params.additionalAmountA,
3008
+ additionalAmountB: params.additionalAmountB,
3009
+ retainInBalance: params.retainInBalance,
3010
+ nonce,
3011
+ signature: getHexFromUint8Array(signature),
3012
+ };
3013
+ const response = await this.typedApi.rebalancePosition(request);
3014
+ if (!response.accepted) {
3015
+ const errorMessage = response.error || "Rebalance position rejected by the AMM";
3016
+ throw new FlashnetError(errorMessage, {
3017
+ response: {
3018
+ errorCode: "UNKNOWN",
3019
+ errorCategory: "System",
3020
+ message: errorMessage,
3021
+ requestId: response.requestId || "",
3022
+ timestamp: new Date().toISOString(),
3023
+ service: "amm-gateway",
3024
+ severity: "Error",
3025
+ },
3026
+ httpStatus: 400,
3027
+ transferIds,
3028
+ lpIdentityPublicKey: params.poolId,
3029
+ });
3030
+ }
3031
+ return response;
3032
+ };
3033
+ // Use auto-clawback if we made transfers
3034
+ if (transferIds.length > 0) {
3035
+ return this.executeWithAutoClawback(executeRebalance, transferIds, params.poolId);
3036
+ }
3037
+ return executeRebalance();
3038
+ }
3039
+ /**
3040
+ * List V3 concentrated liquidity positions
3041
+ *
3042
+ * @param query Optional query parameters
3043
+ * @param query.poolId - Filter by pool ID
3044
+ * @param query.page - Page number (default: 1)
3045
+ * @param query.pageSize - Page size (default: 20, max: 100)
3046
+ */
3047
+ async listConcentratedPositions(query) {
3048
+ await this.ensureInitialized();
3049
+ return this.typedApi.listConcentratedPositions(query);
3050
+ }
3051
+ /**
3052
+ * Get pool liquidity distribution for visualization
3053
+ *
3054
+ * Returns aggregated liquidity ranges for visualizing the liquidity distribution.
3055
+ *
3056
+ * @param poolId - Pool ID (LP identity public key)
3057
+ */
3058
+ async getPoolLiquidity(poolId) {
3059
+ await this.ensureInitialized();
3060
+ return this.typedApi.getPoolLiquidity(poolId);
3061
+ }
3062
+ /**
3063
+ * Get pool ticks for simulation
3064
+ *
3065
+ * Returns all initialized ticks with their liquidity deltas for swap simulation.
3066
+ *
3067
+ * @param poolId - Pool ID (LP identity public key)
3068
+ */
3069
+ async getPoolTicks(poolId) {
3070
+ await this.ensureInitialized();
3071
+ return this.typedApi.getPoolTicks(poolId);
3072
+ }
3073
+ // V3 Free Balance Methods
3074
+ /**
3075
+ * Get user's free balance for a specific V3 pool
3076
+ *
3077
+ * Returns the user's current free balance in the pool, which can be used for
3078
+ * liquidity operations without needing to transfer from the wallet.
3079
+ *
3080
+ * @param poolId - Pool ID (LP identity public key)
3081
+ */
3082
+ async getConcentratedBalance(poolId) {
3083
+ await this.ensureInitialized();
3084
+ return this.typedApi.getConcentratedBalance(poolId);
3085
+ }
3086
+ /**
3087
+ * Get user's free balances across all V3 pools
3088
+ *
3089
+ * Returns all free balances for the authenticated user across all V3 pools.
3090
+ */
3091
+ async getConcentratedBalances() {
3092
+ await this.ensureInitialized();
3093
+ return this.typedApi.getConcentratedBalances();
3094
+ }
3095
+ /**
3096
+ * Withdraw free balance from a V3 pool to user's Spark wallet
3097
+ *
3098
+ * Withdraws accumulated free balance from a pool. Use "0" to skip an asset,
3099
+ * or "max" to withdraw all available balance of that asset.
3100
+ *
3101
+ * @param params Withdrawal parameters
3102
+ * @param params.poolId - Pool ID (LP identity public key)
3103
+ * @param params.amountA - Amount of asset A to withdraw ("0" to skip, "max" to withdraw all)
3104
+ * @param params.amountB - Amount of asset B to withdraw ("0" to skip, "max" to withdraw all)
3105
+ */
3106
+ async withdrawConcentratedBalance(params) {
3107
+ await this.ensureInitialized();
3108
+ // Generate intent
3109
+ const nonce = generateNonce();
3110
+ const intentMessage = generateWithdrawBalanceIntentMessage({
3111
+ userPublicKey: this.publicKey,
3112
+ lpIdentityPublicKey: params.poolId,
3113
+ amountA: params.amountA,
3114
+ amountB: params.amountB,
3115
+ nonce,
3116
+ });
3117
+ // Sign intent
3118
+ const messageHash = sha256(intentMessage);
3119
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
3120
+ const request = {
3121
+ poolId: params.poolId,
3122
+ amountA: params.amountA,
3123
+ amountB: params.amountB,
3124
+ nonce,
3125
+ signature: getHexFromUint8Array(signature),
3126
+ };
3127
+ const response = await this.typedApi.withdrawConcentratedBalance(request);
3128
+ if (!response.accepted) {
3129
+ const errorMessage = response.error || "Withdraw balance rejected by the AMM";
3130
+ throw new Error(errorMessage);
3131
+ }
3132
+ return response;
3133
+ }
2671
3134
  }
2672
3135
 
2673
3136
  export { FlashnetClient };