atomiqlabs SDK
A typescript multichain client for atomiqlabs trustlesss cross-chain swaps.
Installation
npm install @atomiqlabs/sdk
How to use?
Preparations
Set Solana RPC URL to use
const solanaRpc = "https://api.mainnet-beta.solana.com";
Browser
This uses browser's Indexed DB by default
const swapper: MultichainSwapper = new MultichainSwapper({
chains: {
SOLANA: {
rpcUrl: solanaRpc
}
}
});
NodeJS
For NodeJS we need to explicitly use filsystem storage
const swapper: MultichainSwapper = new MultichainSwapper({
chains: {
SOLANA: {
rpcUrl: solanaRpc
}
},
storageCtor: (name: string) => new FileSystemStorageManager(name)
});
Signer
const anchorWallet = useAnchorWallet();
const wallet = new SolanaSigner(anchorWallet);
or
const signer = Keypair.fromSecretKey(_privateKey);
const wallet = new SolanaSigner(signer);
Initialization
Initialize the swapper
await swapper.init();
Now we have the multichain swapper initialized
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.
const solanaSwapper = swapper.withChain("SOLANA").withSigner(signer);
Bitcoin on-chain swaps
Swap Solana -> Bitcoin on-chain
Initiating & executing the swap.
const _exactIn = false;
const _amount = new BN(10000);
const _address = "bc1qtw67hj77rt8zrkkg3jgngutu0yfgt9czjwusxt";
const swap = await solanaSwapper.create(
Tokens.SOLANA.SOL,
Tokens.BITCOIN.BTC,
_amount,
_exactIn,
_address
);
const amountToBePaid: string = swap.getInput().amount;
const fee: string = swap.getFee().amountInSrcToken.amount;
const expiry: number = swap.getExpiry();
await swap.commit();
const result: boolean = await swap.waitForPayment();
if(!result) {
await swap.refund();
} else {
const bitcoinTxId = swap.getBitcoinTxId();
}
Swap states
- 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
Initiating & executing the swap.
const _exactIn = true;
const _amount = new BN(10000);
const swap = await solanaSwapper.create(
Tokens.BITCOIN.BTC,
Tokens.SOLANA.SOL,
_amount,
_exactIn
);
const amountToBePaidOnBitcoin: string = swap.getInput().amount;
const amountToBeReceivedOnSolana: string = swap.getOutput().amount;
const fee: string = swap.getFee().amountInSrcToken.amount;
const expiry: number = swap.getExpiry();
const securityDeposit: string = swap.getSecurityDeposit().amount;
const claimerBounty: string = swap.getClaimerBounty().amount;
await swap.commit();
const receivingAddressOnBitcoin = swap.getAddress();
const qrCodeData = swap.getQrData();
const expiryTime = swap.getTimeoutTime();
try {
await swap.waitForPayment(
null, null,
(
txId: string,
confirmations: number,
targetConfirmations: number,
transactionETAms: number
) => {
}
);
await swap.waitTillClaimed();
} catch(e) {
}
Swap states
- 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
Bitcoin lightning network swaps
Swap Solana -> Bitcoin lightning network
const _lightningInvoice = "lnbc10u1pj2q0g9pp5ejs6m677m39cznpzum7muruvh50ys93ln82p4j9ks2luqm56xxlshp52r2anlhddfa9ex9vpw9gstxujff8a0p8s3pzvua930js0kwfea6scqzzsxqyz5vqsp5073zskc5qfgp7lre0t6s8uexxxey80ax564hsjklfwfjq2ew0ewq9qyyssqvzmgs6f8mvuwgfa9uqxhtza07qem4yfhn9wwlpskccmuwplsqmh8pdy6c42kqdu8p73kky9lsnl40qha5396d8lpgn90y27ltfc5rfqqq59cya";
const swap = await solanaSwapper.create(
Tokens.SOLANA.SOL,
Tokens.BITCOIN.BTCLN,
null,
false,
_lightningInvoice
);
const amountToBePaid: string = swap.getInput().amount;
const fee: string = swap.getFee().amountInSrcToken.amount;
const expiry: number = swap.getExpiry();
await swap.commit();
const result: boolean = await swap.waitForPayment();
if(!result) {
await swap.refund();
} else {
const lightningSecret = swap.getSecret();
}
Swap states
- 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
const _exactIn = true;
const _amount = new BN(10000);
const swap = await solanaSwapper.create(
Tokens.BITCOIN.BTCLN,
Tokens.SOLANA.SOL,
_amount,
_exactIn
);
const receivingLightningInvoice: string = swap.getLightningInvoice();
const qrCodeData: string = swap.getQrData();
const amountToBePaidOnBitcoin: string = swap.getInput().amount;
const amountToBeReceivedOnSolana: string = swap.getOutput().amount;
const fee: string = swap.getFee().amountInSrcToken.amount;
try {
await swap.waitForPayment();
await swap.commitAndClaim();
} catch(e) {
}
Swap states
- 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
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.
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:
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
Helpers
It is good practice to automatically distinguish between lightning network invoices & LNURLs and adjust the UI accordingly.
Therefore there are a few helper functions to help with that:
const isLNInvoice: boolean = swapper.isValidLightningInvoice(_input);
const isLNURL: boolean = swapper.isValidLNURL(_input);
if(isLNURL) {
const result: (LNURLPay | LNURLWithdraw | null) = await swapper.getLNURLTypeAndData(_input);
if(result.type==="pay") {
const lnurlPayData: LNURLPay = result;
const minPayable: BN = lnurlPayData.min;
const maxPayable: BN = lnurlPayData.max;
const icon: (string | null) = lnurlPayData.icon;
const shortDescription: (string | null) = lnurlPayData.shortDescription;
const longDescription: (string | null) = lnurlPayData.longDescription;
const maxCommentLength: (number | 0) = lnurlPayData.commentMaxLength;
}
if(result.type==="withdraw") {
const lnurlWithdrawData: LNURLWithdraw = result;
const minWithdrawable: BN = lnurlWithdrawData.min;
const maxWithdrawable: BN = lnurlWithdrawData.max;
}
}
Swap Solana -> Bitcoin lightning network
const _lnurlOrIdentifier: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27";
const _exactIn = false;
const _amount: BN = new BN(10000);
const swap = await solanaSwapper.create(
Tokens.SOLANA.SOL,
Tokens.BITCOIN.BTCLN,
_amount,
_exactIn,
_lnurlOrIdentifier
);
const amountToBePaid: string = swap.getInput().amount;
const fee: string = swap.getFee().amountInSrcToken.amount;
const expiry: number = swap.getExpiry();
await swap.commit();
const result: boolean = await swap.waitForPayment();
if(!result) {
await swap.refund();
} else {
const lightningSecret = swap.getSecret();
if(swap.hasSuccessAction()) {
const successMessage = swap.getSuccessAction();
const description: string = successMessage.description;
const text: (string | null) = successMessage.text;
const url: (string | null) = successMessage.url;
}
}
Swap Bitcoin lightning network -> Solana
const _lnurl: string = "lnurl1dp68gurn8ghj7ampd3kx2ar0veekzar0wd5xjtnrdakj7tnhv4kxctttdehhwm30d3h82unvwqhkx6rfvdjx2ctvxyesuk0a27";
const _exactIn = true;
const _amount = new BN(10000);
const swap = await solanaSwapper.create(
Tokens.BITCOIN.BTCLN,
Tokens.SOLANA.SOL,
_amount,
_exactIn,
_lnurl
);
const amountToBeWithdrawnOnBitcoin: string = swap.getInput().amount;
const amountToBeReceivedOnSolana: string = swap.getOutput().amount;
const fee: string = swap.getFee().amountInSrcToken.amount;
try {
await swap.waitForPayment();
await swap.commitAndClaim();
} catch(e) {
}
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.
const refundableSwaps = await swapper.getRefundableSwaps();
for(let swap of refundableSwaps) {
await swap.refund();
}
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.
const claimableSwaps = await swapper.getClaimableSwaps();
for(let swap of claimableSwaps) {
await swap.commitAndClaim();
}