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 🙏

© 2026 – Pkg Stats / Ryan Hefner

solana-bounty-sdk

v1.2.0

Published

A flexible SDK for managing blockchain-based bounties on issue trackers with pluggable providers for GitHub, Discord, and Solana

Readme

🎯 Solana Bounty SDK

A powerful, extensible TypeScript SDK for managing blockchain-based bounties on GitHub with Solana escrows. Built with Borsh serialization for optimal performance and type safety.

npm version npm downloads

✨ Features

  • 🔐 Secure by Default: 5-layer security validation for all bounty claims
  • 🔌 Pluggable Architecture: Easily extend with custom providers
  • 🌐 Multi-Platform: Works with GitHub (GitLab, Bitbucket extensible)
  • ⛓️ Blockchain Powered: Trustless Solana escrows using PDAs
  • 📢 Flexible Notifications: Discord, Slack, or custom providers
  • 💪 Type-Safe: Full TypeScript with comprehensive type definitions
  • 🚀 Production Ready: Built with Borsh serialization for optimal performance
  • 🎨 Framework Agnostic: Works with Express, Fastify, or any Node.js framework

📦 Installation

npm install solana-bounty-sdk

Peer Dependencies

npm install @solana/web3.js axios borsh

🚀 Quick Start

Basic Express Server

import express from "express";
import {
  BountyManager,
  GitHubProvider,
  SolanaProvider,
  DiscordProvider,
} from "solana-bounty-sdk";

const app = express();

// Capture raw body for webhook verification
app.use(
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    },
  })
);

// Initialize the Bounty Manager
const bountyManager = new BountyManager({
  issueProvider: new GitHubProvider({
    token: process.env.GITHUB_TOKEN!,
  }),
  paymentProvider: new SolanaProvider({
    keypairJson: process.env.SOLANA_KEYPAIR!,
    programId: process.env.PROGRAM_ID!,
    network: "devnet",
  }),
  notificationProviders: [
    new DiscordProvider({
      webhookUrl: process.env.DISCORD_WEBHOOK_URL!,
    }),
  ],
  bountyConfig: {
    "bounty-1-sol": 1_000_000_000, // 1 SOL
    "bounty-2-sol": 2_000_000_000, // 2 SOL
    "bounty-3-sol": 3_000_000_000, // 3 SOL
  },
});

// Webhook endpoint
app.post("/webhook/github", async (req, res) => {
  try {
    await bountyManager.handleWebhook(
      req.body,
      req.headers["x-hub-signature-256"] as string,
      req.rawBody,
      process.env.GITHUB_WEBHOOK_SECRET!
    );
    res.status(200).send("OK");
  } catch (error) {
    console.error("Webhook error:", error);
    res.status(500).send("Internal Server Error");
  }
});

app.listen(3000, () => {
  console.log("Bounty bot running on port 3000");
});

📚 Core Concepts

Provider Pattern

The SDK uses three main provider types:

  1. Issue Provider - Integrates with issue trackers (GitHub, GitLab, etc.)
  2. Payment Provider - Handles blockchain transactions (Solana, etc.)
  3. Notification Provider - Sends notifications (Discord, Slack, etc.)

Security Validation

Five comprehensive security checks before releasing bounties:

  1. ✅ Verify PR is merged
  2. ✅ Verify claimer is the PR author
  3. ✅ Find linked issue with active bounty
  4. ✅ Verify user was assigned to the issue
  5. ✅ Verify bounty hasn't been claimed already

Borsh Serialization

The SDK uses Borsh for efficient, deterministic serialization that matches your Rust Solana program exactly.

// Automatically serialized with Borsh
const instruction = new CreateEscrowInstruction({
  repo_hash: Array.from(repoHash),
  issue_number: BigInt(issueNumber),
  amount: BigInt(amount),
});

🔧 Configuration

BountyManager Options

interface BountyManagerConfig {
  issueProvider: IIssueProvider;
  paymentProvider: IPaymentProvider;
  notificationProviders?: INotificationProvider[];
  bountyConfig?: BountyConfig;
  securityConfig?: SecurityConfig;
}

Security Configuration

interface SecurityConfig {
  skipAssignmentCheck?: boolean; // Default: false
  requireMergedPR?: boolean; // Default: true
  requireMaintainerForBountyCreation?: boolean; // Default: true
}

Example:

const bountyManager = new BountyManager({
  // ... providers
  securityConfig: {
    skipAssignmentCheck: false,
    requireMergedPR: true,
    requireMaintainerForBountyCreation: true,
  },
});

