← Back to Home

Sparkle Protocol SDK Documentation

Implementation Track – production-ready SDK for Lightning-atomic ordinal swaps.

Status: 100% Production Proven on Bitcoin Mainnet

The complete atomic swap mechanism has been validated on Bitcoin mainnet with real inscriptions and Lightning payments. See the Complete Proof Report for transaction details.

Proven Components:

  • On-chain lock/sweep: Tested with real Darkita #1 inscription
  • Lightning integration: Hold invoices, channel management, settlement
  • Complete atomic swap: Payment, Preimage, Settlement flow
  • Production ready: MIT licensed, open source

Developer SDK - Core Implementation Complete

Version 1.0.0 | December 2025

For Application Developers

Overview

This document describes the Sparkle Protocol SDK for JavaScript/TypeScript developers. The core Sparkle Swap library is now implemented with working Taproot atomic swap functionality. The higher-level SDK for inscriptions and coordinator integration is still in design phase.

Implemented: Taproot address generation, claim transactions, refund transactions, preimage verification. See Section 1 for working code.
Note: The higher-level SDK (inscription creation, coordinator integration, wallet management) described in Sections 2-11 is still conceptual. Only Section 1 contains working, tested code.

1. Working Implementation

This code is real and tested. The core Sparkle Swap library is now implemented.

1.1 Install Dependencies

npm install @scure/btc-signer @noble/hashes

1.2 Create a Sparkle Swap Address

import { createSparkleSwapAddress } from './core/taproot-scripts';
import { sha256 } from '@noble/hashes/sha256';

// Generate payment hash from preimage
const preimage = new Uint8Array(32); // Your 32-byte preimage
crypto.getRandomValues(preimage);
const paymentHash = sha256(preimage);

// Create the swap address
const swapAddress = createSparkleSwapAddress({
  paymentHash,
  buyerPubkey: buyerCompressedPubkey,  // 33 bytes
  sellerPubkey: sellerCompressedPubkey, // 33 bytes
  refundLocktime: currentBlockHeight + 144, // ~24 hours
  network: 'testnet',
});

console.log('Swap Address:', swapAddress.address);
// Seller sends Ordinal to this address

1.3 Build Claim Transaction (Buyer)

import { buildClaimTransaction } from './core/claim-transaction';

// After Lightning payment reveals preimage
const claimTx = buildClaimTransaction({
  swapAddress,
  fundingTxid: 'abc123...', // TX where seller sent Ordinal
  fundingVout: 0,
  fundingAmount: 10000n, // satoshis
  preimage, // 32 bytes - revealed by Lightning payment
  buyerPrivkey, // 32 bytes
  destinationAddress: 'tb1q...', // Buyer's address
  feeRate: 2, // sats/vbyte
  network: 'testnet',
});

console.log('Claim TX:', claimTx.txHex);
console.log('TXID:', claimTx.txid);
// Broadcast to claim the Ordinal

1.4 Build Refund Transaction (Seller)

import { buildRefundTransaction } from './core/refund-transaction';

// If buyer doesn't pay before timeout
const refundTx = buildRefundTransaction({
  swapAddress,
  fundingTxid: 'abc123...',
  fundingVout: 0,
  fundingAmount: 10000n,
  sellerPrivkey, // 32 bytes
  destinationAddress: 'tb1q...', // Seller's address
  feeRate: 2,
  network: 'testnet',
});

console.log('Refund TX:', refundTx.txHex);
console.log('Locktime:', refundTx.locktime);
// Can only broadcast after locktime passes

1.5 Taproot Script Details

The swap address uses a Taproot tree with two spending paths:

PathScriptUsed By
Hashlock (Claim) OP_SHA256 <hash> OP_EQUALVERIFY <buyer_pk> OP_CHECKSIG Buyer with preimage
Timelock (Refund) <locktime> OP_CLTV OP_DROP <seller_pk> OP_CHECKSIG Seller after timeout

1.6 Browser Integration (NIP-07 + Bitcoin Wallets)

NEW in v1.0.0: Secure browser wallet integration - no private keys required.

The browser modules enable secure wallet connections without ever handling private keys:

NIP-07 Nostr Wallet (Alby, nos2x)

import { connectWallet, signEvent, encryptNip04 } from 'sparkle-protocol/browser';

// Connect via browser extension (prompts user)
const connection = await connectWallet();
if (connection.state === 'connected') {
  console.log('Nostr pubkey:', connection.pubkey);
}

// Sign events (extension handles private key)
const signedEvent = await signEvent({
  kind: 4,
  created_at: Math.floor(Date.now() / 1000),
  tags: [['p', recipientPubkey]],
  content: await encryptNip04(recipientPubkey, message),
});

Bitcoin Wallet (Unisat, Xverse)

import {
  detectBitcoinWallets,
  connectBitcoinWallet,
  signPsbt
} from 'sparkle-protocol/browser';

// Detect available wallets
const wallets = detectBitcoinWallets();
// ['unisat', 'xverse']

// Connect (prompts user)
const btcConnection = await connectBitcoinWallet('unisat');
console.log('Address:', btcConnection.accounts[0].address);

