@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
|
@@ -11,6 +11,7 @@ var hex = require('../utils/hex.js');
|
|
|
11
11
|
var intents = require('../utils/intents.js');
|
|
12
12
|
var sparkAddress = require('../utils/spark-address.js');
|
|
13
13
|
var tokenAddress = require('../utils/tokenAddress.js');
|
|
14
|
+
var bigint = require('../utils/bigint.js');
|
|
14
15
|
var errors = require('../types/errors.js');
|
|
15
16
|
|
|
16
17
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -245,7 +246,8 @@ class FlashnetClient {
|
|
|
245
246
|
const tokenIdentifierHex = hex.getHexFromUint8Array(info.rawTokenIdentifier);
|
|
246
247
|
const tokenAddress$1 = tokenAddress.encodeSparkHumanReadableTokenIdentifier(info.rawTokenIdentifier, this.sparkNetwork);
|
|
247
248
|
tokenBalances.set(tokenPubkey, {
|
|
248
|
-
balance:
|
|
249
|
+
balance: FlashnetClient.safeBigInt(tokenData.ownedBalance),
|
|
250
|
+
availableToSendBalance: FlashnetClient.safeBigInt(tokenData.availableToSendBalance),
|
|
249
251
|
tokenInfo: {
|
|
250
252
|
tokenIdentifier: tokenIdentifierHex,
|
|
251
253
|
tokenAddress: tokenAddress$1,
|
|
@@ -258,7 +260,7 @@ class FlashnetClient {
|
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
return {
|
|
261
|
-
balance:
|
|
263
|
+
balance: FlashnetClient.safeBigInt(balance.balance),
|
|
262
264
|
tokenBalances,
|
|
263
265
|
};
|
|
264
266
|
}
|
|
@@ -266,17 +268,17 @@ class FlashnetClient {
|
|
|
266
268
|
* Check if wallet has sufficient balance for an operation
|
|
267
269
|
*/
|
|
268
270
|
async checkBalance(params) {
|
|
269
|
-
const balance = await this.getBalance();
|
|
271
|
+
const balance = params.walletBalance ?? (await this.getBalance());
|
|
270
272
|
// Check balance
|
|
271
273
|
const requirements = {
|
|
272
274
|
tokens: new Map(),
|
|
273
275
|
};
|
|
274
276
|
for (const balance of params.balancesToCheck) {
|
|
275
277
|
if (balance.assetAddress === index$1.BTC_ASSET_PUBKEY) {
|
|
276
|
-
requirements.btc =
|
|
278
|
+
requirements.btc = FlashnetClient.safeBigInt(balance.amount);
|
|
277
279
|
}
|
|
278
280
|
else {
|
|
279
|
-
requirements.tokens?.set(balance.assetAddress,
|
|
281
|
+
requirements.tokens?.set(balance.assetAddress, FlashnetClient.safeBigInt(balance.amount));
|
|
280
282
|
}
|
|
281
283
|
}
|
|
282
284
|
// Check BTC balance
|
|
@@ -294,7 +296,9 @@ class FlashnetClient {
|
|
|
294
296
|
const hrKey = this.toHumanReadableTokenIdentifier(tokenPubkey);
|
|
295
297
|
const effectiveTokenBalance = balance.tokenBalances.get(tokenPubkey) ??
|
|
296
298
|
balance.tokenBalances.get(hrKey);
|
|
297
|
-
const available =
|
|
299
|
+
const available = params.useAvailableBalance
|
|
300
|
+
? (effectiveTokenBalance?.availableToSendBalance ?? 0n)
|
|
301
|
+
: (effectiveTokenBalance?.balance ?? 0n);
|
|
298
302
|
if (available < requiredAmount) {
|
|
299
303
|
throw new Error([
|
|
300
304
|
params.errorPrefix ?? "",
|
|
@@ -356,6 +360,7 @@ class FlashnetClient {
|
|
|
356
360
|
},
|
|
357
361
|
],
|
|
358
362
|
errorPrefix: "Insufficient balance for initial liquidity: ",
|
|
363
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
359
364
|
});
|
|
360
365
|
}
|
|
361
366
|
const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
|
|
@@ -415,6 +420,12 @@ class FlashnetClient {
|
|
|
415
420
|
throw new Error(`${name} must be positive integer`);
|
|
416
421
|
}
|
|
417
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Safely convert a value to BigInt. Delegates to the shared `safeBigInt` utility.
|
|
425
|
+
*/
|
|
426
|
+
static safeBigInt(value, fallback = 0n) {
|
|
427
|
+
return bigint.safeBigInt(value, fallback);
|
|
428
|
+
}
|
|
418
429
|
/**
|
|
419
430
|
* Calculates virtual reserves for a bonding curve AMM.
|
|
420
431
|
*
|
|
@@ -435,7 +446,7 @@ class FlashnetClient {
|
|
|
435
446
|
}
|
|
436
447
|
const supply = FlashnetClient.parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
437
448
|
const targetB = FlashnetClient.parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
438
|
-
const graduationThresholdPct =
|
|
449
|
+
const graduationThresholdPct = FlashnetClient.safeBigInt(params.graduationThresholdPct);
|
|
439
450
|
// Align bounds with Rust AMM (20%..95%), then check feasibility for g=1 (requires >50%).
|
|
440
451
|
const MIN_PCT = 20n;
|
|
441
452
|
const MAX_PCT = 95n;
|
|
@@ -485,6 +496,7 @@ class FlashnetClient {
|
|
|
485
496
|
},
|
|
486
497
|
],
|
|
487
498
|
errorPrefix: "Insufficient balance for pool creation: ",
|
|
499
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
488
500
|
});
|
|
489
501
|
const poolOwnerPublicKey = params.poolOwnerPublicKey ?? this.publicKey;
|
|
490
502
|
// Generate intent
|
|
@@ -605,10 +617,14 @@ class FlashnetClient {
|
|
|
605
617
|
});
|
|
606
618
|
// If using free balance (V3 pools only), skip the Spark transfer
|
|
607
619
|
if (params.useFreeBalance) {
|
|
608
|
-
|
|
620
|
+
const swapResponse = await this.executeSwapIntent({
|
|
609
621
|
...params,
|
|
610
622
|
// No transferId - triggers free balance mode
|
|
611
623
|
});
|
|
624
|
+
return {
|
|
625
|
+
...swapResponse,
|
|
626
|
+
inboundSparkTransferId: swapResponse.requestId,
|
|
627
|
+
};
|
|
612
628
|
}
|
|
613
629
|
// Transfer assets to pool using new address encoding
|
|
614
630
|
const lpSparkAddress = sparkAddress.encodeSparkAddressNew({
|
|
@@ -619,12 +635,13 @@ class FlashnetClient {
|
|
|
619
635
|
receiverSparkAddress: lpSparkAddress,
|
|
620
636
|
assetAddress: params.assetInAddress,
|
|
621
637
|
amount: params.amountIn,
|
|
622
|
-
}, "Insufficient balance for swap: ");
|
|
638
|
+
}, "Insufficient balance for swap: ", params.useAvailableBalance);
|
|
623
639
|
// Execute with auto-clawback on failure
|
|
624
|
-
|
|
640
|
+
const swapResponse = await this.executeWithAutoClawback(() => this.executeSwapIntent({
|
|
625
641
|
...params,
|
|
626
642
|
transferId,
|
|
627
643
|
}), [transferId], params.poolId);
|
|
644
|
+
return { ...swapResponse, inboundSparkTransferId: transferId };
|
|
628
645
|
}
|
|
629
646
|
/**
|
|
630
647
|
* Execute a swap with a pre-created transfer or using free balance.
|
|
@@ -754,7 +771,7 @@ class FlashnetClient {
|
|
|
754
771
|
receiverSparkAddress: lpSparkAddress,
|
|
755
772
|
assetAddress: params.initialAssetAddress,
|
|
756
773
|
amount: params.inputAmount,
|
|
757
|
-
}, "Insufficient balance for route swap: ");
|
|
774
|
+
}, "Insufficient balance for route swap: ", params.useAvailableBalance);
|
|
758
775
|
// Execute with auto-clawback on failure
|
|
759
776
|
return this.executeWithAutoClawback(async () => {
|
|
760
777
|
// Prepare hops for validation
|
|
@@ -877,7 +894,7 @@ class FlashnetClient {
|
|
|
877
894
|
assetAddress: pool.assetBAddress,
|
|
878
895
|
amount: params.assetBAmount,
|
|
879
896
|
},
|
|
880
|
-
], "Insufficient balance for adding liquidity: ");
|
|
897
|
+
], "Insufficient balance for adding liquidity: ", params.useAvailableBalance);
|
|
881
898
|
// Execute with auto-clawback on failure
|
|
882
899
|
return this.executeWithAutoClawback(async () => {
|
|
883
900
|
// Generate add liquidity intent
|
|
@@ -1183,6 +1200,7 @@ class FlashnetClient {
|
|
|
1183
1200
|
depositAddress: createResponse.depositAddress,
|
|
1184
1201
|
assetId: params.assetId,
|
|
1185
1202
|
assetAmount: params.assetAmount,
|
|
1203
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
1186
1204
|
});
|
|
1187
1205
|
}
|
|
1188
1206
|
/**
|
|
@@ -1200,6 +1218,7 @@ class FlashnetClient {
|
|
|
1200
1218
|
{ assetAddress: params.assetId, amount: params.assetAmount },
|
|
1201
1219
|
],
|
|
1202
1220
|
errorPrefix: "Insufficient balance to fund escrow: ",
|
|
1221
|
+
useAvailableBalance: params.useAvailableBalance,
|
|
1203
1222
|
});
|
|
1204
1223
|
// 2. Perform transfer
|
|
1205
1224
|
const escrowSparkAddress = sparkAddress.encodeSparkAddressNew({
|
|
@@ -1714,19 +1733,20 @@ class FlashnetClient {
|
|
|
1714
1733
|
/**
|
|
1715
1734
|
* Performs asset transfer using generalized asset address for both BTC and tokens.
|
|
1716
1735
|
*/
|
|
1717
|
-
async transferAsset(recipient, checkBalanceErrorPrefix) {
|
|
1718
|
-
const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix);
|
|
1736
|
+
async transferAsset(recipient, checkBalanceErrorPrefix, useAvailableBalance) {
|
|
1737
|
+
const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix, useAvailableBalance);
|
|
1719
1738
|
return transferIds[0];
|
|
1720
1739
|
}
|
|
1721
1740
|
/**
|
|
1722
1741
|
* Performs asset transfers using generalized asset addresses for both BTC and tokens.
|
|
1723
1742
|
* Supports optional generic to hardcode recipients length so output list can be typed with same length.
|
|
1724
1743
|
*/
|
|
1725
|
-
async transferAssets(recipients, checkBalanceErrorPrefix) {
|
|
1744
|
+
async transferAssets(recipients, checkBalanceErrorPrefix, useAvailableBalance) {
|
|
1726
1745
|
if (checkBalanceErrorPrefix) {
|
|
1727
1746
|
await this.checkBalance({
|
|
1728
1747
|
balancesToCheck: recipients,
|
|
1729
1748
|
errorPrefix: checkBalanceErrorPrefix,
|
|
1749
|
+
useAvailableBalance,
|
|
1730
1750
|
});
|
|
1731
1751
|
}
|
|
1732
1752
|
const transferIds = [];
|
|
@@ -1741,7 +1761,7 @@ class FlashnetClient {
|
|
|
1741
1761
|
else {
|
|
1742
1762
|
const transferId = await this._wallet.transferTokens({
|
|
1743
1763
|
tokenIdentifier: this.toHumanReadableTokenIdentifier(recipient.assetAddress),
|
|
1744
|
-
tokenAmount:
|
|
1764
|
+
tokenAmount: FlashnetClient.safeBigInt(recipient.amount),
|
|
1745
1765
|
receiverSparkAddress: recipient.receiverSparkAddress,
|
|
1746
1766
|
});
|
|
1747
1767
|
transferIds.push(transferId);
|
|
@@ -1848,7 +1868,8 @@ class FlashnetClient {
|
|
|
1848
1868
|
const lightningFeeEstimate = await this.getLightningFeeEstimate(invoice);
|
|
1849
1869
|
// Total BTC needed = invoice amount + lightning fee (unmasked).
|
|
1850
1870
|
// Bitmasking for V2 pools is handled inside findBestPoolForTokenToBtc.
|
|
1851
|
-
const baseBtcNeeded =
|
|
1871
|
+
const baseBtcNeeded = FlashnetClient.safeBigInt(invoiceAmountSats) +
|
|
1872
|
+
FlashnetClient.safeBigInt(lightningFeeEstimate);
|
|
1852
1873
|
// Check Flashnet minimum amounts early to provide clear error messages
|
|
1853
1874
|
const minAmounts = await this.getEnabledMinAmountsMap();
|
|
1854
1875
|
// Check BTC minimum (output from swap)
|
|
@@ -1875,7 +1896,7 @@ class FlashnetClient {
|
|
|
1875
1896
|
const tokenHex = this.toHexTokenIdentifier(tokenAddress).toLowerCase();
|
|
1876
1897
|
const tokenMinAmount = minAmounts.get(tokenHex);
|
|
1877
1898
|
if (tokenMinAmount &&
|
|
1878
|
-
|
|
1899
|
+
FlashnetClient.safeBigInt(poolQuote.tokenAmountRequired) < tokenMinAmount) {
|
|
1879
1900
|
const msg = `Token amount too small. Minimum input is ${tokenMinAmount} units, but calculated amount is only ${poolQuote.tokenAmountRequired} units.`;
|
|
1880
1901
|
throw new errors.FlashnetError(msg, {
|
|
1881
1902
|
response: {
|
|
@@ -1892,7 +1913,7 @@ class FlashnetClient {
|
|
|
1892
1913
|
}
|
|
1893
1914
|
// BTC variable fee adjustment: difference between what the pool targets and unmasked base.
|
|
1894
1915
|
// For V3 pools this is 0 (no masking). For V2 it's the rounding overhead.
|
|
1895
|
-
const btcVariableFeeAdjustment = Number(
|
|
1916
|
+
const btcVariableFeeAdjustment = Number(FlashnetClient.safeBigInt(poolQuote.btcAmountUsed) - baseBtcNeeded);
|
|
1896
1917
|
return {
|
|
1897
1918
|
poolId: poolQuote.poolId,
|
|
1898
1919
|
tokenAddress: this.toHexTokenIdentifier(tokenAddress),
|
|
@@ -1964,7 +1985,7 @@ class FlashnetClient {
|
|
|
1964
1985
|
amountIn: tokenAmount,
|
|
1965
1986
|
integratorBps: options?.integratorFeeRateBps,
|
|
1966
1987
|
});
|
|
1967
|
-
const btcOut =
|
|
1988
|
+
const btcOut = FlashnetClient.safeBigInt(simulation.amountOut);
|
|
1968
1989
|
if (btcOut > bestBtcOut) {
|
|
1969
1990
|
bestBtcOut = btcOut;
|
|
1970
1991
|
bestResult = {
|
|
@@ -2052,7 +2073,7 @@ class FlashnetClient {
|
|
|
2052
2073
|
await this.ensureInitialized();
|
|
2053
2074
|
const { invoice, tokenAddress, tokenAmount, maxSlippageBps = 500, // 5% default
|
|
2054
2075
|
maxLightningFeeSats, preferSpark = true, integratorFeeRateBps, integratorPublicKey, transferTimeoutMs = 30000, // 30s default
|
|
2055
|
-
rollbackOnFailure = false, useExistingBtcBalance = false, } = options;
|
|
2076
|
+
rollbackOnFailure = false, useExistingBtcBalance = false, useAvailableBalance = false, } = options;
|
|
2056
2077
|
try {
|
|
2057
2078
|
// Step 1: Get a quote for the payment
|
|
2058
2079
|
const quote = await this.getPayLightningWithTokenQuote(invoice, tokenAddress, {
|
|
@@ -2069,6 +2090,7 @@ class FlashnetClient {
|
|
|
2069
2090
|
},
|
|
2070
2091
|
],
|
|
2071
2092
|
errorPrefix: "Insufficient token balance for Lightning payment: ",
|
|
2093
|
+
useAvailableBalance,
|
|
2072
2094
|
});
|
|
2073
2095
|
// Step 3: Get pool details
|
|
2074
2096
|
const pool = await this.getPool(quote.poolId);
|
|
@@ -2091,6 +2113,7 @@ class FlashnetClient {
|
|
|
2091
2113
|
minAmountOut: minBtcOut,
|
|
2092
2114
|
integratorFeeRateBps,
|
|
2093
2115
|
integratorPublicKey,
|
|
2116
|
+
useAvailableBalance,
|
|
2094
2117
|
});
|
|
2095
2118
|
if (!swapResponse.accepted || !swapResponse.outboundTransferId) {
|
|
2096
2119
|
return {
|
|
@@ -2100,6 +2123,7 @@ class FlashnetClient {
|
|
|
2100
2123
|
btcAmountReceived: "0",
|
|
2101
2124
|
swapTransferId: swapResponse.outboundTransferId || "",
|
|
2102
2125
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2126
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2103
2127
|
error: swapResponse.error || "Swap was not accepted",
|
|
2104
2128
|
};
|
|
2105
2129
|
}
|
|
@@ -2110,7 +2134,8 @@ class FlashnetClient {
|
|
|
2110
2134
|
const effectiveMaxLightningFee = maxLightningFeeSats ?? quote.estimatedLightningFee;
|
|
2111
2135
|
const btcNeededForPayment = invoiceAmountSats + effectiveMaxLightningFee;
|
|
2112
2136
|
const balance = await this.getBalance();
|
|
2113
|
-
canPayImmediately =
|
|
2137
|
+
canPayImmediately =
|
|
2138
|
+
balance.balance >= FlashnetClient.safeBigInt(btcNeededForPayment);
|
|
2114
2139
|
}
|
|
2115
2140
|
if (!canPayImmediately) {
|
|
2116
2141
|
const transferComplete = await this.waitForTransferCompletion(swapResponse.outboundTransferId, transferTimeoutMs);
|
|
@@ -2122,6 +2147,7 @@ class FlashnetClient {
|
|
|
2122
2147
|
btcAmountReceived: swapResponse.amountOut || "0",
|
|
2123
2148
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2124
2149
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2150
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2125
2151
|
error: "Transfer did not complete within timeout",
|
|
2126
2152
|
};
|
|
2127
2153
|
}
|
|
@@ -2135,8 +2161,8 @@ class FlashnetClient {
|
|
|
2135
2161
|
let invoiceAmountPaid;
|
|
2136
2162
|
if (quote.isZeroAmountInvoice) {
|
|
2137
2163
|
// Zero-amount invoice: pay whatever BTC we received minus lightning fee
|
|
2138
|
-
const actualBtc =
|
|
2139
|
-
const lnFee =
|
|
2164
|
+
const actualBtc = FlashnetClient.safeBigInt(btcReceived);
|
|
2165
|
+
const lnFee = FlashnetClient.safeBigInt(effectiveMaxLightningFee);
|
|
2140
2166
|
const amountToPay = actualBtc - lnFee;
|
|
2141
2167
|
if (amountToPay <= 0n) {
|
|
2142
2168
|
return {
|
|
@@ -2146,6 +2172,7 @@ class FlashnetClient {
|
|
|
2146
2172
|
btcAmountReceived: btcReceived,
|
|
2147
2173
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2148
2174
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2175
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2149
2176
|
error: `BTC received (${btcReceived} sats) is not enough to cover lightning fee (${effectiveMaxLightningFee} sats).`,
|
|
2150
2177
|
};
|
|
2151
2178
|
}
|
|
@@ -2165,6 +2192,12 @@ class FlashnetClient {
|
|
|
2165
2192
|
preferSpark,
|
|
2166
2193
|
});
|
|
2167
2194
|
}
|
|
2195
|
+
// Extract the Spark transfer ID from the lightning payment result.
|
|
2196
|
+
// payLightningInvoice returns LightningSendRequest | WalletTransfer:
|
|
2197
|
+
// - LightningSendRequest has .transfer?.sparkId (the Sparkscan-visible transfer ID)
|
|
2198
|
+
// - WalletTransfer (Spark-to-Spark) has .id directly as the transfer ID
|
|
2199
|
+
// Note: lightningPayment.id (the SSP request ID) is already returned as lightningPaymentId
|
|
2200
|
+
const sparkLightningTransferId = lightningPayment.transfer?.sparkId;
|
|
2168
2201
|
return {
|
|
2169
2202
|
success: true,
|
|
2170
2203
|
poolId: quote.poolId,
|
|
@@ -2175,6 +2208,8 @@ class FlashnetClient {
|
|
|
2175
2208
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2176
2209
|
lightningFeePaid: effectiveMaxLightningFee,
|
|
2177
2210
|
invoiceAmountPaid,
|
|
2211
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2212
|
+
sparkLightningTransferId,
|
|
2178
2213
|
};
|
|
2179
2214
|
}
|
|
2180
2215
|
catch (lightningError) {
|
|
@@ -2194,6 +2229,7 @@ class FlashnetClient {
|
|
|
2194
2229
|
btcAmountReceived: "0",
|
|
2195
2230
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2196
2231
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2232
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2197
2233
|
error: `Lightning payment failed: ${lightningErrorMessage}. Funds rolled back to ${rollbackResult.tokenAmount} tokens.`,
|
|
2198
2234
|
};
|
|
2199
2235
|
}
|
|
@@ -2209,6 +2245,7 @@ class FlashnetClient {
|
|
|
2209
2245
|
btcAmountReceived: btcReceived,
|
|
2210
2246
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2211
2247
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2248
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2212
2249
|
error: `Lightning payment failed: ${lightningErrorMessage}. Rollback also failed: ${rollbackErrorMessage}. BTC remains in wallet.`,
|
|
2213
2250
|
};
|
|
2214
2251
|
}
|
|
@@ -2220,6 +2257,7 @@ class FlashnetClient {
|
|
|
2220
2257
|
btcAmountReceived: btcReceived,
|
|
2221
2258
|
swapTransferId: swapResponse.outboundTransferId,
|
|
2222
2259
|
ammFeePaid: quote.estimatedAmmFee,
|
|
2260
|
+
sparkTokenTransferId: swapResponse.inboundSparkTransferId,
|
|
2223
2261
|
error: `Lightning payment failed: ${lightningErrorMessage}. BTC (${btcReceived} sats) remains in wallet.`,
|
|
2224
2262
|
};
|
|
2225
2263
|
}
|
|
@@ -2382,7 +2420,7 @@ class FlashnetClient {
|
|
|
2382
2420
|
tokenIsAssetA: pool.tokenIsAssetA,
|
|
2383
2421
|
integratorBps: integratorFeeRateBps,
|
|
2384
2422
|
});
|
|
2385
|
-
tokenAmount =
|
|
2423
|
+
tokenAmount = FlashnetClient.safeBigInt(v3Result.amountIn);
|
|
2386
2424
|
fee = v3Result.totalFee;
|
|
2387
2425
|
executionPrice = v3Result.simulation.executionPrice || "0";
|
|
2388
2426
|
priceImpactPct = v3Result.simulation.priceImpactPct || "0";
|
|
@@ -2391,7 +2429,7 @@ class FlashnetClient {
|
|
|
2391
2429
|
else {
|
|
2392
2430
|
// V2: constant product math + simulation verification
|
|
2393
2431
|
const calculation = this.calculateTokenAmountForBtcOutput(btcTarget.toString(), poolDetails.assetAReserve, poolDetails.assetBReserve, poolDetails.lpFeeBps, poolDetails.hostFeeBps, pool.tokenIsAssetA, integratorFeeRateBps);
|
|
2394
|
-
tokenAmount =
|
|
2432
|
+
tokenAmount = FlashnetClient.safeBigInt(calculation.amountIn);
|
|
2395
2433
|
// Verify with simulation
|
|
2396
2434
|
const simulation = await this.simulateSwap({
|
|
2397
2435
|
poolId: pool.lpPublicKey,
|
|
@@ -2400,7 +2438,7 @@ class FlashnetClient {
|
|
|
2400
2438
|
amountIn: calculation.amountIn,
|
|
2401
2439
|
integratorBps: integratorFeeRateBps,
|
|
2402
2440
|
});
|
|
2403
|
-
if (
|
|
2441
|
+
if (FlashnetClient.safeBigInt(simulation.amountOut) < btcTarget) {
|
|
2404
2442
|
const btcReserve = pool.tokenIsAssetA
|
|
2405
2443
|
? poolDetails.assetBReserve
|
|
2406
2444
|
: poolDetails.assetAReserve;
|
|
@@ -2488,9 +2526,9 @@ class FlashnetClient {
|
|
|
2488
2526
|
* @private
|
|
2489
2527
|
*/
|
|
2490
2528
|
calculateTokenAmountForBtcOutput(btcAmountOut, reserveA, reserveB, lpFeeBps, hostFeeBps, tokenIsAssetA, integratorFeeBps) {
|
|
2491
|
-
const amountOut =
|
|
2492
|
-
const resA =
|
|
2493
|
-
const resB =
|
|
2529
|
+
const amountOut = FlashnetClient.safeBigInt(btcAmountOut);
|
|
2530
|
+
const resA = FlashnetClient.safeBigInt(reserveA);
|
|
2531
|
+
const resB = FlashnetClient.safeBigInt(reserveB);
|
|
2494
2532
|
const totalFeeBps = lpFeeBps + hostFeeBps + (integratorFeeBps || 0);
|
|
2495
2533
|
const feeRate = Number(totalFeeBps) / 10000; // Convert bps to decimal
|
|
2496
2534
|
// Token is the input asset
|
|
@@ -2588,7 +2626,7 @@ class FlashnetClient {
|
|
|
2588
2626
|
amountIn: upperBound.toString(),
|
|
2589
2627
|
integratorBps,
|
|
2590
2628
|
});
|
|
2591
|
-
if (
|
|
2629
|
+
if (FlashnetClient.safeBigInt(sim.amountOut) >= desiredBtcOut) {
|
|
2592
2630
|
upperSim = sim;
|
|
2593
2631
|
break;
|
|
2594
2632
|
}
|
|
@@ -2599,7 +2637,7 @@ class FlashnetClient {
|
|
|
2599
2637
|
throw new Error(`V3 pool ${poolId} has insufficient liquidity for ${desiredBtcOut} sats`);
|
|
2600
2638
|
}
|
|
2601
2639
|
// Step 3: Refine estimate via linear interpolation
|
|
2602
|
-
const upperOut =
|
|
2640
|
+
const upperOut = FlashnetClient.safeBigInt(upperSim.amountOut);
|
|
2603
2641
|
// Scale proportionally: if upperBound produced upperOut, we need roughly
|
|
2604
2642
|
// (upperBound * desiredBtcOut / upperOut). Add +1 to avoid undershoot from truncation.
|
|
2605
2643
|
let refined = (upperBound * desiredBtcOut) / upperOut + 1n;
|
|
@@ -2617,7 +2655,7 @@ class FlashnetClient {
|
|
|
2617
2655
|
amountIn: refined.toString(),
|
|
2618
2656
|
integratorBps,
|
|
2619
2657
|
});
|
|
2620
|
-
if (
|
|
2658
|
+
if (FlashnetClient.safeBigInt(refinedSim.amountOut) >= desiredBtcOut) {
|
|
2621
2659
|
bestAmountIn = refined;
|
|
2622
2660
|
bestSim = refinedSim;
|
|
2623
2661
|
}
|
|
@@ -2651,7 +2689,7 @@ class FlashnetClient {
|
|
|
2651
2689
|
amountIn: mid.toString(),
|
|
2652
2690
|
integratorBps,
|
|
2653
2691
|
});
|
|
2654
|
-
if (
|
|
2692
|
+
if (FlashnetClient.safeBigInt(midSim.amountOut) >= desiredBtcOut) {
|
|
2655
2693
|
hi = mid;
|
|
2656
2694
|
bestAmountIn = mid;
|
|
2657
2695
|
bestSim = midSim;
|
|
@@ -2673,7 +2711,7 @@ class FlashnetClient {
|
|
|
2673
2711
|
* @private
|
|
2674
2712
|
*/
|
|
2675
2713
|
calculateMinAmountOut(expectedAmount, slippageBps) {
|
|
2676
|
-
const amount =
|
|
2714
|
+
const amount = FlashnetClient.safeBigInt(expectedAmount);
|
|
2677
2715
|
const slippageFactor = BigInt(10000 - slippageBps);
|
|
2678
2716
|
const minAmount = (amount * slippageFactor) / 10000n;
|
|
2679
2717
|
return minAmount.toString();
|
|
@@ -2892,8 +2930,11 @@ class FlashnetClient {
|
|
|
2892
2930
|
const map = new Map();
|
|
2893
2931
|
for (const item of config) {
|
|
2894
2932
|
if (item.enabled) {
|
|
2933
|
+
if (item.min_amount == null) {
|
|
2934
|
+
continue;
|
|
2935
|
+
}
|
|
2895
2936
|
const key = item.asset_identifier.toLowerCase();
|
|
2896
|
-
const value =
|
|
2937
|
+
const value = FlashnetClient.safeBigInt(item.min_amount);
|
|
2897
2938
|
map.set(key, value);
|
|
2898
2939
|
}
|
|
2899
2940
|
}
|
|
@@ -2915,8 +2956,8 @@ class FlashnetClient {
|
|
|
2915
2956
|
const outHex = this.getHexAddress(params.assetOutAddress);
|
|
2916
2957
|
const minIn = minMap.get(inHex);
|
|
2917
2958
|
const minOut = minMap.get(outHex);
|
|
2918
|
-
const amountIn =
|
|
2919
|
-
const minAmountOut =
|
|
2959
|
+
const amountIn = FlashnetClient.safeBigInt(params.amountIn);
|
|
2960
|
+
const minAmountOut = FlashnetClient.safeBigInt(params.minAmountOut);
|
|
2920
2961
|
if (minIn && minOut) {
|
|
2921
2962
|
if (amountIn < minIn) {
|
|
2922
2963
|
throw new Error(`Minimum amount not met for input asset. Required \
|
|
@@ -2950,13 +2991,13 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
2950
2991
|
const aMin = minMap.get(aHex);
|
|
2951
2992
|
const bMin = minMap.get(bHex);
|
|
2952
2993
|
if (aMin) {
|
|
2953
|
-
const aAmt =
|
|
2994
|
+
const aAmt = FlashnetClient.safeBigInt(params.assetAAmount);
|
|
2954
2995
|
if (aAmt < aMin) {
|
|
2955
2996
|
throw new Error(`Minimum amount not met for Asset A. Required ${aMin.toString()}, provided ${aAmt.toString()}`);
|
|
2956
2997
|
}
|
|
2957
2998
|
}
|
|
2958
2999
|
if (bMin) {
|
|
2959
|
-
const bAmt =
|
|
3000
|
+
const bAmt = FlashnetClient.safeBigInt(params.assetBAmount);
|
|
2960
3001
|
if (bAmt < bMin) {
|
|
2961
3002
|
throw new Error(`Minimum amount not met for Asset B. Required ${bMin.toString()}, provided ${bAmt.toString()}`);
|
|
2962
3003
|
}
|
|
@@ -2978,14 +3019,14 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
2978
3019
|
const aMin = minMap.get(aHex);
|
|
2979
3020
|
const bMin = minMap.get(bHex);
|
|
2980
3021
|
if (aMin) {
|
|
2981
|
-
const predictedAOut =
|
|
3022
|
+
const predictedAOut = FlashnetClient.safeBigInt(simulation.assetAAmount);
|
|
2982
3023
|
const relaxedA = aMin / 2n; // apply 50% relaxation for outputs
|
|
2983
3024
|
if (predictedAOut < relaxedA) {
|
|
2984
3025
|
throw new Error(`Minimum amount not met for Asset A on withdrawal. Required at least ${relaxedA.toString()} (50% relaxed), predicted ${predictedAOut.toString()}`);
|
|
2985
3026
|
}
|
|
2986
3027
|
}
|
|
2987
3028
|
if (bMin) {
|
|
2988
|
-
const predictedBOut =
|
|
3029
|
+
const predictedBOut = FlashnetClient.safeBigInt(simulation.assetBAmount);
|
|
2989
3030
|
const relaxedB = bMin / 2n;
|
|
2990
3031
|
if (predictedBOut < relaxedB) {
|
|
2991
3032
|
throw new Error(`Minimum amount not met for Asset B on withdrawal. Required at least ${relaxedB.toString()} (50% relaxed), predicted ${predictedBOut.toString()}`);
|
|
@@ -3098,20 +3139,20 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
3098
3139
|
const transferIds = [];
|
|
3099
3140
|
// Transfer assets if not using free balance
|
|
3100
3141
|
if (!params.useFreeBalance) {
|
|
3101
|
-
if (
|
|
3142
|
+
if (FlashnetClient.safeBigInt(params.amountADesired) > 0n) {
|
|
3102
3143
|
assetATransferId = await this.transferAsset({
|
|
3103
3144
|
receiverSparkAddress: lpSparkAddress,
|
|
3104
3145
|
assetAddress: pool.assetAAddress,
|
|
3105
3146
|
amount: params.amountADesired,
|
|
3106
|
-
}, "Insufficient balance for adding V3 liquidity (Asset A): ");
|
|
3147
|
+
}, "Insufficient balance for adding V3 liquidity (Asset A): ", params.useAvailableBalance);
|
|
3107
3148
|
transferIds.push(assetATransferId);
|
|
3108
3149
|
}
|
|
3109
|
-
if (
|
|
3150
|
+
if (FlashnetClient.safeBigInt(params.amountBDesired) > 0n) {
|
|
3110
3151
|
assetBTransferId = await this.transferAsset({
|
|
3111
3152
|
receiverSparkAddress: lpSparkAddress,
|
|
3112
3153
|
assetAddress: pool.assetBAddress,
|
|
3113
3154
|
amount: params.amountBDesired,
|
|
3114
|
-
}, "Insufficient balance for adding V3 liquidity (Asset B): ");
|
|
3155
|
+
}, "Insufficient balance for adding V3 liquidity (Asset B): ", params.useAvailableBalance);
|
|
3115
3156
|
transferIds.push(assetBTransferId);
|
|
3116
3157
|
}
|
|
3117
3158
|
}
|
|
@@ -3304,14 +3345,14 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
3304
3345
|
receiverSparkAddress: lpSparkAddress,
|
|
3305
3346
|
assetAddress: pool.assetAAddress,
|
|
3306
3347
|
amount: params.additionalAmountA,
|
|
3307
|
-
}, "Insufficient balance for rebalance (Asset A): ");
|
|
3348
|
+
}, "Insufficient balance for rebalance (Asset A): ", params.useAvailableBalance);
|
|
3308
3349
|
}
|
|
3309
3350
|
if (params.additionalAmountB && BigInt(params.additionalAmountB) > 0n) {
|
|
3310
3351
|
assetBTransferId = await this.transferAsset({
|
|
3311
3352
|
receiverSparkAddress: lpSparkAddress,
|
|
3312
3353
|
assetAddress: pool.assetBAddress,
|
|
3313
3354
|
amount: params.additionalAmountB,
|
|
3314
|
-
}, "Insufficient balance for rebalance (Asset B): ");
|
|
3355
|
+
}, "Insufficient balance for rebalance (Asset B): ", params.useAvailableBalance);
|
|
3315
3356
|
}
|
|
3316
3357
|
// Collect transfer IDs for potential clawback
|
|
3317
3358
|
const transferIds = [];
|
|
@@ -3503,20 +3544,20 @@ ${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toStri
|
|
|
3503
3544
|
let assetBTransferId = "";
|
|
3504
3545
|
const transferIds = [];
|
|
3505
3546
|
// Transfer assets to pool
|
|
3506
|
-
if (
|
|
3547
|
+
if (FlashnetClient.safeBigInt(params.amountA) > 0n) {
|
|
3507
3548
|
assetATransferId = await this.transferAsset({
|
|
3508
3549
|
receiverSparkAddress: lpSparkAddress,
|
|
3509
3550
|
assetAddress: pool.assetAAddress,
|
|
3510
3551
|
amount: params.amountA,
|
|
3511
|
-
}, "Insufficient balance for depositing to V3 pool (Asset A): ");
|
|
3552
|
+
}, "Insufficient balance for depositing to V3 pool (Asset A): ", params.useAvailableBalance);
|
|
3512
3553
|
transferIds.push(assetATransferId);
|
|
3513
3554
|
}
|
|
3514
|
-
if (
|
|
3555
|
+
if (FlashnetClient.safeBigInt(params.amountB) > 0n) {
|
|
3515
3556
|
assetBTransferId = await this.transferAsset({
|
|
3516
3557
|
receiverSparkAddress: lpSparkAddress,
|
|
3517
3558
|
assetAddress: pool.assetBAddress,
|
|
3518
3559
|
amount: params.amountB,
|
|
3519
|
-
}, "Insufficient balance for depositing to V3 pool (Asset B): ");
|
|
3560
|
+
}, "Insufficient balance for depositing to V3 pool (Asset B): ", params.useAvailableBalance);
|
|
3520
3561
|
transferIds.push(assetBTransferId);
|
|
3521
3562
|
}
|
|
3522
3563
|
const executeDeposit = async () => {
|