@flashnet/sdk 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +479 -0
  2. package/dist/index.d.ts +13 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +15 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/api/client.d.ts +20 -0
  7. package/dist/src/api/client.d.ts.map +1 -0
  8. package/dist/src/api/client.js +85 -0
  9. package/dist/src/api/client.js.map +1 -0
  10. package/dist/src/api/typed-endpoints.d.ts +135 -0
  11. package/dist/src/api/typed-endpoints.d.ts.map +1 -0
  12. package/dist/src/api/typed-endpoints.js +202 -0
  13. package/dist/src/api/typed-endpoints.js.map +1 -0
  14. package/dist/src/api/validation.d.ts +114 -0
  15. package/dist/src/api/validation.d.ts.map +1 -0
  16. package/dist/src/api/validation.js +128 -0
  17. package/dist/src/api/validation.js.map +1 -0
  18. package/dist/src/client/FlashnetClient.d.ts +216 -0
  19. package/dist/src/client/FlashnetClient.d.ts.map +1 -0
  20. package/dist/src/client/FlashnetClient.js +800 -0
  21. package/dist/src/client/FlashnetClient.js.map +1 -0
  22. package/dist/src/config/index.d.ts +14 -0
  23. package/dist/src/config/index.d.ts.map +1 -0
  24. package/dist/src/config/index.js +40 -0
  25. package/dist/src/config/index.js.map +1 -0
  26. package/dist/src/types/index.d.ts +484 -0
  27. package/dist/src/types/index.d.ts.map +1 -0
  28. package/dist/src/types/index.js +2 -0
  29. package/dist/src/types/index.js.map +1 -0
  30. package/dist/src/utils/auth.d.ts +26 -0
  31. package/dist/src/utils/auth.d.ts.map +1 -0
  32. package/dist/src/utils/auth.js +85 -0
  33. package/dist/src/utils/auth.js.map +1 -0
  34. package/dist/src/utils/index.d.ts +8 -0
  35. package/dist/src/utils/index.d.ts.map +1 -0
  36. package/dist/src/utils/index.js +19 -0
  37. package/dist/src/utils/index.js.map +1 -0
  38. package/dist/src/utils/intents.d.ts +86 -0
  39. package/dist/src/utils/intents.d.ts.map +1 -0
  40. package/dist/src/utils/intents.js +133 -0
  41. package/dist/src/utils/intents.js.map +1 -0
  42. package/dist/src/utils/signer.d.ts +29 -0
  43. package/dist/src/utils/signer.d.ts.map +1 -0
  44. package/dist/src/utils/signer.js +34 -0
  45. package/dist/src/utils/signer.js.map +1 -0
  46. package/dist/src/utils/spark-address.d.ts +60 -0
  47. package/dist/src/utils/spark-address.d.ts.map +1 -0
  48. package/dist/src/utils/spark-address.js +227 -0
  49. package/dist/src/utils/spark-address.js.map +1 -0
  50. package/package.json +43 -0
