npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

solana-tip-overlay-sdk

v1.0.0

Published

SDK for building Solana tip overlays for streamers

Readme

@solana-tip/sdk

A complete SDK for building Solana tip overlays for streamers on devnet and mainnet.

Enable your viewers to tip streamers directly with SOL, with real-time overlay animations in OBS. No on-chain program required - uses simple SPL transfers with signature verification.


🎯 Features

  • Streamer wallet linking with ed25519 signature verification
  • Real-time tip detection via Solana RPC polling
  • WebSocket broadcasting for instant overlay updates
  • OBS Browser Source integration with animations
  • Memo support for custom tip messages
  • Rate limiting and abuse prevention
  • Pluggable storage (lowdb, PostgreSQL, Redis, etc.)
  • Easy devnet ↔ mainnet switching

📦 Installation

npm install @solana-tip/sdk

Peer Dependencies:

npm install express @solana/web3.js

🚀 Quick Start

1. Backend Integration

import express from 'express';
import { createServer } from 'http';
import { SolanaTipSDK, LowdbAdapter } from '@solana-tip/sdk';

const app = express();
const httpServer = createServer(app);

// Initialize SDK
const storage = new LowdbAdapter('./db.json');
const sdk = new SolanaTipSDK(storage, {
  indexer: {
    cluster: 'devnet', // or 'mainnet-beta'
    pollInterval: 2500
  }
});

// Start SDK
await sdk.start();

// Initialize WebSocket server
sdk.initWebSocket({ server: httpServer });

// Register streamer endpoint
app.post('/api/register', async (req, res) => {
  const { streamerId, pubkey, message, signature } = req.body;
  const result = await sdk.getRegistry().register(
    streamerId, pubkey, message, signature
  );
  res.json(result);
});

// Get streamer info endpoint
app.get('/api/streamer/:id', async (req, res) => {
  const streamer = await sdk.getRegistry().getStreamer(req.params.id);
  res.json(streamer || { error: 'Not found' });
});

httpServer.listen(3000);

2. Streamer Wallet Linking (Frontend)

import { PhantomWalletAdapter } from '@solana-tip/sdk/client';

const wallet = new PhantomWalletAdapter();
const pubkey = await wallet.connect();

const message = `Link streamer ${streamerId} at ${new Date().toISOString()}`;
const signature = await wallet.signMessage(message);

// Send to your backend
await fetch('/api/register', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ streamerId, pubkey, message, signature })
});

3. Viewer Tipping (Frontend)

import { TipSender } from '@solana-tip/sdk/client';
import * as solanaWeb3 from '@solana/web3.js';

const tipSender = new TipSender();
const connection = new solanaWeb3.Connection(
  solanaWeb3.clusterApiUrl('devnet')
);

const signature = await tipSender.sendTip({
  solanaWeb3,
  connection,
  wallet, // PhantomWalletAdapter instance
  streamerId: 'mychannel',
  amountSol: 0.1,
  memo: 'Great stream!'
});

4. OBS Overlay (Frontend)

import { OverlayClient } from '@solana-tip/sdk/client';

const client = new OverlayClient('ws://localhost:3000/ws', 'mychannel');

client.on('tip', (tipData) => {
  // Display animation
  console.log(`${tipData.amountSol} SOL from ${tipData.from}`);
});

client.connect();

In OBS: Add Browser Source → http://localhost:3000/overlay.html?streamer=mychannel


📚 API Documentation

Core SDK

SolanaTipSDK

Main SDK class that orchestrates all components.

import { SolanaTipSDK } from '@solana-tip/sdk';

const sdk = new SolanaTipSDK(storage, options);
await sdk.start();
sdk.initWebSocket(server);
sdk.stop();

Options:

{
  indexer: {
    cluster: 'devnet' | 'mainnet-beta',
    rpcUrl: string,           // Optional custom RPC
    pollInterval: 2500,       // Poll interval in ms
    commitment: 'confirmed',
    signatureLimit: 20        // Max signatures per poll
  },
  websocket: {
    pingInterval: 30000,
    maxAlertsPerWindow: 4,    // Rate limit
    alertWindowMs: 10000
  }
}

Methods:

  • start() - Initialize storage and start indexing
  • stop() - Stop indexing and close connections
  • initWebSocket(server) - Initialize WebSocket server
  • getRegistry() - Get StreamerRegistry instance
  • getIndexer() - Get TipIndexer instance
  • getBroadcaster() - Get WebSocketBroadcaster instance
  • on(event, handler) - Register event handler

StreamerRegistry

Manages streamer wallet registration and verification.

const registry = sdk.getRegistry();

// Register streamer with signature verification
const result = await registry.register(streamerId, pubkey, message, signature);
// Returns: { success: boolean, error?: string }

// Get streamer info
const streamer = await registry.getStreamer(streamerId);
// Returns: { pubkey, message, registeredAt } | null