// Sign PSBT (extension handles private key)
const result = await signPsbt('unisat', psbtHex, {
  autoFinalized: false
});

Unified Wallet Manager

import { SparkleWalletManager } from 'sparkle-protocol/browser';

const manager = new SparkleWalletManager((state) => {
  console.log('Wallet state changed:', state);
});

// Connect both wallets
await manager.connectNostr();
await manager.connectBitcoin();

// Sign and publish Nostr events
await manager.signAndPublish(event);

// Sign Bitcoin PSBTs
const { signedPsbtHex } = await manager.signPsbt(psbtHex);

Try it live: Taproot Swap Demo

2. Future SDK Installation

2.1 NPM Package

Proposed package name: @sparkle/protocol-sdk

# Future installation command
npm install @sparkle/protocol-sdk

# Or with yarn
yarn add @sparkle/protocol-sdk

2.2 Requirements

Note: All code examples below are conceptual designs, not working code.

2. Quick Start (Conceptual)

2.1 Initialize SDK

import { SparkleClient } from '@sparkle/protocol-sdk'

// Initialize client
const sparkle = new SparkleClient({
  network: 'testnet',
  bitcoinRPC: {
    url: 'http://localhost:18332',
    username: 'your_rpc_user',
    password: 'your_rpc_pass'
  },
  lightningNode: {
    type: 'lnd',
    macaroonPath: '/path/to/admin.macaroon',
    tlsCertPath: '/path/to/tls.cert',
    host: 'localhost:10009'
  },
  coordinator: 'https://coordinator.sparkleprotocol.com'
})

2.2 Create Sparkle Inscription

// Create Sparkle-compatible NFT
const inscription = await sparkle.inscribe({
  file: './my-nft.png',
  metadata: {
    name: 'My First Sparkle NFT',
    description: 'Experimental Lightning-tradeable ordinal',
    lightning: {
      enabled: true,
      minChannelCapacity: 100000
    }
  },
  feeRate: 10
})

console.log('Inscription ID:', inscription.id)
// Output: abc123def456...i0

2.3 List for Sale

// List inscription for Lightning-settled sale
const listing = await sparkle.list({
  inscriptionId: 'abc123def456...i0',
  priceSats: 100000,
  expiresIn: 86400 // 24 hours in seconds
})

console.log('Listing created:', listing.id)
console.log('Share URL:', listing.url)

2.4 Buy Inscription

// Purchase inscription via Lightning
const purchase = await sparkle.buy({
  listingId: 'listing_xyz',
  maxPriceSats: 105000 // Allow 5% slippage
})

console.log('Purchase complete!')
console.log('TXID:', purchase.txid)
console.log('Lightning payment:', purchase.lightningPayment)

3. API Reference

3.1 SparkleClient Class

Constructor

new SparkleClient(config: ClientConfig)

ClientConfig interface:

interface ClientConfig {
  network: 'mainnet' | 'testnet' | 'regtest'
  bitcoinRPC: {
    url: string
    username: string
    password: string
  }
  lightningNode: {
    type: 'lnd' | 'cln' | 'eclair'
    macaroonPath?: string  // LND only
    tlsCertPath?: string   // LND only
    host: string
    port?: number
  }
  coordinator: string  // Coordinator URL
  feeRate?: number     // Default fee rate (sat/vB)
}

Methods

sparkle.inscribe(options)

await sparkle.inscribe({
  file: string | Buffer,
  metadata: {
    name?: string,
    description?: string,
    attributes?: object,
    lightning: {
      enabled: boolean,
      minChannelCapacity?: number
    }
  },
  feeRate?: number
})

Returns: Promise<Inscription>

sparkle.list(options)

await sparkle.list({
  inscriptionId: string,
  priceSats: number,
  expiresIn?: number  // seconds
})

Returns: Promise<Listing>

sparkle.buy(options)

await sparkle.buy({
  listingId: string,
  maxPriceSats?: number
})

Returns: Promise<Purchase>

sparkle.getInscription(id)

await sparkle.getInscription(inscriptionId: string)

Returns: Promise<Inscription>

sparkle.getListings(filter)

await sparkle.getListings({
  minPrice?: number,
  maxPrice?: number,
  limit?: number
})

Returns: Promise<Listing[]>

3.2 Type Definitions

interface Inscription {
  id: string
  owner: string
  metadata: object
  sparkleEnabled: boolean
  contentType: string
  contentLength: number
  genesisTransaction: string
  genesisHeight: number
}

interface Listing {
  id: string
  inscriptionId: string
  seller: string
  priceSats: number
  status: 'active' | 'sold' | 'expired' | 'cancelled'
  createdAt: number
  expiresAt: number
  url: string
}

interface Purchase {
  id: string
  listingId: string
  inscriptionId: string
  buyer: string
  seller: string
  priceSats: number
  txid: string
  lightningPayment: string
  status: 'pending' | 'complete' | 'failed'
}

4. Advanced Usage

4.1 Custom Coordinator

// Use your own coordinator
const sparkle = new SparkleClient({
  // ... other config
  coordinator: 'https://my-coordinator.example.com',
  coordinatorAuth: {
    apiKey: 'your_api_key'
  }
})

