@arkade-os/boltz-swap
v0.2.11
Published
A production-ready TypeScript package that brings Boltz submarine-swaps to Arkade.
Readme
Lightning Swaps
Integrate Lightning Network with Arkade using Submarine Swaps
Arkade provides seamless integration with the Lightning Network through Boltz submarine swaps, allowing users to move funds between Arkade and Lightning channels.
Overview
The BoltzSwapProvider library extends Arkade's functionality by enabling:
- Lightning to Arkade swaps - Receive funds from Lightning payments into your Arkade wallet
- Arkade to Lightning swaps - Send funds from your Arkade wallet to Lightning invoices
This integration is built on top of the Boltz submarine swap protocol, providing a reliable and secure way to bridge the gap between Arkade and the Lightning Network.
Installation
npm install @arkade-os/sdk @arkade-os/boltz-swapBasic Usage
Initializing the Lightning Swap Provider
import { Wallet, SingleKey } from '@arkade-os/sdk';
import { ArkadeLightning, BoltzSwapProvider } from '@arkade-os/boltz-swap';
// Create an identity
const identity = SingleKey.fromHex('your_private_key_in_hex');
// Initialize your Arkade wallet
const wallet = await Wallet.create({
identity,
arkServerUrl: 'https://mutinynet.arkade.sh',
});
// Initialize the Lightning swap provider
const swapProvider = new BoltzSwapProvider({
apiUrl: 'https://api.boltz.mutinynet.arkade.sh',
network: 'mutinynet',
referralId: 'arkade', // optional
});
// Create the ArkadeLightning instance
const arkadeLightning = new ArkadeLightning({
wallet,
swapProvider,
});Create your Wallet
import { Wallet } from '@arkade-os/sdk';
const wallet = await Wallet.create({
identity,
arkServerUrl: 'https://mutinynet.arkade.sh',
// storage defaults to in-memory
});
// Wallet may have built-in providers
const arkadeLightning = new ArkadeLightning({
wallet,
swapProvider
});ServiceWorkerWallet with IndexDB
import { ServiceWorkerWallet, SingleKey, RestArkProvider, RestIndexerProvider } from '@arkade-os/sdk';
import { IndexedDBStorageAdapter } from '@arkade-os/sdk/storage';
// Create your identity
const identity = SingleKey.fromHex('your_private_key_hex');
// Or generate a new one:
// const identity = SingleKey.fromRandomBytes();
// Configure IndexedDB storage adapter for ServiceWorker
const storage = new IndexedDBStorageAdapter('arkade-service-worker-wallet', 1);
const wallet = await ServiceWorkerWallet.setup({
serviceWorkerPath: '/service-worker.js',
arkServerUrl: 'https://mutinynet.arkade.sh',
identity,
storage, // Pass the IndexedDB storage adapter
});
// Must provide external providers for ServiceWorkerWallet (it doesn't have them)
const arkadeLightning = new ArkadeLightning({
wallet: serviceWorkerWallet,
arkProvider: new RestArkProvider('https://mutinynet.arkade.sh'),
indexerProvider: new RestIndexerProvider('https://mutinynet.arkade.sh'),
swapProvider,
});Storage Adapters: The Arkade SDK provides various storage adapters for different environments. For ServiceWorker environments, use IndexedDBStorageAdapter. For more storage options and adapters, see the Arkade SDK storage adapters documentation.
Checking Swap Limits
Before creating Lightning invoices or sending payments, you can check the minimum and maximum swap amounts supported by the Boltz service. This is useful to validate that your invoice amount is within the acceptable range.
// Get current swap limits (in satoshis)
const limits = await arkadeLightning.getLimits();
if (limits) {
console.log('Minimum swap amount:', limits.min, 'sats');
console.log('Maximum swap amount:', limits.max, 'sats');
// Example: Validate invoice amount before creating
const invoiceAmount = 50000; // 50,000 sats
if (invoiceAmount < limits.min) {
console.error(`Amount ${invoiceAmount} is below minimum ${limits.min} sats`);
} else if (invoiceAmount > limits.max) {
console.error(`Amount ${invoiceAmount} is above maximum ${limits.max} sats`);
} else {
console.log('Amount is within valid range');
// Safe to proceed with creating invoice or payment
}
} else {
console.log('Unable to fetch limits - no swap provider configured');
}Validating Lightning Invoice Amounts
import { decodeInvoice } from '@arkade-os/boltz-swap';
// Decode an incoming Lightning invoice to check its amount
const invoice = 'lnbc500u1pj...'; // Lightning invoice string
const decodedInvoice = decodeInvoice(invoice);
console.log('Invoice amount:', decodedInvoice.amountSats, 'sats');
// Check if the invoice amount is within swap limits
const limits = await arkadeLightning.getLimits();
if (limits && decodedInvoice.amountSats >= limits.min && decodedInvoice.amountSats <= limits.max) {
// Amount is valid for swaps
const paymentResult = await arkadeLightning.sendLightningPayment({
invoice: invoice,
});
console.log('Payment successful!');
} else {
console.error('Invoice amount is outside supported swap limits');
}Checking Swap Fees
You can check the fee to pay for different swap amounts supported by the Boltz service. This is useful to validate the user is willing to pay the fees.
// Get current swap fees
const fees: FeesResponse | null = await arkadeLightning.getFees();
if (!fees) throw new Error('something went wrong');
const calcSubmarineSwapFee = (satoshis: number): number => {
if (!satoshis) return 0;
const { percentage, minerFees } = fees.submarine;
return Math.ceil((satoshis * percentage) / 100 + minerFees);
};
const calcReverseSwapFee = (satoshis: number): number => {
if (!satoshis) return 0;
const { percentage, minerFees } = fees.reverse;
return Math.ceil((satoshis * percentage) / 100 + minerFees.claim + minerFees.lockup);
};Checking swap status
const response = await arkadeLightning.getSwapStatus('swap_id');
console.log('swap status = ', response.status);Storage
This library automatically stores pending swaps using the wallet's built-in contract repository. All swap data is persisted automatically and can be retrieved using the following methods:
// Get all pending submarine swaps (those waiting for Lightning payment)
const pendingPaymentsToLightning = await arkadeLightning.getPendingSubmarineSwaps();
// Get all pending reverse swaps (those waiting for claim)
const pendingPaymentsFromLightning = await arkadeLightning.getPendingReverseSwaps();
// Get complete swap history (both completed and pending)
const swapHistory = await arkadeLightning.getSwapHistory();Note: All swap data is automatically persisted and retrieved through the wallet's contract repository. No additional storage configuration is required.
Receiving Lightning Payments
To receive a Lightning payment into your Arkade wallet:
// Create a Lightning invoice that will deposit funds to your Arkade wallet
const result = await arkadeLightning.createLightningInvoice({
amount: 50000, // 50,000 sats
description: 'Payment to my Arkade wallet',
});
console.log('Receive amount:', result.amount);
console.log('Expiry (seconds):', result.expiry);
console.log('Lightning Invoice:', result.invoice);
console.log('Payment Hash:', result.paymentHash);
console.log('Pending swap', result.pendingSwap);
console.log('Preimage', result.preimage);
// The invoice can now be shared with the payer
// When paid, funds will appear in your Arkade walletMonitoring Incoming Lightning Payments
You must monitor the status of incoming Lightning payments. It will automatically claim the payment when it's available.
// Monitor the payment, it will resolve when the payment is received
const receivalResult = await arkadeLightning.waitAndClaim(result.pendingSwap);
console.log('Receival successful!');
console.log('Transaction ID:', receivalResult.txid);Sending Lightning Payments
To send a payment from your Arkade wallet to a Lightning invoice:
import { decodeInvoice } from '@arkade-os/boltz-swap';
// Parse a Lightning invoice
const invoiceDetails = decodeInvoice(
'lnbc500u1pj...' // Lightning invoice string
);
console.log('Invoice amount:', invoiceDetails.amountSats, 'sats');
console.log('Description:', invoiceDetails.description);
console.log('Payment Hash:', invoiceDetails.paymentHash);
// Pay the Lightning invoice from your Arkade wallet
const paymentResult = await arkadeLightning.sendLightningPayment({
invoice: 'lnbc500u1pj...', // Lightning invoice string
});
console.log('Payment successful!');
console.log('Amount:', paymentResult.amount);
console.log('Preimage:', paymentResult.preimage);
console.log('Transaction ID:', paymentResult.txid);Error Handling
The library provides detailed error types to help you handle different failure scenarios:
import {
SwapError,
SchemaError,
NetworkError,
SwapExpiredError,
InvoiceExpiredError,
InvoiceFailedToPayError,
InsufficientFundsError,
TransactionFailedError,
} from '@arkade-os/boltz-swap';
try {
await arkadeLightning.sendLightningPayment({
invoice: 'lnbc500u1pj...',
});
} catch (error) {
if (error instanceof InvoiceExpiredError) {
console.error('The invoice has expired. Please request a new one.');
} else if (error instanceof InvoiceFailedToPayError) {
console.error('The provider failed to pay the invoice. Please request a new one.');
} else if (error instanceof InsufficientFundsError) {
console.error('Not enough funds available:', error.message);
} else if (error instanceof NetworkError) {
console.error('Network issue. Please try again later:', error.message);
} else if (error instanceof SchemaError) {
console.error('Invalid response from API. Please try again later.');
} else if (error instanceof SwapExpiredError) {
console.error('The swap has expired. Please request a new invoice.');
} else if (error instanceof SwapError) {
console.error('Swap failed:', error.message);
} else if (error instanceof TransactionFailedError) {
console.error('Transaction failed. Please try again later');
} else {
console.error('Unknown error:', error);
}
// You might be able to claim a refund
if (error.isRefundable && error.pendingSwap) {
const refundResult = await arkadeLightning.refundVHTLC(error.pendingSwap);
console.log('Refund claimed:', refundResult.txid);
}
}