@flashnet/sdk 0.5.4 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/client/FlashnetClient.d.ts +41 -4
- package/dist/cjs/src/client/FlashnetClient.d.ts.map +1 -1
- package/dist/cjs/src/client/FlashnetClient.js +93 -52
- package/dist/cjs/src/client/FlashnetClient.js.map +1 -1
- package/dist/cjs/src/utils/bigint.d.ts +7 -0
- package/dist/cjs/src/utils/bigint.d.ts.map +1 -0
- package/dist/cjs/src/utils/bigint.js +24 -0
- package/dist/cjs/src/utils/bigint.js.map +1 -0
- 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/intents.d.ts.map +1 -1
- package/dist/cjs/src/utils/intents.js +6 -4
- package/dist/cjs/src/utils/intents.js.map +1 -1
- package/dist/esm/src/client/FlashnetClient.d.ts +41 -4
- package/dist/esm/src/client/FlashnetClient.d.ts.map +1 -1
- package/dist/esm/src/client/FlashnetClient.js +93 -52
- package/dist/esm/src/client/FlashnetClient.js.map +1 -1
- package/dist/esm/src/utils/bigint.d.ts +7 -0
- package/dist/esm/src/utils/bigint.d.ts.map +1 -0
- package/dist/esm/src/utils/bigint.js +22 -0
- package/dist/esm/src/utils/bigint.js.map +1 -0
- 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/intents.d.ts.map +1 -1
- package/dist/esm/src/utils/intents.js +6 -4
- package/dist/esm/src/utils/intents.js.map +1 -1
- package/package.json +3 -3
|
@@ -9,6 +9,7 @@ import { getHexFromUint8Array } from '../utils/hex.js';
|
|
|
9
9
|
import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage, generateCreateEscrowIntentMessage, generateFundEscrowIntentMessage, generateClaimEscrowIntentMessage, generateClawbackIntentMessage, generateCreateConcentratedPoolIntentMessage, generateDecreaseLiquidityIntentMessage, generateCollectFeesIntentMessage, generateWithdrawBalanceIntentMessage, generateIncreaseLiquidityIntentMessage, generateRebalancePositionIntentMessage, generateDepositBalanceIntentMessage } from '../utils/intents.js';
|
|
10
10
|
import { getSparkNetworkFromAddress, encodeSparkAddressNew } from '../utils/spark-address.js';
|
|
11
11
|
import { encodeSparkHumanReadableTokenIdentifier, decodeSparkHumanReadableTokenIdentifier } from '../utils/tokenAddress.js';
|
|
12
|
+
import { safeBigInt } from '../utils/bigint.js';
|
|
12
13
|
import { FlashnetError } from '../types/errors.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -239,7 +240,8 @@ class FlashnetClient {
|
|
|
239
240
|
const tokenIdentifierHex = getHexFromUint8Array(info.rawTokenIdentifier);
|
|
240
241
|
const tokenAddress = encodeSparkHumanReadableTokenIdentifier(info.rawTokenIdentifier, this.sparkNetwork);
|
|
241
242
|
tokenBalances.set(tokenPubkey, {
|
|
242
|
-
balance:
|
|
243
|
+
balance: FlashnetClient.safeBigInt(tokenData.ownedBalance),
|
|
244
|
+
availableToSendBalance: FlashnetClient.safeBigInt(tokenData.availableToSendBalance),
|
|
243
245
|
tokenInfo: {
|
|
244
246
|
tokenIdentifier: tokenIdentifierHex,
|
|
245
247
|
tokenAddress,
|
|
@@ -252,7 +254,7 @@ class FlashnetClient {
|
|
|
252
254
|
}
|
|
253
255
|
}
|
|
254
256
|
return {
|
|
255
|
-
balance:
|
|
257
|
+
balance: FlashnetClient.safeBigInt(balance.balance),
|
|
256
258
|
tokenBalances,
|
|
257
259
|
};
|
|
258
260
|
}
|
|
@@ -260,17 +262,17 @@ class FlashnetClient {
|
|
|
260
262
|
* Check if wallet has sufficient balance for an operation
|
|
261
263
|
*/
|
|
262
264
|
async checkBalance(params) {
|
|
263
|
-
const balance = await this.getBalance();
|
|
265
|
+
const balance = params.walletBalance ?? (await this.getBalance());
|
|
264
266
|
// Check balance
|
|
265
267
|
const requirements = {
|
|
266
268
|
tokens: new Map(),
|
|
267
269
|
};
|
|
268
270
|
for (const balance of params.balancesToCheck) {
|
|
269
271
|
if (balance.assetAddress === BTC_ASSET_PUBKEY) {
|
|
270
|
-
requirements.btc =
|
|
272
|
+
requirements.btc = FlashnetClient.safeBigInt(balance.amount);
|
|
271
273
|
}
|
|
272
274
|
else {
|
|
273
|
-
requirements.tokens?.set(balance.assetAddress,
|
|
275
|
+
requirements.tokens?.set(balance.assetAddress, FlashnetClient.safeBigInt(balance.amount));
|
|
274
276
|
}
|
|
275
277
|
}
|
|
276
278
|
// Check BTC balance
|
|
@@ -288,7 +290,9 @@ class FlashnetClient {
|
|
|
288
290
|
const hrKey = this.toHumanReadableTokenIdentifier(tokenPubkey);
|
|
289
291
|
const effectiveTokenBalance = balance.tokenBalances.get(tokenPubkey) ??
|
|
290
292
|
balance.tokenBalances.get(hrKey);
|
|
291
|
-
const available =
|
|
293
|
+
const available = params.useAvailableBalance
|
|
294
|
+
? (effectiveTokenBalance?.availableToSendBalance ?? 0n)
|
|
295
|
+
: (effectiveTokenBalance?.balance ?? 0n);
|
|
292
296
|
if (available < requiredAmount) {
|
|
293
297
|
throw new Error([
|
|
294
298
|
params.errorPrefix ?? "",
|
|
@@ -350,6 +354,7 @@ class FlashnetClient {
|
|
|
350
354
|
},
|
|
351
355
|
],
|
|
352
356
|
errorPrefix: "Insufficient balance for initial liquidity: ",
|
|
357
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
353
358
|
});
|
|
354
359
|
}
|
|
355
360
|
const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
|
|
@@ -409,6 +414,12 @@ class FlashnetClient {
|
|
|
409
414
|
throw new Error(`${name} must be positive integer`);
|
|
410
415
|
}
|
|
411
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Safely convert a value to BigInt. Delegates to the shared `safeBigInt` utility.
|
|
419
|
+
*/
|
|
420
|
+
static safeBigInt(value, fallback = 0n) {
|
|
421
|
+
return safeBigInt(value, fallback);
|
|
422
|
+
}
|
|
412
423
|
/**
|
|
413
424
|
* Calculates virtual reserves for a bonding curve AMM.
|
|
414
425
|
*
|
|
@@ -429,7 +440,7 @@ class FlashnetClient {
|
|
|
429
440
|
}
|
|
430
441
|
const supply = FlashnetClient.parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
431
442
|
const targetB = FlashnetClient.parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
432
|
-
const graduationThresholdPct =
|
|
443
|
+
const graduationThresholdPct = FlashnetClient.safeBigInt(params.graduationThresholdPct);
|
|
433
444
|
// Align bounds with Rust AMM (20%..95%), then check feasibility for g=1 (requires >50%).
|
|
434
445
|
const MIN_PCT = 20n;
|
|
435
446
|
const MAX_PCT = 95n;
|
|
@@ -479,6 +490,7 @@ class FlashnetClient {
|
|
|
479
490
|
},
|
|
480
491
|
],
|
|
481
492
|
errorPrefix: "Insufficient balance for pool creation: ",
|
|
493
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
482
494
|
});
|
|
483
495
|
const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
|
|
484
496
|
// Generate intent
|
|
@@ -599,10 +611,14 @@ class FlashnetClient {
|
|
|
599
611
|
});
|
|
600
612
|
// If using free balance (V3 pools only), skip the Spark transfer
|
|
601
613
|
if (params.useFreeBalance) {
|
|
602
|
-
|
|
614
|
+
const swapResponse = await this.executeSwapIntent({
|
|
603
615
|
...params,
|
|
604
616
|
// No transferId - triggers free balance mode
|
|
605
617
|
});
|
|
618
|
+
return {
|
|
619
|
+
...swapResponse,
|
|
620
|
+
inboundSparkTransferId: swapResponse.requestId,
|
|
621
|
+
};
|
|
606
622
|
}
|
|
607
623
|
// Transfer assets to pool using new address encoding
|
|
608
624
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
@@ -613,12 +629,13 @@ class FlashnetClient {
|
|
|
613
629
|
receiverSparkAddress: lpSparkAddress,
|
|
614
630
|
assetAddress: params.assetInAddress,
|
|
615
631
|
amount: params.amountIn,
|
|
616
|
-
}, "Insufficient balance for swap: ");
|
|
632
|
+
}, "Insufficient balance for swap: ", params.useAvailableBalance);
|
|
617
633
|
// Execute with auto-clawback on failure
|
|
618
|
-
|
|
634
|
+
const swapResponse = await this.executeWithAutoClawback(() => this.executeSwapIntent({
|
|
619
635
|
...params,
|
|
620
636
|
transferId,
|
|
621
637
|
}), [transferId], params.poolId);
|
|
638
|
+
return { ...swapResponse, inboundSparkTransferId: transferId };
|
|
622
639
|
}
|
|
623
640
|
/**
|
|
624
641
|
* Execute a swap with a pre-created transfer or using free balance.
|
|
@@ -748,7 +765,7 @@ class FlashnetClient {
|
|
|
748
765
|
receiverSparkAddress: lpSparkAddress,
|
|
749
766
|
assetAddress: params.initialAssetAddress,
|
|
750
767
|
amount: params.inputAmount,
|
|
751
|
-
}, "Insufficient balance for route swap: ");
|
|
768
|
+
}, "Insufficient balance for route swap: ", params.useAvailableBalance);
|
|
752
769
|
// Execute with auto-clawback on failure
|
|
753
770
|
return this.executeWithAutoClawback(async () => {
|
|
754
771
|
// Prepare hops for validation
|
|
@@ -871,7 +888,7 @@ class FlashnetClient {
|
|
|
871
888
|
assetAddress: pool.assetBAddress,
|
|
872
889
|
amount: params.assetBAmount,
|
|
873
890
|
},
|
|
874
|
-
], "Insufficient balance for adding liquidity: ");
|
|
891
|
+
], "Insufficient balance for adding liquidity: ", params.useAvailableBalance);
|
|
875
892
|
// Execute with auto-clawback on failure
|
|
876
893
|
return this.executeWithAutoClawback(async () => {
|
|
877
894
|
// Generate add liquidity intent
|
|
@@ -1177,6 +1194,7 @@ class FlashnetClient {
|
|
|
1177
1194
|
depositAddress: createResponse.depositAddress,
|
|
1178
1195
|
assetId: params.assetId,
|
|
1179
1196
|
assetAmount: params.assetAmount,
|
|
1197
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
1180
1198
|
});
|
|
1181
1199
|
}
|
|
1182
1200
|
/**
|
|
@@ -1194,6 +1212,7 @@ class FlashnetClient {
|
|
|
1194
1212
|
{ assetAddress: params.assetId, amount: params.assetAmount },
|
|
1195
1213
|
],
|
|
1196
1214
|
errorPrefix: "Insufficient balance to fund escrow: ",
|
|
1215
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
1197
1216
|
});
|
|
1198
1217
|
// 2. Perform transfer
|
|
1199
1218
|
const escrowSparkAddress = encodeSparkAddressNew({
|
|
@@ -1708,19 +1727,20 @@ class FlashnetClient {
|
|
|
1708
1727
|
/**
|
|
1709
1728
|
* Performs asset transfer using generalized asset address for both BTC and tokens.
|
|
1710
1729
|
*/
|
|
1711
|
-
async transferAsset(recipient, checkBalanceErrorPrefix) {
|
|
1712
|
-
const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix);
|
|
1730
|
+
async transferAsset(recipient, checkBalanceErrorPrefix, useAvailableBalance) {
|
|
1731
|
+
const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix, useAvailableBalance);
|
|
1713
1732
|
return transferIds[0];
|
|
1714
1733
|
}
|
|
1715
1734
|
/**
|
|
1716
1735
|
* Performs asset transfers using generalized asset addresses for both BTC and tokens.
|
|
1717
1736
|
* Supports optional generic to hardcode recipients length so output list can be typed with same length.
|
|
1718
1737
|
*/
|
|
1719
|
-
async transferAssets(recipients, checkBalanceErrorPrefix) {
|
|
1738
|
+
async transferAssets(recipients, checkBalanceErrorPrefix, useAvailableBalance) {
|
|
1720
1739
|
if (checkBalanceErrorPrefix) {
|
|
1721
1740
|
await this.checkBalance({
|
|
1722
1741
|
balancesToCheck: recipients,
|
|
1723
1742
|
errorPrefix: checkBalanceErrorPrefix,
|
|
1743
|
+
useAvailableBalance,
|
|
1724
1744
|
});
|
|
1725
1745
|
}
|
|
1726
1746
|
const transferIds = [];
|
|
@@ -1735,7 +1755,7 @@ class FlashnetClient {
|
|
|
1735
1755
|
else {
|
|
1736
1756
|
const transferId = await this._wallet.transferTokens({
|
|
1737
1757
|
tokenIdentifier: this.toHumanReadableTokenIdentifier(recipient.assetAddress),
|
|
1738
|
-
tokenAmount:
|
|
1758
|
+
tokenAmount: FlashnetClient.safeBigInt(recipient.amount),
|
|
1739
1759
|
receiverSparkAddress: recipient.receiverSparkAddress,
|
|
1740
1760
|
});
|
|
1741
1761
|
transferIds.push(transferId);
|
|
@@ -1842,7 +1862,8 @@ class FlashnetClient {
|
|
|
1842
1862
|
const lightningFeeEstimate = await this.getLightningFeeEstimate(invoice);
|
|
1843
1863
|
// Total BTC needed = invoice amount + lightning fee (unmasked).
|
|
1844
1864
|
// Bitmasking for V2 pools is handled inside findBestPoolForTokenToBtc.
|
|
1845
|
-
const baseBtcNeeded =
|
|
1865
|
+
const baseBtcNeeded = FlashnetClient.safeBigInt(invoiceAmountSats) +
|
|
1866
|
+
FlashnetClient.safeBigInt(lightningFeeEstimate);
|
|
1846
1867
|
// Check Flashnet minimum amounts early to provide clear error messages
|
|
1847
1868
|
const minAmounts = await this.getEnabledMinAmountsMap();
|
|
1848
1869
|
// Check BTC minimum (output from swap)
|
|
@@ -1869,7 +1890,7 @@ class FlashnetClient {
|
|
|
1869
1890
|
const tokenHex = this.toHexTokenIdentifier(tokenAddress).toLowerCase();
|
|
1870
1891
|
const tokenMinAmount = minAmounts.get(tokenHex);
|
|
1871
1892
|
if (tokenMinAmount &&
|
|
1872
|
-
|
|
1893
|
+
FlashnetClient.safeBigInt(poolQuote.tokenAmountRequired) < tokenMinAmount) {
|
|
1873
1894
|
const msg = `Token amount too small. Minimum input is ${tokenMinAmount} units, but calculated amount is only ${poolQuote.tokenAmountRequired} units.`;
|
|
1874
1895
|
throw new FlashnetError(msg, {
|
|
1875
1896
|
response: {
|
|
@@ -1886,7 +1907,7 @@ class FlashnetClient {
|
|
|
1886
1907
|
}
|
|
1887
1908
|
// BTC variable fee adjustment: difference between what the pool targets and unmasked base.
|
|
1888
1909
|
// For V3 pools this is 0 (no masking). For V2 it's the rounding overhead.
|
|
1889
|
-
const btcVariableFeeAdjustment = Number(
|
|
1910
|
+
const btcVariableFeeAdjustment = Number(FlashnetClient.safeBigInt(poolQuote.btcAmountUsed) - baseBtcNeeded);
|
|
1890
1911
|
return {
|
|
1891
1912
|
poolId: poolQuote.poolId,
|
|
1892
1913
|
tokenAddress: this.toHexTokenIdentifier(tokenAddress),
|
|
@@ -1958,7 +1979,7 @@ class FlashnetClient {
|
|
|
1958
1979
|
amountIn: tokenAmount,
|
|
1959
1980
|
integratorBps: options?.integratorFeeRateBps,
|
|
1960
1981
|
});
|
|
1961
|
-
const btcOut =
|
|
1982
|
+
const btcOut = FlashnetClient.safeBigInt(simulation.amountOut);
|
|
1962
1983
|
if (btcOut > bestBtcOut) {
|
|
1963
1984
|
bestBtcOut = btcOut;
|
|
1964
1985
|
bestResult = {
|
|
@@ -2046,7 +2067,7 @@ class FlashnetClient {
|
|
|
2046
2067
|
await this.ensureInitialized();
|
|
2047
2068
|
const { invoice, tokenAddress, tokenAmount, maxSlippageBps = 500, // 5% default
|
|
2048
2069
|
maxLightningFeeSats, preferSpark = true, integratorFeeRateBps, integratorPublicKey, transferTimeoutMs = 30000, // 30s default
|
|
2049
|
-
rollbackOnFailure = false, useExistingBtcBalance = false, } = options;
|
|
2070
|
+
rollbackOnFailure = false, useExistingBtcBalance = false, useAvailableBalance = false, } = options;
|
|
2050
2071
|
try {
|
|
2051
2072
|
// Step 1: Get a quote for the payment
|
|
2052
2073
|
const quote = await this.getPayLightningWithTokenQuote(invoice, tokenAddress, {
|
|
@@ -2063,6 +2084,7 @@ class FlashnetClient {
|
|
|
2063
2084
|
},
|
|
2064
2085
|
],
|
|
2065
2086
|
errorPrefix: "Insufficient token balance for Lightning payment: ",
|
|
2087
|
+
useAvailableBalance,
|
|
2066
2088
|
});
|
|
2067
2089
|
// Step 3: Get pool details
|
|
2068
2090
|
const pool = await this.getPool(quote.poolId);
|
|
@@ -2085,6 +2107,7 @@ class FlashnetClient {
|
|
|
2085
2107
|
minAmountOut: minBtcOut,
|
|
2086
2108
|
integratorFeeRateBps,
|
|
2087
2109
|
integratorPublicKey,
|
|
2110
|
+
useAvailableBalance,
|
|
2088
2111
|
});
|
|
2089
2112
|
if (!swapResponse.accepted || !swapResponse.outboundTransferId) {
|
|
2090
2113
|
return {
|
|
@@ -2094,6 +2117,7 @@ class FlashnetClient {
|
|
|
2094
2117
|
btcAmountReceived: "0",
|
|
2095
2118
|
swapTransferId: swapResponse.outboundTransferId || "",
|
|
2096
2119
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2120
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2097
2121
|
error: swapResponse.error || "Swap was not accepted",
|
|
2098
2122
|
};
|
|
2099
2123
|
}
|
|
@@ -2104,7 +2128,8 @@ class FlashnetClient {
|
|
|
2104
2128
|
const effectiveMaxLightningFee = maxLightningFeeSats ?? quote.estimatedLightningFee;
|
|
2105
2129
|
const btcNeededForPayment = invoiceAmountSats + effectiveMaxLightningFee;
|
|
2106
2130
|
const balance = await this.getBalance();
|
|
2107
|
-
canPayImmediately =
|
|
2131
|
+
canPayImmediately =
|
|
2132
|
+
balance.balance >= FlashnetClient.safeBigInt(btcNeededForPayment);
|
|
2108
2133
|
}
|
|
2109
2134
|
if (!canPayImmediately) {
|
|
2110
2135
|
const transferComplete = await this.waitForTransferCompletion(swapResponse.outboundTransferId, transferTimeoutMs);
|
|
@@ -2116,6 +2141,7 @@ class FlashnetClient {
|
|
|
2116
2141
|
btcAmountReceived: swapResponse.amountOut || "0",
|
|
2117
2142
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2118
2143
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2144
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2119
2145
|
error: "Transfer did not complete within timeout",
|
|
2120
2146
|
};
|
|
2121
2147
|
}
|
|
@@ -2129,8 +2155,8 @@ class FlashnetClient {
|
|
|
2129
2155
|
let invoiceAmountPaid;
|
|
2130
2156
|
if (quote.isZeroAmountInvoice) {
|
|
2131
2157
|
// Zero-amount invoice: pay whatever BTC we received minus lightning fee
|
|
2132
|
-
const actualBtc =
|
|
2133
|
-
const lnFee =
|
|
2158
|
+
const actualBtc = FlashnetClient.safeBigInt(btcReceived);
|
|
2159
|
+
const lnFee = FlashnetClient.safeBigInt(effectiveMaxLightningFee);
|
|
2134
2160
|
const amountToPay = actualBtc - lnFee;
|
|
2135
2161
|
if (amountToPay <= 0n) {
|
|
2136
2162
|
return {
|
|
@@ -2140,6 +2166,7 @@ class FlashnetClient {
|
|
|
2140
2166
|
btcAmountReceived: btcReceived,
|
|
2141
2167
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2142
2168
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2169
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2143
2170
|
error: `BTC received (${btcReceived} sats) is not enough to cover lightning fee (${effectiveMaxLightningFee} sats).`,
|
|
2144
2171
|
};
|
|
2145
2172
|
}
|
|
@@ -2159,6 +2186,12 @@ class FlashnetClient {
|
|
|
2159
2186
|
preferSpark,
|
|
2160
2187
|
});
|
|
2161
2188
|
}
|
|
2189
|
+
// Extract the Spark transfer ID from the lightning payment result.
|
|
2190
|
+
// payLightningInvoice returns LightningSendRequest | WalletTransfer:
|
|
2191
|
+
// - LightningSendRequest has .transfer?.sparkId (the Sparkscan-visible transfer ID)
|
|
2192
|
+
// - WalletTransfer (Spark-to-Spark) has .id directly as the transfer ID
|
|
2193
|
+
// Note: lightningPayment.id (the SSP request ID) is already returned as lightningPaymentId
|
|
2194
|
+
const sparkLightningTransferId = lightningPayment.transfer?.sparkId;
|
|
2162
2195
|
return {
|
|
2163
2196
|
success: true,
|
|
2164
2197
|
poolId: quote.poolId,
|
|
@@ -2169,6 +2202,8 @@ class FlashnetClient {
|
|
|
2169
2202
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2170
2203
|
lightningFeePaid: effectiveMaxLightningFee,
|
|
2171
2204
|
invoiceAmountPaid,
|
|
2205
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2206
|
+
sparkLightningTransferId,
|
|
2172
2207
|
};
|
|
2173
2208
|
}
|
|
2174
2209
|
catch (lightningError) {
|
|
@@ -2188,6 +2223,7 @@ class FlashnetClient {
|
|
|
2188
2223
|
btcAmountReceived: "0",
|
|
2189
2224
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2190
2225
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2226
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2191
2227
|
error: `Lightning payment failed: ${lightningErrorMessage}. Funds rolled back to ${rollbackResult.tokenAmount} tokens.`,
|
|
2192
2228
|
};
|
|
2193
2229
|
}
|
|
@@ -2203,6 +2239,7 @@ class FlashnetClient {
|
|
|
2203
2239
|
btcAmountReceived: btcReceived,
|
|
2204
2240
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2205
2241
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2242
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2206
2243
|
error: `Lightning payment failed: ${lightningErrorMessage}. Rollback also failed: ${rollbackErrorMessage}. BTC remains in wallet.`,
|
|
2207
2244
|
};
|
|
2208
2245
|
}
|
|
@@ -2214,6 +2251,7 @@ class FlashnetClient {
|
|
|
2214
2251
|
btcAmountReceived: btcReceived,
|
|
2215
2252
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2216
2253
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2254
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2217
2255
|
error: `Lightning payment failed: ${lightningErrorMessage}. BTC (${btcReceived} sats) remains in wallet.`,
|
|
2218
2256
|
};
|
|
2219
2257
|
}
|
|
@@ -2376,7 +2414,7 @@ class FlashnetClient {
|
|
|
2376
2414
|
tokenIsAssetA: pool.tokenIsAssetA,
|
|
2377
2415
|
integratorBps: integratorFeeRateBps,
|
|
2378
2416
|
});
|
|
2379
|
-
tokenAmount =
|
|
2417
|
+
tokenAmount = FlashnetClient.safeBigInt(v3Result.amountIn);
|
|
2380
2418
|
fee = v3Result.totalFee;
|
|
2381
2419
|
executionPrice = v3Result.simulation.executionPrice || "0";
|
|
2382
2420
|
priceImpactPct = v3Result.simulation.priceImpactPct || "0";
|
|
@@ -2385,7 +2423,7 @@ class FlashnetClient {
|
|
|
2385
2423
|
else {
|
|
2386
2424
|
// V2: constant product math + simulation verification
|
|
2387
2425
|
const calculation = this.calculateTokenAmountForBtcOutput(btcTarget.toString(), poolDetails.assetAReserve, poolDetails.assetBReserve, poolDetails.lpFeeBps, poolDetails.hostFeeBps, pool.tokenIsAssetA, integratorFeeRateBps);
|
|
2388
|
-
tokenAmount =
|
|
2426
|
+
tokenAmount = FlashnetClient.safeBigInt(calculation.amountIn);
|
|
2389
2427
|
// Verify with simulation
|
|
2390
2428
|
const simulation = await this.simulateSwap({
|
|
2391
2429
|
poolId: pool.lpPublicKey,
|
|
@@ -2394,7 +2432,7 @@ class FlashnetClient {
|
|
|
2394
2432
|
amountIn: calculation.amountIn,
|
|
2395
2433
|
integratorBps: integratorFeeRateBps,
|
|
2396
2434
|
});
|
|
2397
|
-
if (
|
|
2435
|
+
if (FlashnetClient.safeBigInt(simulation.amountOut) < btcTarget) {
|
|
2398
2436
|
const btcReserve = pool.tokenIsAssetA
|
|
2399
2437
|
? poolDetails.assetBReserve
|
|
2400
2438
|
: poolDetails.assetAReserve;
|
|
@@ -2482,9 +2520,9 @@ class FlashnetClient {
|
|
|
2482
2520
|
* @private
|
|
2483
2521
|
*/
|
|
2484
2522
|
calculateTokenAmountForBtcOutput(btcAmountOut, reserveA, reserveB, lpFeeBps, hostFeeBps, tokenIsAssetA, integratorFeeBps) {
|
|
2485
|
-
const amountOut =
|
|
2486
|
-
const resA =
|
|
2487
|
-
const resB =
|
|
2523
|
+
const amountOut = FlashnetClient.safeBigInt(btcAmountOut);
|
|
2524
|
+
const resA = FlashnetClient.safeBigInt(reserveA);
|
|
2525
|
+
const resB = FlashnetClient.safeBigInt(reserveB);
|
|
2488
2526
|
const totalFeeBps = lpFeeBps + hostFeeBps + (integratorFeeBps || 0);
|
|
2489
2527
|
const feeRate = Number(totalFeeBps) / 10000; // Convert bps to decimal
|
|
2490
2528
|
// Token is the input asset
|
|
@@ -2582,7 +2620,7 @@ class FlashnetClient {
|
|
|
2582
2620
|
amountIn: upperBound.toString(),
|
|
2583
2621
|
integratorBps,
|
|
2584
2622
|
});
|
|
2585
|
-
if (
|
|
2623
|
+
if (FlashnetClient.safeBigInt(sim.amountOut) >= desiredBtcOut) {
|
|
2586
2624
|
upperSim = sim;
|
|
2587
2625
|
break;
|
|
2588
2626
|
}
|
|
@@ -2593,7 +2631,7 @@ class FlashnetClient {
|
|
|
2593
2631
|
throw new Error(`V3 pool ${poolId} has insufficient liquidity for ${desiredBtcOut} sats`);
|
|
2594
2632
|
}
|
|
2595
2633
|
// Step 3: Refine estimate via linear interpolation
|
|
2596
|
-
const upperOut =
|
|
2634
|
+
const upperOut = FlashnetClient.safeBigInt(upperSim.amountOut);
|
|
2597
2635
|
// Scale proportionally: if upperBound produced upperOut, we need roughly
|
|
2598
2636
|
// (upperBound * desiredBtcOut / upperOut). Add +1 to avoid undershoot from truncation.
|
|
2599
2637
|
let refined = (upperBound * desiredBtcOut) / upperOut + 1n;
|
|
@@ -2611,7 +2649,7 @@ class FlashnetClient {
|
|
|
2611
2649
|
amountIn: refined.toString(),
|
|
2612
2650
|
integratorBps,
|
|
2613
2651
|
});
|
|
2614
|
-
if (
|
|
2652
|
+
if (FlashnetClient.safeBigInt(refinedSim.amountOut) >= desiredBtcOut) {
|
|
2615
2653
|
bestAmountIn = refined;
|
|
2616
2654
|
bestSim = refinedSim;
|
|
2617
2655
|
}
|
|
@@ -2645,7 +2683,7 @@ class FlashnetClient {
|
|
|
2645
2683
|
amountIn: mid.toString(),
|
|
2646
2684
|
integratorBps,
|
|
2647
2685
|
});
|
|
2648
|
-
if (
|
|
2686
|
+
if (FlashnetClient.safeBigInt(midSim.amountOut) >= desiredBtcOut) {
|
|
2649
2687
|
hi = mid;
|
|
2650
2688
|
bestAmountIn = mid;
|
|
2651
2689
|
bestSim = midSim;
|
|
@@ -2667,7 +2705,7 @@ class FlashnetClient {
|
|
|
2667
2705
|
* @private
|
|
2668
2706
|
*/
|
|
2669
2707
|
calculateMinAmountOut(expectedAmount, slippageBps) {
|
|
2670
|
-
const amount =
|
|
2708
|
+
const amount = FlashnetClient.safeBigInt(expectedAmount);
|
|
2671
2709
|
const slippageFactor = BigInt(10000 - slippageBps);
|
|
2672
2710
|
const minAmount = (amount * slippageFactor) / 10000n;
|
|
2673
2711
|
return minAmount.toString();
|
|
@@ -2886,8 +2924,11 @@ class FlashnetClient {
|
|
|
2886
2924
|
const map = new Map();
|
|
2887
2925
|
for (const item of config) {
|
|
2888
2926
|
if (item.enabled) {
|
|
2927
|
+
if (item.min_amount == null) {
|
|
2928
|
+
continue;
|
|
2929
|
+
}
|
|
2889
2930
|
const key = item.asset_identifier.toLowerCase();
|
|
2890
|
-
const value =
|
|
2931
|
+
const value = FlashnetClient.safeBigInt(item.min_amount);
|
|
2891
2932
|
map.set(key, value);
|
|
2892
2933
|
}
|
|
2893
2934
|
}
|
|
@@ -2909,8 +2950,8 @@ class FlashnetClient {
|
|
|
2909
2950
|
const outHex = this.getHexAddress(params.assetOutAddress);
|
|
2910
2951
|
const minIn = minMap.get(inHex);
|
|
2911
2952
|
const minOut = minMap.get(outHex);
|
|
2912
|
-
const amountIn =
|
|
2913
|
-
const minAmountOut =
|
|
2953
|
+
const amountIn = FlashnetClient.safeBigInt(params.amountIn);
|
|
2954
|
+
const minAmountOut = FlashnetClient.safeBigInt(params.minAmountOut);
|
|
2914
2955
|
if (minIn && minOut) {
|
|
2915
2956
|
if (amountIn < minIn) {
|
|
2916
2957
|
throw new Error(`Minimum amount not met for input asset. Required \
|
|
@@ -2944,13 +2985,13 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
2944
2985
|
const aMin = minMap.get(aHex);
|
|
2945
2986
|
const bMin = minMap.get(bHex);
|
|
2946
2987
|
if (aMin) {
|
|
2947
|
-
const aAmt =
|
|
2988
|
+
const aAmt = FlashnetClient.safeBigInt(params.assetAAmount);
|
|
2948
2989
|
if (aAmt < aMin) {
|
|
2949
2990
|
throw new Error(`Minimum amount not met for Asset A. Required ${aMin.toString()}, provided ${aAmt.toString()}`);
|
|
2950
2991
|
}
|
|
2951
2992
|
}
|
|
2952
2993
|
if (bMin) {
|
|
2953
|
-
const bAmt =
|
|
2994
|
+
const bAmt = FlashnetClient.safeBigInt(params.assetBAmount);
|
|
2954
2995
|
if (bAmt < bMin) {
|
|
2955
2996
|
throw new Error(`Minimum amount not met for Asset B. Required ${bMin.toString()}, provided ${bAmt.toString()}`);
|
|
2956
2997
|
}
|
|
@@ -2972,14 +3013,14 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
2972
3013
|
const aMin = minMap.get(aHex);
|
|
2973
3014
|
const bMin = minMap.get(bHex);
|
|
2974
3015
|
if (aMin) {
|
|
2975
|
-
const predictedAOut =
|
|
3016
|
+
const predictedAOut = FlashnetClient.safeBigInt(simulation.assetAAmount);
|
|
2976
3017
|
const relaxedA = aMin / 2n; // apply 50% relaxation for outputs
|
|
2977
3018
|
if (predictedAOut < relaxedA) {
|
|
2978
3019
|
throw new Error(`Minimum amount not met for Asset A on withdrawal. Required at least ${relaxedA.toString()} (50% relaxed), predicted ${predictedAOut.toString()}`);
|
|
2979
3020
|
}
|
|
2980
3021
|
}
|
|
2981
3022
|
if (bMin) {
|
|
2982
|
-
const predictedBOut =
|
|
3023
|
+
const predictedBOut = FlashnetClient.safeBigInt(simulation.assetBAmount);
|
|
2983
3024
|
const relaxedB = bMin / 2n;
|
|
2984
3025
|
if (predictedBOut < relaxedB) {
|
|
2985
3026
|
throw new Error(`Minimum amount not met for Asset B on withdrawal. Required at least ${relaxedB.toString()} (50% relaxed), predicted ${predictedBOut.toString()}`);
|
|
@@ -3092,20 +3133,20 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
3092
3133
|
const transferIds = [];
|
|
3093
3134
|
// Transfer assets if not using free balance
|
|
3094
3135
|
if (!params.useFreeBalance) {
|
|
3095
|
-
if (
|
|
3136
|
+
if (FlashnetClient.safeBigInt(params.amountADesired) > 0n) {
|
|
3096
3137
|
assetATransferId = await this.transferAsset({
|
|
3097
3138
|
receiverSparkAddress: lpSparkAddress,
|
|
3098
3139
|
assetAddress: pool.assetAAddress,
|
|
3099
3140
|
amount: params.amountADesired,
|
|
3100
|
-
}, "Insufficient balance for adding V3 liquidity (Asset A): ");
|
|
3141
|
+
}, "Insufficient balance for adding V3 liquidity (Asset A): ", params.useAvailableBalance);
|
|
3101
3142
|
transferIds.push(assetATransferId);
|
|
3102
3143
|
}
|
|
3103
|
-
if (
|
|
3144
|
+
if (FlashnetClient.safeBigInt(params.amountBDesired) > 0n) {
|
|
3104
3145
|
assetBTransferId = await this.transferAsset({
|
|
3105
3146
|
receiverSparkAddress: lpSparkAddress,
|
|
3106
3147
|
assetAddress: pool.assetBAddress,
|
|
3107
3148
|
amount: params.amountBDesired,
|
|
3108
|
-
}, "Insufficient balance for adding V3 liquidity (Asset B): ");
|
|
3149
|
+
}, "Insufficient balance for adding V3 liquidity (Asset B): ", params.useAvailableBalance);
|
|
3109
3150
|
transferIds.push(assetBTransferId);
|
|
3110
3151
|
}
|
|
3111
3152
|
}
|
|
@@ -3298,14 +3339,14 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
3298
3339
|
receiverSparkAddress: lpSparkAddress,
|
|
3299
3340
|
assetAddress: pool.assetAAddress,
|
|
3300
3341
|
amount: params.additionalAmountA,
|
|
3301
|
-
}, "Insufficient balance for rebalance (Asset A): ");
|
|
3342
|
+
}, "Insufficient balance for rebalance (Asset A): ", params.useAvailableBalance);
|
|
3302
3343
|
}
|
|
3303
3344
|
if (params.additionalAmountB && BigInt(params.additionalAmountB) > 0n) {
|
|
3304
3345
|
assetBTransferId = await this.transferAsset({
|
|
3305
3346
|
receiverSparkAddress: lpSparkAddress,
|
|
3306
3347
|
assetAddress: pool.assetBAddress,
|
|
3307
3348
|
amount: params.additionalAmountB,
|
|
3308
|
-
}, "Insufficient balance for rebalance (Asset B): ");
|
|
3349
|
+
}, "Insufficient balance for rebalance (Asset B): ", params.useAvailableBalance);
|
|
3309
3350
|
}
|
|
3310
3351
|
// Collect transfer IDs for potential clawback
|
|
3311
3352
|
const transferIds = [];
|
|
@@ -3497,20 +3538,20 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
3497
3538
|
let assetBTransferId = "";
|
|
3498
3539
|
const transferIds = [];
|
|
3499
3540
|
// Transfer assets to pool
|
|
3500
|
-
if (
|
|
3541
|
+
if (FlashnetClient.safeBigInt(params.amountA) > 0n) {
|
|
3501
3542
|
assetATransferId = await this.transferAsset({
|
|
3502
3543
|
receiverSparkAddress: lpSparkAddress,
|
|
3503
3544
|
assetAddress: pool.assetAAddress,
|
|
3504
3545
|
amount: params.amountA,
|
|
3505
|
-
}, "Insufficient balance for depositing to V3 pool (Asset A): ");
|
|
3546
|
+
}, "Insufficient balance for depositing to V3 pool (Asset A): ", params.useAvailableBalance);
|
|
3506
3547
|
transferIds.push(assetATransferId);
|
|
3507
3548
|
}
|
|
3508
|
-
if (
|
|
3549
|
+
if (FlashnetClient.safeBigInt(params.amountB) > 0n) {
|
|
3509
3550
|
assetBTransferId = await this.transferAsset({
|
|
3510
3551
|
receiverSparkAddress: lpSparkAddress,
|
|
3511
3552
|
assetAddress: pool.assetBAddress,
|
|
3512
3553
|
amount: params.amountB,
|
|
3513
|
-
}, "Insufficient balance for depositing to V3 pool (Asset B): ");
|
|
3554
|
+
}, "Insufficient balance for depositing to V3 pool (Asset B): ", params.useAvailableBalance);
|
|
3514
3555
|
transferIds.push(assetBTransferId);
|
|
3515
3556
|
}
|
|
3516
3557
|
const executeDeposit = async () => {
|