Custom Bounty Amounts

const bountyManager = new BountyManager({
  // ... providers
  bountyConfig: {
    "bounty-small": 500_000_000, // 0.5 SOL
    "bounty-medium": 1_000_000_000, // 1 SOL
    "bounty-large": 5_000_000_000, // 5 SOL
    "bounty-critical": 10_000_000_000, // 10 SOL
  },
});

🔌 Creating Custom Providers

Custom Issue Provider (GitLab Example)

import {
  IIssueProvider,
  Issue,
  PullRequest,
  LinkedIssue,
  BountyConfig,
} from "solana-bounty-sdk";

export class GitLabProvider implements IIssueProvider {
  constructor(private config: { token: string; baseUrl: string }) {}

  verifyWebhook(rawBody: Buffer, signature: string, secret: string): boolean {
    // Implement GitLab webhook verification
    return true;
  }

  async postComment(
    repo: string,
    issueNumber: number,
    comment: string
  ): Promise<void> {
    // Implement GitLab API comment posting
  }

  async assignIssue(
    repo: string,
    issueNumber: number,
    username: string
  ): Promise<void> {
    // Implement GitLab API issue assignment
  }

  async getPullRequest(repo: string, prNumber: number): Promise<PullRequest> {
    // Implement GitLab merge request fetching
    return {
      number: prNumber,
      state: "merged",
      merged: true,
      user: { login: "username" },
    };
  }

  // Implement remaining interface methods...
}

import { IPaymentProvider, TransactionResult } from "solana-bounty-sdk"; import { PublicKey } from "@solana/web3.js";

async escrowExists(issueNumber: number, repoHash: Buffer): Promise { return false; }

getRepositoryHash(repoName: string): Buffer { return Buffer.from(repoName); }


### Custom Notification Provider (Slack Example)