@@ -0,0 +1,800 @@
1
+ import { ApiClient } from "../api/client";
2
+ import { TypedAmmApi } from "../api/typed-endpoints";
3
+ import { AuthManager } from "../utils/auth";
4
+ import { createWalletSigner } from "../utils/signer";
5
+ import { getNetworkFromAddress, encodeSparkAddress, } from "../utils/spark-address";
6
+ import { getNetworkConfig } from "../config";
7
+ import { generateNonce } from "../utils";
8
+ import { generatePoolSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, } from "../utils/intents";
9
+ import { BTC_ASSET_PUBKEY } from "../config";
10
+ import { Network } from "@buildonspark/spark-sdk/proto/spark";
11
+ /**
12
+ * FlashnetClient - A comprehensive client for interacting with Flashnet AMM
13
+ *
14
+ * This client wraps a SparkWallet and provides:
15
+ * - Automatic network detection from the wallet
16
+ * - Automatic authentication
17
+ * - Balance checking before operations
18
+ * - All AMM operations (pools, swaps, liquidity, hosts)
19
+ * - Direct wallet access via client.wallet
20
+ */
21
+ export class FlashnetClient {
22
+ _wallet;
23
+ apiClient;
24
+ typedApi;
25
+ authManager;
26
+ network;
27
+ publicKey = "";
28
+ sparkAddress = "";
29
+ isAuthenticated = false;
30
+ /**
31
+ * Get the underlying wallet instance for direct wallet operations
32
+ */
33
+ get wallet() {
34
+ return this._wallet;
35
+ }
36
+ /**
37
+ * Get the network type
38
+ */
39
+ get networkType() {
40
+ return this.network;
41
+ }
42
+ /**
43
+ * Get the wallet's public key
44
+ */
45
+ get pubkey() {
46
+ return this.publicKey;
47
+ }
48
+ /**
49
+ * Get the wallet's Spark address
50
+ */
51
+ get address() {
52
+ return this.sparkAddress;
53
+ }
54
+ /**
55
+ * Create a new FlashnetClient instance
56
+ * @param wallet - The SparkWallet to use
57
+ * @param options - Client options
58
+ */
59
+ constructor(wallet, options = {}) {
60
+ this._wallet = wallet;
61
+ // We'll initialize these in the init method
62
+ // @ts-expect-error - wallet.config is protected
63
+ const networkEnum = wallet.config.getNetwork();
64
+ const networkName = Network[networkEnum];
65
+ this.network = networkName === "MAINNET" ? "MAINNET" : "REGTEST";
66
+ // panic if mainnet for now
67
+ if (networkName === "MAINNET") {
68
+ throw new Error("Mainnet is not supported yet");
69
+ }
70
+ const config = getNetworkConfig(this.network);
71
+ this.apiClient = new ApiClient(config);
72
+ this.typedApi = new TypedAmmApi(this.apiClient);
73
+ this.authManager = new AuthManager(this.apiClient, "", wallet);
74
+ }
75
+ /**
76
+ * Initialize the client by deducing network and authenticating
77
+ * This is called automatically on first use if not called manually
78
+ */
79
+ async initialize() {
80
+ if (this.isAuthenticated) {
81
+ return;
82
+ }
83
+ // Get wallet details
84
+ this.publicKey = await this._wallet.getIdentityPublicKey();
85
+ this.sparkAddress = await this._wallet.getSparkAddress();
86
+ // Deduce network from spark address
87
+ const detectedNetwork = getNetworkFromAddress(this.sparkAddress);
88
+ if (!detectedNetwork) {
89
+ throw new Error(`Unable to determine network from spark address: ${this.sparkAddress}`);
90
+ }
91
+ this.network = detectedNetwork;
92
+ // panic if mainnet for now
93
+ if (detectedNetwork === "MAINNET") {
94
+ throw new Error("Mainnet is not supported yet");
95
+ }
96
+ // Reinitialize API client with correct network
97
+ const config = getNetworkConfig(this.network);
98
+ this.apiClient = new ApiClient(config);
99
+ this.typedApi = new TypedAmmApi(this.apiClient);
100
+ this.authManager = new AuthManager(this.apiClient, this.publicKey, createWalletSigner(this._wallet));
101
+ // Authenticate
102
+ const token = await this.authManager.authenticate();
103
+ this.apiClient.setAuthToken(token);
104
+ this.isAuthenticated = true;
105
+ }
106
+ /**
107
+ * Ensure the client is initialized
108
+ */
109
+ async ensureInitialized() {
110
+ if (!this.isAuthenticated) {
111
+ await this.initialize();
112
+ }
113
+ }
114
+ /**
115
+ * Get wallet balance including BTC and token balances
116
+ */
117
+ async getBalance() {
118
+ const balance = await this._wallet.getBalance();
119
+ // Convert the wallet's balance format to our format
120
+ const tokenBalances = new Map();
121
+ if (balance.tokenBalances) {
122
+ for (const [tokenPubkey, tokenData] of balance.tokenBalances.entries()) {
123
+ tokenBalances.set(tokenPubkey, {
124
+ balance: BigInt(tokenData.balance),
125
+ tokenInfo: {
126
+ tokenPublicKey: tokenData.tokenInfo.tokenPublicKey,
127
+ tokenName: tokenData.tokenInfo.tokenName,
128
+ tokenSymbol: tokenData.tokenInfo.tokenSymbol,
129
+ tokenDecimals: tokenData.tokenInfo.tokenDecimals,
130
+ maxSupply: tokenData.tokenInfo.maxSupply,
131
+ },
132
+ });
133
+ }
134
+ }
135
+ return {
136
+ balance: BigInt(balance.balance),
137
+ tokenBalances,
138
+ };
139
+ }
140
+ /**
141
+ * Check if wallet has sufficient balance for an operation
142
+ */
143
+ async checkBalance(requirements) {
144
+ const balance = await this.getBalance();
145
+ // Check BTC balance
146
+ if (requirements.btc && balance.balance < requirements.btc) {
147
+ return {
148
+ sufficient: false,
149
+ message: `Insufficient BTC balance. Required: ${requirements.btc} sats, Available: ${balance.balance} sats`,
150
+ };
151
+ }
152
+ // Check token balances
153
+ if (requirements.tokens) {
154
+ for (const [tokenPubkey, requiredAmount,] of requirements.tokens.entries()) {
155
+ const tokenBalance = balance.tokenBalances.get(tokenPubkey);
156
+ const available = tokenBalance?.balance ?? 0n;
157
+ if (available < requiredAmount) {
158
+ return {
159
+ sufficient: false,
160
+ message: `Insufficient token balance for ${tokenPubkey}. Required: ${requiredAmount}, Available: ${available}`,
161
+ };
162
+ }
163
+ }
164
+ }
165
+ return { sufficient: true };
166
+ }
167
+ // ===== Pool Operations =====
168
+ /**
169
+ * List pools with optional filters
170
+ */
171
+ async listPools(query) {
172
+ await this.ensureInitialized();
173
+ return this.typedApi.listPools(query);
174
+ }
175
+ /**
176
+ * Get detailed information about a specific pool
177
+ */
178
+ async getPool(poolId) {
179
+ await this.ensureInitialized();
180
+ return this.typedApi.getPool(poolId);
181
+ }
182
+ /**
183
+ * Get LP position details for a provider in a pool
184
+ */
185
+ async getLpPosition(poolId, providerPublicKey) {
186
+ await this.ensureInitialized();
187
+ const provider = providerPublicKey || this.publicKey;
188
+ return this.typedApi.getLpPosition(poolId, provider);
189
+ }
190
+ /**
191
+ * Create a constant product pool
192
+ */
193
+ async createConstantProductPool(params) {
194
+ await this.ensureInitialized();
195
+ // Check if we need to add initial liquidity
196
+ if (params.initialLiquidity) {
197
+ const requirements = {
198
+ tokens: new Map(),
199
+ };
200
+ if (params.assetATokenPublicKey === BTC_ASSET_PUBKEY) {
201
+ requirements.btc = params.initialLiquidity.assetAAmount;
202
+ }
203
+ else {
204
+ requirements.tokens.set(params.assetATokenPublicKey, params.initialLiquidity.assetAAmount);
205
+ }
206
+ if (params.assetBTokenPublicKey === BTC_ASSET_PUBKEY) {
207
+ requirements.btc =
208
+ (requirements.btc || 0n) + params.initialLiquidity.assetBAmount;
209
+ }
210
+ else {
211
+ requirements.tokens.set(params.assetBTokenPublicKey, params.initialLiquidity.assetBAmount);
212
+ }
213
+ const balanceCheck = await this.checkBalance(requirements);
214
+ if (!balanceCheck.sufficient) {
215
+ throw new Error(`Insufficient balance for initial liquidity: ${balanceCheck.message}`);
216
+ }
217
+ }
218
+ // Generate intent
219
+ const nonce = generateNonce();
220
+ const intentMessage = generateConstantProductPoolInitializationIntentMessage({
221
+ poolOwnerPublicKey: this.publicKey,
222
+ assetATokenPublicKey: params.assetATokenPublicKey,
223
+ assetBTokenPublicKey: params.assetBTokenPublicKey,
224
+ lpFeeRateBps: params.lpFeeRateBps.toString(),
225
+ totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
226
+ nonce,
227
+ });
228
+ // Sign intent
229
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
230
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
231
+ // Create pool
232
+ const request = {
233
+ poolOwnerPublicKey: this.publicKey,
234
+ assetATokenPublicKey: params.assetATokenPublicKey,
235
+ assetBTokenPublicKey: params.assetBTokenPublicKey,
236
+ lpFeeRateBps: params.lpFeeRateBps.toString(),
237
+ totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
238
+ integratorNamespace: params.integratorNamespace || "",
239
+ nonce,
240
+ signature: Buffer.from(signature).toString("hex"),
241
+ };
242
+ const response = await this.typedApi.createConstantProductPool(request);
243
+ // Add initial liquidity if specified
244
+ if (params.initialLiquidity && response.poolId) {
245
+ await this.addInitialLiquidity(response.poolId, params.assetATokenPublicKey, params.assetBTokenPublicKey, params.initialLiquidity.assetAAmount, params.initialLiquidity.assetBAmount);
246
+ }
247
+ return response;
248
+ }
249
+ /**
250
+ * Create a single-sided pool with automatic initial deposit
251
+ *
252
+ * This method creates a single-sided pool and automatically handles the initial deposit.
253
+ * The initial reserve amount will be transferred to the pool and confirmed.
254
+ */
255
+ async createSingleSidedPool(params) {
256
+ await this.ensureInitialized();
257
+ // Check balance for initial reserve
258
+ const requirements = {
259
+ tokens: new Map(),
260
+ };
261
+ if (params.assetATokenPublicKey === BTC_ASSET_PUBKEY) {
262
+ requirements.btc = BigInt(params.assetAInitialReserve);
263
+ }
264
+ else {
265
+ requirements.tokens.set(params.assetATokenPublicKey, BigInt(params.assetAInitialReserve));
266
+ }
267
+ const balanceCheck = await this.checkBalance(requirements);
268
+ if (!balanceCheck.sufficient) {
269
+ throw new Error(`Insufficient balance for pool creation: ${balanceCheck.message}`);
270
+ }
271
+ // Generate intent
272
+ const nonce = generateNonce();
273
+ const intentMessage = generatePoolInitializationIntentMessage({
274
+ poolOwnerPublicKey: this.publicKey,
275
+ assetATokenPublicKey: params.assetATokenPublicKey,
276
+ assetBTokenPublicKey: params.assetBTokenPublicKey,
277
+ assetAInitialReserve: params.assetAInitialReserve,
278
+ assetAInitialVirtualReserve: params.assetAInitialVirtualReserve,
279
+ assetBInitialVirtualReserve: params.assetBInitialVirtualReserve,
280
+ threshold: params.threshold,
281
+ lpFeeRateBps: params.lpFeeRateBps.toString(),
282
+ totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
283
+ nonce,
284
+ });
285
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
286
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
287
+ // Create pool
288
+ const request = {
289
+ poolOwnerPublicKey: this.publicKey,
290
+ assetATokenPublicKey: params.assetATokenPublicKey,
291
+ assetBTokenPublicKey: params.assetBTokenPublicKey,
292
+ assetAInitialReserve: params.assetAInitialReserve,
293
+ assetAInitialVirtualReserve: params.assetAInitialVirtualReserve,
294
+ assetBInitialVirtualReserve: params.assetBInitialVirtualReserve,
295
+ threshold: params.threshold,
296
+ lpFeeRateBps: params.lpFeeRateBps.toString(),
297
+ totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
298
+ hostNamespace: params.hostNamespace,
299
+ nonce,
300
+ signature: Buffer.from(signature).toString("hex"),
301
+ };
302
+ const createResponse = await this.typedApi.createSingleSidedPool(request);
303
+ // If pool creation was successful, handle the initial deposit
304
+ if (createResponse.poolId) {
305
+ try {
306
+ // Transfer initial reserve to the pool
307
+ const lpSparkAddress = encodeSparkAddress({
308
+ identityPublicKey: createResponse.poolId,
309
+ network: this.network,
310
+ });
311
+ let assetATransferId;
312
+ if (params.assetATokenPublicKey === BTC_ASSET_PUBKEY) {
313
+ const transfer = await this._wallet.transfer({
314
+ amountSats: Number(params.assetAInitialReserve),
315
+ receiverSparkAddress: lpSparkAddress,
316
+ });
317
+ assetATransferId = transfer.id;
318
+ }
319
+ else {
320
+ assetATransferId = await this._wallet.transferTokens({
321
+ tokenPublicKey: params.assetATokenPublicKey,
322
+ tokenAmount: BigInt(params.assetAInitialReserve),
323
+ receiverSparkAddress: lpSparkAddress,
324
+ });
325
+ }
326
+ // Confirm the initial deposit
327
+ const confirmNonce = generateNonce();
328
+ const confirmIntentMessage = generatePoolConfirmInitialDepositIntentMessage({
329
+ poolOwnerPublicKey: this.publicKey,
330
+ lpIdentityPublicKey: createResponse.poolId,
331
+ assetASparkTransferId: assetATransferId,
332
+ nonce: confirmNonce,
333
+ });
334
+ const confirmMessageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", confirmIntentMessage));
335
+ const confirmSignature = await this._wallet.config.signer.signMessageWithIdentityKey(confirmMessageHash, true);
336
+ const confirmRequest = {
337
+ poolId: createResponse.poolId,
338
+ assetASparkTransferId: assetATransferId,
339
+ nonce: confirmNonce,
340
+ signature: Buffer.from(confirmSignature).toString("hex"),
341
+ poolOwnerPublicKey: this.publicKey,
342
+ };
343
+ const confirmResponse = await this.typedApi.confirmInitialDeposit(confirmRequest);
344
+ if (!confirmResponse.confirmed) {
345
+ throw new Error(`Failed to confirm initial deposit: ${confirmResponse.message}`);
346
+ }
347
+ }
348
+ catch (error) {
349
+ // If initial deposit fails, we should inform the user
350
+ throw new Error(`Pool created with ID ${createResponse.poolId}, but initial deposit failed: ${error instanceof Error ? error.message : String(error)}`);
351
+ }
352
+ }
353
+ return createResponse;
354
+ }
355
+ /**
356
+ * Confirm initial deposit for single-sided pool
357
+ *
358
+ * Note: This is typically handled automatically by createSingleSidedPool().
359
+ * Use this method only if you need to manually confirm a deposit (e.g., after a failed attempt).
360
+ */
361
+ async confirmInitialDeposit(poolId, assetASparkTransferId) {
362
+ await this.ensureInitialized();
363
+ const nonce = generateNonce();
364
+ const intentMessage = generatePoolConfirmInitialDepositIntentMessage({
365
+ poolOwnerPublicKey: this.publicKey,
366
+ lpIdentityPublicKey: poolId,
367
+ assetASparkTransferId,
368
+ nonce,
369
+ });
370
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
371
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
372
+ const request = {
373
+ poolId,
374
+ assetASparkTransferId,
375
+ nonce,
376
+ signature: Buffer.from(signature).toString("hex"),
377
+ poolOwnerPublicKey: this.publicKey,
378
+ };
379
+ return this.typedApi.confirmInitialDeposit(request);
380
+ }
381
+ // ===== Swap Operations =====
382
+ /**
383
+ * Simulate a swap without executing it
384
+ */
385
+ async simulateSwap(params) {
386
+ await this.ensureInitialized();
387
+ return this.typedApi.simulateSwap(params);
388
+ }
389
+ /**
390
+ * Execute a swap
391
+ */
392
+ async executeSwap(params) {
393
+ await this.ensureInitialized();
394
+ // Check balance
395
+ const requirements = {
396
+ tokens: new Map(),
397
+ };
398
+ if (params.assetInTokenPublicKey === BTC_ASSET_PUBKEY) {
399
+ requirements.btc = params.amountIn;
400
+ }
401
+ else {
402
+ requirements.tokens.set(params.assetInTokenPublicKey, params.amountIn);
403
+ }
404
+ const balanceCheck = await this.checkBalance(requirements);
405
+ if (!balanceCheck.sufficient) {
406
+ throw new Error(`Insufficient balance for swap: ${balanceCheck.message}`);
407
+ }
408
+ // Transfer assets to pool
409
+ const lpSparkAddress = encodeSparkAddress({
410
+ identityPublicKey: params.poolId,
411
+ network: this.network,
412
+ });
413
+ let transferId;
414
+ if (params.assetInTokenPublicKey === BTC_ASSET_PUBKEY) {
415
+ const transfer = await this._wallet.transfer({
416
+ amountSats: Number(params.amountIn),
417
+ receiverSparkAddress: lpSparkAddress,
418
+ });
419
+ transferId = transfer.id;
420
+ }
421
+ else {
422
+ transferId = await this._wallet.transferTokens({
423
+ tokenPublicKey: params.assetInTokenPublicKey,
424
+ tokenAmount: params.amountIn,
425
+ receiverSparkAddress: lpSparkAddress,
426
+ });
427
+ }
428
+ // Generate swap intent
429
+ const nonce = generateNonce();
430
+ const intentMessage = generatePoolSwapIntentMessage({
431
+ userPublicKey: this.publicKey,
432
+ lpIdentityPublicKey: params.poolId,
433
+ assetASparkTransferId: transferId,
434
+ assetInTokenPublicKey: params.assetInTokenPublicKey,
435
+ assetOutTokenPublicKey: params.assetOutTokenPublicKey,
436
+ amountIn: params.amountIn.toString(),
437
+ minAmountOut: params.minAmountOut.toString(),
438
+ maxSlippageBps: params.maxSlippageBps.toString(),
439
+ nonce,
440
+ });
441
+ // Sign intent
442
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
443
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
444
+ const request = {
445
+ userPublicKey: this.publicKey,
446
+ poolId: params.poolId,
447
+ assetInTokenPublicKey: params.assetInTokenPublicKey,
448
+ assetOutTokenPublicKey: params.assetOutTokenPublicKey,
449
+ amountIn: params.amountIn.toString(),
450
+ minAmountOut: params.minAmountOut.toString(),
451
+ maxSlippageBps: params.maxSlippageBps?.toString(),
452
+ assetInSparkTransferId: transferId,
453
+ nonce,
454
+ signature: Buffer.from(signature).toString("hex"),
455
+ };
456
+ const response = await this.typedApi.executeSwap(request);
457
+ // Check if the swap was accepted
458
+ if (!response.accepted) {
459
+ const errorMessage = response.error || "Swap rejected by the AMM";
460
+ const refundInfo = response.refundedAmount
461
+ ? ` Refunded ${response.refundedAmount} of ${response.refundedAssetPublicKey} via transfer ${response.refundTransferId}`
462
+ : "";
463
+ throw new Error(`${errorMessage}.${refundInfo}`);
464
+ }
465
+ return response;
466
+ }
467
+ // ===== Liquidity Operations =====
468
+ /**
469
+ * Simulate adding liquidity
470
+ */
471
+ async simulateAddLiquidity(params) {
472
+ await this.ensureInitialized();
473
+ return this.typedApi.simulateAddLiquidity(params);
474
+ }
475
+ /**
476
+ * Add liquidity to a pool
477
+ */
478
+ async addLiquidity(params) {
479
+ await this.ensureInitialized();
480
+ // Get pool details to know which assets we're dealing with
481
+ const pool = await this.getPool(params.poolId);
482
+ // Check balance
483
+ const requirements = {
484
+ tokens: new Map(),
485
+ };
486
+ if (pool.assetATokenPublicKey === BTC_ASSET_PUBKEY) {
487
+ requirements.btc = params.assetAAmount;
488
+ }
489
+ else {
490
+ requirements.tokens.set(pool.assetATokenPublicKey, params.assetAAmount);
491
+ }
492
+ if (pool.assetBTokenPublicKey === BTC_ASSET_PUBKEY) {
493
+ requirements.btc = (requirements.btc || 0n) + params.assetBAmount;
494
+ }
495
+ else {
496
+ requirements.tokens.set(pool.assetBTokenPublicKey, params.assetBAmount);
497
+ }
498
+ const balanceCheck = await this.checkBalance(requirements);
499
+ if (!balanceCheck.sufficient) {
500
+ throw new Error(`Insufficient balance for adding liquidity: ${balanceCheck.message}`);
501
+ }
502
+ // Transfer assets to pool
503
+ const lpSparkAddress = encodeSparkAddress({
504
+ identityPublicKey: params.poolId,
505
+ network: this.network,
506
+ });
507
+ // Transfer asset A
508
+ let assetATransferId;
509
+ if (pool.assetATokenPublicKey === BTC_ASSET_PUBKEY) {
510
+ const transfer = await this._wallet.transfer({
511
+ amountSats: Number(params.assetAAmount),
512
+ receiverSparkAddress: lpSparkAddress,
513
+ });
514
+ assetATransferId = transfer.id;
515
+ }
516
+ else {
517
+ assetATransferId = await this._wallet.transferTokens({
518
+ tokenPublicKey: pool.assetATokenPublicKey,
519
+ tokenAmount: params.assetAAmount,
520
+ receiverSparkAddress: lpSparkAddress,
521
+ });
522
+ }
523
+ // Transfer asset B
524
+ let assetBTransferId;
525
+ if (pool.assetBTokenPublicKey === BTC_ASSET_PUBKEY) {
526
+ const transfer = await this._wallet.transfer({
527
+ amountSats: Number(params.assetBAmount),
528
+ receiverSparkAddress: lpSparkAddress,
529
+ });
530
+ assetBTransferId = transfer.id;
531
+ }
532
+ else {
533
+ assetBTransferId = await this._wallet.transferTokens({
534
+ tokenPublicKey: pool.assetBTokenPublicKey,
535
+ tokenAmount: params.assetBAmount,
536
+ receiverSparkAddress: lpSparkAddress,
537
+ });
538
+ }
539
+ // Generate add liquidity intent
540
+ const nonce = generateNonce();
541
+ const intentMessage = generateAddLiquidityIntentMessage({
542
+ userPublicKey: this.publicKey,
543
+ lpIdentityPublicKey: params.poolId,
544
+ assetASparkTransferId: assetATransferId,
545
+ assetBSparkTransferId: assetBTransferId,
546
+ assetAAmount: params.assetAAmount.toString(),
547
+ assetBAmount: params.assetBAmount.toString(),
548
+ nonce,
549
+ });
550
+ // Sign intent
551
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
552
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
553
+ const request = {
554
+ userPublicKey: this.publicKey,
555
+ poolId: params.poolId,
556
+ assetASparkTransferId: assetATransferId,
557
+ assetBSparkTransferId: assetBTransferId,
558
+ assetAAmountToAdd: params.assetAAmount.toString(),
559
+ assetBAmountToAdd: params.assetBAmount.toString(),
560
+ nonce,
561
+ signature: Buffer.from(signature).toString("hex"),
562
+ };
563
+ const response = await this.typedApi.addLiquidity(request);
564
+ // Check if the liquidity addition was accepted
565
+ if (!response.accepted) {
566
+ const errorMessage = response.error || "Add liquidity rejected by the AMM";
567
+ const refundInfo = response.refund
568
+ ? ` Refunds: Asset A: ${response.refund.assetAAmount || 0}, Asset B: ${response.refund.assetBAmount || 0}`
569
+ : "";
570
+ throw new Error(`${errorMessage}.${refundInfo}`);
571
+ }
572
+ return response;
573
+ }
574
+ /**
575
+ * Simulate removing liquidity
576
+ */
577
+ async simulateRemoveLiquidity(params) {
578
+ await this.ensureInitialized();
579
+ return this.typedApi.simulateRemoveLiquidity(params);
580
+ }
581
+ /**
582
+ * Remove liquidity from a pool
583
+ */
584
+ async removeLiquidity(params) {
585
+ await this.ensureInitialized();
586
+ // Check LP token balance
587
+ const position = await this.getLpPosition(params.poolId);
588
+ const lpTokensOwned = BigInt(position.lpTokensOwned);
589
+ const tokensToRemove = BigInt(params.lpTokensToRemove);
590
+ if (lpTokensOwned < tokensToRemove) {
591
+ throw new Error(`Insufficient LP tokens. Owned: ${lpTokensOwned}, Requested: ${tokensToRemove}`);
592
+ }
593
+ // Generate remove liquidity intent
594
+ const nonce = generateNonce();
595
+ const intentMessage = generateRemoveLiquidityIntentMessage({
596
+ userPublicKey: this.publicKey,
597
+ lpIdentityPublicKey: params.poolId,
598
+ lpTokensToRemove: params.lpTokensToRemove,
599
+ nonce,
600
+ });
601
+ // Sign intent
602
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
603
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
604
+ const request = {
605
+ userPublicKey: this.publicKey,
606
+ poolId: params.poolId,
607
+ lpTokensToRemove: params.lpTokensToRemove,
608
+ nonce,
609
+ signature: Buffer.from(signature).toString("hex"),
610
+ };
611
+ const response = await this.typedApi.removeLiquidity(request);
612
+ // Check if the liquidity removal was accepted
613
+ if (!response.accepted) {
614
+ const errorMessage = response.error || "Remove liquidity rejected by the AMM";
615
+ throw new Error(errorMessage);
616
+ }
617
+ return response;
618
+ }
619
+ // ===== Host Operations =====
620
+ /**
621
+ * Register as a host
622
+ */
623
+ async registerHost(params) {
624
+ await this.ensureInitialized();
625
+ const feeRecipient = params.feeRecipientPublicKey || this.publicKey;
626
+ const nonce = generateNonce();
627
+ // Generate intent
628
+ const intentMessage = generateRegisterHostIntentMessage({
629
+ namespace: params.namespace,
630
+ minFeeBps: params.minFeeBps,
631
+ feeRecipientPublicKey: feeRecipient,
632
+ nonce,
633
+ });
634
+ // Sign intent
635
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
636
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
637
+ const request = {
638
+ namespace: params.namespace,
639
+ minFeeBps: params.minFeeBps,
640
+ feeRecipientPublicKey: feeRecipient,
641
+ nonce,
642
+ signature: Buffer.from(signature).toString("hex"),
643
+ };
644
+ return this.typedApi.registerHost(request);
645
+ }
646
+ /**
647
+ * Get host information
648
+ */
649
+ async getHost(namespace) {
650
+ await this.ensureInitialized();
651
+ return this.typedApi.getHost(namespace);
652
+ }
653
+ /**
654
+ * Get pool host fees
655
+ */
656
+ async getPoolHostFees(hostNamespace, poolId) {
657
+ await this.ensureInitialized();
658
+ return this.typedApi.getPoolHostFees({ hostNamespace, poolId });
659
+ }
660
+ /**
661
+ * Withdraw host fees
662
+ */
663
+ async withdrawHostFees(params) {
664
+ await this.ensureInitialized();
665
+ const nonce = generateNonce();
666
+ const intentMessage = generateWithdrawHostFeesIntentMessage({
667
+ hostPublicKey: this.publicKey,
668
+ lpIdentityPublicKey: params.lpIdentityPublicKey,
669
+ assetAAmount: params.assetAAmount,
670
+ assetBAmount: params.assetBAmount,
671
+ nonce,
672
+ });
673
+ // Sign intent
674
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
675
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
676
+ const request = {
677
+ lpIdentityPublicKey: params.lpIdentityPublicKey,
678
+ assetAAmount: params.assetAAmount,
679
+ assetBAmount: params.assetBAmount,
680
+ nonce,
681
+ signature: Buffer.from(signature).toString("hex"),
682
+ };
683
+ const response = await this.typedApi.withdrawHostFees(request);
684
+ // Check if the withdrawal was accepted
685
+ if (!response.accepted) {
686
+ const errorMessage = response.error || "Withdraw host fees rejected by the AMM";
687
+ throw new Error(errorMessage);
688
+ }
689
+ return response;
690
+ }
691
+ // ===== Swap History =====
692
+ /**
693
+ * Get swaps for a specific pool
694
+ */
695
+ async getPoolSwaps(lpPubkey, query) {
696
+ await this.ensureInitialized();
697
+ return this.typedApi.getPoolSwaps(lpPubkey, query);
698
+ }
699
+ /**
700
+ * Get global swaps across all pools
701
+ */
702
+ async getGlobalSwaps(query) {
703
+ await this.ensureInitialized();
704
+ return this.typedApi.getGlobalSwaps(query);
705
+ }
706
+ /**
707
+ * Get swaps for a specific user
708
+ */
709
+ async getUserSwaps(userPublicKey, query) {
710
+ await this.ensureInitialized();
711
+ const user = userPublicKey || this.publicKey;
712
+ return this.typedApi.getUserSwaps(user, query);
713
+ }
714
+ // ===== Status =====
715
+ /**
716
+ * Ping the settlement service
717
+ */
718
+ async ping() {
719
+ await this.ensureInitialized();
720
+ return this.typedApi.ping();
721
+ }
722
+ // ===== Helper Methods =====
723
+ /**
724
+ * Helper method to add initial liquidity after pool creation
725
+ */
726
+ async addInitialLiquidity(poolId, assetATokenPublicKey, assetBTokenPublicKey, assetAAmount, assetBAmount) {
727
+ const lpSparkAddress = encodeSparkAddress({
728
+ identityPublicKey: poolId,
729
+ network: this.network,
730
+ });
731
+ // Transfer asset A
732
+ let assetATransferId;
733
+ if (assetATokenPublicKey === BTC_ASSET_PUBKEY) {
734
+ const transfer = await this._wallet.transfer({
735
+ amountSats: Number(assetAAmount),
736
+ receiverSparkAddress: lpSparkAddress,
737
+ });
738
+ assetATransferId = transfer.id;
739
+ }
740
+ else {
741
+ assetATransferId = await this._wallet.transferTokens({
742
+ tokenPublicKey: assetATokenPublicKey,
743
+ tokenAmount: assetAAmount,
744
+ receiverSparkAddress: lpSparkAddress,
745
+ });
746
+ }
747
+ // Transfer asset B
748
+ let assetBTransferId;
749
+ if (assetBTokenPublicKey === BTC_ASSET_PUBKEY) {
750
+ const transfer = await this._wallet.transfer({
751
+ amountSats: Number(assetBAmount),
752
+ receiverSparkAddress: lpSparkAddress,
753
+ });
754
+ assetBTransferId = transfer.id;
755
+ }
756
+ else {
757
+ assetBTransferId = await this._wallet.transferTokens({
758
+ tokenPublicKey: assetBTokenPublicKey,
759
+ tokenAmount: assetBAmount,
760
+ receiverSparkAddress: lpSparkAddress,
761
+ });
762
+ }
763
+ // Add liquidity
764
+ const nonce = generateNonce();
765
+ const intentMessage = generateAddLiquidityIntentMessage({
766
+ userPublicKey: this.publicKey,
767
+ lpIdentityPublicKey: poolId,
768
+ assetASparkTransferId: assetATransferId,
769
+ assetBSparkTransferId: assetBTransferId,
770
+ assetAAmount: assetAAmount.toString(),
771
+ assetBAmount: assetBAmount.toString(),
772
+ nonce,
773
+ });
774
+ const messageHash = new Uint8Array(await crypto.subtle.digest("SHA-256", intentMessage));
775
+ const signature = await this._wallet.config.signer.signMessageWithIdentityKey(messageHash, true);
776
+ const request = {
777
+ userPublicKey: this.publicKey,
778
+ poolId: poolId,
779
+ assetASparkTransferId: assetATransferId,
780
+ assetBSparkTransferId: assetBTransferId,
781
+ assetAAmountToAdd: assetAAmount.toString(),
782
+ assetBAmountToAdd: assetBAmount.toString(),
783
+ nonce,
784
+ signature: Buffer.from(signature).toString("hex"),
785
+ };
786
+ const response = await this.typedApi.addLiquidity(request);
787
+ // Check if the initial liquidity addition was accepted
788
+ if (!response.accepted) {
789
+ const errorMessage = response.error || "Initial liquidity addition rejected by the AMM";
790
+ throw new Error(errorMessage);
791
+ }
792
+ }
793
+ /**
794
+ * Clean up wallet connections
795
+ */
796
+ async cleanup() {
797
+ await this._wallet.cleanupConnections();
798
+ }
799
+ }
800
+ //# sourceMappingURL=FlashnetClient.js.map