@atomiqlabs/sdk 6.0.2 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1074 +1,1094 @@
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
- ```
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
+ Using external wallet (e.g. browser-based like Xverse, Unisat, Phantom, etc.)
416
+
417
+ ```typescript
418
+ //Obtain the funded PSBT (input already added) - ready for signing
419
+ const {psbt, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""});
420
+ const psbtBase64 = Buffer.from(psbt.toPSBT(0)).toString("base64"); //Base64 encoded PSBT
421
+ const psbtHex = Buffer.from(psbt.toPSBT(0)).toString("hex"); //hex encoded PSBT
422
+ //Pass `psbtBase64` or `psbtHex` (and also `signInputs`) to an external signer like Xverse, Unisat, etc.
423
+ const signedPsbtBase64 = await <signPsbt function of the external wallet>; //Call the signPsbt function of the external signer with psbtBase64 or psbtHex and signInputs
424
+ //NOTE: The signPsbt can also return hex-encoded PSBT
425
+ // const signedPsbtHex = await <signPsbt function of the external wallet>;
426
+ //Parse signed PSBT back to transaction object
427
+ const signedTransaction = Transaction.fromPSBT(Buffer.from(signedPsbtBase64, "base64")); //Import from @scure/btc-signer
428
+ //Or if the returned PSBT is hex-encoded
429
+ // const signedTransaction = Transaction.fromPSBT(Buffer.from(signedPsbtBase64, "hex")); //Import from @scure/btc-signer
430
+ const bitcoinTxId = await swap.submitPsbt(signedTransaction);
431
+ ```
432
+
433
+ or sign inputs using a private key
434
+
435
+ ```typescript
436
+ //Obtain the funded PSBT (input already added) - ready for signing
437
+ const {psbt, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""});
438
+ for(let signIdx of signInputs) {
439
+ psbt.signIdx(..., signIdx); //Or pass it to external signer
440
+ }
441
+ const bitcoinTxId = await swap.submitPsbt(psbt);
442
+ ```
443
+
444
+ or get raw PSBT and add inputs manually
445
+
446
+ ```typescript
447
+ //Or obtain raw PSBT to which inputs still need to be added
448
+ const {psbt, in1sequence} = await swap.getPsbt();
449
+ psbt.addInput(...);
450
+ //Make sure the second input's sequence (index 1) is as specified in the in1sequence variable
451
+ psbt.updateInput(1, {sequence: in1sequence});
452
+ //Sign the PSBT, sign every input except the first one
453
+ for(let i=1;i<psbt.inputsLength; i++) psbt.signIdx(..., i); //Or pass it to external signer
454
+ //Submit the signed PSBT
455
+ const bitcoinTxId = await swap.submitPsbt(psbt);
456
+ ```
457
+
458
+ Waiting for swap execution
459
+
460
+ ```typescript
461
+ try {
462
+ //Wait for the payment to arrive
463
+ await swap.waitForBitcoinTransaction(
464
+ null, null,
465
+ (
466
+ txId: string, //Transaction ID of the received bitcoin transaction
467
+ confirmations: number, //Current confirmations of the transaction
468
+ targetConfirmations: number, //Required confirmations
469
+ transactionETAms: number //Estimated in time (in milliseconds) until when the transaction will receive required amount of confirmations
470
+ ) => {
471
+ //Callback for transaction updates
472
+ }
473
+ );
474
+ } catch(e) {
475
+ //Error occurred while waiting for payment, this is most likely due to network errors
476
+ return;
477
+ }
478
+
479
+ //Swap should get automatically claimed by the watchtowers, if not we can call swap.claim() ourselves
480
+ try {
481
+ await swap.waitTillClaimedOrFronted(timeoutSignal(30*1000));
482
+ } catch (e) {
483
+ //Claim ourselves when automatic claim doesn't happen in 30 seconds
484
+ await swap.claim(starknetSigner);
485
+ }
486
+ ```
487
+
488
+ ##### Swap states
489
+
490
+ - SpvFromBTCSwapState.CLOSED = -5
491
+ - Catastrophic failure during swap, shall never happen
492
+ - SpvFromBTCSwapState.FAILED = -4
493
+ - Bitcoin transaction was sent, but was double-spent later, therefore the swap was failed (no BTC was sent)
494
+ - SpvFromBTCSwapState.DECLINED = -3
495
+ - LP declined to process the swap transaction, no BTC was sent
496
+ - SpvFromBTCSwapState.QUOTE_EXPIRED = -2
497
+ - Swap quote expired and cannot be executed anymore
498
+ - SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
499
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is a bitcoin transaction being submitted it might still succeed)
500
+ - SpvFromBTCSwapState.CREATED = 0
501
+ - Swap quote is created, waiting on user to sign the bitcoin swap transaction
502
+ - SpvFromBTCSwapState.SIGNED = 1
503
+ - Bitcoin swap transaction was signed by the client
504
+ - SpvFromBTCSwapState.POSTED = 2
505
+ - Bitcoin swap transaction was posted to the LP
506
+ - SpvFromBTCSwapState.BROADCASTED = 3
507
+ - LP broadcasted the bitcoin swap transaction
508
+ - SpvFromBTCSwapState.FRONTED = 4
509
+ - Swap funds have been deposited to the user's wallet in front of the time
510
+ - SpvFromBTCSwapState.BTC_TX_CONFIRMED = 5
511
+ - Bitcoin swap transaction is confirmed
512
+ - SpvFromBTCSwapState.CLAIM_CLAIMED = 6
513
+ - Swap funds are claimed to the user's wallet
514
+
515
+ ### Bitcoin lightning network swaps
516
+
517
+ #### Swap Smart chain -> Bitcoin lightning network
518
+
519
+ Getting swap quote
520
+
521
+ ```typescript
522
+ //Destination lightning network invoice, amount needs to be part of the invoice!
523
+ const _lightningInvoice = "lnbc10u1pj2q0g9pp5ejs6m677m39cznpzum7muruvh50ys93ln82p4j9ks2luqm56xxlshp52r2anlhddfa9ex9vpw9gstxujff8a0p8s3pzvua930js0kwfea6scqzzsxqyz5vqsp5073zskc5qfgp7lre0t6s8uexxxey80ax564hsjklfwfjq2ew0ewq9qyyssqvzmgs6f8mvuwgfa9uqxhtza07qem4yfhn9wwlpskccmuwplsqmh8pdy6c42kqdu8p73kky9lsnl40qha5396d8lpgn90y27ltfc5rfqqq59cya";
524
+
525
+ //Create the swap: swapping SOL to Bitcoin lightning
526
+ const swap = await swapper.swap(
527
+ Tokens.SOLANA.SOL, //From specified source token
528
+ Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
529
+ undefined, //Amount is specified in the lightning network invoice!
530
+ 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!
531
+ solanaSigner.getAddress(), //Source address and smart chain signer
532
+ _lightningInvoice //Destination of the swap
533
+ );
534
+
535
+ //Get the amount required to pay and fee
536
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
537
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
538
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
539
+
540
+ const output: string = swap.getOutput().toString(); //Total output amount
541
+
542
+ //Get swap expiration time
543
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
544
+
545
+ //Get pricing info
546
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
547
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
548
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
549
+ ```
550
+
551
+ Initiating the swap
552
+
553
+ ```typescript
554
+ //Initiate and pay for the swap
555
+ await swap.commit(solanaSigner);
556
+ ```
557
+
558
+ or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
559
+
560
+ Wait for the swap to execute, refund in case of failure
561
+
562
+ ```typescript
563
+ //Wait for the swap to conclude
564
+ const result: boolean = await swap.waitForPayment();
565
+ if(!result) {
566
+ //Swap failed, money can be refunded
567
+ await swap.refund(solanaSigner);
568
+ } else {
569
+ //Swap successful, we can get the lightning payment secret pre-image, which acts as a proof of payment
570
+ const lightningSecret = swap.getSecret();
571
+ }
572
+ ```
573
+
574
+ ##### Swap states
575
+
576
+ - ToBTCSwapState.REFUNDED = -3
577
+ - Swap failed and was successfully refunded
578
+ - ToBTCSwapState.QUOTE_EXPIRED = -2
579
+ - Swap quote expired and cannot be executed anymore
580
+ - ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
581
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
582
+ - ToBTCSwapState.CREATED = 0
583
+ - Swap quote is created, waiting to be executed
584
+ - ToBTCSwapState.COMMITED = 1,
585
+ - Swap was initiated (init transaction sent)
586
+ - ToBTCSwapState.SOFT_CLAIMED = 2,
587
+ - Swap was processed by the counterparty but not yet claimed on-chain (lightning network payment secret was revealed)
588
+ - ToBTCSwapState.CLAIMED = 3
589
+ - Swap was finished and funds were successfully claimed by the counterparty
590
+ - ToBTCSwapState.REFUNDABLE = 4
591
+ - Swap was initiated but counterparty failed to process it, the user can now refund his funds
592
+
593
+ #### Swap Bitcoin lightning network -> Smart chain
594
+
595
+ Getting swap quote
596
+
597
+ ```typescript
598
+ const _exactIn = true; //exactIn = true, so we specify the input amount
599
+ const _amount = 10000n; //Amount in BTC base units - sats
600
+
601
+ const swap = await swapper.swap(
602
+ Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
603
+ Tokens.STARKNET.STRK, //Into specified destination token
604
+ _amount,
605
+ _exactIn, //Whether we define an input or output amount
606
+ undefined, //Source address for the swap, not used for swaps from BTC-LN
607
+ signer.getAddress() //Destination address
608
+ );
609
+
610
+ //Get the bitcoin lightning network invoice (the invoice contains pre-entered amount)
611
+ const receivingLightningInvoice: string = swap.getAddress();
612
+ //Get the URI hyperlink (contains the lightning network invoice) which can be displayed also as QR code
613
+ const qrCodeData: string = swap.getHyperlink();
614
+
615
+ //Get the amount required to pay and fee
616
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
617
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
618
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
619
+
620
+ const output: string = swap.getOutput().toString(); //Total output amount
621
+
622
+ //Get swap expiration time
623
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
624
+
625
+ //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
626
+ const securityDeposit: string = swap.getSecurityDeposit().toString();
627
+
628
+ //Get pricing info
629
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
630
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
631
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
632
+ ```
633
+
634
+ Pay the displayed lightning network invoice from an external wallet
635
+
636
+ Wait for the payment to be received
637
+
638
+ ```typescript
639
+ //Start listening to incoming lightning network payment
640
+ const success = await swap.waitForPayment();
641
+ if(!success) {
642
+ //Lightning network payment not received in time and quote expired!
643
+ return;
644
+ }
645
+ ```
646
+
647
+ Claim the funds on the destination smart chains, this settles the swap and lightning network payment
648
+
649
+ ```typescript
650
+ try {
651
+ //Claim the swap funds - this will initiate 2 transactions
652
+ if(swap.canCommitAndClaimInOneShot()) {
653
+ //Some chains (e.g. Solana) support signing multiple transactions in one flow
654
+ await swap.commitAndClaim(solanaSigner);
655
+ } else {
656
+ //Other chains (e.g. Starknet) don't support signing multiple transaction in one flow, therefore you need to sign one-by-one
657
+ await swap.commit(starknetSigner);
658
+ await swap.claim(starknetSigner);
659
+ }
660
+ } catch(e) {
661
+ //Error occurred while waiting for payment
662
+ }
663
+ ```
664
+
665
+ or [sign and send transactions manually](#manually-signing-smart-chain-transactions)
666
+
667
+ ##### Swap states
668
+
669
+ - FromBTCLNSwapState.FAILED = -4
670
+ - If the claiming of the funds was initiated, but never concluded, the user will get his lightning network payment refunded
671
+ - FromBTCLNSwapState.QUOTE_EXPIRED = -3
672
+ - Swap quote expired and cannot be executed anymore
673
+ - FromBTCLNSwapState.QUOTE_SOFT_EXPIRED = -2
674
+ - Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
675
+ - FromBTCLNSwapState.EXPIRED = -1
676
+ - Lightning network invoice expired, meaning the swap is expired
677
+ - FromBTCLNSwapState.PR_CREATED = 0
678
+ - Swap is created, the user should now pay the provided lightning network invoice
679
+ - FromBTCLNSwapState.PR_PAID = 1
680
+ - Lightning network invoice payment was received (but cannot be settled by the counterparty yet)
681
+ - FromBTCLNSwapState.CLAIM_COMMITED = 2
682
+ - Claiming of the funds was initiated
683
+ - FromBTCLNSwapState.CLAIM_CLAIMED = 3
684
+ - Funds were successfully claimed & lightning network secret pre-image revealed, so the lightning network payment will settle now
685
+
686
+ ### LNURLs & readable lightning identifiers
687
+
688
+ 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)
689
+
690
+ This SDK supports:
691
+ * 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))
692
+ * LNURL-withdraw ([LUD-3](https://github.com/lnurl/luds/blob/luds/03.md))
693
+ * Static internet identifiers ([LUD-16](https://github.com/lnurl/luds/blob/luds/16.md))
694
+
695
+ You can parse LNURLs and lightning invoices automatically using the [Unified address parser](#unified-address-parser)
696
+
697
+ #### Differences
698
+
699
+ Lightning invoices:
700
+ * One time use only
701
+ * Need to have a fixed amount, therefore recipient has to set the amount
702
+ * Static and bounded expiration
703
+ * You can only pay to a lightning invoice, not withdraw funds from it
704
+
705
+ LNURLs & lightning identifiers:
706
+ * Reusable
707
+ * Programmable expiry
708
+ * Allows payer to set an amount
709
+ * Supports both, paying (LNURL-pay) and withdrawing (LNURL-withdraw)
710
+ * Possibility to attach a message/comment to a payment
711
+ * Receive a message/url as a result of the payment
712
+
713
+ #### Swap Smart chain -> Bitcoin lightning network
714
+
715
+ Getting swap quote
716
+
717
+ ```typescript
718
+ const _lnurlOrIdentifier: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27"; //Destination LNURL-pay or readable identifier
719
+ const _exactIn = false; //exactIn = false, so we specify the output amount
720
+ const _amount: bigint = 10000n; //Amount of satoshis to send (1 BTC = 100 000 000 satoshis)
721
+
722
+ //Create the swap: swapping SOL to Bitcoin lightning
723
+ const swap = await swapper.swap(
724
+ Tokens.SOLANA.SOL, //From specified source token
725
+ Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
726
+ _amount, //Now we can specify an amount for a lightning network payment!
727
+ _exactIn, //We can also use exactIn=true here and set an amount in input token
728
+ solanaSigner.getAddress(), //Source address and smart chain signer
729
+ _lnurlOrIdentifier, //Destination of the swap
730
+ {
731
+ comment: "Hello world" //For LNURL-pay we can also pass a comment to the recipient
732
+ }
733
+ );
734
+
735
+ //Get the amount required to pay and fee
736
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
737
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
738
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
739
+
740
+ const output: string = swap.getOutput().toString(); //Total output amount
741
+
742
+ //Get swap expiration time
743
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
744
+
745
+ //Get pricing info
746
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
747
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
748
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
749
+ ```
750
+
751
+ Initiating the swap
752
+
753
+ ```typescript
754
+ //Initiate and pay for the swap
755
+ await swap.commit(solanaSigner);
756
+ ```
757
+
758
+ or [sign and send transaction manually](#manually-signing-smart-chain-transactions)
759
+
760
+ Wait for the swap to execute, refund in case of failure
761
+
762
+ ```typescript
763
+ //Wait for the swap to conclude
764
+ const result: boolean = await swap.waitForPayment();
765
+ if(!result) {
766
+ //Swap failed, money can be refunded
767
+ await swap.refund(solanaSigner);
768
+ } else {
769
+ //Swap successful, we can get the lightning payment secret pre-image, which acts as a proof of payment
770
+ const lightningSecret = swap.getSecret();
771
+ //In case the LNURL contained a success action, we can read it now and display it to user
772
+ if(swap.hasSuccessAction()) {
773
+ //Contains a success action that should displayed to the user
774
+ const successMessage = swap.getSuccessAction();
775
+ const description: string = successMessage.description; //Description of the message
776
+ const text: (string | null) = successMessage.text; //Main text of the message
777
+ const url: (string | null) = successMessage.url; //URL link which should be displayed
778
+ }
779
+ }
780
+ ```
781
+
782
+ #### Swap Bitcoin lightning network -> Smart chain
783
+
784
+ Getting swap quote
785
+
786
+ ```typescript
787
+ const _lnurl: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27"; //Destination LNURL-pay or readable identifier
788
+ const _exactIn = true; //exactIn = true, so we specify the input amount
789
+ const _amount = 10000n; //Amount in BTC base units - sats
790
+
791
+ const swap = await swapper.swap(
792
+ Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
793
+ Tokens.STARKNET.STRK, //Into specified destination token
794
+ _amount,
795
+ _exactIn, //Whether we define an input or output amount
796
+ _lnurl, //Source LNURL for the swap
797
+ signer.getAddress() //Destination address
798
+ );
799
+
800
+ //Get the amount required to pay and fee
801
+ const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
802
+ const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
803
+ const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
804
+
805
+ const output: string = swap.getOutput().toString(); //Total output amount
806
+
807
+ //Get swap expiration time
808
+ const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
809
+
810
+ //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
811
+ const securityDeposit: string = swap.getSecurityDeposit().toString();
812
+
813
+ //Get pricing info
814
+ const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
815
+ const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
816
+ const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market price
817
+ ```
818
+
819
+ Wait for the payment to be received
820
+
821
+ ```typescript
822
+ //Start listening to incoming lightning network payment
823
+ const success = await swap.waitForPayment();
824
+ if(!success) {
825
+ //Lightning network payment not received in time and quote expired!
826
+ return;
827
+ }
828
+ ```
829
+
830
+ Claim the funds on the destination smart chains, this settles the swap and lightning network payment
831
+
832
+ ```typescript
833
+ try {
834
+ //Claim the swap funds - this will initiate 2 transactions
835
+ if(swap.canCommitAndClaimInOneShot()) {
836
+ //Some chains (e.g. Solana) support signing multiple transactions in one flow
837
+ await swap.commitAndClaim(solanaSigner);
838
+ } else {
839
+ //Other chains (e.g. Starknet) don't support signing multiple transaction in one flow, therefore you need to sign one-by-one
840
+ await swap.commit(starknetSigner);
841
+ await swap.claim(starknetSigner);
842
+ }
843
+ } catch(e) {
844
+ //Error occurred while waiting for payment
845
+ }
846
+ ```
847
+
848
+ or [sign and send transactions manually](#manually-signing-smart-chain-transactions)
849
+
850
+ ### Getting state of the swap
851
+
852
+ You can get the current state of the swap with:
853
+
854
+ ```typescript
855
+ const state = swap.getState();
856
+ ```
857
+
858
+ You can also set a listener to listen for swap state changes:
859
+
860
+ ```typescript
861
+ swap.events.on("swapState", swap => {
862
+ const newState = swap.getState();
863
+ });
864
+ ```
865
+
866
+ For the meaning of the states please refer to the "Swap state" section under each swap type.
867
+
868
+ ### Swap size limits
869
+
870
+ Swap sizes are limited by the LPs you are connected to, they are advertised in BTC terms by LPs during handshake
871
+
872
+ ```typescript
873
+ const swapLimits = swapper.getSwapLimits(srcToken, dstToken);
874
+ const inputMin = swapLimits.input.min;
875
+ const inputMax = swapLimits.input.max;
876
+ const outputMin = swapLimits.output.min;
877
+ const outputMax = swapLimits.output.max;
878
+ ```
879
+
880
+ 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.
881
+
882
+ ```typescript
883
+ let swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
884
+ let inputMin = swapLimits.input.min; //Immediately available
885
+ let inputMax = swapLimits.input.max; //Immediately available
886
+ let outputMin = swapLimits.output.min; //Not available from the get-go
887
+ let outputMax = swapLimits.output.max; //Not available from the get-go
888
+
889
+ //You can also listen to swap limit changes (optional)
890
+ swapper.on("swapLimitsChanged", () => {
891
+ //New limits available with swapper.getSwapLimits(srcToken, dstToken)
892
+ //Useful in e.g. a react application where you want to dynamically set min/max swappable amount
893
+ })
894
+
895
+ //Try to swap really small amount of SOL with exactOut swap
896
+ try {
897
+ const swap = await swapper.swap(
898
+ Tokens.BITCOIN.BTC, //Swap from BTC
899
+ Tokens.SOLANA.SOL, //Into specified destination token
900
+ 1n, //1 lamport = 0.000000001 SOL
901
+ false, //Whether we define an input or output amount
902
+ undefined, //Source address for the swap, not used for swaps from BTC
903
+ solanaSigner.getAddress() //Destination address
904
+ );
905
+ } catch (e) {
906
+ //Fails with OutOfBoundsError
907
+ }
908
+
909
+ swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
910
+ inputMin = swapLimits.input.min; //Immediately available
911
+ inputMax = swapLimits.input.max; //Immediately available
912
+ outputMin = swapLimits.output.min; //Now available due to failed quote
913
+ outputMax = swapLimits.output.max; //Now available due to failed quote
914
+ ```
915
+
916
+ ### Stored swaps
917
+
918
+ #### Get swap by ID
919
+
920
+ You can retrieve a swap by it's id, you can get an ID of the swap with
921
+
922
+ ```typescript
923
+ const swapId = swap.getId();
924
+ ```
925
+
926
+ And then later retrieve it from the storage
927
+
928
+ ```typescript
929
+ const swap = await swapper.getSwapById(id);
930
+ ```
931
+
932
+ #### Get refundable swaps
933
+ You can refund the swaps in one of two cases:
934
+ * In case intermediary is non-cooperative and goes offline, you can claim the funds from the swap contract back after some time.
935
+ * In case intermediary tried to pay but was unsuccessful, so he sent you signed message with which you can refund now without waiting.
936
+
937
+ This call can be checked on every startup and periodically every few minutes.
938
+ ```typescript
939
+ //Get refundable swaps and refund them
940
+ const refundableSolanaSwaps = await swapper.getRefundableSwaps("SOLANA", solanaSigner.getAddress());
941
+ for(let swap of refundableSolanaSwaps) await swap.refund(solanaSigner);
942
+ const refundableStarknetSwaps = await swapper.getRefundableSwaps("STARKNET", starknetSigner.getAddress());
943
+ for(let swap of refundableStarknetSwaps) await swap.refund(starknetSigner);
944
+ ```
945
+
946
+ #### Get claimable swaps
947
+ 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.
948
+
949
+ ```typescript
950
+ //Get the swaps
951
+ const claimableSolanaSwaps = await solanaSwapper.getClaimableSwaps("SOLANA", solanaSigner.getAddress());
952
+ //Claim all the claimable swaps
953
+ for(let swap of claimableSolanaSwaps) {
954
+ if(swap.canCommit()) await swap.commit(solanaSigner); //This is for Bitcoin (lightning) -> Smart chain swaps, where commit & claim procedure might be needed
955
+ await swap.claim(solanaSigner);
956
+ }
957
+ //Get the swaps
958
+ const claimableStarknetSwaps = await solanaSwapper.getClaimableSwaps("STARKNET", starknetSigner.getAddress());
959
+ //Claim all the claimable swaps
960
+ for(let swap of claimableStarknetSwaps) {
961
+ if(swap.canCommit()) await swap.commit(starknetSigner); //This is for Bitcoin (lightning) -> Smart chain swaps, where commit & claim procedure might be needed
962
+ await swap.claim(starknetSigner);
963
+ }
964
+ ```
965
+
966
+ ### Helpers
967
+
968
+ #### Getting wallet balances
969
+
970
+ The SDK also contains helper functions for getting the maximum spendable balance of wallets
971
+
972
+ ```typescript
973
+ //Spendable balance of the starknet wallet address (discounting transaction fees)
974
+ const strkBalance = await swapper.Utils.getSpendableBalance(starknetSigner, Tokens.STARKNET.STRK);
975
+ //Spendable balance of the solana wallet address (discounting transaction fees)
976
+ const solBalance = await swapper.Utils.getSpendableBalance(solanaSigner, Tokens.SOLANA.SOL);
977
+ //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)
978
+ const {balance: btcBalance, feeRate: btcFeeRate} = await swapper.Utils.getBitcoinSpendableBalance(bitcoinWalletAddress, "SOLANA");
979
+ ```
980
+
981
+ #### Unified address parser
982
+
983
+ A common way for parsing all address formats supported by the SDK, automatically recognizes:
984
+ - Bitcoin on-chain L1 address formats (p2pkh, p2wpkh, p2wsh, p2wsh, p2tr)
985
+ - [BIP-21](https://en.bitcoin.it/wiki/BIP_0021) bitcoin payment URI
986
+ - BOLT11 lightning network invoices
987
+ - [LUD-6](https://github.com/lnurl/luds/blob/luds/06.md) LNURL-pay links
988
+ - [LUD-3](https://github.com/lnurl/luds/blob/luds/03.md) LNURL-withdraw links
989
+ - [LUD-16](https://github.com/lnurl/luds/blob/luds/16.md) Lightning static internet identifiers
990
+ - Smart chain addresses (Solana, Starknet, etc.)
991
+
992
+ ```typescript
993
+ const res = await swapper.Utils.parseAddress(address);
994
+ switch(res.type) {
995
+ case "BITCOIN":
996
+ //Bitcoin on-chain L1 address or BIP-21 URI scheme with amount
997
+ const btcAmount = res.amount;
998
+ break;
999
+ case "LIGHTNING":
1000
+ //BOLT11 lightning network invoice with pre-set amount
1001
+ const lnAmount = res.amount;
1002
+ break;
1003
+ case "LNURL":
1004
+ //LNURL payment or withdrawal link
1005
+ if(isLNURLWithdraw(res.lnurl)) {
1006
+ //LNURL-withdraw allowing withdrawals over the lightning network
1007
+ const lnurlWithdrawData: LNURLWithdraw = res.lnurl;
1008
+ const minWithdrawable = res.min; //Minimum payment amount
1009
+ const maxWithdrawable = res.max; //Maximum payment amount
1010
+ const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
1011
+ //Should show a UI allowing the user to choose an amount he wishes to withdraw
1012
+ }
1013
+ if(isLNURLPay(res.lnurl)) {
1014
+ //LNURL-pay or static lightning internet identifier allowing repeated payments over the lightning network
1015
+ const lnurlPayData: LNURLPay = res.lnurl;
1016
+ const minPayable = res.min; //Minimum payment amount
1017
+ const maxPayable = res.max; //Maximum payment amount
1018
+ const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
1019
+ const icon: (string | null) = res.lnurl.icon; //URL encoded icon that should be displayed on the UI
1020
+ const shortDescription: (string | null) = res.lnurl.shortDescription; //Short description of the payment
1021
+ const longDescription: (string | null) = res.lnurl.longDescription; //Long description of the payment
1022
+ const maxCommentLength: (number | 0) = res.lnurl.commentMaxLength; //Maximum allowed length of the payment message/comment (0 means no comment allowed)
1023
+ //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
1024
+ }
1025
+ break;
1026
+ default:
1027
+ //Addresses for smart chains
1028
+ break;
1029
+ }
1030
+ ```
1031
+
1032
+ ### Manually signing smart chain transactions
1033
+
1034
+ 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.:
1035
+ - commit() -> txsCommit()
1036
+ - claim() -> txsClaim()
1037
+ - commitAndClaim -> txsCommitAndClaim()
1038
+ - refund() -> txsRefund()
1039
+
1040
+ 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.:
1041
+
1042
+ - commit() -> waitTillCommited()
1043
+ - claim() -> waitTillClaimed()
1044
+ - commitAndClaim -> waitTillClaimed()
1045
+ - refund() -> waitTillRefunded()
1046
+
1047
+ ```typescript
1048
+ //Example for Solana
1049
+ const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1050
+ txns.forEach(val => val.tx.sign(...val.signers));
1051
+ const signedTransactions = await solanaSigner.wallet.signAllTransactions(txns.map(val => val.tx));
1052
+ for(let tx of signedTransactions) {
1053
+ const res = await solanaRpc.sendRawTransaction(tx.serialize());
1054
+ await solanaRpc.confirmTransaction(res);
1055
+ }
1056
+ await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1057
+
1058
+ //Example for Starknet
1059
+ const txns = await swap.txsCommit(); //Also works with txsClaim, txsRefund, txCommitAndClaim
1060
+ for(let tx of txns) {
1061
+ if(tx.type==="INVOKE") await starknetSigner.account.execute(tx.tx, tx.details);
1062
+ if(tx.type==="DEPLOY_ACCOUNT") await starknetSigner.account.deployAccount(tx.tx, tx.details);
1063
+ }
1064
+ await swap.waitTillCommited(); //Or other relevant waitTillClaimed, waitTillRefunded
1065
+ ```
1066
+
1067
+ ### Additional swapper options
1068
+
1069
+ You can further customize the swapper instance with these options, you can:
1070
+ - adjust the maximum accepted pricing difference from the LPs
1071
+ - use custom mempool.space instance
1072
+ - use custom pricing API
1073
+ - use own LP node for swaps
1074
+ - adjust HTTP request timeouts
1075
+ - add parameters to be sent with each LP request
1076
+
1077
+ ```typescript
1078
+ const swapper = Factory.newSwapper({
1079
+ ...
1080
+ //Additional optional options
1081
+ pricingFeeDifferencePPM: 20000n, //Maximum allowed pricing difference for quote (between swap & market price) in ppm (parts per million) (20000 == 2%)
1082
+ mempoolApi: new MempoolApi("<url to custom mempool.space instance>"), //Set the SDK to use a custom mempool.space instance instead of the public one
1083
+ getPriceFn: (tickers: string[], abortSignal?: AbortSignal) => customPricingApi.getUsdPriceForTickers(tickers) //Overrides the default pricing API engine with a custom price getter
1084
+
1085
+ intermediaryUrl: "<url to custom LP node>",
1086
+ registryUrl: "<url to custom LP node registry>",
1087
+
1088
+ getRequestTimeout: 10000, //Timeout in milliseconds for GET requests
1089
+ postRequestTimeout: 10000, //Timeout in milliseconds for POST requests
1090
+ defaultAdditionalParameters: {lpData: "Pls give gud price"}, //Additional request data sent to LPs
1091
+
1092
+ defaultTrustedIntermediaryUrl: "<url to custom LP node>" //LP node/intermediary to use for trusted gas swaps
1093
+ });
1094
+ ```