@flashnet/sdk 0.4.3 → 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.
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +17 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/src/api/typed-endpoints.d.ts +70 -0
- package/dist/cjs/src/api/typed-endpoints.d.ts.map +1 -1
- package/dist/cjs/src/api/typed-endpoints.js +106 -11
- package/dist/cjs/src/api/typed-endpoints.js.map +1 -1
- package/dist/cjs/src/client/FlashnetClient.d.ts +182 -3
- package/dist/cjs/src/client/FlashnetClient.d.ts.map +1 -1
- package/dist/cjs/src/client/FlashnetClient.js +608 -31
- package/dist/cjs/src/client/FlashnetClient.js.map +1 -1
- package/dist/cjs/src/config/index.js +1 -1
- package/dist/cjs/src/types/errors.js +11 -11
- package/dist/cjs/src/types/index.d.ts +414 -0
- package/dist/cjs/src/types/index.d.ts.map +1 -1
- package/dist/cjs/src/types/index.js +1 -1
- package/dist/cjs/src/utils/index.d.ts +1 -0
- package/dist/cjs/src/utils/index.d.ts.map +1 -1
- package/dist/cjs/src/utils/index.js.map +1 -1
- package/dist/cjs/src/utils/intents.d.ts +91 -0
- package/dist/cjs/src/utils/intents.d.ts.map +1 -1
- package/dist/cjs/src/utils/intents.js +117 -0
- package/dist/cjs/src/utils/intents.js.map +1 -1
- package/dist/cjs/src/utils/spark-address.js +2 -2
- package/dist/cjs/src/utils/tick-math.d.ts +240 -0
- package/dist/cjs/src/utils/tick-math.d.ts.map +1 -0
- package/dist/cjs/src/utils/tick-math.js +299 -0
- package/dist/cjs/src/utils/tick-math.js.map +1 -0
- package/dist/cjs/src/utils/tokenAddress.js +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/api/typed-endpoints.d.ts +70 -0
- package/dist/esm/src/api/typed-endpoints.d.ts.map +1 -1
- package/dist/esm/src/api/typed-endpoints.js +106 -11
- package/dist/esm/src/api/typed-endpoints.js.map +1 -1
- package/dist/esm/src/client/FlashnetClient.d.ts +182 -3
- package/dist/esm/src/client/FlashnetClient.d.ts.map +1 -1
- package/dist/esm/src/client/FlashnetClient.js +609 -32
- package/dist/esm/src/client/FlashnetClient.js.map +1 -1
- package/dist/esm/src/config/index.js +1 -1
- package/dist/esm/src/types/errors.js +11 -11
- package/dist/esm/src/types/index.d.ts +414 -0
- package/dist/esm/src/types/index.d.ts.map +1 -1
- package/dist/esm/src/types/index.js +1 -1
- package/dist/esm/src/utils/index.d.ts +1 -0
- package/dist/esm/src/utils/index.d.ts.map +1 -1
- package/dist/esm/src/utils/index.js.map +1 -1
- package/dist/esm/src/utils/intents.d.ts +91 -0
- package/dist/esm/src/utils/intents.d.ts.map +1 -1
- package/dist/esm/src/utils/intents.js +112 -1
- package/dist/esm/src/utils/intents.js.map +1 -1
- package/dist/esm/src/utils/spark-address.js +2 -2
- package/dist/esm/src/utils/tick-math.d.ts +240 -0
- package/dist/esm/src/utils/tick-math.d.ts.map +1 -0
- package/dist/esm/src/utils/tick-math.js +287 -0
- package/dist/esm/src/utils/tick-math.js.map +1 -0
- package/dist/esm/src/utils/tokenAddress.js +1 -1
- package/package.json +6 -3
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import sha256 from 'fast-sha256';
|
|
1
2
|
import { ApiClient } from '../api/client.js';
|
|
2
3
|
import { TypedAmmApi } from '../api/typed-endpoints.js';
|
|
3
4
|
import { getClientEnvironmentName, resolveClientNetworkConfig, getClientNetworkConfig, BTC_ASSET_PUBKEY } from '../config/index.js';
|
|
4
5
|
import { getSparkNetworkFromLegacy, getClientEnvironmentFromLegacy, Network } from '../types/index.js';
|
|
5
6
|
import { generateNonce, compareDecimalStrings } from '../utils/index.js';
|
|
6
7
|
import { AuthManager } from '../utils/auth.js';
|
|
7
|
-
import sha256 from 'fast-sha256';
|
|
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';
|
|
@@ -214,8 +214,7 @@ class FlashnetClient {
|
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
216
216
|
* Convert a token identifier into the raw hex string form expected by the Flashnet backend.
|
|
217
|
-
*
|
|
218
|
-
* If it is in Bech32m human-readable form, it is decoded to hex.
|
|
217
|
+
* Handles BTC constant, hex strings, and Bech32m human-readable format.
|
|
219
218
|
*/
|
|
220
219
|
toHexTokenIdentifier(tokenIdentifier) {
|
|
221
220
|
if (tokenIdentifier === BTC_ASSET_PUBKEY) {
|
|
@@ -300,7 +299,7 @@ class FlashnetClient {
|
|
|
300
299
|
}
|
|
301
300
|
}
|
|
302
301
|
}
|
|
303
|
-
//
|
|
302
|
+
// Pool Operations
|
|
304
303
|
/**
|
|
305
304
|
* List pools with optional filters
|
|
306
305
|
*/
|
|
@@ -563,7 +562,7 @@ class FlashnetClient {
|
|
|
563
562
|
};
|
|
564
563
|
return this.typedApi.confirmInitialDeposit(request);
|
|
565
564
|
}
|
|
566
|
-
//
|
|
565
|
+
// Swap Operations
|
|
567
566
|
/**
|
|
568
567
|
* Simulate a swap without executing it
|
|
569
568
|
*/
|
|
@@ -812,7 +811,7 @@ class FlashnetClient {
|
|
|
812
811
|
return response;
|
|
813
812
|
}, [initialTransferId], firstPoolId);
|
|
814
813
|
}
|
|
815
|
-
//
|
|
814
|
+
// Liquidity Operations
|
|
816
815
|
/**
|
|
817
816
|
* Simulate adding liquidity
|
|
818
817
|
*/
|
|
@@ -963,7 +962,7 @@ class FlashnetClient {
|
|
|
963
962
|
}
|
|
964
963
|
return response;
|
|
965
964
|
}
|
|
966
|
-
//
|
|
965
|
+
// Host Operations
|
|
967
966
|
/**
|
|
968
967
|
* Register as a host
|
|
969
968
|
*/
|
|
@@ -1109,7 +1108,7 @@ class FlashnetClient {
|
|
|
1109
1108
|
await this.ensureInitialized();
|
|
1110
1109
|
return this.typedApi.getIntegratorFees();
|
|
1111
1110
|
}
|
|
1112
|
-
//
|
|
1111
|
+
// Escrow Operations
|
|
1113
1112
|
/**
|
|
1114
1113
|
* Creates a new escrow contract.
|
|
1115
1114
|
* This is the first step in a two-step process: create, then fund.
|
|
@@ -1248,7 +1247,7 @@ class FlashnetClient {
|
|
|
1248
1247
|
await this.ensureInitialized();
|
|
1249
1248
|
return this.typedApi.getEscrow(escrowId);
|
|
1250
1249
|
}
|
|
1251
|
-
//
|
|
1250
|
+
// Swap History
|
|
1252
1251
|
/**
|
|
1253
1252
|
* Get swaps for a specific pool
|
|
1254
1253
|
*/
|
|
@@ -1271,7 +1270,7 @@ class FlashnetClient {
|
|
|
1271
1270
|
const user = userPublicKey || this.publicKey;
|
|
1272
1271
|
return this.typedApi.getUserSwaps(user, query);
|
|
1273
1272
|
}
|
|
1274
|
-
//
|
|
1273
|
+
// Clawback
|
|
1275
1274
|
/**
|
|
1276
1275
|
* Request clawback of a stuck inbound transfer to an LP wallet
|
|
1277
1276
|
*/
|
|
@@ -1456,7 +1455,7 @@ class FlashnetClient {
|
|
|
1456
1455
|
throw flashnetError;
|
|
1457
1456
|
}
|
|
1458
1457
|
}
|
|
1459
|
-
//
|
|
1458
|
+
// Clawback Monitor
|
|
1460
1459
|
/**
|
|
1461
1460
|
* Start a background job that periodically polls for clawbackable transfers
|
|
1462
1461
|
* and automatically claws them back.
|
|
@@ -1590,7 +1589,7 @@ class FlashnetClient {
|
|
|
1590
1589
|
},
|
|
1591
1590
|
};
|
|
1592
1591
|
}
|
|
1593
|
-
//
|
|
1592
|
+
// Token Address Operations
|
|
1594
1593
|
/**
|
|
1595
1594
|
* Encode a token identifier into a human-readable token address using the client's Spark network
|
|
1596
1595
|
* @param tokenIdentifier - Token identifier as hex string or Uint8Array
|
|
@@ -1625,8 +1624,8 @@ class FlashnetClient {
|
|
|
1625
1624
|
decodeLegacyTokenAddress(address) {
|
|
1626
1625
|
return decodeSparkHumanReadableTokenIdentifier(address, this.sparkNetwork);
|
|
1627
1626
|
}
|
|
1628
|
-
//
|
|
1629
|
-
//
|
|
1627
|
+
// Status
|
|
1628
|
+
// Config Inspection
|
|
1630
1629
|
/**
|
|
1631
1630
|
* Get raw feature status list (cached briefly)
|
|
1632
1631
|
*/
|
|
@@ -1687,7 +1686,7 @@ class FlashnetClient {
|
|
|
1687
1686
|
await this.ensureInitialized();
|
|
1688
1687
|
return this.typedApi.ping();
|
|
1689
1688
|
}
|
|
1690
|
-
//
|
|
1689
|
+
// Helper Methods
|
|
1691
1690
|
/**
|
|
1692
1691
|
* Performs asset transfer using generalized asset address for both BTC and tokens.
|
|
1693
1692
|
*/
|
|
@@ -1787,7 +1786,7 @@ class FlashnetClient {
|
|
|
1787
1786
|
throw new Error(errorMessage);
|
|
1788
1787
|
}
|
|
1789
1788
|
}
|
|
1790
|
-
//
|
|
1789
|
+
// Lightning Payment with Token
|
|
1791
1790
|
/**
|
|
1792
1791
|
* Get a quote for paying a Lightning invoice with a token.
|
|
1793
1792
|
* This calculates the optimal pool and token amount needed.
|
|
@@ -1803,7 +1802,18 @@ class FlashnetClient {
|
|
|
1803
1802
|
// Decode the invoice to get the amount
|
|
1804
1803
|
const invoiceAmountSats = await this.decodeInvoiceAmount(invoice);
|
|
1805
1804
|
if (!invoiceAmountSats || invoiceAmountSats <= 0) {
|
|
1806
|
-
throw new
|
|
1805
|
+
throw new FlashnetError("Unable to decode invoice amount. Zero-amount invoices are not supported for token payments.", {
|
|
1806
|
+
response: {
|
|
1807
|
+
errorCode: "FSAG-1002",
|
|
1808
|
+
errorCategory: "Validation",
|
|
1809
|
+
message: "Unable to decode invoice amount. Zero-amount invoices are not supported for token payments.",
|
|
1810
|
+
requestId: "",
|
|
1811
|
+
timestamp: new Date().toISOString(),
|
|
1812
|
+
service: "sdk",
|
|
1813
|
+
severity: "Error",
|
|
1814
|
+
remediation: "Provide a valid BOLT11 invoice with a non-zero amount.",
|
|
1815
|
+
},
|
|
1816
|
+
});
|
|
1807
1817
|
}
|
|
1808
1818
|
// Get Lightning fee estimate
|
|
1809
1819
|
const lightningFeeEstimate = await this.getLightningFeeEstimate(invoice);
|
|
@@ -1822,9 +1832,19 @@ class FlashnetClient {
|
|
|
1822
1832
|
// Check BTC minimum (output from swap)
|
|
1823
1833
|
const btcMinAmount = minAmounts.get(BTC_ASSET_PUBKEY.toLowerCase());
|
|
1824
1834
|
if (btcMinAmount && totalBtcNeeded < btcMinAmount) {
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1835
|
+
const msg = `Invoice amount too small. Minimum BTC output is ${btcMinAmount} sats, but invoice + lightning fee totals only ${totalBtcNeeded} sats.`;
|
|
1836
|
+
throw new FlashnetError(msg, {
|
|
1837
|
+
response: {
|
|
1838
|
+
errorCode: "FSAG-1003",
|
|
1839
|
+
errorCategory: "Validation",
|
|
1840
|
+
message: msg,
|
|
1841
|
+
requestId: "",
|
|
1842
|
+
timestamp: new Date().toISOString(),
|
|
1843
|
+
service: "sdk",
|
|
1844
|
+
severity: "Error",
|
|
1845
|
+
remediation: `Use an invoice of at least ${btcMinAmount} sats.`,
|
|
1846
|
+
},
|
|
1847
|
+
});
|
|
1828
1848
|
}
|
|
1829
1849
|
// Find the best pool to swap token -> BTC
|
|
1830
1850
|
const poolQuote = await this.findBestPoolForTokenToBtc(tokenAddress, totalBtcNeeded.toString(), options?.integratorFeeRateBps);
|
|
@@ -1833,9 +1853,19 @@ class FlashnetClient {
|
|
|
1833
1853
|
const tokenMinAmount = minAmounts.get(tokenHex);
|
|
1834
1854
|
if (tokenMinAmount &&
|
|
1835
1855
|
BigInt(poolQuote.tokenAmountRequired) < tokenMinAmount) {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1856
|
+
const msg = `Token amount too small. Minimum input is ${tokenMinAmount} units, but calculated amount is only ${poolQuote.tokenAmountRequired} units.`;
|
|
1857
|
+
throw new FlashnetError(msg, {
|
|
1858
|
+
response: {
|
|
1859
|
+
errorCode: "FSAG-1003",
|
|
1860
|
+
errorCategory: "Validation",
|
|
1861
|
+
message: msg,
|
|
1862
|
+
requestId: "",
|
|
1863
|
+
timestamp: new Date().toISOString(),
|
|
1864
|
+
service: "sdk",
|
|
1865
|
+
severity: "Error",
|
|
1866
|
+
remediation: "Use a larger invoice amount.",
|
|
1867
|
+
},
|
|
1868
|
+
});
|
|
1839
1869
|
}
|
|
1840
1870
|
// Calculate the BTC variable fee adjustment (how much extra we're requesting)
|
|
1841
1871
|
const btcVariableFeeAdjustment = Number(totalBtcNeeded - baseBtcNeeded);
|
|
@@ -2069,6 +2099,8 @@ class FlashnetClient {
|
|
|
2069
2099
|
const tokenHex = this.toHexTokenIdentifier(tokenAddress);
|
|
2070
2100
|
const btcHex = BTC_ASSET_PUBKEY;
|
|
2071
2101
|
// Find all pools that have this token paired with BTC
|
|
2102
|
+
// Note: The API may return the same pool for both filter combinations,
|
|
2103
|
+
// so we need to deduplicate and determine tokenIsAssetA from actual pool data
|
|
2072
2104
|
const poolsWithTokenAsA = await this.listPools({
|
|
2073
2105
|
assetAAddress: tokenHex,
|
|
2074
2106
|
assetBAddress: btcHex,
|
|
@@ -2077,17 +2109,57 @@ class FlashnetClient {
|
|
|
2077
2109
|
assetAAddress: btcHex,
|
|
2078
2110
|
assetBAddress: tokenHex,
|
|
2079
2111
|
});
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2112
|
+
// Deduplicate pools by poolId and determine tokenIsAssetA from actual pool addresses
|
|
2113
|
+
const poolMap = new Map();
|
|
2114
|
+
for (const p of [...poolsWithTokenAsA.pools, ...poolsWithTokenAsB.pools]) {
|
|
2115
|
+
if (!poolMap.has(p.lpPublicKey)) {
|
|
2116
|
+
// Determine tokenIsAssetA from actual pool asset addresses, not from which query returned it
|
|
2117
|
+
const tokenIsAssetA = p.assetAAddress?.toLowerCase() === tokenHex.toLowerCase();
|
|
2118
|
+
poolMap.set(p.lpPublicKey, { pool: p, tokenIsAssetA });
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
const allPools = Array.from(poolMap.values()).map(({ pool, tokenIsAssetA }) => ({
|
|
2122
|
+
...pool,
|
|
2123
|
+
tokenIsAssetA,
|
|
2124
|
+
}));
|
|
2084
2125
|
if (allPools.length === 0) {
|
|
2085
|
-
throw new
|
|
2126
|
+
throw new FlashnetError(`No liquidity pool found for token ${tokenAddress} paired with BTC`, {
|
|
2127
|
+
response: {
|
|
2128
|
+
errorCode: "FSAG-4001",
|
|
2129
|
+
errorCategory: "Business",
|
|
2130
|
+
message: `No liquidity pool found for token ${tokenAddress} paired with BTC`,
|
|
2131
|
+
requestId: "",
|
|
2132
|
+
timestamp: new Date().toISOString(),
|
|
2133
|
+
service: "sdk",
|
|
2134
|
+
severity: "Error",
|
|
2135
|
+
},
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
// Pre-check: Get minimum amounts to provide clear error if invoice is too small
|
|
2139
|
+
const minAmounts = await this.getMinAmountsMap();
|
|
2140
|
+
const btcMinAmount = minAmounts.get(BTC_ASSET_PUBKEY.toLowerCase());
|
|
2141
|
+
// Check if the BTC amount needed is below the minimum
|
|
2142
|
+
if (btcMinAmount && BigInt(btcAmountNeeded) < btcMinAmount) {
|
|
2143
|
+
const msg = `Invoice amount too small. Minimum ${btcMinAmount} sats required, but invoice only requires ${btcAmountNeeded} sats.`;
|
|
2144
|
+
throw new FlashnetError(msg, {
|
|
2145
|
+
response: {
|
|
2146
|
+
errorCode: "FSAG-1003",
|
|
2147
|
+
errorCategory: "Validation",
|
|
2148
|
+
message: msg,
|
|
2149
|
+
requestId: "",
|
|
2150
|
+
timestamp: new Date().toISOString(),
|
|
2151
|
+
service: "sdk",
|
|
2152
|
+
severity: "Error",
|
|
2153
|
+
remediation: `Use an invoice with at least ${btcMinAmount} sats.`,
|
|
2154
|
+
},
|
|
2155
|
+
});
|
|
2086
2156
|
}
|
|
2087
2157
|
// Find the best pool (lowest token cost for the required BTC)
|
|
2088
2158
|
let bestPool = null;
|
|
2089
2159
|
let bestTokenAmount = BigInt(Number.MAX_SAFE_INTEGER);
|
|
2090
2160
|
let bestSimulation = null;
|
|
2161
|
+
// Track errors for each pool to provide better diagnostics
|
|
2162
|
+
const poolErrors = [];
|
|
2091
2163
|
for (const pool of allPools) {
|
|
2092
2164
|
try {
|
|
2093
2165
|
// Get pool details for reserves
|
|
@@ -2121,12 +2193,53 @@ class FlashnetClient {
|
|
|
2121
2193
|
warningMessage: simulation.warningMessage,
|
|
2122
2194
|
};
|
|
2123
2195
|
}
|
|
2196
|
+
else {
|
|
2197
|
+
// Simulation output was insufficient
|
|
2198
|
+
const btcReserve = pool.tokenIsAssetA
|
|
2199
|
+
? poolDetails.assetBReserve
|
|
2200
|
+
: poolDetails.assetAReserve;
|
|
2201
|
+
poolErrors.push({
|
|
2202
|
+
poolId: pool.lpPublicKey,
|
|
2203
|
+
error: `Simulation output (${simulation.amountOut} sats) < required (${btcAmountNeeded} sats)`,
|
|
2204
|
+
btcReserve,
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2124
2207
|
}
|
|
2125
2208
|
}
|
|
2126
|
-
catch {
|
|
2209
|
+
catch (e) {
|
|
2210
|
+
// Capture pool errors for diagnostics
|
|
2211
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
2212
|
+
poolErrors.push({
|
|
2213
|
+
poolId: pool.lpPublicKey,
|
|
2214
|
+
error: errorMessage,
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2127
2217
|
}
|
|
2128
2218
|
if (!bestPool || !bestSimulation) {
|
|
2129
|
-
|
|
2219
|
+
let errorMessage = `No pool has sufficient liquidity for ${btcAmountNeeded} sats`;
|
|
2220
|
+
if (poolErrors.length > 0) {
|
|
2221
|
+
const details = poolErrors
|
|
2222
|
+
.map((pe) => {
|
|
2223
|
+
const reserveInfo = pe.btcReserve
|
|
2224
|
+
? ` (BTC reserve: ${pe.btcReserve})`
|
|
2225
|
+
: "";
|
|
2226
|
+
return ` - Pool ${pe.poolId.slice(0, 12)}...${reserveInfo}: ${pe.error}`;
|
|
2227
|
+
})
|
|
2228
|
+
.join("\n");
|
|
2229
|
+
errorMessage += `\n\nPool evaluation details:\n${details}`;
|
|
2230
|
+
}
|
|
2231
|
+
throw new FlashnetError(errorMessage, {
|
|
2232
|
+
response: {
|
|
2233
|
+
errorCode: "FSAG-4201",
|
|
2234
|
+
errorCategory: "Business",
|
|
2235
|
+
message: errorMessage,
|
|
2236
|
+
requestId: "",
|
|
2237
|
+
timestamp: new Date().toISOString(),
|
|
2238
|
+
service: "sdk",
|
|
2239
|
+
severity: "Error",
|
|
2240
|
+
remediation: "Try a smaller amount or wait for more liquidity.",
|
|
2241
|
+
},
|
|
2242
|
+
});
|
|
2130
2243
|
}
|
|
2131
2244
|
const poolDetails = await this.getPool(bestPool.lpPublicKey);
|
|
2132
2245
|
return {
|
|
@@ -2375,7 +2488,7 @@ class FlashnetClient {
|
|
|
2375
2488
|
async cleanup() {
|
|
2376
2489
|
await this._wallet.cleanupConnections();
|
|
2377
2490
|
}
|
|
2378
|
-
//
|
|
2491
|
+
// Config and Policy Enforcement Helpers
|
|
2379
2492
|
async ensureAmmOperationAllowed(requiredFeature) {
|
|
2380
2493
|
await this.ensurePingOk();
|
|
2381
2494
|
const featureMap = await this.getFeatureStatusMap();
|
|
@@ -2555,6 +2668,470 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
2555
2668
|
throw new Error(`Asset B is not allowed for pool creation: ${assetBHex}`);
|
|
2556
2669
|
}
|
|
2557
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
|
+
}
|
|
2558
3135
|
}
|
|
2559
3136
|
|
|
2560
3137
|
export { FlashnetClient };
|