@flashnet/sdk 0.3.12-rc.1 → 0.3.12-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/src/api/typed-endpoints.d.ts +2 -23
  2. package/dist/cjs/src/api/typed-endpoints.d.ts.map +1 -1
  3. package/dist/cjs/src/api/typed-endpoints.js +3 -33
  4. package/dist/cjs/src/api/typed-endpoints.js.map +1 -1
  5. package/dist/cjs/src/client/FlashnetClient.d.ts +44 -78
  6. package/dist/cjs/src/client/FlashnetClient.d.ts.map +1 -1
  7. package/dist/cjs/src/client/FlashnetClient.js +163 -453
  8. package/dist/cjs/src/client/FlashnetClient.js.map +1 -1
  9. package/dist/cjs/src/types/index.d.ts +17 -223
  10. package/dist/cjs/src/types/index.d.ts.map +1 -1
  11. package/dist/cjs/src/types/index.js +0 -38
  12. package/dist/cjs/src/types/index.js.map +1 -1
  13. package/dist/cjs/src/utils/index.d.ts.map +1 -1
  14. package/dist/cjs/src/utils/index.js +0 -1
  15. package/dist/cjs/src/utils/index.js.map +1 -1
  16. package/dist/cjs/src/utils/intents.d.ts +5 -40
  17. package/dist/cjs/src/utils/intents.d.ts.map +1 -1
  18. package/dist/cjs/src/utils/intents.js +11 -60
  19. package/dist/cjs/src/utils/intents.js.map +1 -1
  20. package/dist/esm/src/api/typed-endpoints.d.ts +2 -23
  21. package/dist/esm/src/api/typed-endpoints.d.ts.map +1 -1
  22. package/dist/esm/src/api/typed-endpoints.js +3 -33
  23. package/dist/esm/src/api/typed-endpoints.js.map +1 -1
  24. package/dist/esm/src/client/FlashnetClient.d.ts +44 -78
  25. package/dist/esm/src/client/FlashnetClient.d.ts.map +1 -1
  26. package/dist/esm/src/client/FlashnetClient.js +164 -454
  27. package/dist/esm/src/client/FlashnetClient.js.map +1 -1
  28. package/dist/esm/src/types/index.d.ts +17 -223
  29. package/dist/esm/src/types/index.d.ts.map +1 -1
  30. package/dist/esm/src/types/index.js +1 -39
  31. package/dist/esm/src/types/index.js.map +1 -1
  32. package/dist/esm/src/utils/index.d.ts.map +1 -1
  33. package/dist/esm/src/utils/index.js +1 -2
  34. package/dist/esm/src/utils/index.js.map +1 -1
  35. package/dist/esm/src/utils/intents.d.ts +5 -40
  36. package/dist/esm/src/utils/intents.d.ts.map +1 -1
  37. package/dist/esm/src/utils/intents.js +12 -58
  38. package/dist/esm/src/utils/intents.js.map +1 -1
  39. package/package.json +1 -1
@@ -4,7 +4,7 @@ import { getClientEnvironmentName, resolveClientNetworkConfig, getClientNetworkC
4
4
  import { getSparkNetworkFromLegacy, getClientEnvironmentFromLegacy, Network } from '../types/index.js';
5
5
  import { generateNonce, compareDecimalStrings } from '../utils/index.js';
6
6
  import { AuthManager } from '../utils/auth.js';
7
- import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage, generateCreateEscrowIntentMessage, generateFundEscrowIntentMessage, generateClaimEscrowIntentMessage } from '../utils/intents.js';
7
+ import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage } from '../utils/intents.js';
8
8
  import { createWalletSigner } from '../utils/signer.js';
9
9
  import { getSparkNetworkFromAddress, encodeSparkAddressNew } from '../utils/spark-address.js';
10
10
  import { encodeSparkHumanReadableTokenIdentifier, decodeSparkHumanReadableTokenIdentifier } from '../utils/tokenAddress.js';