// Get all streamers
const all = await registry.getAllStreamers();
// Returns: Map<streamerId, streamerData>

TipIndexer

Polls Solana for incoming tips and emits events.

const indexer = sdk.getIndexer();

indexer.on('tip', (tipData) => {
  console.log('Tip detected:', tipData);
});

await indexer.start();
indexer.stop();

Tip Event Data:

{
  streamerId: string,
  streamerPubkey: string,
  from: string,              // Sender wallet address
  amountLamports: number,
  amountSol: number,
  txHash: string,
  slot: number,
  blockTime: number,
  memo?: string              // Optional memo message
}

WebSocketBroadcaster

Manages WebSocket connections and broadcasts tip events.

const broadcaster = sdk.getBroadcaster();

broadcaster.init(server);
broadcaster.broadcastTip(streamerId, tipData);
broadcaster.close();

WebSocket Protocol:

Client → Server:

{ "action": "subscribe", "streamer": "mychannel" }
{ "type": "ping" }

Server → Client:

{ "type": "subscribed", "streamer": "mychannel" }
{ "type": "tip", "payload": { ...tipData } }
{ "type": "pong" }

Storage Adapters

StorageAdapter (Interface)

Base interface for custom storage backends.

class CustomAdapter extends StorageAdapter {
  async registerStreamer(streamerId, pubkey, message) { }
  async getStreamer(streamerId) { }
  async getAllStreamers() { }
  async updateLastSeen(pubkey, signature) { }
  async getLastSeen(pubkey) { }
}

LowdbAdapter

File-based JSON storage (good for MVP/development).

import { LowdbAdapter } from '@solana-tip/sdk/adapters';

const storage = new LowdbAdapter('./db.json');
await storage.init();

MemoryAdapter

In-memory storage (testing only, data lost on restart).

import { MemoryAdapter } from '@solana-tip/sdk/adapters';

const storage = new MemoryAdapter();

PostgreSQL Adapter (Bring Your Own)

Example implementation:

import { StorageAdapter } from '@solana-tip/sdk/adapters';
import pg from 'pg';

class PostgresAdapter extends StorageAdapter {
  constructor(connectionString) {
    super();
    this.pool = new pg.Pool({ connectionString });
  }

  async registerStreamer(streamerId, pubkey, message) {
    await this.pool.query(
      'INSERT INTO streamers (streamer_id, pubkey, message, registered_at) VALUES ($1, $2, $3, $4)',
      [streamerId, pubkey, message, Date.now()]
    );
  }

  async getStreamer(streamerId) {
    const res = await this.pool.query(
      'SELECT pubkey, message, registered_at FROM streamers WHERE streamer_id = $1',
      [streamerId]
    );
    return res.rows[0] || null;
  }

  // Implement other methods...
}

Client SDK (Browser)

PhantomWalletAdapter

Browser utility for Phantom wallet integration.

import { PhantomWalletAdapter } from '@solana-tip/sdk/client';

const wallet = new PhantomWalletAdapter();

// Check if Phantom is installed
if (wallet.isInstalled()) {
  // Connect wallet
  const pubkey = await wallet.connect();
  
  // Sign message
  const signature = await wallet.signMessage(message);
  
  // Sign and send transaction
  const txSignature = await wallet.signAndSendTransaction(transaction);
  
  // Disconnect
  await wallet.disconnect();
}

TipSender

Browser utility for sending tips.

import { TipSender } from '@solana-tip/sdk/client';

const tipSender = new TipSender('/api');

// Get streamer wallet
const streamer = await tipSender.getStreamerWallet(streamerId);

// Build tip transaction
const tx = await tipSender.buildTipTransaction(
  solanaWeb3, fromPubkey, toPubkey, lamports, memo
);

// Send tip (all-in-one)
const signature = await tipSender.sendTip({
  solanaWeb3,
  connection,
  wallet,
  streamerId,
  amountSol,
  memo
});

// Get explorer URL
const url = tipSender.getExplorerUrl(signature, 'devnet');

OverlayClient

Browser WebSocket client for overlays.

import { OverlayClient } from '@solana-tip/sdk/client';

const client = new OverlayClient('ws://localhost:3000/ws', 'mychannel');

// Event handlers
client.on('connected', () => console.log('Connected'));
client.on('disconnected', () => console.log('Disconnected'));
client.on('tip', (tipData) => console.log('Tip:', tipData));
client.on('error', (error) => console.error('Error:', error));

// Connect (with auto-reconnect)
client.connect();

// Disconnect
client.disconnect();

🛠️ Complete Example

See examples/express-server/ for a full working implementation:

cd examples/express-server
npm install
cp .env.example .env
npm start

Then open:

  • Streamer Link: http://localhost:3000/link.html
  • Viewer Tip: http://localhost:3000/viewer.html
  • OBS Overlay: http://localhost:3000/overlay.html?streamer=mychannel

