@atomiqlabs/sdk
v7.0.8
Published
atomiq labs SDK for cross-chain swaps between smart chains and bitcoin
Downloads
1,141
Maintainers
Readme
atomiqlabs SDK
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).
Example SDK integration in NodeJS available here
Installation
npm install @atomiqlabs/sdk@latestInstalling chain-specific connectors
You can install only the chain-specific connectors that your project requires
npm install @atomiqlabs/chain-solana@latest
npm install @atomiqlabs/chain-starknet@latest
npm install @atomiqlabs/chain-evm@latestHow to use?
- Preparations
- Setting up signers
- Initialization
- Swaps:
- Smart Chain -> BTC L1
- BTC L1 -> Solana (Old swap protocol)
- BTC L1 -> Starknet/EVM (New swap protocol)
- Smart Chain -> BTC Lightning network L2
- Smart Chain -> BTC Lightning network L2 (LNURL-pay)
- BTC Lightning network L2 -> Solana (Old swap protocol)
- BTC Lightning network L2 -> Starknet/EVM (New swap protocol)
- BTC Lightning network L2 (LNURL-withdraw) -> Solana (Old swap protocol)
- BTC Lightning network L2 (LNURL-withdraw) -> Starknet/EVM (New swap protocol)
- Swap states
- Swap size limits
- Stored swaps
- Helpers
- Customize swapper instance
Preparations
Set Solana & Starknet RPC URL to use
const solanaRpc = "https://api.mainnet-beta.solana.com";
const starknetRpc = "https://starknet-mainnet.public.blastapi.io/rpc/v0_8";
const citreaRpc = "https://rpc.testnet.citrea.xyz";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.
import {SolanaInitializer, SolanaInitializerType} from "@atomiqlabs/chain-solana";
import {StarknetInitializer, StarknetInitializerType} from "@atomiqlabs/chain-starknet";
import {CitreaInitializer, CitreaInitializerType} from "@atomiqlabs/chain-evm";
import {SwapperFactory} from "@atomiqlabs/sdk";
const Factory = new SwapperFactory<[SolanaInitializerType, StarknetInitializerType, CitreaInitializerType]>([SolanaInitializer, StarknetInitializer, CitreaInitializer] as const);
const Tokens = Factory.Tokens; //Get the supported tokens for all the specified chains.Browser
This uses browser's Indexed DB by default
import {BitcoinNetwork} from "@atomiqlabs/sdk";
const swapper = Factory.newSwapper({
chains: {
SOLANA: {
rpcUrl: solanaRpc //You can also pass Connection object here
},
STARKNET: {
rpcUrl: starknetRpc //You can also pass Provider object here
},
CITREA: {
rpcUrl: citreaRpc, //You can also pass JsonApiProvider object here
}
},
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)
});if you want to use custom pricing api, mempool.space RPC url, or tune HTTP request timeouts check out additional options
NodeJS
For NodeJS we need to use sqlite storage, for that we first need to install the sqlite storage adaptor
npm install @atomiqlabs/storage-sqlite@latestThen use pass it in the newSwapper function
import {SqliteStorageManager, SqliteUnifiedStorage} from "@atomiqlabs/storage-sqlite";
import {BitcoinNetwork} from "@atomiqlabs/sdk";
const swapper = Factory.newSwapper({
chains: {
SOLANA: {
rpcUrl: solanaRpc //You can also pass Connection object here
},
STARKNET: {
rpcUrl: starknetRpc //You can also pass Provider object here
},
CITREA: {
rpcUrl: citreaRpc, //You can also pass JsonApiProvider object here
}
},
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)
//The following lines are important for running on backend node.js,
// because the SDK by default uses browser's Indexed DB
swapStorage: chainId => new SqliteUnifiedStorage("CHAIN_"+chainId+".sqlite3"),
chainStorageCtor: name => new SqliteStorageManager("STORE_"+name+".sqlite3"),
});if you want to use custom pricing api, mempool.space RPC url, or tune HTTP request timeouts check out additional options
Signer
import {SolanaSigner} from "@atomiqlabs/chain-solana";
//Browser - react, using solana wallet adapter
const anchorWallet = useAnchorWallet();
const wallet = new SolanaSigner(anchorWallet);import {WalletAccount} from "starknet";
import {StarknetSigner} from "@atomiqlabs/chain-starknet";
//Browser, using get-starknet
const swo = await connect();
const wallet = new StarknetBrowserSigner(new WalletAccount(starknetRpc, swo.wallet));or
import {Keypair} from "@solana/web3.js";
import {SolanaKeypairWallet, SolanaSigner} from "@atomiqlabs/chain-solana";
//Creating Solana signer from private key
const solanaSigner = new SolanaSigner(new SolanaKeypairWallet(Keypair.fromSecretKey(solanaKey)), Keypair.fromSecretKey(solanaKey));import {StarknetSigner, StarknetKeypairWallet} from "@atomiqlabs/chain-starknet";
//Creating Starknet signer from private key
const starknetSigner = new StarknetSigner(new StarknetKeypairWallet(starknetRpc, starknetKey));import {BaseWallet, SigningKey} from "ethers";
import {EVMSigner} from "@atomiqlabs/chain-evm";
//Creating EVM signer from private key
const wallet = new BaseWallet(new SigningKey(evmKey));
const evmWallet = new EVMSigner(wallet, wallet.address);Initialization
Initialize the swapper, this should be done once when your app starts. Checks existing in-progress swaps and does initial LP discovery
await swapper.init();Now we have the multichain swapper initialized
Extract chain-specific swapper with signer
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.:
const solanaSwapper = swapper.withChain<"SOLANA">("SOLANA");Bitcoin on-chain swaps
Swap Smart chain -> Bitcoin on-chain
Getting swap quote
//Create the swap: swapping SOL to Bitcoin on-chain, receiving _amount of satoshis (smallest unit of bitcoin) to _address
const swap = await swapper.swap(
Tokens.SOLANA.SOL, //From specified source token
Tokens.BITCOIN.BTC, //Swap to BTC
"0.0001", //Amount can be either passed in base units as bigint or in decimal format as string
SwapAmountType.EXACT_OUT, //EXACT_OUT, so we specify the output amount
solanaSigner.getAddress(), //Source address and smart chain signer
"bc1qtw67hj77rt8zrkkg3jgngutu0yfgt9czjwusxt" //BTC address of the recipient
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const swapSuccessful = await swap.execute(
solanaSigner,
{ //Callbacks
onSourceTransactionSent: (txId: string) => {
//Transaction on the source chain was sent
},
onSourceTransactionConfirmed: (txId: string) => {
//Transaction on the source chain was confirmed
},
onSwapSettled: (destinationTxId: string) => {
//Bitcoin transaction on the destination chain was sent and swap settled
}
}
);
//Refund in case of failure
if(!swapSuccessful) {
//Swap failed, money can be refunded
await swap.refund(solanaSigner);
} else {
//Swap successful!
}1. Initiate the swap on the smart-chain side
- a. Commit with a signer
await swap.commit(solanaSigner);- b. Or get the transactions & sign and send transaction manually
const txsCommit = await swap.txsCommit(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited();2. Wait for the swap to execute and for the payment to be sent
const swapSuccessful = await swap.waitForPayment();3. In case the swap fails we can refund our funds on the source chain
- a. Refund with a signer
if(!swapSuccessful) { await swap.refund(solanaSigner); return; }- b. Or get the transactions & sign and send transaction manually
if(!swapSuccessful) { const txsRefund = await swap.txsRefund(); //Sign and send these... ... }
- ToBTCSwapState.REFUNDED = -3
- Swap failed and was successfully refunded
- ToBTCSwapState.QUOTE_EXPIRED = -2
- Swap quote expired and cannot be executed anymore
- ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
- Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
- ToBTCSwapState.CREATED = 0
- Swap quote is created, waiting to be executed
- ToBTCSwapState.COMMITED = 1,
- Swap was initiated (init transaction sent)
- ToBTCSwapState.SOFT_CLAIMED = 2,
- Swap was processed by the counterparty but not yet claimed on-chain (bitcoin transaction was sent, but unconfirmed yet)
- ToBTCSwapState.CLAIMED = 3
- Swap was finished and funds were successfully claimed by the counterparty
- ToBTCSwapState.REFUNDABLE = 4
- Swap was initiated but counterparty failed to process it, the user can now refund his funds
Swap Bitcoin on-chain -> Solana
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.
Getting swap quote
//Create the swap: swapping _amount of satoshis of Bitcoin on-chain to SOL
const swap = await swapper.swap(
Tokens.BITCOIN.BTC, //Swap from BTC
Tokens.SOLANA.SOL, //Into specified destination token
"0.0001", //Amount can be either passed in base units as bigint or in decimal format as string
SwapAmountType.EXACT_IN, //EXACT_IN, so we specify the input amount
undefined, //Source address for the swap, not used for swaps from BTC
solanaSigner.getAddress() //Destination address
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//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
const securityDeposit: string = swap.getSecurityDeposit().toString();
//Get claimer bounty (Human readable amount of SOL reserved as a reward for watchtowers to claim the swap on your behalf)
const claimerBounty: string = swap.getClaimerBounty().toString();
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const automaticSettlementSuccess = await swap.execute(
solanaSigner,
{ //Bitcoin wallet, you can also pass null/undefined and send the bitcoin transaction from an external wallet
address: "bc1pscnrk588hdj79mwccucu06007mj5np2jurwfwp5mvhkjldzyphzqyk62m5",
publicKey: "03a2d8b728935f61d5bcba0cfb09c2c443c483b5c31ebd180e1833f37344bd34ba",
signPsbt: (psbt: {psbt, psbtHex: string, psbtBase64: string}, signInputs: number[]) => {
//Sign the PSBT with the bitcoin wallet
...
//Return the signed PSBT in the hex or base64 format!
return "<signed PSBT>";
}
},
{ //Callbacks
onDestinationCommitSent: (swapAddressOpeningTxId: string) => {
//Swap address opening transaction sent on the destination chain
},
onSourceTransactionSent: (txId: string) => {
//Bitcoin transaction sent on the source
},
onSourceTransactionConfirmationStatus: (txId: string, confirmations: number, targetConfirmations: number, txEtaMs: number) => {
//Bitcoin transaction confirmation status updates
},
onSourceTransactionConfirmed: (txId: string) => {
//Bitcoin transaction confirmed
},
onSwapSettled: (destinationTxId: string) => {
//Swap settled on the destination
}
}
);
//In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
if(!automaticSettlementSuccess) {
await swap.claim(solanaSigner);
}1. Initiate the swap on the destination chain (Solana) by opening up the bitcoin swap address
- a. Commit using signer
await swap.commit(solanaWallet);- b. Or get the transactions & sign and send transaction manually
const txsCommit = await swap.txsCommit(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited();2. Send bitcoin transaction
- a. Get funded PSBT and sign it
const {psbt, psbtHex, psbtBase64, signInputs} = await swap.getFundedPsbt({ address: "bc1pscnrk588hdj79mwccucu06007mj5np2jurwfwp5mvhkjldzyphzqyk62m5", publicKey: "03a2d8b728935f61d5bcba0cfb09c2c443c483b5c31ebd180e1833f37344bd34ba" }); //Sign the psbt const signedPsbt = ...; //Can be hex or base64 encoded const bitcoinTxId = await swap.submitPsbt(signedPsbt);- b. Get the bitcoin address or deeplink and send from external wallet
//It is imporant to send the EXACT amount, sending different amount will lead to loss of funds! const btcSwapAddress = swap.getAddress(); const btcDeepLink = swap.getHyperlink();3. Wait for the bitcoin on-chain transaction to confirm
await swap.waitForBitcoinTransaction( (txId, confirmations, targetConfirmations, txEtaMs) => { //Bitcoin transaction confirmation status callback } );4. Wait for the automatic settlement of the swap
const automaticSettlementSuccess = await swap.waitTillClaimed(30);5. In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
- a. Claim with a signer
if(!automaticSettlementSuccess) { await swap.claim(solanaSigner); }- b. Or get the transactions & sign and send transaction manually
if(!automaticSettlementSuccess) {} const txsClaim = await swap.txsClaim(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited(); }
- FromBTCSwapState.EXPIRED = -3
- Bitcoin swap address expired
- FromBTCSwapState.QUOTE_EXPIRED = -2
- Swap quote expired and cannot be executed anymore
- FromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
- Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
- FromBTCSwapState.PR_CREATED = 0
- Swap quote is created, waiting for the user to open a bitcoin swap address
- FromBTCSwapState.CLAIM_COMMITED = 1
- Bitcoin swap address is opened
- FromBTCSwapState.BTC_TX_CONFIRMED = 2
- Bitcoin transaction sending funds to the swap address is confirmed
- FromBTCSwapState.CLAIM_CLAIMED = 3
- Swap funds are claimed to the user's wallet
Swap Bitcoin on-chain -> Starknet/EVM
NOTE: Starknet & EVM uses a new swap protocol for Bitcoin on-chain -> Smart chain swaps, the flow here is different from the one for Solana!
Getting swap quote
//Create the swap: swapping _amount of satoshis of Bitcoin on-chain to SOL
const swap = await swapper.swap(
Tokens.BITCOIN.BTC, //Swap from BTC
Tokens.STARKNET.STRK, //Into specified destination token
"0.0001", //Amount can be either passed in base units as bigint or in decimal format as string
SwapAmountType.EXACT_IN, //EXACT_IN, so we specify the input amount
undefined, //Source address for the swap, not used for swaps from BTC
starknetSigner.getAddress(), //Destination address
{
gasAmount: 1_000_000_000_000_000_000n //We can also request a gas drop on the destination chain (here requesting 1 STRK)
}
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const automaticSettlementSuccess = await swap.execute(
{ //Bitcoin wallet
address: "bc1pscnrk588hdj79mwccucu06007mj5np2jurwfwp5mvhkjldzyphzqyk62m5",
publicKey: "03a2d8b728935f61d5bcba0cfb09c2c443c483b5c31ebd180e1833f37344bd34ba",
signPsbt: (psbt: {psbt, psbtHex: string, psbtBase64: string}, signInputs: number[]) => {
//Sign the PSBT with the bitcoin wallet
...
//Return the signed PSBT in the hex or base64 format!
return "<signed PSBT>";
}
},
{ //Callbacks
onSourceTransactionSent: (txId: string) => {
//Bitcoin transaction sent on the source
},
onSourceTransactionConfirmationStatus: (txId: string, confirmations: number, targetConfirmations: number, txEtaMs: number) => {
//Bitcoin transaction confirmation status updates
},
onSourceTransactionConfirmed: (txId: string) => {
//Bitcoin transaction confirmed
},
onSwapSettled: (destinationTxId: string) => {
//Swap settled on the destination
}
}
);
//In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
if(!automaticSettlementSuccess) {
await swap.claim(starknetWallet);
}1. Send bitcoin transaction
- a. Get funded PSBT and sign it using external wallet (e.g. browser-based like Xverse, Unisat, Phantom, etc.)
//Obtain the funded PSBT (input already added) - ready for signing const {psbt, psbtHex, psbtBase64, signInputs} = await swap.getFundedPsbt({address: "", publicKey: ""}); //Pass `psbtBase64` or `psbtHex` (and also `signInputs`) to an external signer like Xverse, Unisat, etc. const signedPsbtHexOrBase64 = await <signPsbt function of the external wallet>; //Call the signPsbt function of the external signer with psbtBase64 or psbtHex and signInputs //The SDK automatically recognizes hex & base64 encoded PSBTs const bitcoinTxId = await swap.submitPsbt(signedPsbtHexOrBase64);- b. Or obtain raw PSBT to which inputs still need to be added
const {psbt, psbtHex, psbtBase64, in1sequence} = await swap.getPsbt(); psbt.addInput(...); //Make sure the second input's sequence (index 1) is as specified in the in1sequence variable psbt.updateInput(1, {sequence: in1sequence}); //Sign the PSBT, sign every input except the first one for(let i=1;i<psbt.inputsLength; i++) psbt.signIdx(..., i); //Or pass it to external signer //Submit the signed PSBT, can be the Transaction object, or hex/base64 serialized const bitcoinTxId = await swap.submitPsbt(psbt);2. Wait for the bitcoin on-chain transaction to confirm
await swap.waitForBitcoinTransaction( (txId, confirmations, targetConfirmations, txEtaMs) => { //Bitcoin transaction confirmation status callback } );3. Wait for the automatic settlement of the swap
const automaticSettlementSuccess = await swap.waitTillClaimed(60);4. In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
- a. Claim with a signer
if(!automaticSettlementSuccess) { await swap.claim(starknetSigner); }- b. Or get the transactions & sign and send transaction manually
if(!automaticSettlementSuccess) {} const txsClaim = await swap.txsClaim(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited(); }
- SpvFromBTCSwapState.CLOSED = -5
- Catastrophic failure during swap, shall never happen
- SpvFromBTCSwapState.FAILED = -4
- Bitcoin transaction was sent, but was double-spent later, therefore the swap was failed (no BTC was sent)
- SpvFromBTCSwapState.DECLINED = -3
- LP declined to process the swap transaction, no BTC was sent
- SpvFromBTCSwapState.QUOTE_EXPIRED = -2
- Swap quote expired and cannot be executed anymore
- SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED = -1
- Swap quote soft-expired (i.e. the quote probably expired, but if there is a bitcoin transaction being submitted it might still succeed)
- SpvFromBTCSwapState.CREATED = 0
- Swap quote is created, waiting on user to sign the bitcoin swap transaction
- SpvFromBTCSwapState.SIGNED = 1
- Bitcoin swap transaction was signed by the client
- SpvFromBTCSwapState.POSTED = 2
- Bitcoin swap transaction was posted to the LP
- SpvFromBTCSwapState.BROADCASTED = 3
- LP broadcasted the bitcoin swap transaction
- SpvFromBTCSwapState.FRONTED = 4
- Swap funds have been deposited to the user's wallet in front of the time
- SpvFromBTCSwapState.BTC_TX_CONFIRMED = 5
- Bitcoin swap transaction is confirmed
- SpvFromBTCSwapState.CLAIM_CLAIMED = 6
- Swap funds are claimed to the user's wallet
Bitcoin lightning network swaps
Swap Smart chain -> Bitcoin lightning network
Getting swap quote
//Create the swap: swapping SOL to Bitcoin lightning
const swap = await swapper.swap(
Tokens.SOLANA.SOL, //From specified source token
Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
undefined, //Amount is specified in the lightning network invoice!
SwapAmountType.EXACT_OUT, //Make sure we use EXACT_OUT for swaps to BTC-LN, if you want to use EXACT_IN and set an amount, use LNURL-pay!
solanaSigner.getAddress(), //Source address and smart chain signer
//Destination lightning network invoice, amount needs to be part of the invoice!
"lnbc10u1pj2q0g9pp5ejs6m677m39cznpzum7muruvh50ys93ln82p4j9ks2luqm56xxlshp52r2anlhddfa9ex9vpw9gstxujff8a0p8s3pzvua930js0kwfea6scqzzsxqyz5vqsp5073zskc5qfgp7lre0t6s8uexxxey80ax564hsjklfwfjq2ew0ewq9qyyssqvzmgs6f8mvuwgfa9uqxhtza07qem4yfhn9wwlpskccmuwplsqmh8pdy6c42kqdu8p73kky9lsnl40qha5396d8lpgn90y27ltfc5rfqqq59cya"
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const swapSuccessful = await swap.execute(
solanaSigner,
{ //Callbacks
onSourceTransactionSent: (txId: string) => {
//Transaction on the source chain was sent
},
onSourceTransactionConfirmed: (txId: string) => {
//Transaction on the source chain was confirmed
},
onSwapSettled: (destinationTxId: string) => {
//Lightning payment on the destination chain was sent and swap settled
}
}
);
//Refund in case of failure
if(!swapSuccessful) {
//Swap failed, money can be refunded
await swap.refund(solanaSigner);
} else {
//Swap successful!
}1. Initiate the swap on the smart-chain side
- a. Commit with a signer
await swap.commit(solanaSigner);- b. Or get the transactions & sign and send transaction manually
const txsCommit = await swap.txsCommit(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited();2. Wait for the swap to execute and for the payment to be sent
const swapSuccessful = await swap.waitForPayment();3. In case the swap fails we can refund our funds on the source chain
- a. Refund with a signer
if(!swapSuccessful) { await swap.refund(solanaSigner); return; }- b. Or get the transactions & sign and send transaction manually
if(!swapSuccessful) { const txsRefund = await swap.txsRefund(); //Sign and send these... ... }
- ToBTCSwapState.REFUNDED = -3
- Swap failed and was successfully refunded
- ToBTCSwapState.QUOTE_EXPIRED = -2
- Swap quote expired and cannot be executed anymore
- ToBTCSwapState.QUOTE_SOFT_EXPIRED = -1
- Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
- ToBTCSwapState.CREATED = 0
- Swap quote is created, waiting to be executed
- ToBTCSwapState.COMMITED = 1,
- Swap was initiated (init transaction sent)
- ToBTCSwapState.SOFT_CLAIMED = 2,
- Swap was processed by the counterparty but not yet claimed on-chain (lightning network payment secret was revealed)
- ToBTCSwapState.CLAIMED = 3
- Swap was finished and funds were successfully claimed by the counterparty
- ToBTCSwapState.REFUNDABLE = 4
- Swap was initiated but counterparty failed to process it, the user can now refund his funds
Swap Bitcoin lightning network -> Solana
NOTE: Solana uses an old swap protocol for Bitcoin lightning network -> Solana swaps, the flow here is different from the one for Starknet and other chains.
Getting swap quote
const swap = await swapper.swap(
Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
Tokens.SOLANA.SOL, //Into specified destination token
10000n, //Amount can be either passed in base units as bigint or in decimal format as string
SwapAmountType.EXACT_IN, //SwapAmountType.EXACT_IN, so we specify the input amount
undefined, //Source address for the swap, not used for swaps from BTC-LN
signer.getAddress() //Destination address
);
//Get the bitcoin lightning network invoice (the invoice contains pre-entered amount)
const receivingLightningInvoice: string = swap.getAddress();
//Get the URI hyperlink (contains the lightning network invoice) which can be displayed also as QR code
const qrCodeData: string = swap.getHyperlink();
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//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
const securityDeposit: string = swap.getSecurityDeposit().toString();
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
await swap.execute(
solanaSigner,
{ //Lightning network wallet, you can also pass null/undefined and pay the LN invoice from an external wallet
payInvoice: (bolt11PaymentRequest: string) => {
//Here you would usually call the WebLN or NWC to execute the payment, it's completely fine if the
// promise here would block till the payment is settled
return Promise.resolve("");
}
},
{ //Callbacks
onSourceTransactionReceived: (sourceLnPaymentHash: string) => {
//Lightning network payment received by the LP
},
onDestinationCommitSent: (destinationCommitTxId: string) => {
//HTLC initialization transaction sent on the destination chain
},
onDestinationClaimSent: (destinationClaimTxId: string) => {
//HTLC claim transaction sent on the destination chain
},
onSwapSettled: (destinationClaimTxId: string) => {
//Swap settled and funds received on destination
}
}
);1. Pay the LN invoice from a lightning network wallet
const lightningInvoice = swap.getAddress();2. Start listening to incoming lightning network payment
const success = await swap.waitForPayment(); if(!success) { //Lightning network payment not received in time and quote expired return; }3. Claim the swap at the destination
- a. Commit & claim with signer
await swap.commitAndClaim(solanaSigner);- b. Or get the transactions & sign and send transaction manually
const txsCommitAndClaim = await swap.txsCommitAndClaim(); //Take EXTRA care to make sure transaction are sent sequentially and in order - always wait // for prior transaction confirmation before sending the next one //Sign and send these... ...
- FromBTCLNSwapState.FAILED = -4
- If the claiming of the funds was initiated, but never concluded, the user will get his lightning network payment refunded
- FromBTCLNSwapState.QUOTE_EXPIRED = -3
- Swap quote expired and cannot be executed anymore
- FromBTCLNSwapState.QUOTE_SOFT_EXPIRED = -2
- Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
- FromBTCLNSwapState.EXPIRED = -1
- Lightning network invoice expired, meaning the swap is expired
- FromBTCLNSwapState.PR_CREATED = 0
- Swap is created, the user should now pay the provided lightning network invoice
- FromBTCLNSwapState.PR_PAID = 1
- Lightning network invoice payment was received (but cannot be settled by the counterparty yet)
- FromBTCLNSwapState.CLAIM_COMMITED = 2
- Claiming of the funds was initiated
- FromBTCLNSwapState.CLAIM_CLAIMED = 3
- Funds were successfully claimed & lightning network secret pre-image revealed, so the lightning network payment will settle now
Swap Bitcoin lightning network -> Starknet/EVM
Getting swap quote
const swap = await swapper.swap(
Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
Tokens.STARKNET.STRK, //Into specified destination token
10000n, //Amount can be either passed in base units as bigint or in decimal format as string
SwapAmountType.EXACT_IN, //SwapAmountType.EXACT_IN, so we specify the input amount
undefined, //Source address for the swap, not used for swaps from BTC-LN
signer.getAddress(), //Destination address
{
gasAmount: 1_000_000_000_000_000_000n //We can also request a gas drop on the destination chain (here requesting 1 STRK)
}
);
//Get the bitcoin lightning network invoice (the invoice contains pre-entered amount)
const receivingLightningInvoice: string = swap.getAddress();
//Get the URI hyperlink (contains the lightning network invoice) which can be displayed also as QR code
const qrCodeData: string = swap.getHyperlink();
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const automaticSettlementSuccess = await swap.execute(
{ //Lightning network wallet, you can also pass null/undefined and pay the LN invoice from an external wallet
payInvoice: (bolt11PaymentRequest: string) => {
//Here you would usually call the WebLN or NWC to execute the payment, it's completely fine if the
// promise here would block till the payment is settled
return Promise.resolve("");
}
},
{ //Callbacks
onSourceTransactionReceived: (sourceLnPaymentHash: string) => {
//Lightning network payment received by the LP
},
onSwapSettled: (destinationClaimTxId: string) => {
//Swap settled and funds received on destination
}
}
);
//In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
if(!automaticSettlementSuccess) {
await swap.claim(starknetSigner);
}1. Pay the LN invoice from a lightning network wallet
const lightningInvoice = swap.getAddress();2. Start listening to incoming lightning network payment
const success = await swap.waitForPayment(); if(!success) { //Lightning network payment not received in time and quote expired return; }3. Wait for the swap to be automatically settled
const automaticSettlementSuccess = await swap.waitTillClaimed(60);4. In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
- a. Claim with signer
if(!automaticSettlementSuccess) { await swap.claim(starknetSigner); }- b. Or get the transactions & sign and send transaction manually
if(!automaticSettlementSuccess) { const txsClaim = await swap.txsClaim(); //Sign and send these... ... }
- FromBTCLNAutoSwapState.FAILED = -4
- If the claiming of the funds was initiated, but never concluded, the user will get his lightning network payment refunded
- FromBTCLNAutoSwapState.QUOTE_EXPIRED = -3
- Swap quote expired and cannot be executed anymore
- FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED = -2
- Swap quote soft-expired (i.e. the quote probably expired, but if there is already an initialization transaction sent it might still succeed)
- FromBTCLNAutoSwapState.EXPIRED = -1
- Lightning network invoice expired, meaning the swap is expired
- FromBTCLNAutoSwapState.PR_CREATED = 0
- Swap is created, the user should now pay the provided lightning network invoice
- FromBTCLNAutoSwapState.PR_PAID = 1
- Lightning network invoice payment was received (but cannot be settled by the counterparty yet)
- FromBTCLNAutoSwapState.CLAIM_COMMITED = 2
- A swap HTLC was offered by the LP to the user
- FromBTCLNAutoSwapState.CLAIM_CLAIMED = 3
- Funds were successfully claimed & lightning network secret pre-image revealed, so the lightning network payment will settle now
LNURLs & readable lightning identifiers
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)
This SDK supports:
You can parse LNURLs and lightning invoices automatically using the Unified address parser
Differences
Lightning invoices:
- One time use only
- Need to have a fixed amount, therefore recipient has to set the amount
- Static and bounded expiration
- You can only pay to a lightning invoice, not withdraw funds from it
LNURLs & lightning identifiers:
- Reusable
- Programmable expiry
- Allows payer to set an amount
- Supports both, paying (LNURL-pay) and withdrawing (LNURL-withdraw)
- Possibility to attach a message/comment to a payment
- Receive a message/url as a result of the payment
Swap Smart chain -> Bitcoin lightning network
Getting swap quote
//Create the swap: swapping SOL to Bitcoin lightning
const swap = await swapper.swap(
Tokens.SOLANA.SOL, //From specified source token
Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
10000n, //Now we can specify an amount for a lightning network payment!
SwapAmountType.EXACT_OUT, //We can also use exactIn=true here and set an amount in input token
solanaSigner.getAddress(), //Source address and smart chain signer
//Destination LNURL-pay or readable identifier
"lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27",
{
comment: "Hello world" //For LNURL-pay we can also pass a comment to the recipient
}
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const swapSuccessful = await swap.execute(
solanaSigner,
{ //Callbacks
onSourceTransactionSent: (txId: string) => {
//Transaction on the source chain was sent
},
onSourceTransactionConfirmed: (txId: string) => {
//Transaction on the source chain was confirmed
},
onSwapSettled: (destinationTxId: string) => {
//Lightning payment on the destination chain was sent and swap settled
}
}
);
//Refund in case of failure
if(!swapSuccessful) {
//Swap failed, money can be refunded
await swap.refund(solanaSigner);
return;
}
//Swap successful!
const lightningSecret = swap.getSecret();
//In case the LNURL contained a success action, we can read it now and display it to user
if(swap.hasSuccessAction()) {
//Contains a success action that should displayed to the user
const successMessage = swap.getSuccessAction();
const description: string = successMessage.description; //Description of the message
const text: (string | null) = successMessage.text; //Main text of the message
const url: (string | null) = successMessage.url; //URL link which should be displayed
}1. Initiate the swap on the smart-chain side
- a. Commit with a signer
await swap.commit(solanaSigner);- b. Or get the transactions & sign and send transaction manually
const txsCommit = await swap.txsCommit(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited();2. Wait for the swap to execute and for the payment to be sent
const swapSuccessful = await swap.waitForPayment();3. In case the swap fails we can refund our funds on the source chain
- a. Refund with a signer
if(!swapSuccessful) { await swap.refund(solanaSigner); return; }- b. Or get the transactions & sign and send transaction manually
if(!swapSuccessful) { const txsRefund = await swap.txsRefund(); //Sign and send these... ... }
Swap Bitcoin lightning network -> Solana
NOTE: Solana uses an old swap protocol for Bitcoin lightning network -> Solana swaps, the flow here is different from the one for Starknet and other chains.
Getting swap quote
const swap = await swapper.swap(
Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
Tokens.SOLANA.SOL, //Into specified destination token
10000n,
SwapAmountType.EXACT_IN, //EXACT_IN, so we specify the input amount
//Source LNURL-withdraw link
"lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27",
signer.getAddress(), //Destination address
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//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
const securityDeposit: string = swap.getSecurityDeposit().toString();
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
await swap.execute(
solanaSigner,
undefined, //No need to specify a wallet, we are sourcing the fund from LNURL-withdraw link
{ //Callbacks
onSourceTransactionReceived: (sourceLnPaymentHash: string) => {
//Lightning network payment received by the LP
},
onDestinationCommitSent: (destinationCommitTxId: string) => {
//HTLC initialization transaction sent on the destination chain
},
onDestinationClaimSent: (destinationClaimTxId: string) => {
//HTLC claim transaction sent on the destination chain
},
onSwapSettled: (destinationClaimTxId: string) => {
//Swap settled and funds received on destination
}
}
);1. Start listening to incoming lightning network payment (this also requests the payment from LNURL-withdraw service)
const success = await swap.waitForPayment(); if(!success) { //Lightning network payment not received in time and quote expired return; }2. Claim the swap at the destination
- a. Commit & claim with signer
await swap.commitAndClaim(solanaSigner);- b. Or get the transactions & sign and send transaction manually
const txsCommitAndClaim = await swap.txsCommitAndClaim(); //Take EXTRA care to make sure transaction are sent sequentially and in order - always wait // for prior transaction confirmation before sending the next one //Sign and send these... ...
Swap Bitcoin lightning network -> Starknet/EVM
Getting swap quote
const swap = await swapper.swap(
Tokens.BITCOIN.BTCLN, //Swap from BTC-LN
Tokens.STARKNET.STRK, //Into specified destination token
10000n,
SwapAmountType.EXACT_IN, //EXACT_IN, so we specify the input amount
//Source LNURL-withdraw link
"lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27",
signer.getAddress(), //Destination address
{
gasAmount: 1_000_000_000_000_000_000n //We can also request a gas drop on the destination chain (here requesting 1 STRK)
}
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const automaticSettlementSuccess = await swap.execute(
undefined, //No need to specify a wallet, we are sourcing the funds from LNURL-withdraw link
{ //Callbacks
onSourceTransactionReceived: (sourceLnPaymentHash: string) => {
//Lightning network payment received by the LP
},
onSwapSettled: (destinationClaimTxId: string) => {
//Swap settled and funds received on destination
}
}
);
//In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
if(!automaticSettlementSuccess) {
await swap.claim(starknetSigner);
}1. Start listening to incoming lightning network payment (this also requests the payment from LNURL-withdraw service)
const success = await swap.waitForPayment(); if(!success) { //Lightning network payment not received in time and quote expired return; }2. Wait for the swap to be automatically settled
const automaticSettlementSuccess = await swap.waitTillClaimed(60);3. In case the automatic swap settlement fails, we can settle it manually using the wallet of the destination chain
- a. Claim with signer
if(!automaticSettlementSuccess) { await swap.claim(starknetSigner); }- b. Or get the transactions & sign and send transaction manually
if(!automaticSettlementSuccess) { const txsClaim = await swap.txsClaim(); //Sign and send these... ... }
Exact-In Smart Chain - Lightning swaps
The main limitation of regular lightning network swaps (Smart chains -> Lightning), is the fact that exactIn swaps are not possible (as invoices need to have a fixed amount). LNURL-pay links solve this issue, but are not supported by all the wallets. Therefore, the SDK exposes a hook/callback that can be implemented by lightning wallets directly, which request fixed amount invoices on-demand. This then makes exact input amount swaps possible. The way it works:
- SDK sends a request to the LP saying it wants to swap
xUSDC to BTC, with a dummy invoice (either 1 sat or as specified in theminMsatsparameter - this is requested from thegetInvoice()function) - this dummy invoice is used to estimate the routing fees by the LP (extra care must be taken for both invoices, dummy and the real one to have the same destination node public key & routing hints). - LP responds with the output amount of
yBTC - SDK calls the provided
getInvoice()callback to request the real invoice for theyamount of BTC (in satoshis) - SDK forwards the returned fixed amount (
yBTC) lightning network invoice back to the LP to finish creating the quote
Getting swap quote
//Create the swap: swapping SOL to Bitcoin lightning
const swap = await swapper.swap(
Tokens.SOLANA.SOL, //From specified source token
Tokens.BITCOIN.BTCLN, //Swap to BTC-LN
1_000_000_000n, //We can specify an amount for a lightning network payment!
SwapAmountType.EXACT_IN, //We can use exactIn=true here and set an amount in input token
solanaSigner.getAddress(), //Source address and smart chain signer
//Instead of the destination we pass a handler object
{
getInvoice: async (amountSats: number, abortSignal?: AbortSignal) => {
//Generate invoice with fixed amountSats here!
...
return invoice;
},
//Optionally you can also specify minimum and maximum in msats (millisatoshis, 1 sat = 1000 msats)
minMsats: 1_000_000n,
maxMsats: 1_000_000_000n
},
{
comment: "Hello world" //For LNURL-pay we can also pass a comment to the recipient
}
);
//Get the amount required to pay and fee
const input: string = swap.getInputWithoutFee().toString(); //Input amount excluding fees
const fee: string = swap.getFee().amountInSrcToken.toString(); //Fees paid on the output
const inputWithFees: string = swap.getInput().toString(); //Total amount paid including fees
const output: string = swap.getOutput().toString(); //Total output amount
//Get swap expiration time
const expiry: number = swap.getQuoteExpiry(); //Expiration time of the swap quote in UNIX milliseconds, swap needs to be initiated before this time
//Get pricing info
const swapPrice = swap.getPriceInfo().swapPrice; //Price of the current swap (excluding fees)
const marketPrice = swap.getPriceInfo().marketPrice; //Current market price
const difference = swap.getPriceInfo().difference; //Difference between the swap price & current market priceExecuting the swap (simple)
const swapSuccessful = await swap.execute(
solanaSigner,
{ //Callbacks
onSourceTransactionSent: (txId: string) => {
//Transaction on the source chain was sent
},
onSourceTransactionConfirmed: (txId: string) => {
//Transaction on the source chain was confirmed
},
onSwapSettled: (destinationTxId: string) => {
//Lightning payment on the destination chain was sent and swap settled
}
}
);
//Refund in case of failure
if(!swapSuccessful) {
//Swap failed, money can be refunded
await swap.refund(solanaSigner);
return;
}
//Swap successful!
const lightningSecret = swap.getSecret();1. Initiate the swap on the smart-chain side
- a. Commit with a signer
await swap.commit(solanaSigner);- b. Or get the transactions & sign and send transaction manually
const txsCommit = await swap.txsCommit(); //Sign and send these... ... //Important to wait till SDK processes the swap initialization await swap.waitTillCommited();2. Wait for the swap to execute and for the payment to be sent
const swapSuccessful = await swap.waitForPayment();3. In case the swap fails we can refund our funds on the source chain
- a. Refund with a signer
if(!swapSuccessful) { await swap.refund(solanaSigner); return; }- b. Or get the transactions & sign and send transaction manually
if(!swapSuccessful) { const txsRefund = await swap.txsRefund(); //Sign and send these... ... }
Getting state of the swap
You can get the current state of the swap with:
const state = swap.getState();You can also set a listener to listen for swap state changes:
swap.events.on("swapState", swap => {
const newState = swap.getState();
});For the meaning of the states please refer to the "Swap state" section under each swap type.
Swap size limits
Swap sizes are limited by the LPs you are connected to, they are advertised in BTC terms by LPs during handshake
const swapLimits = swapper.getSwapLimits(srcToken, dstToken);
const inputMin = swapLimits.input.min;
const inputMax = swapLimits.input.max;
const outputMin = swapLimits.output.min;
const outputMax = swapLimits.output.max;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.
let swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
let inputMin = swapLimits.input.min; //Immediately available
let inputMax = swapLimits.input.max; //Immediately available
let outputMin = swapLimits.output.min; //Not available from the get-go
let outputMax = swapLimits.output.max; //Not available from the get-go
//You can also listen to swap limit changes (optional)
swapper.on("swapLimitsChanged", () => {
//New limits available with swapper.getSwapLimits(srcToken, dstToken)
//Useful in e.g. a react application where you want to dynamically set min/max swappable amount
})
//Try to swap really small amount of SOL with exactOut swap
try {
const swap = await swapper.swap(
Tokens.BITCOIN.BTC, //Swap from BTC
Tokens.SOLANA.SOL, //Into specified destination token
1n, //1 lamport = 0.000000001 SOL
false, //Whether we define an input or output amount
undefined, //Source address for the swap, not used for swaps from BTC
solanaSigner.getAddress() //Destination address
);
} catch (e) {
//Fails with OutOfBoundsError
}
swapLimits = swapper.getSwapLimits(Tokens.BITCOIN.BTC, Tokens.SOLANA.SOL);
inputMin = swapLimits.input.min; //Immediately available
inputMax = swapLimits.input.max; //Immediately available
outputMin = swapLimits.output.min; //Now available due to failed quote
outputMax = swapLimits.output.max; //Now available due to failed quoteStored swaps
Get swap by ID
You can retrieve a swap by it's id, you can get an ID of the swap with
const swapId = swap.getId();And then later retrieve it from the storage
const swap = await swapper.getSwapById(id);Get refundable swaps
You can refund the swaps in one of two cases:
- In case intermediary is non-cooperative and goes offline, you can claim the funds from the swap contract back after some time.
- In case intermediary tried to pay but was unsuccessful, so he sent you signed message with which you can refund now without waiting.
This call can be checked on every startup and periodically every few minutes.
//Get refundable swaps and refund them
const refundableSolanaSwaps = await swapper.getRefundableSwaps("SOLANA", solanaSigner.getAddress());
for(let swap of refundableSolanaSwaps) await swap.refund(solanaSigner);
const refundableStarknetSwaps = await swapper.getRefundableSwaps("STARKNET", starknetSigner.getAddress());
for(let swap of refundableStarknetSwaps) await swap.refund(starknetSigner);Get claimable swaps
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.
//Get the swaps
const claimableSolanaSwaps = await solanaSwapper.getClaimableSwaps("SOLANA", solanaSigner.getAddress());
//Claim all the claimable swaps
for(let swap of claimableSolanaSwaps) {
await swap.claim(solanaSigner);
}
//Get the swaps
const claimableStarknetSwaps = await solanaSwapper.getClaimableSwaps("STARKNET", starknetSigner.getAddress());
//Claim all the claimable swaps
for(let swap of claimableStarknetSwaps) {
await swap.claim(starknetSigner);
}Helpers
Getting wallet balances
The SDK also contains helper functions for getting the maximum spendable balance of wallets
//Spendable balance of the starknet wallet address (discounting transaction fees)
const strkBalance = await swapper.Utils.getSpendableBalance(starknetSigner, Tokens.STARKNET.STRK);
//Spendable balance of the solana wallet address (discounting transaction fees)
const solBalance = await swapper.Utils.getSpendableBalance(solanaSigner, Tokens.SOLANA.SOL);
//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)
const {balance: btcBalance, feeRate: btcFeeRate} = await swapper.Utils.getBitcoinSpendableBalance(bitcoinWalletAddress, "SOLANA");Unified address parser
A common way for parsing all address formats supported by the SDK, automatically recognizes:
- Bitcoin on-chain L1 address formats (p2pkh, p2wpkh, p2wsh, p2wsh, p2tr)
- BIP-21 bitcoin payment URI
- BOLT11 lightning network invoices
- LUD-6 LNURL-pay links
- LUD-3 LNURL-withdraw links
- LUD-16 Lightning static internet identifiers
- Smart chain addresses (Solana, Starknet, etc.)
const res = await swapper.Utils.parseAddress(address);
switch(res.type) {
case "BITCOIN":
//Bitcoin on-chain L1 address or BIP-21 URI scheme with amount
const btcAmount = res.amount;
break;
case "LIGHTNING":
//BOLT11 lightning network invoice with pre-set amount
const lnAmount = res.amount;
break;
case "LNURL":
//LNURL payment or withdrawal link
if(isLNURLWithdraw(res.lnurl)) {
//LNURL-withdraw allowing withdrawals over the lightning network
const lnurlWithdrawData: LNURLWithdraw = res.lnurl;
const minWithdrawable = res.min; //Minimum payment amount
const maxWithdrawable = res.max; //Maximum payment amount
const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
//Should show a UI allowing the user to choose an amount he wishes to withdraw
}
if(isLNURLPay(res.lnurl)) {
//LNURL-pay or static lightning internet identifier allowing repeated payments over the lightning network
const lnurlPayData: LNURLPay = res.lnurl;
const minPayable = res.min; //Minimum payment amount
const maxPayable = res.max; //Maximum payment amount
const fixedAmount = res.amount; //If res.min===res.max, an fixed amount is returned instead
const icon: (string | null) = res.lnurl.icon; //URL encoded icon that should be displayed on the UI
const shortDescription: (string | null) = res.lnurl.shortDescription; //Short description of the payment
const longDescription: (string | null) = res.lnurl.longDescription; //Long description of the payment
const maxCommentLength: (number | 0) = res.lnurl.commentMaxLength; //Maximum allowed length of the payment message/comment (0 means no comment allowed)
//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
}
break;
default:
//Addresses for smart chains
break;
}Manually signing smart chain transactions
You can also sign the transactions on smart chain side (Solana, Starknet, etc.) of the SDK