@@ -248,14 +248,27 @@ class FlashnetClient {
248
248
  /**
249
249
  * Check if wallet has sufficient balance for an operation
250
250
  */
251
- async checkBalance(requirements) {
252
- const balance = await this.getBalance();
251
+ async checkBalance(params) {
252
+ const balance = params.walletBalance ?? (await this.getBalance());
253
+ // Check balance
254
+ const requirements = {
255
+ tokens: new Map(),
256
+ };
257
+ for (const balance of params.balancesToCheck) {
258
+ if (balance.assetAddress === BTC_ASSET_PUBKEY) {
259
+ requirements.btc = BigInt(balance.amount);
260
+ }
261
+ else {
262
+ requirements.tokens?.set(balance.assetAddress, BigInt(balance.amount));
263
+ }
264
+ }
253
265
  // Check BTC balance
254
266
  if (requirements.btc && balance.balance < requirements.btc) {
255
- return {
256
- sufficient: false,
257
- message: `Insufficient BTC balance. Required: ${requirements.btc} sats, Available: ${balance.balance} sats`,
258
- };
267
+ throw new Error([
268
+ params.errorPrefix ?? "",
269
+ `Insufficient BTC balance. `,
270
+ `Required: ${requirements.btc} sats, Available: ${balance.balance} sats`,
271
+ ].join(""));
259
272
  }
260
273
  // Check token balances
261
274
  if (requirements.tokens) {
@@ -266,14 +279,14 @@ class FlashnetClient {
266
279
  balance.tokenBalances.get(hrKey);
267
280
  const available = effectiveTokenBalance?.balance ?? 0n;
268
281
  if (available < requiredAmount) {
269
- return {
270
- sufficient: false,
271
- message: `Insufficient token balance for ${tokenPubkey}. Required: ${requiredAmount}, Available: ${available}`,
272
- };
282
+ throw new Error([
283
+ params.errorPrefix ?? "",
284
+ `Insufficient token balance for ${tokenPubkey}. `,
285
+ `Required: ${requiredAmount}, Available: ${available}`,
286
+ ].join(""));
273
287
  }
274
288
  }
275
289
  }
276
- return { sufficient: true };
277
290
  }
278
291
  // ===== Pool Operations =====
279
292
  /**
@@ -312,26 +325,19 @@ class FlashnetClient {
312
325
  await this.ensureInitialized();
313
326
  // Check if we need to add initial liquidity
314
327
  if (params.initialLiquidity) {
315
- const requirements = {
316
- tokens: new Map(),
317
- };
318
- if (params.assetAAddress === BTC_ASSET_PUBKEY) {
319
- requirements.btc = params.initialLiquidity.assetAAmount;
320
- }
321
- else {
322
- requirements.tokens?.set(params.assetAAddress, params.initialLiquidity.assetAAmount);
323
- }
324
- if (params.assetBAddress === BTC_ASSET_PUBKEY) {
325
- requirements.btc =
326
- (requirements.btc || 0n) + params.initialLiquidity.assetBAmount;
327
- }
328
- else {
329
- requirements.tokens?.set(params.assetBAddress, params.initialLiquidity.assetBAmount);
330
- }
331
- const balanceCheck = await this.checkBalance(requirements);
332
- if (!balanceCheck.sufficient) {
333
- throw new Error(`Insufficient balance for initial liquidity: ${balanceCheck.message}`);
334
- }
328
+ await this.checkBalance({
329
+ balancesToCheck: [
330
+ {
331
+ assetAddress: params.assetAAddress,
332
+ amount: params.initialLiquidity.assetAAmount,
333
+ },
334
+ {
335
+ assetAddress: params.assetBAddress,
336
+ amount: params.initialLiquidity.assetBAmount,
337
+ },
338
+ ],
339
+ errorPrefix: "Insufficient balance for initial liquidity: ",
340
+ });
335
341
  }
336
342
  // Generate intent
337
343
  const nonce = generateNonce();
@@ -360,101 +366,44 @@ class FlashnetClient {
360
366
  const response = await this.typedApi.createConstantProductPool(request);
361
367
  // Add initial liquidity if specified
362
368
  if (params.initialLiquidity && response.poolId) {
363
- await this.addInitialLiquidity(response.poolId, params.assetAAddress, params.assetBAddress, params.initialLiquidity.assetAAmount.toString(), params.initialLiquidity.assetBAmount.toString(), params.initialLiquidity.assetAMinAmountIn.toString(), params.initialLiquidity.assetBMinAmountIn.toString());
369
+ await this.addInitialLiquidity(response.poolId, params.assetAAddress, params.assetBAddress, params.initialLiquidity.assetAAmount.toString(), params.initialLiquidity.assetBAmount.toString());
364
370
  }
365
371
  return response;
366
372
  }
367
- /**
368
- * Calculates virtual reserves for a bonding curve AMM.
369
- *
370
- * This helper function calculates the initial virtual reserves (`v_A^0`, `v_B^0`)
371
- * based on the bonding curve parameters. These virtual reserves ensure smooth
372
- * pricing and price continuity during graduation to the double-sided phase.
373
- *
374
- * @param params - The parameters for the calculation.
375
- * @param params.initialTokenSupply - The initial supply of Asset A (tokens to be sold).
376
- * @param params.graduationThresholdPct - The percentage of tokens that need to be sold for graduation (20-95%).
377
- * @param params.targetRaise - The target amount of Asset B to raise at graduation.
378
- * @returns An object containing `virtualReserveA`, `virtualReserveB`, and `threshold`.
379
- */
380
- static calculateVirtualReserves(params) {
381
- const supply = Number(params.initialTokenSupply);
382
- const targetB = Number(params.targetRaise);
383
- const lpFrac = 1.0;
384
- // Validate inputs
385
- if (supply <= 0) {
386
- throw new Error("Initial token supply must be positive");
387
- }
388
- if (targetB <= 0) {
389
- throw new Error("Target raise must be positive");
390
- }
391
- const MIN_GRADUATION_THRESHOLD_PCT = 20;
392
- const MAX_GRADUATION_THRESHOLD_PCT = 95;
393
- // Validate graduation threshold is a positive whole number
394
- if (!Number.isInteger(params.graduationThresholdPct) ||
395
- params.graduationThresholdPct <= 0) {
396
- throw new Error("Graduation threshold percentage must be a positive whole number");
397
- }
398
- if (params.graduationThresholdPct < MIN_GRADUATION_THRESHOLD_PCT ||
399
- params.graduationThresholdPct > MAX_GRADUATION_THRESHOLD_PCT) {
400
- throw new Error(`Graduation threshold percentage must be between ${MIN_GRADUATION_THRESHOLD_PCT} and ${MAX_GRADUATION_THRESHOLD_PCT}`);
401
- }
402
- // Calculate graduation parameters
403
- const f = params.graduationThresholdPct / 100;
404
- const g = lpFrac;
405
- // Check feasibility: f - g*(1-f) > 0
406
- const denom = f - g * (1 - f);
407
- if (denom <= 0) {
408
- throw new Error(`Invalid configuration: graduation threshold ${f * 100}% with LP fraction ${g} is infeasible. Need f > g/(1+g)`);
409
- }
410
- // Calculate virtual reserves and round down to integers
411
- const virtualA = Math.floor((supply * f * f) / denom);
412
- const virtualB = Math.floor((targetB * g * (1 - f)) / denom);
413
- return {
414
- virtualReserveA: virtualA,
415
- virtualReserveB: virtualB,
416
- threshold: params.graduationThresholdPct,
417
- };
418
- }
419
373
  /**
420
374
  * Create a single-sided pool with automatic initial deposit
421
375
  *
422
- * This method creates a single-sided pool and automatically handles the initial deposit.
376
+ * This method creates a single-sided pool and by default automatically handles the initial deposit.
423
377
  * The initial reserve amount will be transferred to the pool and confirmed.
424
378
  */
425
379
  async createSingleSidedPool(params) {
426
380
  await this.ensureInitialized();
381
+ // check that assetAPctSoldAtGraduation is between 0 and 100 - no decimals
382
+ if (params.assetAPctSoldAtGraduation < 0 ||
383
+ params.assetAPctSoldAtGraduation > 100) {
384
+ throw new Error(`assetAPctSoldAtGraduation must be between 0 and 100`);
385
+ }
427
386
  if (!params.hostNamespace && params.totalHostFeeRateBps < 10) {
428
387
  throw new Error(`Host fee must be greater than 10 bps when no host namespace is provided`);
429
388
  }
430
- // Clip decimals off reserves before any operations
431
- const clippedAssetAInitialReserve = Math.floor(Number(params.assetAInitialReserve)).toString();
432
- const clippedVirtualReserveA = Math.floor(Number(params.virtualReserveA)).toString();
433
- const clippedVirtualReserveB = Math.floor(Number(params.virtualReserveB)).toString();
434
- // Check balance for initial reserve
435
- const requirements = {
436
- tokens: new Map(),
437
- };
438
- if (params.assetAAddress === BTC_ASSET_PUBKEY) {
439
- requirements.btc = BigInt(clippedAssetAInitialReserve);
440
- }
441
- else {
442
- requirements.tokens?.set(params.assetAAddress, BigInt(clippedAssetAInitialReserve));
443
- }
444
- const balanceCheck = await this.checkBalance(requirements);
445
- if (!balanceCheck.sufficient) {
446
- throw new Error(`Insufficient balance for pool creation: ${balanceCheck.message}`);
447
- }
389
+ await this.checkBalance({
390
+ balancesToCheck: [
391
+ {
392
+ assetAddress: params.assetAInitialReserve,
393
+ amount: params.assetAInitialReserve,
394
+ },
395
+ ],
396
+ errorPrefix: "Insufficient balance for pool creation: ",
397
+ });
448
398
  // Generate intent
449
399
  const nonce = generateNonce();
450
400
  const intentMessage = generatePoolInitializationIntentMessage({
451
401
  poolOwnerPublicKey: this.publicKey,
452
402
  assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
453
403
  assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
454
- assetAInitialReserve: clippedAssetAInitialReserve,
455
- virtualReserveA: clippedVirtualReserveA,
456
- virtualReserveB: clippedVirtualReserveB,
457
- threshold: params.threshold,
404
+ assetAInitialReserve: params.assetAInitialReserve,
405
+ graduationThresholdPct: params.assetAPctSoldAtGraduation.toString(),
406
+ targetBRaisedAtGraduation: params.targetBRaisedAtGraduation,
458
407
  lpFeeRateBps: params.lpFeeRateBps.toString(),
459
408
  totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
460
409
  nonce,
@@ -466,10 +415,9 @@ class FlashnetClient {
466
415
  poolOwnerPublicKey: this.publicKey,
467
416
  assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
468
417
  assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
469
- assetAInitialReserve: clippedAssetAInitialReserve,
470
- virtualReserveA: clippedVirtualReserveA,
471
- virtualReserveB: clippedVirtualReserveB,
472
- threshold: params.threshold,
418
+ assetAInitialReserve: params.assetAInitialReserve,
419
+ graduationThresholdPct: params.assetAPctSoldAtGraduation,
420
+ targetBRaisedAtGraduation: params.targetBRaisedAtGraduation,
473
421
  lpFeeRateBps: params.lpFeeRateBps.toString(),
474
422
  totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
475
423
  hostNamespace: params.hostNamespace,
@@ -477,56 +425,29 @@ class FlashnetClient {
477
425
  signature: Buffer.from(signature).toString("hex"),
478
426
  };
479
427
  const createResponse = await this.typedApi.createSingleSidedPool(request);
480
- // If pool creation was successful, handle the initial deposit
481
- if (createResponse.poolId) {
482
- try {
483
- // Transfer initial reserve to the pool using new address encoding
484
- const lpSparkAddress = encodeSparkAddressNew({
485
- identityPublicKey: createResponse.poolId,
486
- network: this.sparkNetwork,
487
- });
488
- let assetATransferId;
489
- if (params.assetAAddress === BTC_ASSET_PUBKEY) {
490
- const transfer = await this._wallet.transfer({
491
- amountSats: Number(clippedAssetAInitialReserve),
492
- receiverSparkAddress: lpSparkAddress,
493
- });
494
- assetATransferId = transfer.id;
495
- }
496
- else {
497
- assetATransferId = await this._wallet.transferTokens({
498
- tokenIdentifier: this.toHumanReadableTokenIdentifier(params.assetAAddress),
499
- tokenAmount: BigInt(clippedAssetAInitialReserve),
500
- receiverSparkAddress: lpSparkAddress,
501
- });
502
- }
503
- // Confirm the initial deposit
504
- const confirmNonce = generateNonce();
505
- const confirmIntentMessage = generatePoolConfirmInitialDepositIntentMessage({
506
- poolOwnerPublicKey: this.publicKey,
507
- lpIdentityPublicKey: createResponse.poolId,
508
- assetASparkTransferId: assetATransferId,
509
- nonce: confirmNonce,
510
- });
511
- const confirmMessageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", confirmIntentMessage));
512
- const confirmSignature = await this._wallet.config.signer.signMessageWithIdentityKey(confirmMessageHash, true);
513
- const confirmRequest = {
514
- poolId: createResponse.poolId,
515
- assetASparkTransferId: assetATransferId,
516
- nonce: confirmNonce,
517
- signature: Buffer.from(confirmSignature).toString("hex"),
518
- poolOwnerPublicKey: this.publicKey,
519
- };
520
- const confirmResponse = await this.typedApi.confirmInitialDeposit(confirmRequest);
521
- if (!confirmResponse.confirmed) {
522
- throw new Error(`Failed to confirm initial deposit: ${confirmResponse.message}`);
523
- }
524
- }
525
- catch (error) {
526
- // If initial deposit fails, we should inform the user
527
- throw new Error(`Pool created with ID ${createResponse.poolId}, but initial deposit failed: ${error instanceof Error ? error.message : String(error)}`);
428
+ if (params.disableInitialDeposit) {
429
+ return createResponse;
430
+ }
431
+ try {
432
+ // Transfer initial reserve to the pool using new address encoding
433
+ const lpSparkAddress = encodeSparkAddressNew({
434
+ identityPublicKey: createResponse.poolId,
435
+ network: this.sparkNetwork,
436
+ });
437
+ const assetATransferId = await this.transferAsset({
438
+ receiverSparkAddress: lpSparkAddress,
439
+ assetAddress: params.assetAAddress,
440
+ amount: params.assetAInitialReserve,
441
+ });
442
+ const confirmResponse = await this.confirmInitialDeposit(createResponse.poolId, assetATransferId);
443
+ if (!confirmResponse.confirmed) {
444
+ throw new Error(`Failed to confirm initial deposit: ${confirmResponse.message}`);
528
445
  }
529
446
  }
447
+ catch (error) {
448
+ // If initial deposit fails, we should inform the user
449
+ throw new Error(`Pool created with ID ${createResponse.poolId}, but initial deposit failed: ${error instanceof Error ? error.message : String(error)}`);
450
+ }
530
451
  return createResponse;
531
452
  }
532
453
  /**
@@ -568,46 +489,30 @@ class FlashnetClient {
568
489
  */
569
490
  async executeSwap(params) {
570
491
  await this.ensureInitialized();
571
- // Check balance
572
- const requirements = {
573
- tokens: new Map(),
574
- };
575
- if (params.assetInAddress === BTC_ASSET_PUBKEY) {
576
- requirements.btc = BigInt(params.amountIn);
577
- }
578
- else {
579
- requirements.tokens?.set(params.assetInAddress, BigInt(params.amountIn));
580
- }
581
- const balanceCheck = await this.checkBalance(requirements);
582
- if (!balanceCheck.sufficient) {
583
- throw new Error(`Insufficient balance for swap: ${balanceCheck.message}`);
584
- }
585
492
  // Transfer assets to pool using new address encoding
586
493
  const lpSparkAddress = encodeSparkAddressNew({
587
494
  identityPublicKey: params.poolId,
588
495
  network: this.sparkNetwork,
589
496
  });
590
- let transferId;
591
- if (params.assetInAddress === BTC_ASSET_PUBKEY) {
592
- const transfer = await this._wallet.transfer({
593
- amountSats: Number(params.amountIn),
594
- receiverSparkAddress: lpSparkAddress,
595
- });
596
- transferId = transfer.id;
597
- }
598
- else {
599
- transferId = await this._wallet.transferTokens({
600
- tokenIdentifier: this.toHumanReadableTokenIdentifier(params.assetInAddress),
601
- tokenAmount: BigInt(params.amountIn),
602
- receiverSparkAddress: lpSparkAddress,
603
- });
604
- }
497
+ const transferId = await this.transferAsset({
498
+ receiverSparkAddress: lpSparkAddress,
499
+ assetAddress: params.assetInAddress,
500
+ amount: params.amountIn,
501
+ }, "Insufficient balance for swap: ");
502
+ const response = await this.executeSwapIntent({
503
+ ...params,
504
+ transferId,
505
+ });
506
+ return response;
507
+ }
508
+ async executeSwapIntent(params) {
509
+ await this.ensureInitialized();
605
510
  // Generate swap intent
606
511
  const nonce = generateNonce();
607
512
  const intentMessage = generatePoolSwapIntentMessage({
608
513
  userPublicKey: this.publicKey,
609
514
  lpIdentityPublicKey: params.poolId,
610
- assetInSparkTransferId: transferId,
515
+ assetInSparkTransferId: params.transferId,
611
516
  assetInTokenPublicKey: this.toHexTokenIdentifier(params.assetInAddress),
612
517
  assetOutTokenPublicKey: this.toHexTokenIdentifier(params.assetOutAddress),
613
518
  amountIn: params.amountIn.toString(),
@@ -627,7 +532,7 @@ class FlashnetClient {
627
532
  amountIn: params.amountIn.toString(),
628
533
  maxSlippageBps: params.maxSlippageBps?.toString(),
629
534
  minAmountOut: params.minAmountOut,
630
- assetInSparkTransferId: transferId,
535
+ assetInSparkTransferId: params.transferId,
631
536
  totalIntegratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
632
537
  integratorPublicKey: params.integratorPublicKey || "",
633
538
  nonce,
@@ -656,20 +561,6 @@ class FlashnetClient {
656
561
  */
657
562
  async executeRouteSwap(params) {
658
563
  await this.ensureInitialized();
659
- // Check balance for initial asset
660
- const requirements = {
661
- tokens: new Map(),
662
- };
663
- if (params.initialAssetAddress === BTC_ASSET_PUBKEY) {
664
- requirements.btc = BigInt(params.inputAmount);
665
- }
666
- else {
667
- requirements.tokens?.set(params.initialAssetAddress, BigInt(params.inputAmount));
668
- }
669
- const balanceCheck = await this.checkBalance(requirements);
670
- if (!balanceCheck.sufficient) {
671
- throw new Error(`Insufficient balance for route swap: ${balanceCheck.message}`);
672
- }
673
564
  // Validate hops array
674
565
  if (!params.hops.length) {
675
566
  throw new Error("Route swap requires at least one hop");
@@ -683,21 +574,11 @@ class FlashnetClient {
683
574
  identityPublicKey: firstPoolId,
684
575
  network: this.sparkNetwork,
685
576
  });
686
- let initialTransferId;
687
- if (params.initialAssetAddress === BTC_ASSET_PUBKEY) {
688
- const transfer = await this._wallet.transfer({
689
- amountSats: Number(params.inputAmount),
690
- receiverSparkAddress: lpSparkAddress,
691
- });
692
- initialTransferId = transfer.id;
693
- }
694
- else {
695
- initialTransferId = await this._wallet.transferTokens({
696
- tokenIdentifier: this.toHumanReadableTokenIdentifier(params.initialAssetAddress),
697
- tokenAmount: BigInt(params.inputAmount),
698
- receiverSparkAddress: lpSparkAddress,
699
- });
700
- }
577
+ const initialTransferId = await this.transferAsset({
578
+ receiverSparkAddress: lpSparkAddress,
579
+ assetAddress: params.initialAssetAddress,
580
+ amount: params.inputAmount,
581
+ }, "Insufficient balance for route swap: ");
701
582
  // Prepare hops for validation
702
583
  const hops = params.hops.map((hop) => ({
703
584
  lpIdentityPublicKey: hop.poolId,
@@ -776,63 +657,23 @@ class FlashnetClient {
776
657
  await this.ensureInitialized();
777
658
  // Get pool details to know which assets we're dealing with
778
659
  const pool = await this.getPool(params.poolId);
779
- // Check balance
780
- const requirements = {
781
- tokens: new Map(),
782
- };
783
- if (pool.assetAAddress === BTC_ASSET_PUBKEY) {
784
- requirements.btc = BigInt(params.assetAAmount);
785
- }
786
- else {
787
- requirements.tokens?.set(pool.assetAAddress, BigInt(params.assetAAmount));
788
- }
789
- if (pool.assetBAddress === BTC_ASSET_PUBKEY) {
790
- requirements.btc = (requirements.btc || 0n) + BigInt(params.assetBAmount);
791
- }
792
- else {
793
- requirements.tokens?.set(pool.assetBAddress, BigInt(params.assetBAmount));
794
- }
795
- const balanceCheck = await this.checkBalance(requirements);
796
- if (!balanceCheck.sufficient) {
797
- throw new Error(`Insufficient balance for adding liquidity: ${balanceCheck.message}`);
798
- }
799
660
  // Transfer assets to pool using new address encoding
800
661
  const lpSparkAddress = encodeSparkAddressNew({
801
662
  identityPublicKey: params.poolId,
802
663
  network: this.sparkNetwork,
803
664
  });
804
- // Transfer asset A
805
- let assetATransferId;
806
- if (pool.assetAAddress === BTC_ASSET_PUBKEY) {
807
- const transfer = await this._wallet.transfer({
808
- amountSats: Number(params.assetAAmount),
809
- receiverSparkAddress: lpSparkAddress,
810
- });
811
- assetATransferId = transfer.id;
812
- }
813
- else {
814
- assetATransferId = await this._wallet.transferTokens({
815
- tokenIdentifier: this.toHumanReadableTokenIdentifier(pool.assetAAddress),
816
- tokenAmount: BigInt(params.assetAAmount),
665
+ const [assetATransferId, assetBTransferId] = await this.transferAssets([
666
+ {
817
667
  receiverSparkAddress: lpSparkAddress,
818
- });
819
- }
820
- // Transfer asset B
821
- let assetBTransferId;
822
- if (pool.assetBAddress === BTC_ASSET_PUBKEY) {
823
- const transfer = await this._wallet.transfer({
824
- amountSats: Number(params.assetBAmount),
668
+ assetAddress: pool.assetAAddress,
669
+ amount: params.assetAAmount,
670
+ },
671
+ {
825
672
  receiverSparkAddress: lpSparkAddress,
826
- });
827
- assetBTransferId = transfer.id;
828
- }
829
- else {
830
- assetBTransferId = await this._wallet.transferTokens({
831
- tokenIdentifier: this.toHumanReadableTokenIdentifier(pool.assetBAddress),
832
- tokenAmount: BigInt(params.assetBAmount),
833
- receiverSparkAddress: lpSparkAddress,
834
- });
835
- }
673
+ assetAddress: pool.assetBAddress,
674
+ amount: params.assetBAmount,
675
+ },
676
+ ], "Insufficient balance for adding liquidity: ");
836
677
  // Generate add liquidity intent
837
678
  const nonce = generateNonce();
838
679
  const intentMessage = generateAddLiquidityIntentMessage({
@@ -842,8 +683,6 @@ class FlashnetClient {
842
683
  assetBSparkTransferId: assetBTransferId,
843
684
  assetAAmount: params.assetAAmount.toString(),
844
685
  assetBAmount: params.assetBAmount.toString(),
845
- assetAMinAmountIn: params.assetAMinAmountIn.toString(),
846
- assetBMinAmountIn: params.assetBMinAmountIn.toString(),
847
686
  nonce,
848
687
  });
849
688
  // Sign intent
@@ -856,8 +695,6 @@ class FlashnetClient {
856
695
  assetBSparkTransferId: assetBTransferId,
857
696
  assetAAmountToAdd: params.assetAAmount.toString(),
858
697
  assetBAmountToAdd: params.assetBAmount.toString(),
859
- assetAMinAmountIn: params.assetAMinAmountIn.toString(),
860
- assetBMinAmountIn: params.assetBMinAmountIn.toString(),
861
698
  nonce,
862
699
  signature: Buffer.from(signature).toString("hex"),
863
700
  };
@@ -967,6 +804,7 @@ class FlashnetClient {
967
804
  const intentMessage = generateWithdrawHostFeesIntentMessage({
968
805
  hostPublicKey: this.publicKey,
969
806
  lpIdentityPublicKey: params.lpIdentityPublicKey,
807
+ assetAAmount: params.assetAAmount,
970
808
  assetBAmount: params.assetBAmount,
971
809
  nonce,
972
810
  });
@@ -975,6 +813,7 @@ class FlashnetClient {
975
813
  const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
976
814
  const request = {
977
815
  lpIdentityPublicKey: params.lpIdentityPublicKey,
816
+ assetAAmount: params.assetAAmount,
978
817
  assetBAmount: params.assetBAmount,
979
818
  nonce,
980
819
  signature: Buffer.from(signature).toString("hex"),
@@ -1006,6 +845,7 @@ class FlashnetClient {
1006
845
  const intentMessage = generateWithdrawIntegratorFeesIntentMessage({
1007
846
  integratorPublicKey: this.publicKey,
1008
847
  lpIdentityPublicKey: params.lpIdentityPublicKey,
848
+ assetAAmount: params.assetAAmount,
1009
849
  assetBAmount: params.assetBAmount,
1010
850
  nonce,
1011
851
  });
@@ -1015,6 +855,7 @@ class FlashnetClient {
1015
855
  const request = {
1016
856
  integratorPublicKey: this.publicKey,
1017
857
  lpIdentityPublicKey: params.lpIdentityPublicKey,
858
+ assetAAmount: params.assetAAmount,
1018
859
  assetBAmount: params.assetBAmount,
1019
860
  nonce,
1020
861
  signature: Buffer.from(signature).toString("hex"),
@@ -1034,151 +875,6 @@ class FlashnetClient {
1034
875
  await this.ensureInitialized();
1035
876
  return this.typedApi.getIntegratorFees();
1036
877
  }
1037
- // ===== Escrow Operations =====
1038
- /**
1039
- * Creates a new escrow contract.
1040
- * This is the first step in a two-step process: create, then fund.
1041
- * @param params Parameters to create the escrow.
1042
- * @returns The escrow creation response, including the ID and deposit address.
1043
- */
1044
- async createEscrow(params) {
1045
- await this.ensureInitialized();
1046
- const nonce = generateNonce();
1047
- // The intent message requires a different structure for recipients and conditions
1048
- const intentRecipients = params.recipients.map((r) => ({
1049
- recipientId: r.id,
1050
- amount: r.amount,
1051
- hasClaimed: false, // Default value for creation
1052
- }));
1053
- const intentMessage = generateCreateEscrowIntentMessage({
1054
- creatorPublicKey: this.publicKey,
1055
- assetId: params.assetId,
1056
- assetAmount: params.assetAmount,
1057
- recipients: intentRecipients,
1058
- claimConditions: params.claimConditions, // Assuming API `Condition` is compatible
1059
- abandonHost: params.abandonHost,
1060
- abandonConditions: params.abandonConditions || undefined,
1061
- nonce,
1062
- });
1063
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1064
- const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1065
- const request = {
1066
- creatorPublicKey: this.publicKey,
1067
- assetId: params.assetId,
1068
- assetAmount: params.assetAmount,
1069
- recipients: params.recipients,
1070
- claimConditions: params.claimConditions,
1071
- abandonHost: params.abandonHost,
1072
- abandonConditions: params.abandonConditions,
1073
- nonce,
1074
- signature: Buffer.from(signature).toString("hex"),
1075
- };
1076
- const createResponse = await this.typedApi.createEscrow(request);
1077
- const autoFund = params.autoFund !== false;
1078
- if (!autoFund) {
1079
- return createResponse;
1080
- }
1081
- // Auto-fund the escrow
1082
- return this.fundEscrow({
1083
- escrowId: createResponse.escrowId,
1084
- depositAddress: createResponse.depositAddress,
1085
- assetId: params.assetId,
1086
- assetAmount: params.assetAmount,
1087
- });
1088
- }
1089
- /**
1090
- * Funds an escrow contract to activate it.
1091
- * This handles the asset transfer and confirmation in one step.
1092
- * @param params Parameters to fund the escrow, including asset details and deposit address.
1093
- * @returns The funding confirmation response.
1094
- */
1095
- async fundEscrow(params) {
1096
- await this.ensureInitialized();
1097
- // 1. Balance check
1098
- const requirements = {
1099
- tokens: new Map(),
1100
- };
1101
- if (params.assetId === BTC_ASSET_PUBKEY) {
1102
- requirements.btc = BigInt(params.assetAmount);
1103
- }
1104
- else {
1105
- requirements.tokens?.set(params.assetId, BigInt(params.assetAmount));
1106
- }
1107
- const balanceCheck = await this.checkBalance(requirements);
1108
- if (!balanceCheck.sufficient) {
1109
- throw new Error(`Insufficient balance to fund escrow: ${balanceCheck.message}`);
1110
- }
1111
- // 2. Perform transfer
1112
- const escrowSparkAddress = encodeSparkAddressNew({
1113
- identityPublicKey: params.depositAddress,
1114
- network: this.sparkNetwork,
1115
- });
1116
- let sparkTransferId;
1117
- if (params.assetId === BTC_ASSET_PUBKEY) {
1118
- const transfer = await this._wallet.transfer({
1119
- amountSats: Number(params.assetAmount),
1120
- receiverSparkAddress: escrowSparkAddress,
1121
- });
1122
- sparkTransferId = transfer.id;
1123
- }
1124
- else {
1125
- sparkTransferId = await this._wallet.transferTokens({
1126
- tokenIdentifier: this.toHumanReadableTokenIdentifier(params.assetId),
1127
- tokenAmount: BigInt(params.assetAmount),
1128
- receiverSparkAddress: escrowSparkAddress,
1129
- });
1130
- }
1131
- // 3. Generate intent, sign, and call API
1132
- const nonce = generateNonce();
1133
- const intentMessage = generateFundEscrowIntentMessage({
1134
- escrowId: params.escrowId,
1135
- creatorPublicKey: this.publicKey,
1136
- sparkTransferId,
1137
- nonce,
1138
- });
1139
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1140
- const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1141
- const request = {
1142
- escrowId: params.escrowId,
1143
- sparkTransferId,
1144
- nonce,
1145
- signature: Buffer.from(signature).toString("hex"),
1146
- };
1147
- return this.typedApi.fundEscrow(request);
1148
- }
1149
- /**
1150
- * Claims funds from an active escrow contract.
1151
- * The caller must be a valid recipient and all claim conditions must be met.
1152
- * @param params Parameters for the claim.
1153
- * @returns The claim processing response.
1154
- */
1155
- async claimEscrow(params) {
1156
- await this.ensureInitialized();
1157
- const nonce = generateNonce();
1158
- const intentMessage = generateClaimEscrowIntentMessage({
1159
- escrowId: params.escrowId,
1160
- recipientPublicKey: this.publicKey,
1161
- nonce,
1162
- });
1163
- const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
1164
- const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
1165
- const request = {
1166
- escrowId: params.escrowId,
1167
- nonce,
1168
- signature: Buffer.from(signature).toString("hex"),
1169
- };
1170
- return this.typedApi.claimEscrow(request);
1171
- }
1172
- /**
1173
- * Retrieves the current state of an escrow contract.
1174
- * This is a read-only operation and does not require authentication.
1175
- * @param escrowId The unique identifier of the escrow.
1176
- * @returns The full state of the escrow.
1177
- */
1178
- async getEscrow(escrowId) {
1179
- await this.ensureInitialized();
1180
- return this.typedApi.getEscrow(escrowId);
1181
- }
1182
878
  // ===== Swap History =====
1183
879
  /**
1184
880
  * Get swaps for a specific pool
@@ -1246,46 +942,64 @@ class FlashnetClient {
1246
942
  return this.typedApi.ping();
1247
943
  }
1248
944
  // ===== Helper Methods =====
945
+ /**
946
+ * Performs asset transfer using generalized asset address for both BTC and tokens.
947
+ */
948
+ async transferAsset(recipient, checkBalanceErrorPrefix) {
949
+ const transferIds = await this.transferAssets([recipient], checkBalanceErrorPrefix);
950
+ return transferIds[0];
951
+ }
952
+ /**
953
+ * Performs asset transfers using generalized asset addresses for both BTC and tokens.
954
+ * Supports optional generic to hardcode recipients length so output list can be typed with same length.
955
+ */
956
+ async transferAssets(recipients, checkBalanceErrorPrefix) {
957
+ if (checkBalanceErrorPrefix) {
958
+ await this.checkBalance({
959
+ balancesToCheck: recipients,
960
+ errorPrefix: checkBalanceErrorPrefix,
961
+ });
962
+ }
963
+ const transferIds = [];
964
+ for (const recipient of recipients) {
965
+ if (recipient.assetAddress === BTC_ASSET_PUBKEY) {
966
+ const transfer = await this._wallet.transfer({
967
+ amountSats: Number(recipient.amount),
968
+ receiverSparkAddress: recipient.receiverSparkAddress,
969
+ });
970
+ transferIds.push(transfer.id);
971
+ }
972
+ else {
973
+ const transferId = await this._wallet.transferTokens({
974
+ tokenIdentifier: this.toHumanReadableTokenIdentifier(recipient.assetAddress),
975
+ tokenAmount: BigInt(recipient.amount),
976
+ receiverSparkAddress: recipient.receiverSparkAddress,
977
+ });
978
+ transferIds.push(transferId);
979
+ }
980
+ }
981
+ return transferIds;
982
+ }
1249
983
  /**
1250
984
  * Helper method to add initial liquidity after pool creation
1251
985
  */
1252
- async addInitialLiquidity(poolId, assetAAddress, assetBAddress, assetAAmount, assetBAmount, assetAMinAmountIn, assetBMinAmountIn) {
986
+ async addInitialLiquidity(poolId, assetAAddress, assetBAddress, assetAAmount, assetBAmount) {
1253
987
  const lpSparkAddress = encodeSparkAddressNew({
1254
988
  identityPublicKey: poolId,
1255
989
  network: this.sparkNetwork,
1256
990
  });
1257
- // Transfer asset A
1258
- let assetATransferId;
1259
- if (assetAAddress === BTC_ASSET_PUBKEY) {
1260
- const transfer = await this._wallet.transfer({
1261
- amountSats: Number(assetAAmount),
991
+ const [assetATransferId, assetBTransferId] = await this.transferAssets([
992
+ {
1262
993
  receiverSparkAddress: lpSparkAddress,
1263
- });
1264
- assetATransferId = transfer.id;
1265
- }
1266
- else {
1267
- assetATransferId = await this._wallet.transferTokens({
1268
- tokenIdentifier: this.toHumanReadableTokenIdentifier(assetAAddress),
1269
- tokenAmount: BigInt(assetAAmount),
1270
- receiverSparkAddress: lpSparkAddress,
1271
- });
1272
- }
1273
- // Transfer asset B
1274
- let assetBTransferId;
1275
- if (assetBAddress === BTC_ASSET_PUBKEY) {
1276
- const transfer = await this._wallet.transfer({
1277
- amountSats: Number(assetBAmount),
994
+ assetAddress: assetAAddress,
995
+ amount: assetAAmount,
996
+ },
997
+ {
1278
998
  receiverSparkAddress: lpSparkAddress,
1279
- });
1280
- assetBTransferId = transfer.id;
1281
- }
1282
- else {
1283
- assetBTransferId = await this._wallet.transferTokens({
1284
- tokenIdentifier: this.toHumanReadableTokenIdentifier(assetBAddress),
1285
- tokenAmount: BigInt(assetBAmount),
1286
- receiverSparkAddress: lpSparkAddress,
1287
- });
1288
- }
999
+ assetAddress: assetBAddress,
1000
+ amount: assetBAmount,
1001
+ },
1002
+ ]);
1289
1003
  // Add liquidity
1290
1004
  const nonce = generateNonce();
1291
1005
  const intentMessage = generateAddLiquidityIntentMessage({
@@ -1295,8 +1009,6 @@ class FlashnetClient {
1295
1009
  assetBSparkTransferId: assetBTransferId,
1296
1010
  assetAAmount: assetAAmount.toString(),
1297
1011
  assetBAmount: assetBAmount.toString(),
1298
- assetAMinAmountIn: assetAMinAmountIn.toString(),
1299
- assetBMinAmountIn: assetBMinAmountIn.toString(),
1300
1012
  nonce,
1301
1013
  });
1302
1014
  const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
@@ -1308,8 +1020,6 @@ class FlashnetClient {
1308
1020
  assetBSparkTransferId: assetBTransferId,
1309
1021
  assetAAmountToAdd: assetAAmount.toString(),
1310
1022
  assetBAmountToAdd: assetBAmount.toString(),
1311
- assetAMinAmountIn: assetAMinAmountIn.toString(),
1312
- assetBMinAmountIn: assetBMinAmountIn.toString(),
1313
1023
  nonce,
1314
1024
  signature: Buffer.from(signature).toString("hex"),
1315
1025
  };