4.2 Batch Inscriptions

// Inscribe multiple files at once
const inscriptions = await sparkle.batchInscribe({
  files: [
    { path: './nft1.png', metadata: { name: 'NFT #1' } },
    { path: './nft2.png', metadata: { name: 'NFT #2' } },
    { path: './nft3.png', metadata: { name: 'NFT #3' } }
  ],
  feeRate: 10
})

console.log('Inscribed:', inscriptions.length, 'NFTs')

4.3 Trade Monitoring

// Monitor trade status
sparkle.on('trade:initiated', (trade) => {
  console.log('Trade initiated:', trade.id)
})

sparkle.on('trade:payment', (trade) => {
  console.log('Lightning payment sent:', trade.lightningPayment)
})

sparkle.on('trade:complete', (trade) => {
  console.log('Trade complete! TXID:', trade.txid)
})

sparkle.on('trade:failed', (trade, error) => {
  console.error('Trade failed:', error.message)
})

4.4 Wallet Integration

// Get wallet balance
const balance = await sparkle.wallet.balance()
console.log('Ordinals:', balance.ordinals)
console.log('Cardinal sats:', balance.cardinal)

// Get wallet address
const address = await sparkle.wallet.address()
console.log('Receive address:', address)

// List wallet inscriptions
const inscriptions = await sparkle.wallet.inscriptions()
console.log('You own:', inscriptions.length, 'inscriptions')

5. Coordinator API (For Coordinator Operators)

5.1 Running a Coordinator

import { SparkleCoordinator } from '@sparkle/coordinator'

const coordinator = new SparkleCoordinator({
  network: 'testnet',
  lightningNode: {
    type: 'lnd',
    macaroonPath: '/path/to/admin.macaroon',
    tlsCertPath: '/path/to/tls.cert'
  },
  bitcoinRPC: {
    url: 'http://localhost:18332',
    username: 'user',
    password: 'pass'
  },
  port: 3000,
  feePercent: 0.5  // 0.5% coordinator fee
})

await coordinator.start()
console.log('Coordinator running on port 3000')

5.2 Coordinator Configuration

interface CoordinatorConfig {
  network: 'mainnet' | 'testnet'
  lightningNode: LightningNodeConfig
  bitcoinRPC: BitcoinRPCConfig
  port: number
  feePercent: number
  maxTradeValue?: number
  htlcTimeout?: number     // blocks
  requireKYC?: boolean
  bondAmount?: number      // sats
  reputationThreshold?: number
}

6. Error Handling

6.1 Common Errors

try {
  await sparkle.buy({ listingId: 'xyz' })
} catch (error) {
  if (error instanceof SparkleError) {
    switch (error.code) {
      case 'INSUFFICIENT_FUNDS':
        console.error('Not enough sats in Lightning channel')
        break
      case 'LISTING_EXPIRED':
        console.error('Listing has expired')
        break
      case 'COORDINATOR_OFFLINE':
        console.error('Coordinator is not responding')
        break
      case 'HTLC_TIMEOUT':
        console.error('Lightning payment timed out')
        break
      case 'INSCRIPTION_ALREADY_SOLD':
        console.error('Inscription was sold to another buyer')
        break
      default:
        console.error('Unknown error:', error.message)
    }
  }
}

6.2 Error Codes Reference

Error Code Description Recovery
INSUFFICIENT_FUNDS Not enough Lightning channel capacity Open larger channel or top up
LISTING_EXPIRED Listing time window closed Find another listing
COORDINATOR_OFFLINE Cannot reach coordinator Retry or use different coordinator
HTLC_TIMEOUT Lightning payment timed out Funds automatically refunded
INVALID_INSCRIPTION Inscription not Sparkle-compatible Check metadata format

7. Testing

7.1 Testnet Example

import { SparkleClient } from '@sparkle/protocol-sdk'

// Initialize for testnet
const sparkle = new SparkleClient({
  network: 'testnet',
  bitcoinRPC: { /*...*/ },
  lightningNode: { /*...*/ },
  coordinator: 'https://testnet-coordinator.sparkleprotocol.com'
})

// Test inscription
const inscription = await sparkle.inscribe({
  file: './test-nft.png',
  metadata: {
    name: 'Test NFT',
    lightning: { enabled: true }
  }
})

console.log('Test inscription created:', inscription.id)

7.2 Mock Coordinator

import { MockCoordinator } from '@sparkle/protocol-sdk/testing'

// Use mock coordinator for local testing
const sparkle = new SparkleClient({
  network: 'regtest',
  coordinator: new MockCoordinator({
    simulateDelay: 100,  // ms
    failureRate: 0.1     // 10% failure rate for testing
  })
})

8. Best Practices

8.1 Security

8.2 Performance

8.3 User Experience

9. Contributing

Future: Once the SDK exists, contributions would be welcome. Potential areas:

10. SDK Development Roadmap

Phase 1 Complete! Core protocol implementation is done.

Remaining work: Lightning integration, coordinator API, inscription management.

11. Support & Resources

11.1 Documentation

11.2 Community