🔐 Security & Best Practices

Signature Verification

✅ All streamer registrations are verified with ed25519 signatures
✅ No custody of funds - direct wallet-to-wallet transfers

Rate Limiting

✅ Configurable alert throttling (default: 4 alerts per 10s)
✅ Minimum tip amount (0.001 SOL recommended)

Production Checklist

  • [ ] Use HTTPS + WSS (TLS certificates via Let's Encrypt)
  • [ ] Migrate from lowdb to PostgreSQL
  • [ ] Add CORS restrictions to allowed origins
  • [ ] Implement proper logging and monitoring (Sentry, etc.)
  • [ ] Use custom RPC endpoint (Helius, QuickNode, etc.)
  • [ ] Add rate limiting on API endpoints
  • [ ] Set up process manager (PM2, systemd)
  • [ ] Configure reverse proxy (NGINX)

Switching to Mainnet

  1. Change environment variable:
SOLANA_CLUSTER=mainnet-beta
  1. Update frontend cluster references:
const connection = new solanaWeb3.Connection(
  solanaWeb3.clusterApiUrl('mainnet-beta')
);
  1. Use custom RPC for production (public endpoints rate-limit aggressively):
RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY

📖 Advanced Usage

Custom Storage Backend

class RedisAdapter extends StorageAdapter {
  constructor(redisClient) {
    super();
    this.redis = redisClient;
  }

  async registerStreamer(streamerId, pubkey, message) {
    await this.redis.hSet(`streamer:${streamerId}`, {
      pubkey,
      message,
      registeredAt: Date.now()
    });
  }

  async getStreamer(streamerId) {
    const data = await this.redis.hGetAll(`streamer:${streamerId}`);
    return Object.keys(data).length ? data : null;
  }
  
  // Implement remaining methods...
}

const storage = new RedisAdapter(redisClient);
const sdk = new SolanaTipSDK(storage, options);

Manual Tip Simulation (Dev Only)

// In your server
app.post('/api/simulate-tip', (req, res) => {
  const { streamerId, amountSol } = req.body;
  
  sdk.getBroadcaster().broadcastTip(streamerId, {
    from: 'TestWallet',
    amountSol,
    amountLamports: amountSol * 1e9,
    txHash: 'simulated_' + Date.now(),
    slot: 123456,
    blockTime: Math.floor(Date.now() / 1000)
  });
  
  res.json({ ok: true });
});

SPL Token Support (Future)

The SDK is designed to support SPL tokens. Future versions will include:

  • Token account detection
  • Token transfer parsing
  • Multi-token overlay support

🧪 Testing

Manual Test Plan

  1. Register Streamer
# Open link.html, connect Phantom, register with streamer ID
curl -X POST http://localhost:3000/api/register \
  -H "Content-Type: application/json" \
  -d '{
    "streamerId": "test",
    "pubkey": "YOUR_PUBKEY",
    "message": "Link streamer test at 2025-11-09T...",
    "signature": "SIGNATURE_BASE58"
  }'
  1. Send Test Tip
# Use Phantom in viewer.html to send 0.01 SOL on devnet
# Or get devnet SOL from: https://faucet.solana.com/
  1. Verify Overlay
# Open overlay.html?streamer=test in browser or OBS
# Should see tip alert within 5 seconds of confirmation
  1. Simulate Tip (Dev)
curl -X POST http://localhost:3000/api/simulate-tip \
  -H "Content-Type: application/json" \
  -d '{
    "streamerId": "test",
    "amountSol": 0.5,
    "memo": "Test tip"
  }'

🗺️ Roadmap

Next Features

  • [ ] SPL token tipping support
  • [ ] On-chain event program (optional)
  • [ ] Persistent leaderboards
  • [ ] CSV export for payouts
  • [ ] Multi-language support
  • [ ] Dashboard UI for streamers
  • [ ] Webhook notifications
  • [ ] Mobile app support

Migration Notes

  • PostgreSQL: Implement PostgresAdapter extending StorageAdapter
  • Authentication: Add JWT/session middleware for streamer dashboard
  • Monitoring: Integrate Sentry, Datadog, or similar
  • CI/CD: Add GitHub Actions for automated testing

⚖️ Legal & Compliance

Important: This SDK facilitates direct peer-to-peer transfers on Solana. Operators should:

  • Review local regulations regarding money transmission
  • Implement KYC/AML if required by jurisdiction
  • Add transaction limits and monitoring for compliance
  • Consult legal counsel before deploying to production

Anonymous tipping may have regulatory implications. For MVP (devnet), this is low-risk. For production (mainnet), especially at scale, seek legal guidance.


📄 License

MIT License - see LICENSE file


🤝 Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Submit a pull request

💬 Support


🙏 Acknowledgments

Built with:


Ready to accept tips on Solana? Get started now! 🚀