```typescript
import { INotificationProvider, NotificationData } from "solana-bounty-sdk";

export class SlackProvider implements INotificationProvider {
  constructor(private webhookUrl: string) {}

  async notify(data: NotificationData): Promise<void> {
    const message = this.formatMessage(data);

    await fetch(this.webhookUrl, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(message),
    });
  }

  getName(): string {
    return "Slack";
  }

  private formatMessage(data: NotificationData) {
    return {
      text: `Bounty ${data.eventType} for ${data.repository}`,
      blocks: [
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: `*${data.eventType}* in ${data.repository}`,
          },
        },
      ],
    };
  }
}

📖 Usage Examples

Example 1: Multiple Notification Providers

const manager = new BountyManager({
  issueProvider: new GitHubProvider({ token: process.env.GITHUB_TOKEN! }),
  paymentProvider: new SolanaProvider({
    keypairJson: process.env.SOLANA_KEYPAIR!,
    programId: process.env.PROGRAM_ID!,
    network: "mainnet-beta",
  }),
  notificationProviders: [
    new DiscordProvider({ webhookUrl: process.env.DISCORD_WEBHOOK_URL! }),
    new SlackProvider({ webhookUrl: process.env.SLACK_WEBHOOK_URL! }),
    new EmailProvider({ apiKey: process.env.SENDGRID_API_KEY! }),
  ],
});

Example 2: Custom Security Configuration

const manager = new BountyManager({
  // ... providers
  securityConfig: {
    skipAssignmentCheck: true, // Allow anyone to claim
    requireMergedPR: true,
    requireMaintainerForBountyCreation: false, // Anyone can create bounties
  },
});

Example 3: Using Utility Functions

import {
  lamportsToSol,
  solToLamports,
  extractIssueNumbers,
} from "solana-bounty-sdk";

// Convert lamports to SOL
const sol = lamportsToSol(1_000_000_000); // 1.0

// Convert SOL to lamports
const lamports = solToLamports(1.5); // 1_500_000_000

// Extract issue numbers from text
const text = "This PR fixes #123 and closes #456";
const issues = extractIssueNumbers(text); // [123, 456]

Example 4: Using with Fastify

import Fastify from "fastify";
import {
  BountyManager,
  GitHubProvider,
  SolanaProvider,
} from "solana-bounty-sdk";

const fastify = Fastify();

const bountyManager = new BountyManager({
  issueProvider: new GitHubProvider({ token: process.env.GITHUB_TOKEN! }),
  paymentProvider: new SolanaProvider({
    keypairJson: process.env.SOLANA_KEYPAIR!,
    programId: process.env.PROGRAM_ID!,
    network: "devnet",
  }),
});

fastify.addContentTypeParser(
  "application/json",
  { parseAs: "buffer" },
  (req, body, done) => {
    req.rawBody = body;
    done(null, JSON.parse(body.toString()));
  }
);

fastify.post("/webhook", async (request, reply) => {
  await bountyManager.handleWebhook(
    request.body,
    request.headers["x-hub-signature-256"],
    request.rawBody,
    process.env.GITHUB_WEBHOOK_SECRET!
  );
  reply.send("OK");
});

fastify.listen({ port: 3000 });

🔒 Security Best Practices

  1. Always verify webhooks: The SDK does this automatically
  2. Use environment variables: Never hardcode secrets
  3. Enable all security checks: Only disable if you understand the implications
  4. Monitor transactions: Set up alerts for bounty claims
  5. Use separate keypairs: Don't use your main wallet for bounties
  6. Test on devnet first: Always test before going to mainnet

🎮 Bot Commands

For Contributors

  • /assign - Assign yourself to an issue (comment on the issue)
  • /claim <wallet_address> - Claim a bounty (comment on the merged PR)

For Maintainers

Add labels to issues to create bounties:

  • bounty-1-sol - Creates a 1 SOL bounty
  • bounty-2-sol - Creates a 2 SOL bounty
  • Custom labels based on your bountyConfig

🔍 API Documentation

BountyManager

class BountyManager {
  constructor(config: BountyManagerConfig);

  async handleWebhook(
    payload: WebhookPayload,
    signature: string,
    rawBody: Buffer,
    secret: string
  ): Promise<void>;
}

IIssueProvider

interface IIssueProvider {
  verifyWebhook(rawBody: Buffer, signature: string, secret: string): boolean;
  postComment(repository: string, issueNumber: number, comment: string): Promise<void>;
  assignIssue(repository: string, issueNumber: number, username: string): Promise<void>;
  getPullRequest(repository: string, prNumber: number): Promise<PullRequest>;
  findLinkedIssueWithBounty(...): Promise<LinkedIssue | null>;
  wasUserEverAssigned(...): Promise<boolean>;
  isBountyAlreadyClaimed(...): Promise<boolean>;
  checkIsMaintainer(repository: string, username: string): Promise<boolean>;
  getIssue(repository: string, issueNumber: number): Promise<Issue>;
}

IPaymentProvider

interface IPaymentProvider {
  createEscrow(
    issueNumber: number,
    repoHash: Buffer,
    amount: number
  ): Promise<TransactionResult>;
  releaseEscrow(
    issueNumber: number,
    repoHash: Buffer,
    recipient: PublicKey
  ): Promise<TransactionResult>;
  escrowExists(issueNumber: number, repoHash: Buffer): Promise<boolean>;
  getRepositoryHash(repoName: string): Buffer;
  parseWalletAddress(address: string): PublicKey;
}

INotificationProvider

interface INotificationProvider {
  notify(data: NotificationData): Promise<void>;
  getName(): string;
}

🐛 Error Handling

app.post("/webhook", async (req, res) => {
  try {
    await bountyManager.handleWebhook(
      req.body,
      req.headers["x-hub-signature-256"] as string,
      req.rawBody,
      process.env.GITHUB_WEBHOOK_SECRET!
    );
    res.status(200).send("OK");
  } catch (error) {
    if (error instanceof Error) {
      if (error.message.includes("Invalid webhook signature")) {
        return res.status(401).send("Unauthorized");
      }
      console.error("Webhook processing error:", error.message);
    }
    res.status(500).send("Internal Server Error");
  }
});

📝 Environment Variables

Required environment variables:

# GitHub
GITHUB_TOKEN=ghp_xxx
GITHUB_WEBHOOK_SECRET=your_secret

# Solana
SOLANA_KEYPAIR=[1,2,3,...]  # JSON array of keypair bytes
PROGRAM_ID=YourProgramId

# Discord (optional)
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...

🧪 Testing

# Run tests
npm test

# Run with coverage
npm run test:coverage

# Lint
npm run lint

# Format
npm run format

📄 License

MIT © [Your Name]

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

🙏 Acknowledgments

📞 Support

🗺️ Roadmap

  • [ ] GitLab provider implementation
  • [ ] Bitbucket provider implementation
  • [ ] Email notification provider
  • [ ] Web dashboard for bounty management
  • [ ] Advanced analytics and reporting
  • [ ] Multi-sig wallet support
  • [ ] Automatic bounty amount suggestions

Made with ❤️ by the Solana Bounty SDK team