@atomiqlabs/sdk 5.0.0-dev.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1098 +1,1074 @@
1
- # atomiqlabs SDK
2
-
3
- A typescript multichain client for atomiqlabs trustlesss cross-chain swaps. Enables trustless swaps between smart chains (Solana, EVM, Starknet, etc.) and bitcoin (on-chain - L1 and lightning network - L2).
4
-
5
- Example SDK integration in NodeJS available [here](https://github.com/atomiqlabs/atomiq-sdk-demo/blob/main/src/index.ts)
6
-
7
- ## Installation
8
- ```
9
- npm install @atomiqlabs/sdk@dev
10
- ```
11
-
12
- ## Installing chain-specific connectors
13
-
14
- You can install only the chain-specific connectors that your project requires
15
-
16
- ```
17
- npm install @atomiqlabs/chain-solana@dev
18
- npm install @atomiqlabs/chain-starknet@dev
19
- npm install @atomiqlabs/chain-evm@dev
20
- ```
21
-
22
- ## How to use?
23
-
24
- - [Preparations](#preparations)
25
- - [Setting up signers](#signer)
26
- - [Initialization](#initialization)
27
- - Swaps:
28
- - [Smart Chain -> BTC L1](#swap-smart-chain---bitcoin-on-chain)
29
- - [BTC L1 -> Solana (Old swap protocol)](#swap-bitcoin-on-chain---solana)
30
- - [BTC L1 -> Starknet/EVM (New swap protocol)](#swap-bitcoin-on-chain---starknetevm)
31
- - [Smart Chain -> BTC Lightning network L2](#swap-smart-chain---bitcoin-lightning-network)
32
- - [Smart Chain -> BTC Lightning network L2 (LNURL-pay)](#swap-smart-chain---bitcoin-lightning-network-1)
33
- - [BTC Lightning network L2 -> Smart Chain](#swap-bitcoin-lightning-network---smart-chain)
34
- - [BTC Lightning network L2 (LNURL-withdraw) -> Smart Chain](#swap-bitcoin-lightning-network---smart-chain-1)
35
- - [Swap states](#getting-state-of-the-swap)
36
- - [Swap size limits](#swap-size-limits)
37
- - [Stored swaps](#stored-swaps)
38
- - [Get existing swaps](#get-swap-by-id)
39
- - [Refundable swaps](#get-refundable-swaps)
40
- - [Claimable swaps](#get-claimable-swaps)
41
- - [Helpers](#helpers)
42
- - [Wallet spendable balance](#getting-wallet-balances)
43
- - [Unified address parsers](#unified-address-parser)
44
- - [Customize swapper instance](#additional-swapper-options)
45
-
46
- ### Preparations
47
-
48
- Set Solana & Starknet RPC URL to use
49
-
50
- ```typescript
51
- const solanaRpc = "https://api.mainnet-beta.solana.com";
52
- const starknetRpc = "https://starknet-mainnet.public.blastapi.io/rpc/v0_8";
53
- const citreaRpc = "https://rpc.testnet.citrea.xyz";
54
- ```
55
-
56
- Create swapper factory, here we can pick and choose which chains we want to have supported in the SDK, ensure the "as const" keyword is used such that the typescript compiler can properly infer the types.
57
-
58
- ```typescript
59
- import {SolanaInitializer, SolanaInitializerType} from "@atomiqlabs/chain-solana";
60
- import {StarknetInitializer, StarknetInitializerType} from "@atomiqlabs/chain-starknet";
61
- import {CitreaInitializer, CitreaInitializerType} from "@atomiqlabs/chain-evm";
62
- import {SwapperFactory} from "@atomiqlabs/sdk";
63
-
64
- const Factory = new SwapperFactory<[SolanaInitializerType, StarknetInitializerType, CitreaInitializerType]>([SolanaInitializer, StarknetInitializer, CitreaInitializer] as const);
65
- const Tokens = Factory.Tokens; //Get the supported tokens for all the specified chains.
66
- ```
67
-
68
- #### Browser
69
-
70
- This uses browser's Indexed DB by default
71
-
72
- ```typescript
73
- import {BitcoinNetwork} from "@atomiqlabs/sdk";
74
-
75
- const swapper = Factory.newSwapper({
76
- chains: {
77
- SOLANA: {
78
- rpcUrl: solanaRpc //You can also pass Connection object here
79
- },
80
- STARKNET: {
81
- rpcUrl: starknetRpc //You can also pass Provider object here
82
- },
83
- CITREA: {
84
- rpcUrl: citreaRpc, //You can also pass JsonApiProvider object here
85
- }
86
- },
87
- bitcoinNetwork: BitcoinNetwork.TESTNET //or BitcoinNetwork.MAINNET, BitcoinNetwork.TESTNET4 - this also sets the network to use for Solana (solana devnet for bitcoin testnet) & Starknet (sepolia for bitcoin testnet)
88
- });
89
- ```
90
-
91
- if you want to use custom pricing api, mempool.space RPC url, or tune HTTP request timeouts check out [additional options](#additional-swapper-options)
92
-
93
- #### NodeJS
94
-
95
- For NodeJS we need to use sqlite storage, for that we first need to install the sqlite storage adaptor
96
-
97
- ```
98
- npm install @atomiqlabs/storage-sqlite
99
- ```
100
-
101
- Then use pass it in the newSwapper function
102
-
103
- ```typescript
104
- import {SqliteStorageManager, SqliteUnifiedStorage} from "@atomiqlabs/storage-sqlite";
105
- import {BitcoinNetwork} from "@atomiqlabs/sdk";
106
-
107
- const swapper = Factory.newSwapper({
108
- chains: {
109
- SOLANA: {
110
- rpcUrl: solanaRpc //You can also pass Connection object here
111
- },
112
- STARKNET: {
113
- rpcUrl: starknetRpc //You can also pass Provider object here
114
- },
115
- CITREA: {
116
- rpcUrl: citreaRpc, //You can also pass JsonApiProvider object here
117
- }
118
- },
119
- bitcoinNetwork: BitcoinNetwork.TESTNET, //or BitcoinNetwork.MAINNET - this also sets the network to use for Solana (solana devnet for bitcoin testnet) & Starknet (sepolia for bitcoin testnet)
120
- //The following lines are important for running on backend node.js,
121
- // because the SDK by default uses browser's Indexed DB
122
- swapStorage: chainId => new SqliteUnifiedStorage("CHAIN_"+chainId+".sqlite3"),
123
- chainStorageCtor: name => new SqliteStorageManager("STORE_"+name+".sqlite3"),
124
- });
125
- ```
126
-
127
- if you want to use custom pricing api, mempool.space RPC url, or tune HTTP request timeouts check out [additional options](#additional-swapper-options)
128
-
129
- ### Signer
130
-
131
- ```typescript
132
- import {SolanaSigner} from "@atomiqlabs/chain-solana";
133
- //Browser - react, using solana wallet adapter
134
- const anchorWallet = useAnchorWallet();
135
- const wallet = new SolanaSigner(anchorWallet);
136
- ```
137
-
138
- ```typescript
139
- import {WalletAccount} from "starknet";
140
- import {StarknetSigner} from "@atomiqlabs/chain-starknet";
141
- //Browser, using get-starknet
142
- const swo = await connect();
143
- const wallet = new StarknetSigner(new WalletAccount(starknetRpc, swo.wallet));
144
- ```
145
-
146
- or
147
-
148
- ```typescript
149
- import {Keypair} from "@solana/web3.js";
150
- import {SolanaKeypairWallet, SolanaSigner} from "@atomiqlabs/chain-solana";
151
- //Creating Solana signer from private key
152
- const solanaSigner = new SolanaSigner(new SolanaKeypairWallet(Keypair.fromSecretKey(solanaKey)), Keypair.fromSecretKey(solanaKey));
153
- ```
154
-
155
- ```typescript
156
- import {StarknetSigner, StarknetKeypairWallet} from "@atomiqlabs/chain-starknet";
157
- //Creating Starknet signer from private key
158
- const starknetSigner = new StarknetSigner(new StarknetKeypairWallet(starknetRpc, starknetKey));
159
- ```
160
-
161
- ```typescript
162
- import {BaseWallet, SigningKey} from "ethers";
163
- import {EVMSigner} from "@atomiqlabs/chain-evm";
164
- //Creating EVM signer from private key
165
- const wallet = new BaseWallet(new SigningKey(evmKey));
166
- const evmWallet = new EVMSigner(wallet, wallet.address);
167
- ```
168
-
169
- ### Initialization
170
-
171
- Initialize the swapper
172
-
173
- ```typescript
174
- await swapper.init();
175
- ```
176
-
177
- Now we have the multichain swapper initialized
178
-
179
- ### Extract chain-specific swapper with signer
180
-
181
- To make it easier to do swaps between bitcoin and a specific chain we can extract a chain-specific swapper, and also set a signer. e.g.:
182
-
183
- ```typescript
184
- const solanaSwapper = swapper.withChain<"SOLANA">("SOLANA");
185
- ```
186
-
187
- or also with signer
188
-
189
- ```typescript
190
- const starknetSwapperWithSigner = swapper.withChain<"STARKNET">("STARKNET").withSigner(signer);
191
- ```
192
-
193
- ### Bitcoin on-chain swaps
194
-
195
- #### Swap Smart chain -> Bitcoin on-chain
196
-
197
- Getting swap quote
198
-
199
- ```typescript
200
- const _exactIn = false; //exactIn = false, so we specify the output amount
201
- const _amount = 10000n; //Amount in BTC base units - sats (10000 sats = 0.0001 BTC)
202
- const _address = "bc1qtw67hj77rt8zrkkg3jgngutu0yfgt9czjwusxt"; //BTC address of the recipient
203
-
204
- //Create the swap: swapping SOL to Bitcoin on-chain, receiving _amount of satoshis (smallest unit of bitcoin) to _address
205
- const swap = await swapper.swap(
206
- Tokens.SOLANA.SOL, //From specified source token
207
- Tokens.BITCOIN.BTC, //Swap to BTC
208
- _amount,
209
- _exactIn,
210
- solanaSigner.getAddress(), //Source address and smart chain signer
211
- _address //Destination of the swap
212
- );
213
-
214
- //Get the amount required to pay and fee
215
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
216
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
217
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
218
-
219
- const output: string = swap.getOutput().toString(); //Total output amount
220
-
221
- //Get swap expiration time
222
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
223
-
224
- //Get pricing info
225
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
226
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
227
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
228
- ```
229
-
230
- Initiating the swap
231
-
232
- ```typescript
233
- //Initiate and pay for the swap
234
- await swap.commit(solanaSigner);
235
- ```
236
-
237
- or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
238
-
239
- Wait for the swap to execute, refund in case of failure
240
-
241
- ```typescript
242
- //Wait for the swap to conclude
243
- const result: boolean = await swap.waitForPayment();
244
- if(!result) {
245
- //Swap failed, money can be refunded
246
- await swap.refund();
247
- } else {
248
- //Swap successful, we can get the bitcoin txId
249
- const bitcoinTxId = swap.getBitcoinTxId();
250
- }
251
- ```
252
-
253
- ##### Swap states
254
-
255
- - ToBTCSwapState.REFUNDED = -3
256
- - Swap failed and was successfully refunded
257
- - ToBTCSwapState.QUOTE_EXPIRED = -2
258
- - Swap quote expired and cannot be executed anymore
259
- - ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
260
- - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
261
- - ToBTCSwapState.CREATED = 0
262
- - Swap quote is created, waiting to be executed
263
- - ToBTCSwapState.COMMITED = 1,
264
- - Swap was initiated (init transaction sent)
265
- - ToBTCSwapState.SOFT_CLAIMED = 2,
266
- - Swap was processed by the counterparty but not yet claimed on-chain (bitcoin transaction was sent, but unconfirmed yet)
267
- - ToBTCSwapState.CLAIMED = 3
268
- - Swap was finished and funds were successfully claimed by the counterparty
269
- - ToBTCSwapState.REFUNDABLE = 4
270
- - Swap was initiated but counterparty failed to process it, the user can now refund his funds
271
-
272
- #### Swap Bitcoin on-chain -> Solana
273
-
274
- NOTE: Solana uses an old swap protocol for Bitcoin on-chain -> Solana swaps, the flow here is different from the one for Starknet and other chains.
275
-
276
- Getting swap quote
277
-
278
- ```typescript
279
- const _exactIn = true; //exactIn = true, so we specify the input amount
280
- const _amount = fromHumanReadableString("0.0001", Tokens.BITCOIN.BTC); //Amount in BTC base units - sats, we can also use a utility function here
281
-
282
- //Create the swap: swapping _amount of satoshis of Bitcoin on-chain to SOL
283
- const swap = await swapper.swap(
284
- Tokens.BITCOIN.BTC, //Swap from BTC
285
- Tokens.SOLANA.SOL, //Into specified destination token
286
- _amount,
287
- _exactIn, //Whether we define an input or output amount
288
- undefined, //Source address for the swap, not used for swaps from BTC
289
- solanaSigner.getAddress() //Destination address
290
- );
291
-
292
- //Get the amount required to pay and fee
293
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
294
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
295
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
296
-
297
- const output: string = swap.getOutput().toString(); //Total output amount
298
-
299
- //Get swap expiration time
300
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
301
-
302
- //Get security deposit amount (Human readable amount of SOL that needs to be put down to rent the liquidity from swap intermediary), you will get this deposit back if the swap succeeds
303
- const securityDeposit: string = swap.getSecurityDeposit().toString();
304
- //Get claimer bounty (Human readable amount of SOL reserved as a reward for watchtowers to claim the swap on your behalf)
305
- const claimerBounty: string = swap.getClaimerBounty().toString();
306
-
307
- //Get pricing info
308
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
309
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
310
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
311
- ```
312
-
313
- Initiating the swap
314
-
315
- ```typescript
316
- //Initiate the swap on the destination chain (Solana) by opening up the bitcoin swap address
317
- await swap.commit(solanaSigner);
318
- ```
319
-
320
- or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
321
-
322
- Sending bitcoin
323
-
324
- ```typescript
325
- //Get the bitcoin address
326
- const receivingAddressOnBitcoin = swap.getAddress();
327
- //Get the QR code data (contains the address and amount)
328
- const qrCodeData = swap.getHyperlink(); //Data that can be displayed in the form of QR code
329
- //Send the exact amount of BTC to the provided address
330
- ```
331
-
332
- or get a psbt and sign it
333
-
334
- ```typescript
335
- //Or obtain the funded PSBT (input already added) - ready for signing
336
- const {psbt, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""});
337
- for(let signIdx of signInputs) {
338
- psbt.signIdx(..., signIdx); //Or pass it to external signer
339
- }
340
- const bitcoinTxId = await swap.submitPsbt(psbt);
341
- ```
342
-
343
- Waiting for swap execution
344
-
345
- ```typescript
346
- try {
347
- //Wait for the payment to arrive
348
- await swap.waitForBitcoinTransaction(
349
- null, null,
350
- (
351
- txId: string, //Transaction ID of the received bitcoin transaction
352
- confirmations: number, //Current confirmations of the transaction
353
- targetConfirmations: number, //Required confirmations
354
- transactionETAms: number //Estimated in time (in milliseconds) until when the transaction will receive required amount of confirmations
355
- ) => {
356
- //Callback for transaction updates
357
- }
358
- );
359
- } catch(e) {
360
- //Error occurred while waiting for payment, this is most likely due to network errors
361
- return;
362
- }
363
-
364
- //Swap should get automatically claimed by the watchtowers, if not we can call swap.claim() ourselves
365
- try {
366
- await swap.waitTillClaimed(timeoutSignal(30*1000));
367
- } catch (e) {
368
- //Claim ourselves when automatic claim doesn't happen in 30 seconds
369
- await swap.claim(solanaSigner);
370
- }
371
- ```
372
-
373
- ##### Swap states
374
-
375
- - FromBTCSwapState.EXPIRED = -3
376
- - Bitcoin swap address expired
377
- - FromBTCSwapState.QUOTE_EXPIRED = -2
378
- - Swap quote expired and cannot be executed anymore
379
- - FromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
380
- - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
381
- - FromBTCSwapState.PR_CREATED = 0
382
- - Swap quote is created, waiting for the user to open a bitcoin swap address
383
- - FromBTCSwapState.CLAIM_COMMITED = 1
384
- - Bitcoin swap address is opened
385
- - FromBTCSwapState.BTC_TX_CONFIRMED = 2
386
- - Bitcoin transaction sending funds to the swap address is confirmed
387
- - FromBTCSwapState.CLAIM_CLAIMED = 3
388
- - Swap funds are claimed to the user's wallet
389
-
390
-
391
- #### Swap Bitcoin on-chain -> Starknet/EVM
392
-
393
- NOTE: Starknet & EVM uses a new swap protocol for Bitcoin on-chain -> Smart chain swaps, the flow here is different from the one for Solana!
394
-
395
- Getting swap quote
396
-
397
- ```typescript
398
- const _exactIn = true; //exactIn = true, so we specify the input amount
399
- const _amount = fromHumanReadableString("0.0001", Tokens.BITCOIN.BTC); //Amount in BTC base units - sats, we can also use a utility function here
400
-
401
- //Create the swap: swapping _amount of satoshis of Bitcoin on-chain to SOL
402
- const swap = await swapper.swap(
403
- Tokens.BITCOIN.BTC, //Swap from BTC
404
- Tokens.STARKNET.STRK, //Into specified destination token
405
- _amount,
406
- _exactIn, //Whether we define an input or output amount
407
- undefined, //Source address for the swap, not used for swaps from BTC
408
- starknetSigner.getAddress(), //Destination address
409
- {
410
- gasAmount: 1_000_000_000_000_000_000n //We can also request a gas drop on the destination chain (here requesting 1 STRK)
411
- }
412
- );
413
-
414
- //Get the amount required to pay and fee
415
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
416
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
417
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
418
-
419
- const output: string = swap.getOutput().toString(); //Total output amount
420
-
421
- //Get swap expiration time
422
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
423
-
424
- //Get pricing info
425
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
426
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
427
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
428
- ```
429
-
430
- Initiating the swap
431
-
432
- ```typescript
433
- //Obtain the funded PSBT (input already added) - ready for signing
434
- const {psbt, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""});
435
- for(let signIdx of signInputs) {
436
- psbt.signIdx(..., signIdx); //Or pass it to external signer
437
- }
438
- const bitcoinTxId = await swap.submitPsbt(psbt);
439
- ```
440
-
441
- or get raw PSBT and add inputs manually
442
-
443
- ```typescript
444
- //Or obtain raw PSBT to which inputs still need to be added
445
- const {psbt, in1sequence} = await swap.getPsbt();
446
- psbt.addInput(...);
447
- //Make sure the second input's sequence (index 1) is as specified in the in1sequence variable
448
- psbt.updateInput(1, {sequence: in1sequence});
449
- //Sign the PSBT, sign every input except the first one
450
- for(let i=1;i<psbt.inputsLength; i++) psbt.signIdx(..., i); //Or pass it to external signer
451
- //Submit the signed PSBT
452
- const bitcoinTxId = await swap.submitPsbt(psbt);
453
- ```
454
-
455
- Waiting for swap execution
456
-
457
- ```typescript
458
- try {
459
- //Wait for the payment to arrive
460
- await swap.waitForBitcoinTransaction(
461
- null, null,
462
- (
463
- txId: string, //Transaction ID of the received bitcoin transaction
464
- confirmations: number, //Current confirmations of the transaction
465
- targetConfirmations: number, //Required confirmations
466
- transactionETAms: number //Estimated in time (in milliseconds) until when the transaction will receive required amount of confirmations
467
- ) => {
468
- //Callback for transaction updates
469
- }
470
- );
471
- } catch(e) {
472
- //Error occurred while waiting for payment, this is most likely due to network errors
473
- return;
474
- }
475
-
476
- //Swap should get automatically claimed by the watchtowers, if not we can call swap.claim() ourselves
477
- try {
478
- await swap.waitTillClaimedOrFronted(timeoutSignal(30*1000));
479
- } catch (e) {
480
- //Claim ourselves when automatic claim doesn't happen in 30 seconds
481
- await swap.claim(starknetSigner);
482
- }
483
- ```
484
-
485
- ##### Swap states
486
-
487
- - SpvFromBTCSwapState.CLOSED = -5
488
- - Catastrophic failure during swap, shall never happen
489
- - SpvFromBTCSwapState.FAILED = -4
490
- - Bitcoin transaction was sent, but was double-spent later, therefore the swap was failed (no BTC was sent)
491
- - SpvFromBTCSwapState.DECLINED = -3
492
- - LP declined to process the swap transaction, no BTC was sent
493
- - SpvFromBTCSwapState.QUOTE_EXPIRED = -2
494
- - Swap quote expired and cannot be executed anymore
495
- - SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
496
- - Swap quote soft-expired (i.e. the quote probably expired, but if there is a bitcoin transaction being submitted it might still succeed)
497
- - SpvFromBTCSwapState.CREATED = 0
498
- - Swap quote is created, waiting on user to sign the bitcoin swap transaction
499
- - SpvFromBTCSwapState.SIGNED = 1
500
- - Bitcoin swap transaction was signed by the client
501
- - SpvFromBTCSwapState.POSTED = 2
502
- - Bitcoin swap transaction was posted to the LP
503
- - SpvFromBTCSwapState.BROADCASTED = 3
504
- - LP broadcasted the bitcoin swap transaction
505
- - SpvFromBTCSwapState.FRONTED = 4
506
- - Swap funds have been deposited to the user's wallet in front of the time
507
- - SpvFromBTCSwapState.BTC_TX_CONFIRMED = 5
508
- - Bitcoin swap transaction is confirmed
509
- - SpvFromBTCSwapState.CLAIM_CLAIMED = 6
510
- - Swap funds are claimed to the user's wallet
511
-
512
- ### Bitcoin lightning network swaps
513
-
514
- #### Swap Smart chain -> Bitcoin lightning network
515
-
516
- Getting swap quote
517
-
518
- ```typescript
519
- //Destination lightning network invoice, amount needs to be part of the invoice!
520
- const _lightningInvoice = "lnbc10u1pj2q0g9pp5ejs6m677m39cznpzum7muruvh50ys93ln82p4j9ks2luqm56xxlshp52r2anlhddfa9ex9vpw9gstxujff8a0p8s3pzvua930js0kwfea6scqzzsxqyz5vqsp5073zskc5qfgp7lre0t6s8uexxxey80ax564hsjklfwfjq2ew0ewq9qyyssqvzmgs6f8mvuwgfa9uqxhtza07qem4yfhn9wwlpskccmuwplsqmh8pdy6c42kqdu8p73kky9lsnl40qha5396d8lpgn90y27ltfc5rfqqq59cya";
521
-
522
- //Create the swap: swapping SOL to Bitcoin lightning
523
- const swap = await swapper.swap(
524
- Tokens.SOLANA.SOL, //From specified source token
525
- Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
526
- undefined, //Amount is specified in the lightning network invoice!
527
- false, //Make sure we use exactIn=false for swaps to BTC-LN, if you want to use exactIn=true and set an amount, use LNURL-pay!
528
- solanaSigner.getAddress(), //Source address and smart chain signer
529
- _lightningInvoice //Destination of the swap
530
- );
531
-
532
- //Get the amount required to pay and fee
533
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
534
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
535
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
536
-
537
- const output: string = swap.getOutput().toString(); //Total output amount
538
-
539
- //Get swap expiration time
540
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
541
-
542
- //Get pricing info
543
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
544
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
545
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
546
- ```
547
-
548
- Initiating the swap
549
-
550
- ```typescript
551
- //Initiate and pay for the swap
552
- await swap.commit(solanaSigner);
553
- ```
554
-
555
- or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
556
-
557
- Wait for the swap to execute, refund in case of failure
558
-
559
- ```typescript
560
- //Wait for the swap to conclude
561
- const result: boolean = await swap.waitForPayment();
562
- if(!result) {
563
- //Swap failed, money can be refunded
564
- await swap.refund(solanaSigner);
565
- } else {
566
- //Swap successful, we can get the lightning payment secret pre-image, which acts as a proof of payment
567
- const lightningSecret = swap.getSecret();
568
- }
569
- ```
570
-
571
- ##### Swap states
572
-
573
- - ToBTCSwapState.REFUNDED = -3
574
- - Swap failed and was successfully refunded
575
- - ToBTCSwapState.QUOTE_EXPIRED = -2
576
- - Swap quote expired and cannot be executed anymore
577
- - ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
578
- - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
579
- - ToBTCSwapState.CREATED = 0
580
- - Swap quote is created, waiting to be executed
581
- - ToBTCSwapState.COMMITED = 1,
582
- - Swap was initiated (init transaction sent)
583
- - ToBTCSwapState.SOFT_CLAIMED = 2,
584
- - Swap was processed by the counterparty but not yet claimed on-chain (lightning network payment secret was revealed)
585
- - ToBTCSwapState.CLAIMED = 3
586
- - Swap was finished and funds were successfully claimed by the counterparty
587
- - ToBTCSwapState.REFUNDABLE = 4
588
- - Swap was initiated but counterparty failed to process it, the user can now refund his funds
589
-
590
- #### Swap Bitcoin lightning network -> Smart chain
591
-
592
- Getting swap quote
593
-
594
- ```typescript
595
- const _exactIn = true; //exactIn = true, so we specify the input amount
596
- const _amount = 10000n; //Amount in BTC base units - sats
597
-
598
- const swap = await swapper.swap(
599
- Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
600
- Tokens.STARKNET.STRK, //Into specified destination token
601
- _amount,
602
- _exactIn, //Whether we define an input or output amount
603
- undefined, //Source address for the swap, not used for swaps from BTC-LN
604
- signer.getAddress() //Destination address
605
- );
606
-
607
- //Get the bitcoin lightning network invoice (the invoice contains pre-entered amount)
608
- const receivingLightningInvoice: string = swap.getAddress();
609
- //Get the URI hyperlink (contains the lightning network invoice) which can be displayed also as QR code
610
- const qrCodeData: string = swap.getHyperlink();
611
-
612
- //Get the amount required to pay and fee
613
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
614
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
615
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
616
-
617
- const output: string = swap.getOutput().toString(); //Total output amount
618
-
619
- //Get swap expiration time
620
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
621
-
622
- //Get security deposit amount (Human readable amount of STRK that needs to be put down to rent the liquidity from swap intermediary), you will get this deposit back if the swap succeeds
623
- const securityDeposit: string = swap.getSecurityDeposit().toString();
624
-
625
- //Get pricing info
626
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
627
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
628
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
629
- ```
630
-
631
- Pay the displayed lightning network invoice from an external wallet
632
-
633
- Wait for the payment to be received
634
-
635
- ```typescript
636
- //Start listening to incoming lightning network payment
637
- const success = await swap.waitForPayment();
638
- if(!success) {
639
- //Lightning network payment not received in time and quote expired!
640
- return;
641
- }
642
- ```
643
-
644
- Claim the funds on the destination smart chains, this settles the swap and lightning network payment
645
-
646
- ```typescript
647
- try {
648
- //Claim the swap funds - this will initiate 2 transactions
649
- if(swap.canCommitAndClaimInOneShot()) {
650
- //Some chains (e.g. Solana) support signing multiple transactions in one flow
651
- await swap.commitAndClaim(solanaSigner);
652
- } else {
653
- //Other chains (e.g. Starknet) don't support signing multiple transaction in one flow, therefore you need to sign one-by-one
654
- await swap.commit(starknetSigner);
655
- await swap.claim(starknetSigner);
656
- }
657
- } catch(e) {
658
- //Error occurred while waiting for payment
659
- }
660
- ```
661
-
662
- or [sign and send transactions manually](#manually-signing-smart-chain-transactions)
663
-
664
- ##### Swap states
665
-
666
- - FromBTCLNSwapState.FAILED = -4
667
- - If the claiming of the funds was initiated, but never concluded, the user will get his lightning network payment refunded
668
- - FromBTCLNSwapState.QUOTE_EXPIRED = -3
669
- - Swap quote expired and cannot be executed anymore
670
- - FromBTCLNSwapState.QUOTE_SOFT_EXPIRED = -2
671
- - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
672
- - FromBTCLNSwapState.EXPIRED = -1
673
- - Lightning network invoice expired, meaning the swap is expired
674
- - FromBTCLNSwapState.PR_CREATED = 0
675
- - Swap is created, the user should now pay the provided lightning network invoice
676
- - FromBTCLNSwapState.PR_PAID = 1
677
- - Lightning network invoice payment was received (but cannot be settled by the counterparty yet)
678
- - FromBTCLNSwapState.CLAIM_COMMITED = 2
679
- - Claiming of the funds was initiated
680
- - FromBTCLNSwapState.CLAIM_CLAIMED = 3
681
- - Funds were successfully claimed & lightning network secret pre-image revealed, so the lightning network payment will settle now
682
-
683
- ### LNURLs & readable lightning identifiers
684
-
685
- LNURLs extend the lightning network functionality by creating static lightning addreses (LNURL-pay & static internet identifiers) and QR codes which allow you to pull funds from them (LNURL-withdraw)
686
-
687
- This SDK supports:
688
- * LNURL-pay ([LUD-6](https://github.com/lnurl/luds/blob/luds/06.md), [LUD-9](https://github.com/lnurl/luds/blob/luds/09.md), [LUD-10](https://github.com/lnurl/luds/blob/luds/10.md), [LUD-12](https://github.com/lnurl/luds/blob/luds/12.md))
689
- * LNURL-withdraw ([LUD-3](https://github.com/lnurl/luds/blob/luds/03.md))
690
- * Static internet identifiers ([LUD-16](https://github.com/lnurl/luds/blob/luds/16.md))
691
-
692
- You can parse LNURLs and lightning invoices automatically using the [Unified address parser](#unified-address-parser)
693
-
694
- #### Differences
695
-
696
- Lightning invoices:
697
- * One time use only
698
- * Need to have a fixed amount, therefore recipient has to set the amount
699
- * Static and bounded expiration
700
- * You can only pay to a lightning invoice, not withdraw funds from it
701
-
702
- LNURLs & lightning identifiers:
703
- * Reusable
704
- * Programmable expiry
705
- * Allows payer to set an amount
706
- * Supports both, paying (LNURL-pay) and withdrawing (LNURL-withdraw)
707
- * Possibility to attach a message/comment to a payment
708
- * Receive a message/url as a result of the payment
709
-
710
- #### Swap Smart chain -> Bitcoin lightning network
711
-
712
- Getting swap quote
713
-
714
- ```typescript
715
- const _lnurlOrIdentifier: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27"; //Destination LNURL-pay or readable identifier
716
- const _exactIn = false; //exactIn = false, so we specify the output amount
717
- const _amount: bigint = 10000n; //Amount of satoshis to send (1 BTC = 100 000 000 satoshis)
718
-
719
- //Create the swap: swapping SOL to Bitcoin lightning
720
- const swap = await swapper.swap(
721
- Tokens.SOLANA.SOL, //From specified source token
722
- Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
723
- _amount, //Now we can specify an amount for a lightning network payment!
724
- _exactIn, //We can also use exactIn=true here and set an amount in input token
725
- solanaSigner.getAddress(), //Source address and smart chain signer
726
- _lnurlOrIdentifier, //Destination of the swap
727
- {
728
- comment: "Hello world" //For LNURL-pay we can also pass a comment to the recipient
729
- }
730
- );
731
-
732
- //Get the amount required to pay and fee
733
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
734
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
735
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
736
-
737
- const output: string = swap.getOutput().toString(); //Total output amount
738
-
739
- //Get swap expiration time
740
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
741
-
742
- //Get pricing info
743
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
744
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
745
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
746
- ```
747
-
748
- Initiating the swap
749
-
750
- ```typescript
751
- //Initiate and pay for the swap
752
- await swap.commit(solanaSigner);
753
- ```
754
-
755
- or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
756
-
757
- Wait for the swap to execute, refund in case of failure
758
-
759
- ```typescript
760
- //Wait for the swap to conclude
761
- const result: boolean = await swap.waitForPayment();
762
- if(!result) {
763
- //Swap failed, money can be refunded
764
- await swap.refund(solanaSigner);
765
- } else {
766
- //Swap successful, we can get the lightning payment secret pre-image, which acts as a proof of payment
767
- const lightningSecret = swap.getSecret();
768
- //In case the LNURL contained a success action, we can read it now and display it to user
769
- if(swap.hasSuccessAction()) {
770
- //Contains a success action that should displayed to the user
771
- const successMessage = swap.getSuccessAction();
772
- const description: string = successMessage.description; //Description of the message
773
- const text: (string | null) = successMessage.text; //Main text of the message
774
- const url: (string | null) = successMessage.url; //URL link which should be displayed
775
- }
776
- }
777
- ```
778
-
779
- #### Swap Bitcoin lightning network -> Smart chain
780
-
781
- Getting swap quote
782
-
783
- ```typescript
784
- const _lnurl: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27"; //Destination LNURL-pay or readable identifier
785
- const _exactIn = true; //exactIn = true, so we specify the input amount
786
- const _amount = 10000n; //Amount in BTC base units - sats
787
-
788
- const swap = await swapper.swap(
789
- Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
790
- Tokens.STARKNET.STRK, //Into specified destination token
791
- _amount,
792
- _exactIn, //Whether we define an input or output amount
793
- _lnurl, //Source LNURL for the swap
794
- signer.getAddress(), //Destination address
795
- );
796
-
797
- //Get the amount required to pay and fee
798
- const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
799
- const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
800
- const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
801
-
802
- const output: string = swap.getOutput().toString(); //Total output amount
803
-
804
- //Get swap expiration time
805
- const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
806
-
807
- //Get security deposit amount (Human readable amount of STRK that needs to be put down to rent the liquidity from swap intermediary), you will get this deposit back if the swap succeeds
808
- const securityDeposit: string = swap.getSecurityDeposit().toString();
809
-
810
- //Get pricing info
811
- const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
812
- const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
813
- const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
814
- ```
815
-
816
- Wait for the payment to be received
817
-
818
- ```typescript
819
- //Start listening to incoming lightning network payment
820
- const success = await swap.waitForPayment();
821
- if(!success) {
822
- //Lightning network payment not received in time and quote expired!
823
- return;
824
- }
825
- ```
826
-
827
- Claim the funds on the destination smart chains, this settles the swap and lightning network payment
828
-
829
- ```typescript
830
- try {
831
- //Claim the swap funds - this will initiate 2 transactions
832
- if(swap.canCommitAndClaimInOneShot()) {
833
- //Some chains (e.g. Solana) support signing multiple transactions in one flow
834
- await swap.commitAndClaim(solanaSigner);
835
- } else {
836
- //Other chains (e.g. Starknet) don't support signing multiple transaction in one flow, therefore you need to sign one-by-one
837
- await swap.commit(starknetSigner);
838
- await swap.claim(starknetSigner);
839
- }
840
- } catch(e) {
841
- //Error occurred while waiting for payment
842
- }
843
- ```
844
-
845
- or [sign and send transactions manually](#manually-signing-smart-chain-transactions)
846
-
847
- ### Getting state of the swap
848
-
849
- You can get the current state of the swap with:
850
-
851
- ```typescript
852
- const state = swap.getState();
853
- ```
854
-
855
- You can also set a listener to listen for swap state changes:
856
-
857
- ```typescript
858
- swap.events.on("swapState", swap => {
859
- const newState = swap.getState();
860
- });
861
- ```
862
-
863
- For the meaning of the states please refer to the "Swap state" section under each swap type.
864
-
865
- ### Swap size limits
866
-
867
- Swap sizes are limited by the LPs you are connected to, they are advertised in BTC terms by LPs during handshake
868
-
869
- ```typescript
870
- const swapLimits = swapper.getSwapLimits(srcToken, dstToken);
871
- const inputMin = swapLimits.input.min;
872
- const inputMax = swapLimits.input.max;
873
- const outputMin = swapLimits.output.min;
874
- const outputMax = swapLimits.output.max;
875
- ```
876
-
877
- NOTE: swap limits denominated in BTC are retrieved from the LPs during initial handshake, however limits in other tokens are only returned when getting a quote fails due to amount being too low or too high. For example if you want to get swap limits for the BTC -> SOL swap, the input limits will be immediately available, while the output limits will only get populated once a quote request fails due to amount being too low or high.
878
-
879
- ```typescript
880
- let swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
881
- let inputMin = swapLimits.input.min; //Immediately available
882
- let inputMax = swapLimits.input.max; //Immediately available
883
- let outputMin = swapLimits.output.min; //Not available from the get-go
884
- let outputMax = swapLimits.output.max; //Not available from the get-go
885
-
886
- //You can also listen to swap limit changes (optional)
887
- swapper.on("swapLimitsChanged", () => {
888
- //New limits available with swapper.getSwapLimits(srcToken, dstToken)
889
- //Useful in e.g. a react application where you want to dynamically set min/max swappable amount
890
- })
891
-
892
- //Try to swap really small amount of SOL with exactOut swap
893
- try {
894
- const swap = await swapper.swap(
895
- Tokens.BITCOIN.BTC, //Swap from BTC
896
- Tokens.SOLANA.SOL, //Into specified destination token
897
- 1n, //1 lamport = 0.000000001 SOL
898
- false, //Whether we define an input or output amount
899
- undefined, //Source address for the swap, not used for swaps from BTC
900
- solanaSigner.getAddress() //Destination address
901
- );
902
- } catch (e) {
903
- //Fails with OutOfBoundsError
904
- }
905
-
906
- swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
907
- inputMin = swapLimits.input.min; //Immediately available
908
- inputMax = swapLimits.input.max; //Immediately available
909
- outputMin = swapLimits.output.min; //Now available due to failed quote
910
- outputMax = swapLimits.output.max; //Now available due to failed quote
911
- ```
912
-
913
- ### Stored swaps
914
-
915
- #### Get swap by ID
916
-
917
- You can retrieve a swap by it's id, you can get an ID of the swap with
918
-
919
- ```typescript
920
- const swapId = swap.getId();
921
- ```
922
-
923
- And then later retrieve it from the storage
924
-
925
- ```typescript
926
- const swap = await swapper.getSwapById(id);
927
- ```
928
-
929
- #### Get refundable swaps
930
- You can refund the swaps in one of two cases:
931
- * In case intermediary is non-cooperative and goes offline, you can claim the funds from the swap contract back after some time.
932
- * In case intermediary tried to pay but was unsuccessful, so he sent you signed message with which you can refund now without waiting.
933
-
934
- This call can be checked on every startup and periodically every few minutes.
935
- ```typescript
936
- //Get refundable swaps and refund them
937
- const refundableSolanaSwaps = await swapper.getRefundableSwaps("SOLANA", solanaSigner.getAddress());
938
- for(let swap of refundableSolanaSwaps) await swap.refund(solanaSigner);
939
- const refundableStarknetSwaps = await swapper.getRefundableSwaps("STARKNET", starknetSigner.getAddress());
940
- for(let swap of refundableStarknetSwaps) await swap.refund(starknetSigner);
941
- ```
942
-
943
- #### Get claimable swaps
944
- Returns swaps that are ready to be claimed by the client, this can happen if client closes the application when a swap is in-progress and the swap is concluded while the client is offline.
945
-
946
- ```typescript
947
- //Get the swaps
948
- const claimableSolanaSwaps = await solanaSwapper.getClaimableSwaps("SOLANA", solanaSigner.getAddress());
949
- //Claim all the claimable swaps
950
- for(let swap of claimableSolanaSwaps) {
951
- if(swap.canCommit()) await swap.commit(solanaSigner); //This is for Bitcoin (lightning) -> Smart chain swaps, where commit & claim procedure might be needed
952
- await swap.claim(solanaSigner);
953
- }
954
- //Get the swaps
955
- const claimableStarknetSwaps = await solanaSwapper.getClaimableSwaps("STARKNET", starknetSigner.getAddress());
956
- //Claim all the claimable swaps
957
- for(let swap of claimableStarknetSwaps) {
958
- if(swap.canCommit()) await swap.commit(starknetSigner); //This is for Bitcoin (lightning) -> Smart chain swaps, where commit & claim procedure might be needed
959
- await swap.claim(starknetSigner);
960
- }
961
- ```
962
-
963
- ### Helpers
964
-
965
- #### Getting wallet balances
966
-
967
- The SDK also contains helper functions for getting the maximum spendable balance of wallets
968
-
969
- ```typescript
970
- //Spendable balance of the starknet wallet address (discounting transaction fees)
971
- const strkBalance = await swapper.Utils.getSpendableBalance(starknetSigner, Tokens.STARKNET.STRK);
972
- //Spendable balance of the solana wallet address (discounting transaction fees)
973
- const solBalance = await swapper.Utils.getSpendableBalance(solanaSigner, Tokens.SOLANA.SOL);
974
- //Spendable balance of the bitcoin wallet - here we also need to specify the destination chain (as there are different swap protocols available with different on-chain footprints)
975
- const {balance: btcBalance, feeRate: btcFeeRate} = await swapper.Utils.getBitcoinSpendableBalance(bitcoinWalletAddress, "SOLANA");
976
- ```
977
-
978
- #### Unified address parser
979
-
980
- A common way for parsing all address formats supported by the SDK, automatically recognizes:
981
- - Bitcoin on-chain L1 address formats (p2pkh, p2wpkh, p2wsh, p2wsh, p2tr)
982
- - [BIP-21](https://en.bitcoin.it/wiki/BIP_0021) bitcoin payment URI
983
- - BOLT11 lightning network invoices
984
- - [LUD-6](https://github.com/lnurl/luds/blob/luds/06.md) LNURL-pay links
985
- - [LUD-3](https://github.com/lnurl/luds/blob/luds/03.md) LNURL-withdraw links
986
- - [LUD-16](https://github.com/lnurl/luds/blob/luds/16.md) Lightning static internet identifiers
987
- - Smart chain addresses (Solana, Starknet, etc.)
988
-
989
- ```typescript
990
- const res = await swapper.Utils.parseAddress(address);
991
- switch(res.type) {
992
- case "BITCOIN":
993
- //Bitcoin on-chain L1 address or BIP-21 URI scheme with amount
994
- const btcAmount = res.amount;
995
- break;
996
- case "LIGHTNING":
997
- //BOLT11 lightning network invoice with pre-set amount
998
- const lnAmount = res.amount;
999
- break;
1000
- case "LNURL":
1001
- //LNURL payment or withdrawal link
1002
- if(isLNURLWithdraw(res.lnurl)) {
1003
- //LNURL-withdraw allowing withdrawals over the lightning network
1004
- const lnurlWithdrawData: LNURLWithdraw = res.lnurl;
1005
- const minWithdrawable = res.min; //Minimum payment amount
1006
- const maxWithdrawable = res.max; //Maximum payment amount
1007
- const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
1008
- //Should show a UI allowing the user to choose an amount he wishes to withdraw
1009
- }
1010
- if(isLNURLPay(res.lnurl)) {
1011
- //LNURL-pay or static lightning internet identifier allowing repeated payments over the lightning network
1012
- const lnurlPayData: LNURLPay = res.lnurl;
1013
- const minPayable = res.min; //Minimum payment amount
1014
- const maxPayable = res.max; //Maximum payment amount
1015
- const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
1016
- const icon: (string | null) = res.lnurl.icon; //URL encoded icon that should be displayed on the UI
1017
- const shortDescription: (string | null) = res.lnurl.shortDescription; //Short description of the payment
1018
- const longDescription: (string | null) = res.lnurl.longDescription; //Long description of the payment
1019
- const maxCommentLength: (number | 0) = res.lnurl.commentMaxLength; //Maximum allowed length of the payment message/comment (0 means no comment allowed)
1020
- //Should show a UI displaying the icon, short description, long description, allowing the user to choose an amount he wishes to pay and possibly also a comment
1021
- }
1022
- break;
1023
- default:
1024
- //Addresses for smart chains
1025
- break;
1026
- }
1027
- ```
1028
-
1029
- ### Manually signing smart chain transactions
1030
-
1031
- You can also sign the transactions on smart chain side (Solana, Starknet, etc.) of the SDK externally by a separate wallet. Each function which executes any transaction has its txs(action) counterpart, e.g.:
1032
- - commit() -> txsCommit()
1033
- - claim() -> txsClaim()
1034
- - commitAndClaim -> txsCommitAndClaim()
1035
- - refund() -> txsRefund()
1036
-
1037
- After sending the transactions, you also need to make sure the SDK has enough time to receive an event notification of the transaction being executed, for this you have the waitTill(action) functions, e.g.:
1038
-
1039
- - commit() -> waitTillCommited()
1040
- - claim() -> waitTillClaimed()
1041
- - commitAndClaim -> waitTillClaimed()
1042
- - refund() -> waitTillRefunded()
1043
-
1044
- ```typescript
1045
- //Example for Solana
1046
- const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1047
- txns.forEach(val => val.tx.sign(...val.signers));
1048
- const signedTransactions = await solanaSigner.wallet.signAllTransactions(txns.map(val => val.tx));
1049
- for(let tx of signedTransactions) {
1050
- const res = await solanaRpc.sendRawTransaction(tx.serialize());
1051
- await solanaRpc.confirmTransaction(res);
1052
- }
1053
- await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1054
-
1055
- //Example for Starknet
1056
- const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1057
- for(let tx of txns) {
1058
- if(tx.type==="INVOKE") await starknetSigner.account.execute(tx.tx, tx.details);
1059
- if(tx.type==="DEPLOY_ACCOUNT") await starknetSigner.account.deployAccount(tx.tx, tx.details);
1060
- }
1061
- await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1062
-
1063
- //Example for EVM
1064
- const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1065
- for(let tx of txns) {
1066
- await evmSigner.account.sendTransaction(tx);
1067
- }
1068
- await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1069
- ```
1070
-
1071
- ### Additional swapper options
1072
-
1073
- You can further customize the swapper instance with these options, you can:
1074
- - adjust the maximum accepted pricing difference from the LPs
1075
- - use custom mempool.space instance
1076
- - use custom pricing API
1077
- - use own LP node for swaps
1078
- - adjust HTTP request timeouts
1079
- - add parameters to be sent with each LP request
1080
-
1081
- ```typescript
1082
- const swapper = Factory.newSwapper({
1083
- ...
1084
- //Additional optional options
1085
- pricingFeeDifferencePPM: 20000n, //Maximum allowed pricing difference for quote (between swap & market price) in ppm (parts per million) (20000 == 2%)
1086
- mempoolApi: new MempoolApi("<url to custom mempool.space instance>"), //Set the SDK to use a custom mempool.space instance instead of the public one
1087
- getPriceFn: (tickers: string[], abortSignal?: AbortSignal) => customPricingApi.getUsdPriceForTickers(tickers) //Overrides the default pricing API engine with a custom price getter
1088
-
1089
- intermediaryUrl: "<url to custom LP node>",
1090
- registryUrl: "<url to custom LP node registry>",
1091
-
1092
- getRequestTimeout: 10000, //Timeout in milliseconds for GET requests
1093
- postRequestTimeout: 10000, //Timeout in milliseconds for POST requests
1094
- defaultAdditionalParameters: {lpData: "Pls give gud price"}, //Additional request data sent to LPs
1095
-
1096
- defaultTrustedIntermediaryUrl: "<url to custom LP node>" //LP node/intermediary to use for trusted gas swaps
1097
- });
1098
- ```
1
+ # atomiqlabs SDK
2
+
3
+ A typescript multichain client for atomiqlabs trustlesss cross-chain swaps. Enables trustless swaps between smart chains (Solana, EVM, Starknet, etc.) and bitcoin (on-chain - L1 and lightning network - L2).
4
+
5
+ Example SDK integration in NodeJS available [here](https://github.com/atomiqlabs/atomiq-sdk-demo/blob/main/src/index.ts)
6
+
7
+ ## Installation
8
+ ```
9
+ npm install @atomiqlabs/sdk
10
+ ```
11
+
12
+ ## Installing chain-specific connectors
13
+
14
+ You can install only the chain-specific connectors that your project requires
15
+
16
+ ```
17
+ npm install @atomiqlabs/chain-solana
18
+ npm install @atomiqlabs/chain-starknet
19
+ ```
20
+
21
+ ## How to use?
22
+
23
+ - [Preparations](#preparations)
24
+ - [Setting up signers](#signer)
25
+ - [Initialization](#initialization)
26
+ - Swaps:
27
+ - [Smart Chain -> BTC L1](#swap-smart-chain---bitcoin-on-chain)
28
+ - [BTC L1 -> Solana (Old swap protocol)](#swap-bitcoin-on-chain---solana)
29
+ - [BTC L1 -> Starknet (New swap protocol)](#swap-bitcoin-on-chain---starknet)
30
+ - [Smart Chain -> BTC Lightning network L2](#swap-smart-chain---bitcoin-lightning-network)
31
+ - [Smart Chain -> BTC Lightning network L2 (LNURL-pay)](#swap-smart-chain---bitcoin-lightning-network-1)
32
+ - [BTC Lightning network L2 -> Smart Chain](#swap-bitcoin-lightning-network---smart-chain)
33
+ - [BTC Lightning network L2 (LNURL-withdraw) -> Smart Chain](#swap-bitcoin-lightning-network---smart-chain-1)
34
+ - [Swap states](#getting-state-of-the-swap)
35
+ - [Swap size limits](#swap-size-limits)
36
+ - [Stored swaps](#stored-swaps)
37
+ - [Get existing swaps](#get-swap-by-id)
38
+ - [Refundable swaps](#get-refundable-swaps)
39
+ - [Claimable swaps](#get-claimable-swaps)
40
+ - [Helpers](#helpers)
41
+ - [Wallet spendable balance](#getting-wallet-balances)
42
+ - [Unified address parsers](#unified-address-parser)
43
+ - [Customize swapper instance](#additional-swapper-options)
44
+
45
+ ### Preparations
46
+
47
+ Set Solana & Starknet RPC URL to use
48
+
49
+ ```typescript
50
+ const solanaRpc = "https://api.mainnet-beta.solana.com";
51
+ const starknetRpc = "https://starknet-mainnet.public.blastapi.io/rpc/v0_7";
52
+ ```
53
+
54
+ Create swapper factory, here we can pick and choose which chains we want to have supported in the SDK, ensure the "as const" keyword is used such that the typescript compiler can properly infer the types.
55
+
56
+ ```typescript
57
+ import {SolanaInitializer, SolanaInitializerType} from "@atomiqlabs/chain-solana";
58
+ import {StarknetInitializer, StarknetInitializerType} from "@atomiqlabs/chain-starknet";
59
+ import {SwapperFactory} from "@atomiqlabs/sdk";
60
+
61
+ const Factory = new SwapperFactory<[SolanaInitializerType, StarknetInitializerType]>([SolanaInitializer, StarknetInitializer] as const);
62
+ const Tokens = Factory.Tokens; //Get the supported tokens for all the specified chains.
63
+ ```
64
+
65
+ #### Browser
66
+
67
+ This uses browser's Indexed DB by default
68
+
69
+ ```typescript
70
+ import {BitcoinNetwork} from "@atomiqlabs/sdk";
71
+
72
+ const swapper = Factory.newSwapper({
73
+ chains: {
74
+ SOLANA: {
75
+ rpcUrl: solanaRpc //You can also pass Connection object here
76
+ },
77
+ STARKNET: {
78
+ rpcUrl: starknetRpc //You can also pass Provider object here
79
+ }
80
+ },
81
+ bitcoinNetwork: BitcoinNetwork.TESTNET //or BitcoinNetwork.MAINNET, BitcoinNetwork.TESTNET4 - this also sets the network to use for Solana (solana devnet for bitcoin testnet) & Starknet (sepolia for bitcoin testnet)
82
+ });
83
+ ```
84
+
85
+ if you want to use custom pricing api, mempool.space RPC url, or tune HTTP request timeouts check out [additional options](#additional-swapper-options)
86
+
87
+ #### NodeJS
88
+
89
+ For NodeJS we need to use sqlite storage, for that we first need to install the sqlite storage adaptor
90
+
91
+ ```
92
+ npm install @atomiqlabs/storage-sqlite
93
+ ```
94
+
95
+ Then use pass it in the newSwapper function
96
+
97
+ ```typescript
98
+ import {SqliteStorageManager, SqliteUnifiedStorage} from "@atomiqlabs/storage-sqlite";
99
+ import {BitcoinNetwork} from "@atomiqlabs/sdk";
100
+
101
+ const swapper = Factory.newSwapper({
102
+ chains: {
103
+ SOLANA: {
104
+ rpcUrl: solanaRpc //You can also pass Connection object here
105
+ },
106
+ STARKNET: {
107
+ rpcUrl: starknetRpc //You can also pass Provider object here
108
+ }
109
+ },
110
+ bitcoinNetwork: BitcoinNetwork.TESTNET, //or BitcoinNetwork.MAINNET - this also sets the network to use for Solana (solana devnet for bitcoin testnet) & Starknet (sepolia for bitcoin testnet)
111
+ //The following lines are important for running on backend node.js,
112
+ // because the SDK by default uses browser's Indexed DB
113
+ swapStorage: chainId => new SqliteUnifiedStorage("CHAIN_"+chainId+".sqlite3"),
114
+ chainStorageCtor: name => new SqliteStorageManager("STORE_"+name+".sqlite3"),
115
+ });
116
+ ```
117
+
118
+ if you want to use custom pricing api, mempool.space RPC url, or tune HTTP request timeouts check out [additional options](#additional-swapper-options)
119
+
120
+ ### Signer
121
+
122
+ ```typescript
123
+ import {SolanaSigner} from "@atomiqlabs/chain-solana";
124
+ //Browser - react, using solana wallet adapter
125
+ const anchorWallet = useAnchorWallet();
126
+ const wallet = new SolanaSigner(anchorWallet);
127
+ ```
128
+
129
+ ```typescript
130
+ import {WalletAccount} from "starknet";
131
+ import {StarknetSigner} from "@atomiqlabs/chain-starknet";
132
+ //Browser, using get-starknet
133
+ const swo = await connect();
134
+ const wallet = new StarknetSigner(new WalletAccount(starknetRpc, swo.wallet));
135
+ ```
136
+
137
+ or
138
+
139
+ ```typescript
140
+ import {Keypair} from "@solana/web3.js";
141
+ import {SolanaKeypairWallet, SolanaSigner} from "@atomiqlabs/chain-solana";
142
+ //Creating Solana signer from private key
143
+ const solanaSigner = new SolanaSigner(new SolanaKeypairWallet(Keypair.fromSecretKey(solanaKey)), Keypair.fromSecretKey(solanaKey));
144
+ ```
145
+
146
+ ```typescript
147
+ import {SolanaKeypairWallet, SolanaSigner} from "@atomiqlabs/chain-solana";
148
+ //Creating Starknet signer from private key
149
+ const starknetSigner = new StarknetSigner(new StarknetKeypairWallet(starknetRpc, starknetKey));
150
+ ```
151
+
152
+ ### Initialization
153
+
154
+ Initialize the swapper
155
+
156
+ ```typescript
157
+ await swapper.init();
158
+ ```
159
+
160
+ Now we have the multichain swapper initialized
161
+
162
+ ### Extract chain-specific swapper with signer
163
+
164
+ To make it easier to do swaps between bitcoin and a specific chain we can extract a chain-specific swapper, and also set a signer. e.g.:
165
+
166
+ ```typescript
167
+ const solanaSwapper = swapper.withChain<"SOLANA">("SOLANA");
168
+ ```
169
+
170
+ or also with signer
171
+
172
+ ```typescript
173
+ const starknetSwapperWithSigner = swapper.withChain<"STARKNET">("STARKNET").withSigner(signer);
174
+ ```
175
+
176
+ ### Bitcoin on-chain swaps
177
+
178
+ #### Swap Smart chain -> Bitcoin on-chain
179
+
180
+ Getting swap quote
181
+
182
+ ```typescript
183
+ const _exactIn = false; //exactIn = false, so we specify the output amount
184
+ const _amount = 10000n; //Amount in BTC base units - sats (10000 sats = 0.0001 BTC)
185
+ const _address = "bc1qtw67hj77rt8zrkkg3jgngutu0yfgt9czjwusxt"; //BTC address of the recipient
186
+
187
+ //Create the swap: swapping SOL to Bitcoin on-chain, receiving _amount of satoshis (smallest unit of bitcoin) to _address
188
+ const swap = await swapper.swap(
189
+ Tokens.SOLANA.SOL, //From specified source token
190
+ Tokens.BITCOIN.BTC, //Swap to BTC
191
+ _amount,
192
+ _exactIn,
193
+ solanaSigner.getAddress(), //Source address and smart chain signer
194
+ _address //Destination of the swap
195
+ );
196
+
197
+ //Get the amount required to pay and fee
198
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
199
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
200
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
201
+
202
+ const output: string = swap.getOutput().toString(); //Total output amount
203
+
204
+ //Get swap expiration time
205
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
206
+
207
+ //Get pricing info
208
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
209
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
210
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
211
+ ```
212
+
213
+ Initiating the swap
214
+
215
+ ```typescript
216
+ //Initiate and pay for the swap
217
+ await swap.commit(solanaSigner);
218
+ ```
219
+
220
+ or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
221
+
222
+ Wait for the swap to execute, refund in case of failure
223
+
224
+ ```typescript
225
+ //Wait for the swap to conclude
226
+ const result: boolean = await swap.waitForPayment();
227
+ if(!result) {
228
+ //Swap failed, money can be refunded
229
+ await swap.refund();
230
+ } else {
231
+ //Swap successful, we can get the bitcoin txId
232
+ const bitcoinTxId = swap.getBitcoinTxId();
233
+ }
234
+ ```
235
+
236
+ ##### Swap states
237
+
238
+ - ToBTCSwapState.REFUNDED = -3
239
+ - Swap failed and was successfully refunded
240
+ - ToBTCSwapState.QUOTE_EXPIRED = -2
241
+ - Swap quote expired and cannot be executed anymore
242
+ - ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
243
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
244
+ - ToBTCSwapState.CREATED = 0
245
+ - Swap quote is created, waiting to be executed
246
+ - ToBTCSwapState.COMMITED = 1,
247
+ - Swap was initiated (init transaction sent)
248
+ - ToBTCSwapState.SOFT_CLAIMED = 2,
249
+ - Swap was processed by the counterparty but not yet claimed on-chain (bitcoin transaction was sent, but unconfirmed yet)
250
+ - ToBTCSwapState.CLAIMED = 3
251
+ - Swap was finished and funds were successfully claimed by the counterparty
252
+ - ToBTCSwapState.REFUNDABLE = 4
253
+ - Swap was initiated but counterparty failed to process it, the user can now refund his funds
254
+
255
+ #### Swap Bitcoin on-chain -> Solana
256
+
257
+ NOTE: Solana uses an old swap protocol for Bitcoin on-chain -> Solana swaps, the flow here is different from the one for Starknet and other chains.
258
+
259
+ Getting swap quote
260
+
261
+ ```typescript
262
+ const _exactIn = true; //exactIn = true, so we specify the input amount
263
+ const _amount = fromHumanReadableString("0.0001", Tokens.BITCOIN.BTC); //Amount in BTC base units - sats, we can also use a utility function here
264
+
265
+ //Create the swap: swapping _amount of satoshis of Bitcoin on-chain to SOL
266
+ const swap = await swapper.swap(
267
+ Tokens.BITCOIN.BTC, //Swap from BTC
268
+ Tokens.SOLANA.SOL, //Into specified destination token
269
+ _amount,
270
+ _exactIn, //Whether we define an input or output amount
271
+ undefined, //Source address for the swap, not used for swaps from BTC
272
+ solanaSigner.getAddress() //Destination address
273
+ );
274
+
275
+ //Get the amount required to pay and fee
276
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
277
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
278
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
279
+
280
+ const output: string = swap.getOutput().toString(); //Total output amount
281
+
282
+ //Get swap expiration time
283
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
284
+
285
+ //Get security deposit amount (Human readable amount of SOL that needs to be put down to rent the liquidity from swap intermediary), you will get this deposit back if the swap succeeds
286
+ const securityDeposit: string = swap.getSecurityDeposit().toString();
287
+ //Get claimer bounty (Human readable amount of SOL reserved as a reward for watchtowers to claim the swap on your behalf)
288
+ const claimerBounty: string = swap.getClaimerBounty().toString();
289
+
290
+ //Get pricing info
291
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
292
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
293
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
294
+ ```
295
+
296
+ Initiating the swap
297
+
298
+ ```typescript
299
+ //Initiate the swap on the destination chain (Solana) by opening up the bitcoin swap address
300
+ await swap.commit(solanaSigner);
301
+ ```
302
+
303
+ or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
304
+
305
+ Sending bitcoin
306
+
307
+ ```typescript
308
+ //Get the bitcoin address
309
+ const receivingAddressOnBitcoin = swap.getAddress();
310
+ //Get the QR code data (contains the address and amount)
311
+ const qrCodeData = swap.getHyperlink(); //Data that can be displayed in the form of QR code
312
+ //Send the exact amount of BTC to the provided address
313
+ ```
314
+
315
+ or get a psbt and sign it
316
+
317
+ ```typescript
318
+ //Or obtain the funded PSBT (input already added) - ready for signing
319
+ const {psbt, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""});
320
+ for(let signIdx of signInputs) {
321
+ psbt.signIdx(..., signIdx); //Or pass it to external signer
322
+ }
323
+ const bitcoinTxId = await swap.submitPsbt(psbt);
324
+ ```
325
+
326
+ Waiting for swap execution
327
+
328
+ ```typescript
329
+ try {
330
+ //Wait for the payment to arrive
331
+ await swap.waitForBitcoinTransaction(
332
+ null, null,
333
+ (
334
+ txId: string, //Transaction ID of the received bitcoin transaction
335
+ confirmations: number, //Current confirmations of the transaction
336
+ targetConfirmations: number, //Required confirmations
337
+ transactionETAms: number //Estimated in time (in milliseconds) until when the transaction will receive required amount of confirmations
338
+ ) => {
339
+ //Callback for transaction updates
340
+ }
341
+ );
342
+ } catch(e) {
343
+ //Error occurred while waiting for payment, this is most likely due to network errors
344
+ return;
345
+ }
346
+
347
+ //Swap should get automatically claimed by the watchtowers, if not we can call swap.claim() ourselves
348
+ try {
349
+ await swap.waitTillClaimed(timeoutSignal(30*1000));
350
+ } catch (e) {
351
+ //Claim ourselves when automatic claim doesn't happen in 30 seconds
352
+ await swap.claim(solanaSigner);
353
+ }
354
+ ```
355
+
356
+ ##### Swap states
357
+
358
+ - FromBTCSwapState.EXPIRED = -3
359
+ - Bitcoin swap address expired
360
+ - FromBTCSwapState.QUOTE_EXPIRED = -2
361
+ - Swap quote expired and cannot be executed anymore
362
+ - FromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
363
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
364
+ - FromBTCSwapState.PR_CREATED = 0
365
+ - Swap quote is created, waiting for the user to open a bitcoin swap address
366
+ - FromBTCSwapState.CLAIM_COMMITED = 1
367
+ - Bitcoin swap address is opened
368
+ - FromBTCSwapState.BTC_TX_CONFIRMED = 2
369
+ - Bitcoin transaction sending funds to the swap address is confirmed
370
+ - FromBTCSwapState.CLAIM_CLAIMED = 3
371
+ - Swap funds are claimed to the user's wallet
372
+
373
+
374
+ #### Swap Bitcoin on-chain -> Starknet
375
+
376
+ NOTE: Starknet uses a new swap protocol for Bitcoin on-chain -> Solana swaps, the flow here is different from the one for Solana!
377
+
378
+ Getting swap quote
379
+
380
+ ```typescript
381
+ const _exactIn = true; //exactIn = true, so we specify the input amount
382
+ const _amount = fromHumanReadableString("0.0001", Tokens.BITCOIN.BTC); //Amount in BTC base units - sats, we can also use a utility function here
383
+
384
+ //Create the swap: swapping _amount of satoshis of Bitcoin on-chain to SOL
385
+ const swap = await swapper.swap(
386
+ Tokens.BITCOIN.BTC, //Swap from BTC
387
+ Tokens.STARKNET.STRK, //Into specified destination token
388
+ _amount,
389
+ _exactIn, //Whether we define an input or output amount
390
+ undefined, //Source address for the swap, not used for swaps from BTC
391
+ starknetSigner.getAddress(), //Destination address
392
+ {
393
+ gasAmount: 1_000_000_000_000_000_000n //We can also request a gas drop on the destination chain (here requesting 1 STRK)
394
+ }
395
+ );
396
+
397
+ //Get the amount required to pay and fee
398
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
399
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
400
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
401
+
402
+ const output: string = swap.getOutput().toString(); //Total output amount
403
+
404
+ //Get swap expiration time
405
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
406
+
407
+ //Get pricing info
408
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
409
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
410
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
411
+ ```
412
+
413
+ Initiating the swap
414
+
415
+ ```typescript
416
+ //Obtain the funded PSBT (input already added) - ready for signing
417
+ const {psbt, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""});
418
+ for(let signIdx of signInputs) {
419
+ psbt.signIdx(..., signIdx); //Or pass it to external signer
420
+ }
421
+ const bitcoinTxId = await swap.submitPsbt(psbt);
422
+ ```
423
+
424
+ or get raw PSBT and add inputs manually
425
+
426
+ ```typescript
427
+ //Or obtain raw PSBT to which inputs still need to be added
428
+ const {psbt, in1sequence} = await swap.getPsbt();
429
+ psbt.addInput(...);
430
+ //Make sure the second input's sequence (index 1) is as specified in the in1sequence variable
431
+ psbt.updateInput(1, {sequence: in1sequence});
432
+ //Sign the PSBT, sign every input except the first one
433
+ for(let i=1;i<psbt.inputsLength; i++) psbt.signIdx(..., i); //Or pass it to external signer
434
+ //Submit the signed PSBT
435
+ const bitcoinTxId = await swap.submitPsbt(psbt);
436
+ ```
437
+
438
+ Waiting for swap execution
439
+
440
+ ```typescript
441
+ try {
442
+ //Wait for the payment to arrive
443
+ await swap.waitForBitcoinTransaction(
444
+ null, null,
445
+ (
446
+ txId: string, //Transaction ID of the received bitcoin transaction
447
+ confirmations: number, //Current confirmations of the transaction
448
+ targetConfirmations: number, //Required confirmations
449
+ transactionETAms: number //Estimated in time (in milliseconds) until when the transaction will receive required amount of confirmations
450
+ ) => {
451
+ //Callback for transaction updates
452
+ }
453
+ );
454
+ } catch(e) {
455
+ //Error occurred while waiting for payment, this is most likely due to network errors
456
+ return;
457
+ }
458
+
459
+ //Swap should get automatically claimed by the watchtowers, if not we can call swap.claim() ourselves
460
+ try {
461
+ await swap.waitTillClaimedOrFronted(timeoutSignal(30*1000));
462
+ } catch (e) {
463
+ //Claim ourselves when automatic claim doesn't happen in 30 seconds
464
+ await swap.claim(starknetSigner);
465
+ }
466
+ ```
467
+
468
+ ##### Swap states
469
+
470
+ - SpvFromBTCSwapState.CLOSED = -5
471
+ - Catastrophic failure during swap, shall never happen
472
+ - SpvFromBTCSwapState.FAILED = -4
473
+ - Bitcoin transaction was sent, but was double-spent later, therefore the swap was failed (no BTC was sent)
474
+ - SpvFromBTCSwapState.DECLINED = -3
475
+ - LP declined to process the swap transaction, no BTC was sent
476
+ - SpvFromBTCSwapState.QUOTE_EXPIRED = -2
477
+ - Swap quote expired and cannot be executed anymore
478
+ - SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
479
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is a bitcoin transaction being submitted it might still succeed)
480
+ - SpvFromBTCSwapState.CREATED = 0
481
+ - Swap quote is created, waiting on user to sign the bitcoin swap transaction
482
+ - SpvFromBTCSwapState.SIGNED = 1
483
+ - Bitcoin swap transaction was signed by the client
484
+ - SpvFromBTCSwapState.POSTED = 2
485
+ - Bitcoin swap transaction was posted to the LP
486
+ - SpvFromBTCSwapState.BROADCASTED = 3
487
+ - LP broadcasted the bitcoin swap transaction
488
+ - SpvFromBTCSwapState.FRONTED = 4
489
+ - Swap funds have been deposited to the user's wallet in front of the time
490
+ - SpvFromBTCSwapState.BTC_TX_CONFIRMED = 5
491
+ - Bitcoin swap transaction is confirmed
492
+ - SpvFromBTCSwapState.CLAIM_CLAIMED = 6
493
+ - Swap funds are claimed to the user's wallet
494
+
495
+ ### Bitcoin lightning network swaps
496
+
497
+ #### Swap Smart chain -> Bitcoin lightning network
498
+
499
+ Getting swap quote
500
+
501
+ ```typescript
502
+ //Destination lightning network invoice, amount needs to be part of the invoice!
503
+ const _lightningInvoice = "lnbc10u1pj2q0g9pp5ejs6m677m39cznpzum7muruvh50ys93ln82p4j9ks2luqm56xxlshp52r2anlhddfa9ex9vpw9gstxujff8a0p8s3pzvua930js0kwfea6scqzzsxqyz5vqsp5073zskc5qfgp7lre0t6s8uexxxey80ax564hsjklfwfjq2ew0ewq9qyyssqvzmgs6f8mvuwgfa9uqxhtza07qem4yfhn9wwlpskccmuwplsqmh8pdy6c42kqdu8p73kky9lsnl40qha5396d8lpgn90y27ltfc5rfqqq59cya";
504
+
505
+ //Create the swap: swapping SOL to Bitcoin lightning
506
+ const swap = await swapper.swap(
507
+ Tokens.SOLANA.SOL, //From specified source token
508
+ Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
509
+ undefined, //Amount is specified in the lightning network invoice!
510
+ false, //Make sure we use exactIn=false for swaps to BTC-LN, if you want to use exactIn=true and set an amount, use LNURL-pay!
511
+ solanaSigner.getAddress(), //Source address and smart chain signer
512
+ _lightningInvoice //Destination of the swap
513
+ );
514
+
515
+ //Get the amount required to pay and fee
516
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
517
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
518
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
519
+
520
+ const output: string = swap.getOutput().toString(); //Total output amount
521
+
522
+ //Get swap expiration time
523
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
524
+
525
+ //Get pricing info
526
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
527
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
528
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
529
+ ```
530
+
531
+ Initiating the swap
532
+
533
+ ```typescript
534
+ //Initiate and pay for the swap
535
+ await swap.commit(solanaSigner);
536
+ ```
537
+
538
+ or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
539
+
540
+ Wait for the swap to execute, refund in case of failure
541
+
542
+ ```typescript
543
+ //Wait for the swap to conclude
544
+ const result: boolean = await swap.waitForPayment();
545
+ if(!result) {
546
+ //Swap failed, money can be refunded
547
+ await swap.refund(solanaSigner);
548
+ } else {
549
+ //Swap successful, we can get the lightning payment secret pre-image, which acts as a proof of payment
550
+ const lightningSecret = swap.getSecret();
551
+ }
552
+ ```
553
+
554
+ ##### Swap states
555
+
556
+ - ToBTCSwapState.REFUNDED = -3
557
+ - Swap failed and was successfully refunded
558
+ - ToBTCSwapState.QUOTE_EXPIRED = -2
559
+ - Swap quote expired and cannot be executed anymore
560
+ - ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
561
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
562
+ - ToBTCSwapState.CREATED = 0
563
+ - Swap quote is created, waiting to be executed
564
+ - ToBTCSwapState.COMMITED = 1,
565
+ - Swap was initiated (init transaction sent)
566
+ - ToBTCSwapState.SOFT_CLAIMED = 2,
567
+ - Swap was processed by the counterparty but not yet claimed on-chain (lightning network payment secret was revealed)
568
+ - ToBTCSwapState.CLAIMED = 3
569
+ - Swap was finished and funds were successfully claimed by the counterparty
570
+ - ToBTCSwapState.REFUNDABLE = 4
571
+ - Swap was initiated but counterparty failed to process it, the user can now refund his funds
572
+
573
+ #### Swap Bitcoin lightning network -> Smart chain
574
+
575
+ Getting swap quote
576
+
577
+ ```typescript
578
+ const _exactIn = true; //exactIn = true, so we specify the input amount
579
+ const _amount = 10000n; //Amount in BTC base units - sats
580
+
581
+ const swap = await swapper.swap(
582
+ Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
583
+ Tokens.STARKNET.STRK, //Into specified destination token
584
+ _amount,
585
+ _exactIn, //Whether we define an input or output amount
586
+ undefined, //Source address for the swap, not used for swaps from BTC-LN
587
+ signer.getAddress() //Destination address
588
+ );
589
+
590
+ //Get the bitcoin lightning network invoice (the invoice contains pre-entered amount)
591
+ const receivingLightningInvoice: string = swap.getAddress();
592
+ //Get the URI hyperlink (contains the lightning network invoice) which can be displayed also as QR code
593
+ const qrCodeData: string = swap.getHyperlink();
594
+
595
+ //Get the amount required to pay and fee
596
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
597
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
598
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
599
+
600
+ const output: string = swap.getOutput().toString(); //Total output amount
601
+
602
+ //Get swap expiration time
603
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
604
+
605
+ //Get security deposit amount (Human readable amount of STRK that needs to be put down to rent the liquidity from swap intermediary), you will get this deposit back if the swap succeeds
606
+ const securityDeposit: string = swap.getSecurityDeposit().toString();
607
+
608
+ //Get pricing info
609
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
610
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
611
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
612
+ ```
613
+
614
+ Pay the displayed lightning network invoice from an external wallet
615
+
616
+ Wait for the payment to be received
617
+
618
+ ```typescript
619
+ //Start listening to incoming lightning network payment
620
+ const success = await swap.waitForPayment();
621
+ if(!success) {
622
+ //Lightning network payment not received in time and quote expired!
623
+ return;
624
+ }
625
+ ```
626
+
627
+ Claim the funds on the destination smart chains, this settles the swap and lightning network payment
628
+
629
+ ```typescript
630
+ try {
631
+ //Claim the swap funds - this will initiate 2 transactions
632
+ if(swap.canCommitAndClaimInOneShot()) {
633
+ //Some chains (e.g. Solana) support signing multiple transactions in one flow
634
+ await swap.commitAndClaim(solanaSigner);
635
+ } else {
636
+ //Other chains (e.g. Starknet) don't support signing multiple transaction in one flow, therefore you need to sign one-by-one
637
+ await swap.commit(starknetSigner);
638
+ await swap.claim(starknetSigner);
639
+ }
640
+ } catch(e) {
641
+ //Error occurred while waiting for payment
642
+ }
643
+ ```
644
+
645
+ or [sign and send transactions manually](#manually-signing-smart-chain-transactions)
646
+
647
+ ##### Swap states
648
+
649
+ - FromBTCLNSwapState.FAILED = -4
650
+ - If the claiming of the funds was initiated, but never concluded, the user will get his lightning network payment refunded
651
+ - FromBTCLNSwapState.QUOTE_EXPIRED = -3
652
+ - Swap quote expired and cannot be executed anymore
653
+ - FromBTCLNSwapState.QUOTE_SOFT_EXPIRED = -2
654
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
655
+ - FromBTCLNSwapState.EXPIRED = -1
656
+ - Lightning network invoice expired, meaning the swap is expired
657
+ - FromBTCLNSwapState.PR_CREATED = 0
658
+ - Swap is created, the user should now pay the provided lightning network invoice
659
+ - FromBTCLNSwapState.PR_PAID = 1
660
+ - Lightning network invoice payment was received (but cannot be settled by the counterparty yet)
661
+ - FromBTCLNSwapState.CLAIM_COMMITED = 2
662
+ - Claiming of the funds was initiated
663
+ - FromBTCLNSwapState.CLAIM_CLAIMED = 3
664
+ - Funds were successfully claimed & lightning network secret pre-image revealed, so the lightning network payment will settle now
665
+
666
+ ### LNURLs & readable lightning identifiers
667
+
668
+ LNURLs extend the lightning network functionality by creating static lightning addreses (LNURL-pay & static internet identifiers) and QR codes which allow you to pull funds from them (LNURL-withdraw)
669
+
670
+ This SDK supports:
671
+ * LNURL-pay ([LUD-6](https://github.com/lnurl/luds/blob/luds/06.md), [LUD-9](https://github.com/lnurl/luds/blob/luds/09.md), [LUD-10](https://github.com/lnurl/luds/blob/luds/10.md), [LUD-12](https://github.com/lnurl/luds/blob/luds/12.md))
672
+ * LNURL-withdraw ([LUD-3](https://github.com/lnurl/luds/blob/luds/03.md))
673
+ * Static internet identifiers ([LUD-16](https://github.com/lnurl/luds/blob/luds/16.md))
674
+
675
+ You can parse LNURLs and lightning invoices automatically using the [Unified address parser](#unified-address-parser)
676
+
677
+ #### Differences
678
+
679
+ Lightning invoices:
680
+ * One time use only
681
+ * Need to have a fixed amount, therefore recipient has to set the amount
682
+ * Static and bounded expiration
683
+ * You can only pay to a lightning invoice, not withdraw funds from it
684
+
685
+ LNURLs & lightning identifiers:
686
+ * Reusable
687
+ * Programmable expiry
688
+ * Allows payer to set an amount
689
+ * Supports both, paying (LNURL-pay) and withdrawing (LNURL-withdraw)
690
+ * Possibility to attach a message/comment to a payment
691
+ * Receive a message/url as a result of the payment
692
+
693
+ #### Swap Smart chain -> Bitcoin lightning network
694
+
695
+ Getting swap quote
696
+
697
+ ```typescript
698
+ const _lnurlOrIdentifier: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27"; //Destination LNURL-pay or readable identifier
699
+ const _exactIn = false; //exactIn = false, so we specify the output amount
700
+ const _amount: bigint = 10000n; //Amount of satoshis to send (1 BTC = 100 000 000 satoshis)
701
+
702
+ //Create the swap: swapping SOL to Bitcoin lightning
703
+ const swap = await swapper.swap(
704
+ Tokens.SOLANA.SOL, //From specified source token
705
+ Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
706
+ _amount, //Now we can specify an amount for a lightning network payment!
707
+ _exactIn, //We can also use exactIn=true here and set an amount in input token
708
+ solanaSigner.getAddress(), //Source address and smart chain signer
709
+ _lnurlOrIdentifier, //Destination of the swap
710
+ {
711
+ comment: "Hello world" //For LNURL-pay we can also pass a comment to the recipient
712
+ }
713
+ );
714
+
715
+ //Get the amount required to pay and fee
716
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
717
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
718
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
719
+
720
+ const output: string = swap.getOutput().toString(); //Total output amount
721
+
722
+ //Get swap expiration time
723
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
724
+
725
+ //Get pricing info
726
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
727
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
728
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
729
+ ```
730
+
731
+ Initiating the swap
732
+
733
+ ```typescript
734
+ //Initiate and pay for the swap
735
+ await swap.commit(solanaSigner);
736
+ ```
737
+
738
+ or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
739
+
740
+ Wait for the swap to execute, refund in case of failure
741
+
742
+ ```typescript
743
+ //Wait for the swap to conclude
744
+ const result: boolean = await swap.waitForPayment();
745
+ if(!result) {
746
+ //Swap failed, money can be refunded
747
+ await swap.refund(solanaSigner);
748
+ } else {
749
+ //Swap successful, we can get the lightning payment secret pre-image, which acts as a proof of payment
750
+ const lightningSecret = swap.getSecret();
751
+ //In case the LNURL contained a success action, we can read it now and display it to user
752
+ if(swap.hasSuccessAction()) {
753
+ //Contains a success action that should displayed to the user
754
+ const successMessage = swap.getSuccessAction();
755
+ const description: string = successMessage.description; //Description of the message
756
+ const text: (string | null) = successMessage.text; //Main text of the message
757
+ const url: (string | null) = successMessage.url; //URL link which should be displayed
758
+ }
759
+ }
760
+ ```
761
+
762
+ #### Swap Bitcoin lightning network -> Smart chain
763
+
764
+ Getting swap quote
765
+
766
+ ```typescript
767
+ const _lnurl: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27"; //Destination LNURL-pay or readable identifier
768
+ const _exactIn = true; //exactIn = true, so we specify the input amount
769
+ const _amount = 10000n; //Amount in BTC base units - sats
770
+
771
+ const swap = await swapper.swap(
772
+ Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
773
+ Tokens.STARKNET.STRK, //Into specified destination token
774
+ _amount,
775
+ _exactIn, //Whether we define an input or output amount
776
+ _lnurl, //Source LNURL for the swap
777
+ signer.getAddress() //Destination address
778
+ );
779
+
780
+ //Get the amount required to pay and fee
781
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
782
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
783
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
784
+
785
+ const output: string = swap.getOutput().toString(); //Total output amount
786
+
787
+ //Get swap expiration time
788
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
789
+
790
+ //Get security deposit amount (Human readable amount of STRK that needs to be put down to rent the liquidity from swap intermediary), you will get this deposit back if the swap succeeds
791
+ const securityDeposit: string = swap.getSecurityDeposit().toString();
792
+
793
+ //Get pricing info
794
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
795
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
796
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
797
+ ```
798
+
799
+ Wait for the payment to be received
800
+
801
+ ```typescript
802
+ //Start listening to incoming lightning network payment
803
+ const success = await swap.waitForPayment();
804
+ if(!success) {
805
+ //Lightning network payment not received in time and quote expired!
806
+ return;
807
+ }
808
+ ```
809
+
810
+ Claim the funds on the destination smart chains, this settles the swap and lightning network payment
811
+
812
+ ```typescript
813
+ try {
814
+ //Claim the swap funds - this will initiate 2 transactions
815
+ if(swap.canCommitAndClaimInOneShot()) {
816
+ //Some chains (e.g. Solana) support signing multiple transactions in one flow
817
+ await swap.commitAndClaim(solanaSigner);
818
+ } else {
819
+ //Other chains (e.g. Starknet) don't support signing multiple transaction in one flow, therefore you need to sign one-by-one
820
+ await swap.commit(starknetSigner);
821
+ await swap.claim(starknetSigner);
822
+ }
823
+ } catch(e) {
824
+ //Error occurred while waiting for payment
825
+ }
826
+ ```
827
+
828
+ or [sign and send transactions manually](#manually-signing-smart-chain-transactions)
829
+
830
+ ### Getting state of the swap
831
+
832
+ You can get the current state of the swap with:
833
+
834
+ ```typescript
835
+ const state = swap.getState();
836
+ ```
837
+
838
+ You can also set a listener to listen for swap state changes:
839
+
840
+ ```typescript
841
+ swap.events.on("swapState", swap => {
842
+ const newState = swap.getState();
843
+ });
844
+ ```
845
+
846
+ For the meaning of the states please refer to the "Swap state" section under each swap type.
847
+
848
+ ### Swap size limits
849
+
850
+ Swap sizes are limited by the LPs you are connected to, they are advertised in BTC terms by LPs during handshake
851
+
852
+ ```typescript
853
+ const swapLimits = swapper.getSwapLimits(srcToken, dstToken);
854
+ const inputMin = swapLimits.input.min;
855
+ const inputMax = swapLimits.input.max;
856
+ const outputMin = swapLimits.output.min;
857
+ const outputMax = swapLimits.output.max;
858
+ ```
859
+
860
+ NOTE: swap limits denominated in BTC are retrieved from the LPs during initial handshake, however limits in other tokens are only returned when getting a quote fails due to amount being too low or too high. For example if you want to get swap limits for the BTC -> SOL swap, the input limits will be immediately available, while the output limits will only get populated once a quote request fails due to amount being too low or high.
861
+
862
+ ```typescript
863
+ let swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
864
+ let inputMin = swapLimits.input.min; //Immediately available
865
+ let inputMax = swapLimits.input.max; //Immediately available
866
+ let outputMin = swapLimits.output.min; //Not available from the get-go
867
+ let outputMax = swapLimits.output.max; //Not available from the get-go
868
+
869
+ //You can also listen to swap limit changes (optional)
870
+ swapper.on("swapLimitsChanged", () => {
871
+ //New limits available with swapper.getSwapLimits(srcToken, dstToken)
872
+ //Useful in e.g. a react application where you want to dynamically set min/max swappable amount
873
+ })
874
+
875
+ //Try to swap really small amount of SOL with exactOut swap
876
+ try {
877
+ const swap = await swapper.swap(
878
+ Tokens.BITCOIN.BTC, //Swap from BTC
879
+ Tokens.SOLANA.SOL, //Into specified destination token
880
+ 1n, //1 lamport = 0.000000001 SOL
881
+ false, //Whether we define an input or output amount
882
+ undefined, //Source address for the swap, not used for swaps from BTC
883
+ solanaSigner.getAddress() //Destination address
884
+ );
885
+ } catch (e) {
886
+ //Fails with OutOfBoundsError
887
+ }
888
+
889
+ swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
890
+ inputMin = swapLimits.input.min; //Immediately available
891
+ inputMax = swapLimits.input.max; //Immediately available
892
+ outputMin = swapLimits.output.min; //Now available due to failed quote
893
+ outputMax = swapLimits.output.max; //Now available due to failed quote
894
+ ```
895
+
896
+ ### Stored swaps
897
+
898
+ #### Get swap by ID
899
+
900
+ You can retrieve a swap by it's id, you can get an ID of the swap with
901
+
902
+ ```typescript
903
+ const swapId = swap.getId();
904
+ ```
905
+
906
+ And then later retrieve it from the storage
907
+
908
+ ```typescript
909
+ const swap = await swapper.getSwapById(id);
910
+ ```
911
+
912
+ #### Get refundable swaps
913
+ You can refund the swaps in one of two cases:
914
+ * In case intermediary is non-cooperative and goes offline, you can claim the funds from the swap contract back after some time.
915
+ * In case intermediary tried to pay but was unsuccessful, so he sent you signed message with which you can refund now without waiting.
916
+
917
+ This call can be checked on every startup and periodically every few minutes.
918
+ ```typescript
919
+ //Get refundable swaps and refund them
920
+ const refundableSolanaSwaps = await swapper.getRefundableSwaps("SOLANA", solanaSigner.getAddress());
921
+ for(let swap of refundableSolanaSwaps) await swap.refund(solanaSigner);
922
+ const refundableStarknetSwaps = await swapper.getRefundableSwaps("STARKNET", starknetSigner.getAddress());
923
+ for(let swap of refundableStarknetSwaps) await swap.refund(starknetSigner);
924
+ ```
925
+
926
+ #### Get claimable swaps
927
+ Returns swaps that are ready to be claimed by the client, this can happen if client closes the application when a swap is in-progress and the swap is concluded while the client is offline.
928
+
929
+ ```typescript
930
+ //Get the swaps
931
+ const claimableSolanaSwaps = await solanaSwapper.getClaimableSwaps("SOLANA", solanaSigner.getAddress());
932
+ //Claim all the claimable swaps
933
+ for(let swap of claimableSolanaSwaps) {
934
+ if(swap.canCommit()) await swap.commit(solanaSigner); //This is for Bitcoin (lightning) -> Smart chain swaps, where commit & claim procedure might be needed
935
+ await swap.claim(solanaSigner);
936
+ }
937
+ //Get the swaps
938
+ const claimableStarknetSwaps = await solanaSwapper.getClaimableSwaps("STARKNET", starknetSigner.getAddress());
939
+ //Claim all the claimable swaps
940
+ for(let swap of claimableStarknetSwaps) {
941
+ if(swap.canCommit()) await swap.commit(starknetSigner); //This is for Bitcoin (lightning) -> Smart chain swaps, where commit & claim procedure might be needed
942
+ await swap.claim(starknetSigner);
943
+ }
944
+ ```
945
+
946
+ ### Helpers
947
+
948
+ #### Getting wallet balances
949
+
950
+ The SDK also contains helper functions for getting the maximum spendable balance of wallets
951
+
952
+ ```typescript
953
+ //Spendable balance of the starknet wallet address (discounting transaction fees)
954
+ const strkBalance = await swapper.Utils.getSpendableBalance(starknetSigner, Tokens.STARKNET.STRK);
955
+ //Spendable balance of the solana wallet address (discounting transaction fees)
956
+ const solBalance = await swapper.Utils.getSpendableBalance(solanaSigner, Tokens.SOLANA.SOL);
957
+ //Spendable balance of the bitcoin wallet - here we also need to specify the destination chain (as there are different swap protocols available with different on-chain footprints)
958
+ const {balance: btcBalance, feeRate: btcFeeRate} = await swapper.Utils.getBitcoinSpendableBalance(bitcoinWalletAddress, "SOLANA");
959
+ ```
960
+
961
+ #### Unified address parser
962
+
963
+ A common way for parsing all address formats supported by the SDK, automatically recognizes:
964
+ - Bitcoin on-chain L1 address formats (p2pkh, p2wpkh, p2wsh, p2wsh, p2tr)
965
+ - [BIP-21](https://en.bitcoin.it/wiki/BIP_0021) bitcoin payment URI
966
+ - BOLT11 lightning network invoices
967
+ - [LUD-6](https://github.com/lnurl/luds/blob/luds/06.md) LNURL-pay links
968
+ - [LUD-3](https://github.com/lnurl/luds/blob/luds/03.md) LNURL-withdraw links
969
+ - [LUD-16](https://github.com/lnurl/luds/blob/luds/16.md) Lightning static internet identifiers
970
+ - Smart chain addresses (Solana, Starknet, etc.)
971
+
972
+ ```typescript
973
+ const res = await swapper.Utils.parseAddress(address);
974
+ switch(res.type) {
975
+ case "BITCOIN":
976
+ //Bitcoin on-chain L1 address or BIP-21 URI scheme with amount
977
+ const btcAmount = res.amount;
978
+ break;
979
+ case "LIGHTNING":
980
+ //BOLT11 lightning network invoice with pre-set amount
981
+ const lnAmount = res.amount;
982
+ break;
983
+ case "LNURL":
984
+ //LNURL payment or withdrawal link
985
+ if(isLNURLWithdraw(res.lnurl)) {
986
+ //LNURL-withdraw allowing withdrawals over the lightning network
987
+ const lnurlWithdrawData: LNURLWithdraw = res.lnurl;
988
+ const minWithdrawable = res.min; //Minimum payment amount
989
+ const maxWithdrawable = res.max; //Maximum payment amount
990
+ const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
991
+ //Should show a UI allowing the user to choose an amount he wishes to withdraw
992
+ }
993
+ if(isLNURLPay(res.lnurl)) {
994
+ //LNURL-pay or static lightning internet identifier allowing repeated payments over the lightning network
995
+ const lnurlPayData: LNURLPay = res.lnurl;
996
+ const minPayable = res.min; //Minimum payment amount
997
+ const maxPayable = res.max; //Maximum payment amount
998
+ const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
999
+ const icon: (string | null) = res.lnurl.icon; //URL encoded icon that should be displayed on the UI
1000
+ const shortDescription: (string | null) = res.lnurl.shortDescription; //Short description of the payment
1001
+ const longDescription: (string | null) = res.lnurl.longDescription; //Long description of the payment
1002
+ const maxCommentLength: (number | 0) = res.lnurl.commentMaxLength; //Maximum allowed length of the payment message/comment (0 means no comment allowed)
1003
+ //Should show a UI displaying the icon, short description, long description, allowing the user to choose an amount he wishes to pay and possibly also a comment
1004
+ }
1005
+ break;
1006
+ default:
1007
+ //Addresses for smart chains
1008
+ break;
1009
+ }
1010
+ ```
1011
+
1012
+ ### Manually signing smart chain transactions
1013
+
1014
+ You can also sign the transactions on smart chain side (Solana, Starknet, etc.) of the SDK externally by a separate wallet. Each function which executes any transaction has its txs(action) counterpart, e.g.:
1015
+ - commit() -> txsCommit()
1016
+ - claim() -> txsClaim()
1017
+ - commitAndClaim -> txsCommitAndClaim()
1018
+ - refund() -> txsRefund()
1019
+
1020
+ After sending the transactions, you also need to make sure the SDK has enough time to receive an event notification of the transaction being executed, for this you have the waitTill(action) functions, e.g.:
1021
+
1022
+ - commit() -> waitTillCommited()
1023
+ - claim() -> waitTillClaimed()
1024
+ - commitAndClaim -> waitTillClaimed()
1025
+ - refund() -> waitTillRefunded()
1026
+
1027
+ ```typescript
1028
+ //Example for Solana
1029
+ const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1030
+ txns.forEach(val => val.tx.sign(...val.signers));
1031
+ const signedTransactions = await solanaSigner.wallet.signAllTransactions(txns.map(val => val.tx));
1032
+ for(let tx of signedTransactions) {
1033
+ const res = await solanaRpc.sendRawTransaction(tx.serialize());
1034
+ await solanaRpc.confirmTransaction(res);
1035
+ }
1036
+ await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1037
+
1038
+ //Example for Starknet
1039
+ const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1040
+ for(let tx of txns) {
1041
+ if(tx.type==="INVOKE") await starknetSigner.account.execute(tx.tx, tx.details);
1042
+ if(tx.type==="DEPLOY_ACCOUNT") await starknetSigner.account.deployAccount(tx.tx, tx.details);
1043
+ }
1044
+ await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1045
+ ```
1046
+
1047
+ ### Additional swapper options
1048
+
1049
+ You can further customize the swapper instance with these options, you can:
1050
+ - adjust the maximum accepted pricing difference from the LPs
1051
+ - use custom mempool.space instance
1052
+ - use custom pricing API
1053
+ - use own LP node for swaps
1054
+ - adjust HTTP request timeouts
1055
+ - add parameters to be sent with each LP request
1056
+
1057
+ ```typescript
1058
+ const swapper = Factory.newSwapper({
1059
+ ...
1060
+ //Additional optional options
1061
+ pricingFeeDifferencePPM: 20000n, //Maximum allowed pricing difference for quote (between swap & market price) in ppm (parts per million) (20000 == 2%)
1062
+ mempoolApi: new MempoolApi("<url to custom mempool.space instance>"), //Set the SDK to use a custom mempool.space instance instead of the public one
1063
+ getPriceFn: (tickers: string[], abortSignal?: AbortSignal) => customPricingApi.getUsdPriceForTickers(tickers) //Overrides the default pricing API engine with a custom price getter
1064
+
1065
+ intermediaryUrl: "<url to custom LP node>",
1066
+ registryUrl: "<url to custom LP node registry>",
1067
+
1068
+ getRequestTimeout: 10000, //Timeout in milliseconds for GET requests
1069
+ postRequestTimeout: 10000, //Timeout in milliseconds for POST requests
1070
+ defaultAdditionalParameters: {lpData: "Pls give gud price"}, //Additional request data sent to LPs
1071
+
1072
+ defaultTrustedIntermediaryUrl: "<url to custom LP node>" //LP node/intermediary to use for trusted gas swaps
1073
+ });
1074
+ ```