npm package: @ordchan/sdk
Description: TypeScript SDK for interacting with the OrdChan protocol
Author: OrdChan Team
Latest version: 0.3.0

# OrdChan SDK

[![npm version](https://img.shields.io/npm/v/@ordchan/sdk.svg)](https://www.npmjs.com/package/@ordchan/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A TypeScript SDK for interacting with the OrdChan protocol - a decentralized messaging platform built on Bitcoin Ordinals.

## Features

- 🔍 **Search & Discover** - Find boards, threads, and replies
- 📝 **Content Creation** - Create boards, threads, and replies
- 🔔 **Real-time Updates** - Subscribe to new content via WebSockets
- 👛 **Wallet Integration** - Built-in adapters for popular Bitcoin wallets (UniSat, OKX, Xverse)
- 🔑 **Direct Key Access** - Use private keys or seed phrases without a browser extension
- 🌍 **Network Support** - Works on both mainnet and testnet
- 🏷️ **Username Market** - Buy, sell, and make offers on usernames
- 📊 **Voting System** - Upvote/downvote content with satoshis
- 🧰 **Ordinals Processing** - Extract and parse ordinals inscriptions from blocks and transactions
- 🧩 **Extensible Architecture** - Easily add custom wallet adapters
- 🔄 **Transaction Access** - Get raw transaction data from Bitcoin nodes or public APIs
- 💱 **NFT Marketplace** - Buy and sell NFTs with our marketplace utilities (new in v0.3.0)
- 🌐 **Cross-Platform** - Compatible with both browser and Node.js environments (new in v0.3.0)

## Installation

```bash
npm install @ordchan/sdk
```

Or using Yarn:

```bash
yarn add @ordchan/sdk
```

## Quick Start

```typescript
import { OrdChanSDK, UniSatWalletAdapter, PrivateKeyWalletAdapter } from '@ordchan/sdk';

// Initialize the SDK
const sdk = new OrdChanSDK({
  apiUrl: 'https://api.ordchan.com'
});

// Connect wallet (choose one method)
const connectBrowserWallet = async () => {
  const wallet = new UniSatWalletAdapter();
  await wallet.connect();
  sdk.setWalletAdapter(wallet);
  console.log('Connected to browser wallet');
};

const connectWithSeedPhrase = async () => {
  const wallet = new PrivateKeyWalletAdapter({
    seedPhrase: 'your twelve or twenty four word mnemonic seed phrase',
    network: 'mainnet'
  });
  await wallet.connect();
  sdk.setWalletAdapter(wallet);
  console.log('Connected with seed phrase');
};

// Get all boards
const getBoards = async () => {
  const boards = await sdk.boards.getAll();
  console.log('Boards:', boards);
};

// Create a new thread
const createThread = async () => {
  const thread = await sdk.threads.create({
    board: 'bitcoin',
    title: 'Hello OrdChan',
    content: 'This is my first thread'
  });
  console.log('Created thread:', thread);
};

// Subscribe to new replies in a thread
const subscribeToReplies = (threadId) => {
  const unsubscribe = sdk.replies.subscribe(
    { threadId },
    (reply) => {
      console.log('New reply:', reply);
    }
  );
  
  // To stop listening later:
  // unsubscribe();
};
```

## NFT Marketplace (New in v0.3.0)

The SDK includes a complete marketplace implementation for buying and selling NFTs on Bitcoin:

```typescript
import { marketplace } from '@ordchan/sdk';
import { UniSatWalletAdapter, PrivateKeyWalletAdapter } from '@ordchan/sdk';

// Example 1: Sell an NFT by creating a listing
const sellNft = async () => {
  // Connect a wallet (browser extension or private key)
  const wallet = new PrivateKeyWalletAdapter({ 
    seedPhrase: 'your seed phrase here',
    network: 'bitcoin'
  });
  await wallet.connect();
  
  // Get seller's address
  const sellerAddress = await wallet.getAddress();
  
  // Find the UTXO containing the inscription
  const inscriptionId = '1d33f2a46047fbb366652d003d8049bbadc6e38062ce1b9b57408a2a5d4373a7i0';
  const inscriptionUtxo = await marketplace.utxo.findInscriptionUtxo(
    inscriptionId,
    sellerAddress,
    'bitcoin' // or 'fractal' for Fractal network
  );
  
  if (!inscriptionUtxo) {
    console.error('Inscription not found in this wallet');
    return;
  }
  
  // Create a listing PSBT
  const price = 10000; // 10,000 satoshis
  const listingPsbt = marketplace.psbt.createNftListingPsbt({
    inscriptionUtxo,
    sellerAddress,
    recipientAddress: sellerAddress, // Where to receive payment
    price,
    platformFeeAddress: 'bc1q...' // Optional: platform fee recipient
  });
  
  // Sign the listing PSBT with the wallet
  const privateKey = await wallet.getPrivateKey(); // Implementation depends on wallet
  const signedPsbt = marketplace.psbt.signNftSellerPsbt(listingPsbt, privateKey);
  
  console.log('Listing PSBT created and signed:');
  console.log(signedPsbt);
  
  // This PSBT can now be shared with potential buyers
  return signedPsbt;
};

// Example 2: Buy an NFT from a listing
const buyNft = async (listingPsbtHex) => {
  // Connect a wallet
  const wallet = new UniSatWalletAdapter();
  await wallet.connect();
  
  // Get buyer's address
  const buyerAddress = await wallet.getAddress();
  
  // Find payment UTXOs
  const utxos = await marketplace.utxo.findAllUtxos(buyerAddress, 'bitcoin');
  
  // Create buyer's PSBT
  const buyerPsbt = marketplace.psbt.createNftBuyerPsbt({
    sellerSignedPsbtHex: listingPsbtHex,
    buyerAddress,
    changeAddress: buyerAddress,
    paymentUtxos: utxos,
    feeRate: 5 // sats/byte
  });
  
  // Sign the buyer's PSBT
  const signedBuyerPsbt = await wallet.signPsbt(buyerPsbt);
  
  // Combine PSBTs
  const completePsbt = marketplace.psbt.combineNftPsbts(
    listingPsbtHex,
    signedBuyerPsbt
  );
  
  // Extract and broadcast the final transaction
  const txHex = marketplace.transaction.extractTransaction(completePsbt);
  if (txHex) {
    const result = await marketplace.transaction.broadcastTransaction(
      txHex,
      'bitcoin' // or 'fractal'
    );
    
    console.log('NFT purchase successful!');
    console.log('Transaction ID:', result.txid);
    console.log('Explorer URL:', marketplace.transaction.getExplorerUrl(result.txid, 'bitcoin'));
  }
};
```

## Cross-Platform Compatibility (New in v0.3.0)

The SDK now works seamlessly in both browser and Node.js environments:

```typescript
import { marketplace, wallet } from '@ordchan/sdk';

// Browser environment: Use a browser wallet
const useBrowserWallet = async () => {
  // This will only attempt to connect if running in a browser
  try {
    const walletAdapter = new wallet.UniSatWalletAdapter();
    await walletAdapter.connect();
    console.log('Connected to UniSat wallet');
    return walletAdapter;
  } catch (error) {
    console.error('Browser wallet not available:', error);
    return null;
  }
};

// Node.js environment: Use a private key or seed phrase
const usePrivateKeyWallet = async () => {
  const walletAdapter = new wallet.PrivateKeyWalletAdapter({
    seedPhrase: 'your seed phrase here',
    // or privateKey: 'your WIF private key',
    network: 'bitcoin'
  });
  await walletAdapter.connect();
  console.log('Connected with private key wallet');
  return walletAdapter;
};

// Works in both environments
const getWalletAdapter = async () => {
  // Try browser wallet first
  const browserWallet = await useBrowserWallet().catch(() => null);
  if (browserWallet) return browserWallet;
  
  // Fall back to private key wallet
  return await usePrivateKeyWallet();
};
```

## UTXO Management (New in v0.3.0)

The SDK provides comprehensive UTXO utilities:

```typescript
import { marketplace } from '@ordchan/sdk';

// Find UTXOs containing a specific inscription
const findInscription = async () => {
  const inscriptionId = '1d33f2a46047fbb366652d003d8049bbadc6e38062ce1b9b57408a2a5d4373a7i0';
  const address = 'bc1q...';
  const network = 'bitcoin'; // or 'fractal'
  
  const utxo = await marketplace.utxo.findInscriptionUtxo(
    inscriptionId,
    address,
    network
  );
  
  if (utxo) {
    console.log(`Inscription found in UTXO: ${utxo.txid}:${utxo.vout}`);
    console.log(`Value: ${utxo.value} sats`);
  } else {
    console.log('Inscription not found in this address');
  }
};

// Find all UTXOs for an address
const findAllUtxos = async () => {
  const address = 'bc1q...';
  const utxos = await marketplace.utxo.findAllUtxos(address, 'bitcoin');
  console.log(`Found ${utxos.length} UTXOs with total value: ${
    utxos.reduce((sum, u) => sum + u.value, 0)
  } sats`);
};

// Select UTXOs for a transaction
const selectUtxos = () => {
  const availableUtxos = [/* array of UTXOs */];
  const amountNeeded = 50000; // 50,000 sats
  const minUtxosNeeded = 2; // Need at least 2 UTXOs for placeholder inputs
  
  const selectedUtxos = marketplace.utxo.selectUtxosForPayment(
    availableUtxos,
    amountNeeded,
    minUtxosNeeded
  );
  
  console.log(`Selected ${selectedUtxos.length} UTXOs with value: ${
    selectedUtxos.reduce((sum, u) => sum + u.value, 0)
  } sats`);
};

// Create placeholder UTXOs for complex transactions
const createPlaceholderUtxos = async () => {
  const wallet = new wallet.PrivateKeyWalletAdapter({
    seedPhrase: 'your seed phrase here'
  });
  await wallet.connect();
  
  const address = await wallet.getAddress();
  const utxos = await marketplace.utxo.findAllUtxos(address);
  
  // Create multiple small UTXOs from a larger one
  const txid = await marketplace.transaction.createPlaceholderUtxos(
    utxos,
    address,
    wallet.getPrivateKey(),
    5, // number of UTXOs to create
    5000, // value of each UTXO (5,000 sats)
    5, // fee rate (sats/byte)
    'bitcoin' // network
  );
  
  console.log(`Created placeholder UTXOs in transaction: ${txid}`);
};
```

## Transaction Handling (New in v0.3.0)

The SDK provides comprehensive transaction utilities:

```typescript
import { marketplace } from '@ordchan/sdk';

// Broadcast a transaction
const broadcastTx = async () => {
  const txHex = '020000000001...'; // Transaction hex
  const network = 'bitcoin'; // or 'fractal'
  
  try {
    const result = await marketplace.transaction.broadcastTransaction(txHex, network);
    console.log('Transaction broadcast successful!');
    console.log('Transaction ID:', result.txid);
    
    // Get explorer URL
    const url = marketplace.transaction.getExplorerUrl(result.txid, network);
    console.log('View transaction:', url);
  } catch (error) {
    console.error('Broadcast failed:', error);
  }
};

// Sign a PSBT with a private key
const signPsbt = () => {
  const psbtHex = '70736274ff...'; // PSBT hex
  const privateKey = Buffer.from('...'); // Private key buffer
  
  // Optionally specify sighash types for each input
  const sighashTypes = [
    0x01, // SIGHASH_ALL for input 0
    0x81  // SIGHASH_SINGLE|ANYONECANPAY for input 1
  ];
  
  const signedPsbtHex = marketplace.transaction.signPsbt(
    psbtHex,
    privateKey,
    sighashTypes // optional
  );
  
  console.log('Signed PSBT:', signedPsbtHex);
};

// Extract a transaction from a PSBT
const extractTx = () => {
  const psbtHex = '70736274ff...'; // PSBT hex
  
  // Try to extract a complete transaction
  const txHex = marketplace.transaction.extractTransaction(psbtHex);
  
  if (txHex) {
    console.log('Extracted transaction:', txHex);
  } else {
    // Try with allowIncomplete=true for partially signed PSBTs
    const incompleteTxHex = marketplace.transaction.extractTransaction(psbtHex, true);
    console.log('Extracted incomplete transaction:', incompleteTxHex);
  }
};

// Calculate transaction fee
const calculateFee = () => {
  const txHex = '020000000001...'; // Transaction hex
  const feeRate = 5; // sats/byte
  
  const estimatedFee = marketplace.transaction.estimateTransactionFee(txHex, feeRate);
  console.log(`Estimated fee: ${estimatedFee} sats`);
};
```

## Ordinals Processing

The SDK includes powerful utilities for processing Bitcoin blocks and transactions to extract ordinals inscriptions:

```typescript
// Initialize the SDK
const sdk = new OrdChanSDK({
  apiUrl: 'https://api.ordchan.com'
});

// Process a Bitcoin block to extract all ordinals inscriptions
const processBlock = async () => {
  const blockHash = '000000000000000000076c036ff5119e5c5a633a9e8e5ba71b7ad89e52a0dcec'; // Block 800000
  const messages = await sdk.ordinals.processBlock(blockHash);
  console.log(`Found ${messages.length} ordinals in block`);
  
  // Filter to only OrdChan protocol messages
  const ordchanMessages = sdk.ordinals.filterByProtocol(messages, ['ordchan']);
  console.log(`Found ${ordchanMessages.length} OrdChan messages`);
};

// Process a single transaction to extract ordinals
const processTransaction = async () => {
  const txid = 'f34c43b1158de337e8e4f74d09256a574e865d8ab280926a0cd40d4e7aabf9a2';
  const messages = await sdk.ordinals.processTransaction(txid);
  console.log(`Found ${messages.length} inscriptions in transaction`);
};

// Process a transaction directly from its raw hex
const processTransactionHex = async () => {
  // Get transaction hex from anywhere (from wallet, API, or manual construction)
  const txHex = '020000000001018c57c91a45a44ba....'; // Transaction hex data
  
  // Process directly without needing to broadcast or have txid
  const { txid, ordinals } = await sdk.ordinals.processTransactionFromHex(txHex);
  console.log(`Transaction ID: ${txid}`);
  console.log(`Found ${ordinals.length} inscriptions in transaction hex`);
  
  // You can also provide optional block information if available
  const result = await sdk.ordinals.processTransactionFromHex(txHex, {
    blockInfo: {
      blockHeight: 800000,
      blockHash: '000000000000000000076c036ff5119e5c5a633a9e8e5ba71b7ad89e52a0dcec',
      timestamp: 1685341196
    }
  });
  console.log(`Transaction ${result.txid} has ${result.ordinals.length} inscriptions`);
};

// Extract a message from an inscription ID
const extractMessage = async () => {
  const inscriptionId = 'f34c43b1158de337e8e4f74d09256a574e865d8ab280926a0cd40d4e7aabf9a2i0';
  const content = await sdk.ordinals.extractMessage(inscriptionId);
  console.log('Inscription content:', content);
};
```

See [ORDINALS_PROCESSING.md](./docs/ORDINALS_PROCESSING.md) for more details.

## Transaction Data Access

The SDK provides methods to retrieve raw Bitcoin transaction data from various sources:

```typescript
// Initialize the SDK with Bitcoin RPC configuration (optional)
const sdk = new OrdChanSDK({
  apiUrl: 'https://api.ordchan.com',
  // Optionally configure Bitcoin RPC access
  bitcoinRpc: {
    host: 'localhost',
    port: 8332,
    user: 'username',
    password: 'password'
    // Alternatively, use a cookie file
    // cookieFile: '~/.bitcoin/.cookie'
  },
  // Optionally configure fallbacks and preferred data sources
  dataSourceConfig: {
    preferBlockstream: false, // Prefer blockstream.info API over RPC
    preferMempool: true,      // Prefer mempool.space API over RPC
    fallbackToPublicApis: true // Use public APIs if RPC fails
  }
});

// Get raw transaction hex
const getRawTx = async (txid) => {
  try {
    const txHex = await sdk.transactions.getRawTransactionHex(txid);
    console.log(`Transaction hex (${txHex.length / 2} bytes):`);
    console.log(txHex.substring(0, 100) + '...');
    
    // Parse the transaction
    const tx = bitcoin.Transaction.fromHex(txHex);
    console.log(`Transaction has ${tx.ins.length} inputs and ${tx.outs.length} outputs`);
    
    return txHex;
  } catch (error) {
    console.error('Failed to get transaction hex:', error);
  }
};
```

The SDK will automatically try multiple sources to get transaction data:
1. Local Bitcoin node (via RPC) if configured
2. mempool.space API
3. blockstream.info API
4. blockchain.info API
5. OrdChan API as a last resort

## Documentation

For full API documentation, see the [SDK Docs](./docs/README.md).

## Examples

The repository includes several examples demonstrating how to use the SDK:

- [Connect to wallet](./examples/ConnectWalletExample.ts)
- [Create a new thread](./examples/CreateThreadExample.ts)
- [Register a username](./examples/InscribeUsernameExample.ts)
- [Process ordinals inscriptions](./examples/ProcessOrdinals.ts)
- [Get transaction data](./examples/TransactionDataExample.ts)
- [Buy and sell NFTs](./examples/NftMarketplaceExample.ts)

## Version History

### v0.3.0
- Added comprehensive NFT marketplace functionality
- Made wallet adapters compatible with both browser and Node.js
- Added UTXO management utilities
- Added transaction handling utilities
- Fixed TypeScript compilation issues
- Improved error handling and type safety

### v0.2.3
- Added ordinals processing utilities
- Added transaction data access
- Added username market functionality
- Improved wallet adapters

### v0.2.0
- Initial public release
- Basic board and thread functionality
- Wallet integration

## License

MIT