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

@maithinh/paymaster-sdk

v1.1.2

Published

Paymaster SDK for Solana

Readme

Paymaster SDK

TypeScript SDK for interacting with Paymaster Server on Solana blockchain. Simplifies transaction processing with automatic fee handling and server-side transaction modification.

Features

  • Health Check - Verify Paymaster Server is online
  • Token Price Fetching - Get real-time token prices and decimals
  • Swap Fee Integration - Automatically add swap fees to transactions
  • VersionedTransaction Support - Full support for V0 messages and VersionedTransaction
  • Server-Side Signing - Server handles transaction signing with MASTER_WALLET
  • Full TypeScript Support - Complete type definitions for all APIs
  • ESM Module Support - Native ES Module support
  • Comprehensive Error Handling - Detailed error messages and codes

Installation

npm install paymaster-sdk

Or from git:

npm install github:Harmori-Finance/paymaster-sdk

Prerequisites

  • Node.js: >= 18
  • Solana Web3.js: ^1.98.4
  • @solana/spl-token: ^0.4.13
  • @coral-xyz/anchor: ^0.31.1

Quick Start

1. Initialize SDK

import { PaymasterSDK } from 'paymaster-sdk';

const sdk = new PaymasterSDK({
  baseUrl: 'http://localhost:8234',
  timeout: 30000, // optional, default 30s
});

2. Check Server Health

const isHealthy = await sdk.healthCheck();
console.log('Server is healthy:', isHealthy);

3. Get Token Price

const priceInfo = await sdk.getTokenPrice('EPjFWaLb3odcccccccccccccccccccccccccccccccc');
console.log('Token Price:', priceInfo.price);
console.log('Token Decimals:', priceInfo.decimals);

4. Add Swap Fee to Transaction

Option A: Regular Transaction

// Create and prepare transaction
const tx = new Transaction();
tx.add(/* your instruction here */);
tx.feePayer = user.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;

// Serialize WITHOUT signing
const serialized = tx.serialize({ requireAllSignatures: false }).toString('base64');

// Send to Paymaster server
const result = await sdk.swapFee({
  base64Tx: serialized,
  token: 'EPjFWaLb3odcccccccccccccccccccccccccccccccc',
  userAddress: user.publicKey.toString(),
});

// Parse modified transaction (already signed by MASTER_WALLET)
const modifiedTx = Transaction.from(Buffer.from(result.base64_tx, 'base64'));
modifiedTx.partialSign(user);

// Send to blockchain
const txid = await connection.sendRawTransaction(modifiedTx.serialize());

Option B: VersionedTransaction (V0)

import { VersionedTransaction, TransactionMessage } from '@solana/web3.js';

// Create V0 message
const message = new TransactionMessage({
  payerKey: user.publicKey,
  recentBlockhash: (await connection.getLatestBlockhash('finalized')).blockhash,
  instructions: [/* your instructions */],
}).compileToV0Message();

// Create VersionedTransaction
const versionedTx = new VersionedTransaction(message);

// Serialize to base64
const serialized = Buffer.from(versionedTx.serialize()).toString('base64');

// Send to Paymaster server
const result = await sdk.versionedSwapFee({
  base64Tx: serialized,
  token: 'EPjFWaLb3odcccccccccccccccccccccccccccccccc',
  userAddress: user.publicKey.toString(),
});

// Deserialize and sign modified VersionedTransaction
const modifiedTx = VersionedTransaction.deserialize(
  new Uint8Array(Buffer.from(result.base64_tx, 'base64'))
);
modifiedTx.sign([user]);

// Send to blockchain
const txid = await connection.sendRawTransaction(modifiedTx.serialize());

API Reference

PaymasterSDK

Constructor

new PaymasterSDK(config: PaymasterSDKConfig)

Parameters:

  • config.baseUrl (string): Base URL of Paymaster Server
  • config.timeout (number, optional): Request timeout in milliseconds (default: 30000)

Methods

healthCheck(): Promise<boolean>

Check if the Paymaster Server is running and healthy.

Returns: true if server is healthy, false otherwise

Example:

const isHealthy = await sdk.healthCheck();

getTokenPrice(tokenAddress: string): Promise<TokenPriceInfo>

Get the price and decimals for a specific token.

Parameters:

  • tokenAddress (string): The token mint address

Returns:

interface TokenPriceInfo {
  price: number;
  decimals: number;
}

Example:

const priceInfo = await sdk.getTokenPrice('EPjFWaLb3od...');
console.log(`1 token = ${priceInfo.price} USDC`);

swapFee(payload: SwapFeePayload): Promise<SwapFeeResponse>

Add swap fee instruction to a regular Transaction. The server will handle the fee addition and sign the transaction with MASTER_WALLET.

Important Flow:

  1. Client creates transaction and serializes to base64 (WITHOUT signing)
  2. Client sends base64 to server via swapFee()
  3. Server:
    • Deserializes transaction
    • Adds swap fee instruction
    • Sets feePayer = MASTER_WALLET
    • Signs with MASTER_WALLET.partialSign()
    • Returns modified base64Tx
  4. Client receives modified transaction (already has MASTER_WALLET's signature)
  5. Client deserializes and signs with user's keypair using partialSign()
  6. Now transaction has both MASTER_WALLET and user signatures
  7. Client sends to blockchain

Parameters:

interface SwapFeePayload {
  base64Tx: string;      // Base64 encoded transaction (unsigned)
  token: string;          // Token mint address
  userAddress: string;    // User's Solana wallet address
}

Returns:

interface SwapFeeResponse {
  base64_tx: string;     // Modified transaction (signed by MASTER_WALLET)
}

versionedSwapFee(payload: VersionedSwapFeePayload): Promise<SwapFeeResponse>

Add swap fee instruction to a VersionedTransaction (V0 message). The server will handle the fee addition and sign with MASTER_WALLET.

Important Flow:

  1. Client creates VersionedTransaction (V0 message) and serializes to base64
  2. Client sends base64 to server via versionedSwapFee()
  3. Server:
    • Deserializes VersionedTransaction
    • Decodes V0 message
    • Adds swap fee instructions
    • Sets feePayer = MASTER_WALLET
    • Signs with MASTER_WALLET
    • Returns modified base64Tx (VersionedTransaction format)
  4. Client receives modified VersionedTransaction (already has MASTER_WALLET's signature)
  5. Client deserializes and signs with user's keypair using sign()
  6. Now VersionedTransaction has both MASTER_WALLET and user signatures
  7. Client sends to blockchain

Parameters:

interface VersionedSwapFeePayload {
  base64Tx: string;      // Base64 encoded VersionedTransaction
  token: string;          // Token mint address
  userAddress: string;    // User's Solana wallet address
}

Returns:

interface SwapFeeResponse {
  base64_tx: string;     // Modified VersionedTransaction (signed by MASTER_WALLET)
}

Differences from Regular Transaction:

  • Use VersionedTransaction.deserialize() instead of Transaction.from()
  • Use tx.sign([user]) instead of tx.partialSign(user)
  • Supports Address Lookup Tables (ALTs) for lower fees
  • Required for complex transactions with many accounts

Error Handling

The SDK throws PaymasterSDKError for API errors:

import { PaymasterSDKError } from 'paymaster-sdk';

try {
  const priceInfo = await sdk.getTokenPrice(tokenAddress);
} catch (error) {
  if (error instanceof PaymasterSDKError) {
    console.error('Error Code:', error.code);
    console.error('Status:', error.status);
    console.error('Message:', error.message);
  }
}

Main Exports

The SDK exports the following items:

// Main client
export { PaymasterSDK } from 'paymaster-sdk';

// Error class and utilities
export { PaymasterSDKError, handleResponse, makeRequest } from 'paymaster-sdk';

// Type definitions
export type {
  ApiResponse,
  TokenPriceInfo,
  SwapFeeResponse,
  SwapFeePayload,
  VersionedSwapFeePayload,
  PaymasterSDKConfig,
  ApiError,
} from 'paymaster-sdk';

Type Definitions

/**
 * SDK Configuration
 */
interface PaymasterSDKConfig {
  baseUrl: string;        // URL of Paymaster Server
  timeout?: number;       // Request timeout (ms), default: 30000
}

/**
 * Token price response
 */
interface TokenPriceInfo {
  price: number;          // Token price
  decimals: number;       // Token decimals
}

/**
 * Swap fee request payload
 */
interface SwapFeePayload {
  base64Tx: string;       // Base64 encoded transaction (unsigned)
  token: string;          // Token mint address
  userAddress: string;    // User's Solana wallet address
}

/**
 * Versioned swap fee request payload (VersionedTransaction)
 */
interface VersionedSwapFeePayload {
  base64Tx: string;       // Base64 encoded VersionedTransaction
  token: string;          // Token mint address
  userAddress: string;    // User's Solana wallet address
}

/**
 * Swap fee response
 */
interface SwapFeeResponse {
  base64_tx: string;      // Modified transaction (signed by MASTER_WALLET)
}

/**
 * API Error response
 */
interface ApiError {
  message: string;        // Error message
  code?: string;          // Error code
  status?: number;        // HTTP status code
}

/**
 * Generic API response
 */
interface ApiResponse<T = any> {
  success: boolean;       // Whether request was successful
  data?: T;               // Response data
  error?: {
    message: string;
    code?: string;
  };
}

Building from Source

Prerequisites

  • TypeScript 5.9+
  • Node.js 18+

Build Commands

# Install dependencies
npm install

# Build TypeScript to JavaScript
npm run build

# Run development example
npm run dev:loader

# Check build output
ls -la dist/

Project Structure

paymaster-sdk/
├── src/
│   ├── index.ts                # Main entry point (exports)
│   ├── client.ts               # PaymasterSDK class
│   │   ├── swapFee()           # Regular Transaction
│   │   ├── versionedSwapFee()  # VersionedTransaction (V0)
│   │   ├── getTokenPrice()
│   │   └── healthCheck()
│   ├── types.ts                # TypeScript interfaces
│   └── utils.ts                # Utilities & error handling
├── examples/
│   ├── example.ts              # Regular Transaction example
│   └── examples-versioned.ts   # VersionedTransaction example
├── dist/                        # Compiled JavaScript (generated after build)
├── package.json                 # Project config & scripts
├── tsconfig.json                # TypeScript config
└── README.md                    # This file

Build Output

After running npm run build, the dist/ folder contains:

  • index.js and index.d.ts - Main entry point
  • client.js and client.d.ts - SDK client
  • types.js and types.d.ts - Type definitions
  • utils.js and utils.d.ts - Utilities
  • Source maps (.js.map and .d.ts.map) for debugging

Complete Examples

Example 1: Regular Transaction

This example demonstrates:

  • Creating a transaction with ATA creation (if needed)
  • Token transfer instruction
  • Health check
  • Token price fetching
  • Swap fee addition
  • Transaction signing and sending
import { 
  createAssociatedTokenAccountInstruction,
  createTransferInstruction, 
  getAssociatedTokenAddress,
  ASSOCIATED_TOKEN_PROGRAM_ID,
  TOKEN_PROGRAM_ID
} from '@solana/spl-token';
import { Connection, Keypair, PublicKey, Transaction, SystemProgram } from '@solana/web3.js';
import { PaymasterSDK } from 'paymaster-sdk';
import 'dotenv/config';
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';

async function main() {
    // Setup
    const SECRET_KEY = process.env.SECRET_KEY ?? '';
    const connection = new Connection('https://api.devnet.solana.com');
    const mintUSDC = new PublicKey('USDCoctVLVnvTXBEuP9s8hntucdJokbo17RwHuNXemT');
    const user = Keypair.fromSecretKey(bs58.decode(SECRET_KEY));
    const recipient = new PublicKey('GS69bzeoeQTB2TspXt1cvGhqDigLGvK6AgcAGAxaYraW');

    // Get token accounts
    const fromAccount = await getAssociatedTokenAddress(mintUSDC, user.publicKey);
    const toAccount = await getAssociatedTokenAddress(mintUSDC, recipient);

    const tx = new Transaction();

    // Check if recipient has ATA, create if needed
    const recipientATAInfo = await connection.getAccountInfo(toAccount);
    
    if (!recipientATAInfo) {
        const createAtaIx = createAssociatedTokenAccountInstruction(
            user.publicKey,           // payer
            toAccount,                // ATA address
            recipient,                // owner
            mintUSDC,                 // mint
            TOKEN_PROGRAM_ID,
            ASSOCIATED_TOKEN_PROGRAM_ID
        );
        tx.add(createAtaIx);
        console.log('Added: Create ATA for recipient');
    }

    // Add transfer instruction
    const transferIx = createTransferInstruction(
        fromAccount,
        toAccount,
        user.publicKey,
        10 * 10 ** 6  // 10 USDC
    );
    tx.add(transferIx);

    // Set temporary feePayer (server will replace with MASTER_WALLET)
    tx.feePayer = user.publicKey;
    tx.recentBlockhash = (await connection.getLatestBlockhash('finalized')).blockhash;

    // Serialize WITHOUT signing
    const serialized = tx.serialize({ requireAllSignatures: false }).toString('base64');

    // Initialize SDK
    const sdk = new PaymasterSDK({
        baseUrl: 'http://localhost:8234',
        timeout: 30000,
    });

    try {
        // Health check
        console.log('Checking server health...');
        const isHealthy = await sdk.healthCheck();
        console.log('Server is healthy:', isHealthy);

        // Get token price
        console.log('\nGetting token price...');
        const priceInfo = await sdk.getTokenPrice(mintUSDC.toString());
        console.log('Token Price:', priceInfo.price);

        // Add swap fee to transaction
        console.log('\nAdding swap fee to transaction...');
        const swapFeeResult = await sdk.swapFee({
            base64Tx: serialized,
            token: mintUSDC.toString(),
            userAddress: user.publicKey.toString(),
        });

        // Parse modified transaction (already signed by MASTER_WALLET)
        const transaction = Transaction.from(Buffer.from(swapFeeResult.base64_tx, 'base64'));
        
        // Sign with user's keypair
        transaction.partialSign(user);

        // Send to blockchain
        console.log('\nSending transaction...');
        const serializedTx = transaction.serialize();
        const txid = await connection.sendRawTransaction(serializedTx);
        console.log("TX Hash:", txid);

        // Confirm transaction
        const confirmation = await connection.confirmTransaction(txid);
        console.log("Confirmation:", confirmation);
    } catch (error: any) {
        console.error('Error:', error.message);
    }
}

main().catch(console.error);

Example 2: VersionedTransaction (V0)

This example demonstrates:

  • Creating VersionedTransaction with V0 message
  • Support for Address Lookup Tables (ALTs)
  • Lower transaction size for complex transactions
  • Swap fee addition with VersionedTransaction
  • Signing and sending
import {
    Connection,
    Keypair,
    PublicKey,
    VersionedTransaction,
    TransactionMessage,
} from '@solana/web3.js';
import { 
    ASSOCIATED_TOKEN_PROGRAM_ID, 
    createAssociatedTokenAccountInstruction, 
    createTransferInstruction, 
    getAssociatedTokenAddress, 
    TOKEN_PROGRAM_ID 
} from '@solana/spl-token';
import { PaymasterSDK } from 'paymaster-sdk';
import 'dotenv/config';
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';

async function testVersionedTransaction() {
    const SECRET_KEY = process.env.SECRET_KEY ?? '';
    const connection = new Connection('https://api.devnet.solana.com');
    const mintUSDC = new PublicKey('USDCoctVLVnvTXBEuP9s8hntucdJokbo17RwHuNXemT');
    const user = Keypair.fromSecretKey(bs58.decode(SECRET_KEY));
    const recipient = new PublicKey('2fn3dJEUADumJBriz8nJfv2H5Kk9ejj5h5YwkATarGHY');

    console.log('=== VersionedTransaction Example ===\n');

    try {
        // Get token accounts
        const fromAccount = await getAssociatedTokenAddress(mintUSDC, user.publicKey);
        const toAccount = await getAssociatedTokenAddress(mintUSDC, recipient);

        const instructions = [];
        
        // Check if recipient has ATA
        const recipientATAInfo = await connection.getAccountInfo(toAccount);
        
        if (!recipientATAInfo) {
            const createAtaIx = createAssociatedTokenAccountInstruction(
                user.publicKey,
                toAccount,
                recipient,
                mintUSDC,
                TOKEN_PROGRAM_ID,
                ASSOCIATED_TOKEN_PROGRAM_ID
            );
            instructions.push(createAtaIx);
            console.log('Added: Create ATA for recipient');
        }

        // Add transfer instruction
        const transferIx = createTransferInstruction(
            fromAccount,
            toAccount,
            user.publicKey,
            10 * 10 ** 6  // 10 USDC
        );
        instructions.push(transferIx);

        // Get blockhash for message
        const { blockhash } = await connection.getLatestBlockhash('finalized');

        // Create VersionedTransaction with V0 message
        const message = new TransactionMessage({
            payerKey: user.publicKey,
            recentBlockhash: blockhash,
            instructions: instructions,
        }).compileToV0Message();

        const versionedTx = new VersionedTransaction(message);

        // Serialize to base64
        const serialized = Buffer.from(versionedTx.serialize()).toString('base64');
        console.log('✓ Created VersionedTransaction');

        // Initialize SDK
        const sdk = new PaymasterSDK({
            baseUrl: 'http://localhost:8234',
            timeout: 30000,
        });

        // Health check
        console.log('\n=== Server Health ===');
        const isHealthy = await sdk.healthCheck();
        console.log('✓ Server is healthy:', isHealthy);

        // Get token price
        console.log('\n=== Token Price ===');
        const priceInfo = await sdk.getTokenPrice(mintUSDC.toString());
        console.log('✓ Token Price:', priceInfo.price);

        // Add swap fee to VersionedTransaction
        console.log('\n=== Add Swap Fee (VersionedTransaction) ===');
        const swapFeeResult = await sdk.versionedSwapFee({
            base64Tx: serialized,
            token: mintUSDC.toString(),
            userAddress: user.publicKey.toString(),
        });
        console.log('✓ Modified VersionedTransaction received from server');

        // Deserialize and sign
        console.log('\n=== Parse and Sign ===');
        const resultTx = VersionedTransaction.deserialize(
            new Uint8Array(Buffer.from(swapFeeResult.base64_tx, 'base64'))
        );
        console.log('✓ Deserialized VersionedTransaction');

        // Sign with user keypair
        resultTx.sign([user]);
        console.log('✓ Signed with user keypair');

        // Send transaction
        console.log('\n=== Send Transaction ===');
        try {
            const txid = await connection.sendRawTransaction(resultTx.serialize());
            console.log('✓ TX Hash:', txid);

            const confirmation = await connection.confirmTransaction(txid);
            console.log('✓ Confirmation:', confirmation);
        } catch (error: any) {
            console.warn('⚠️ Warning: Could not send transaction:', error.message);
            console.log('Transaction was modified successfully by server');
        }

        console.log('\n✅ All tests passed!');
    } catch (error: any) {
        console.error('❌ Error:', error.message);
    }
}

testVersionedTransaction().catch(console.error);

Run the examples:

# Development mode (with hot reload)
npm run dev:loader

# Build first
npm run build

# Then run with Node
node --loader ts-node/esm examples/example.ts         # Regular Transaction
node --loader ts-node/esm examples/examples-versioned.ts  # VersionedTransaction

NPM Scripts

npm run build        # Build TypeScript to dist/
npm run dev:loader   # Run examples/example.ts with ts-node ESM loader

Package Configuration

package.json exports:

{
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts"
}

Environment Variables

Create a .env file in your project root for configuration:

# Paymaster Server
PAYMASTER_URL=http://localhost:8234

# Solana RPC Endpoint
SOLANA_RPC_URL=https://api.devnet.solana.com

# Your wallet secret key (base58 encoded)
SECRET_KEY=<your-base58-encoded-keypair>

Using in code:

import 'dotenv/config';
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';

const sdk = new PaymasterSDK({
  baseUrl: process.env.PAYMASTER_URL || 'http://localhost:8234',
  timeout: 30000,
});

const connection = new Connection(
  process.env.SOLANA_RPC_URL || 'https://api.devnet.solana.com'
);

const user = Keypair.fromSecretKey(
  bs58.decode(process.env.SECRET_KEY || '')
);

Common Patterns

When to use Regular Transaction vs VersionedTransaction?

| Feature | Regular Tx | VersionedTx | |---------|-----------|-------------| | Simple transfers | ✅ Simpler | ⚠️ Overkill | | Few accounts | ✅ Better | ⚠️ Overkill | | ALT support | ❌ No | ✅ Yes | | Complex txs | ⚠️ May fail | ✅ Recommended | | Many accounts | ❌ Limited | ✅ Better | | Lower fees | ⚠️ Higher | ✅ Lower | | Compatibility | ✅ Wider | ⚠️ Needs v0 support |

Choose Regular Transaction if:

  • Simple token transfers or swaps
  • 3-5 accounts max
  • Quick and simple transactions
  • Don't need advanced features

Choose VersionedTransaction if:

  • Complex transactions with many accounts
  • Need to use Address Lookup Tables (ALTs)
  • Want to minimize transaction size
  • Need lower fees for complex operations
  • Building advanced dApps

Creating ATA if not exists

import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction } from '@solana/spl-token';

const ataAddress = await getAssociatedTokenAddress(mint, owner);
const ataInfo = await connection.getAccountInfo(ataAddress);

if (!ataInfo) {
  // Create ATA instruction
  const ix = createAssociatedTokenAccountInstruction(
    feePayer,
    ataAddress,
    owner,
    mint
  );
  tx.add(ix);
}

Getting token account balance

const balance = await connection.getTokenAccountBalance(tokenAccount);
console.log(`Balance: ${balance.value.amount}`);
console.log(`Decimals: ${balance.value.decimals}`);

Decoding secret key

import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';

const secretKey = process.env.SECRET_KEY!;
const keypair = Keypair.fromSecretKey(bs58.decode(secretKey));

Regular Transaction vs VersionedTransaction signing

// ✅ Regular Transaction - use partialSign()
const tx = Transaction.from(Buffer.from(base64, 'base64'));
tx.partialSign(user);  // Only adds user signature
await connection.sendRawTransaction(tx.serialize());

// ✅ VersionedTransaction - use sign()
const vTx = VersionedTransaction.deserialize(
  new Uint8Array(Buffer.from(base64, 'base64'))
);
vTx.sign([user]);  // Signs transaction, replaces signatures
await connection.sendRawTransaction(vTx.serialize());

Troubleshooting

Import errors with .js extension

Error: Cannot find module './client'

Solution: Ensure all TypeScript imports have .js extension:

// ✅ Correct
import { PaymasterSDK } from './client.js';

// ❌ Wrong
import { PaymasterSDK } from './client';

This is required for ESM module resolution in Node.js.

Transaction Signing Issues

Problem: Transaction fails because you signed it before sending to Paymaster server.

Solution: Never sign before sending to server:

// ✅ CORRECT: Don't sign before sending
const serialized = tx.serialize({ requireAllSignatures: false }).toString('base64');
const result = await sdk.swapFee({ base64Tx: serialized, ... });

// Server returns transaction signed by MASTER_WALLET
const modifiedTx = Transaction.from(Buffer.from(result.base64_tx, 'base64'));

// Then sign with your keypair
modifiedTx.partialSign(user);

// Now send
await connection.sendRawTransaction(modifiedTx.serialize());

"Blockhash not found" error

Problem: Error: Simulation failed: Blockhash not found

Reason: Solana blockhash expires after ~2 minutes. If server takes time processing, blockhash may expire.

Solution: Get fresh blockhash before sending:

// Get blockhash BEFORE creating transaction
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized');
tx.recentBlockhash = blockhash;

Connection refused to Paymaster Server

Problem: Error: connect ECONNREFUSED 127.0.0.1:8234

Solution: Ensure server is running:

# Check if server is running
curl http://localhost:8234/

# Verify healthCheck
const isHealthy = await sdk.healthCheck();
if (!isHealthy) {
  console.error('Cannot reach Paymaster server');
}

Build/Compilation errors

Problem: TypeScript compilation errors after changes

Solution: Rebuild the project:

npm run build

And ensure:

  • TypeScript 5.9+ is installed: npm list typescript
  • tsconfig.json has correct settings
  • All source files are in src/ directory

"Blockhash not found" error

  • This is normal when transaction blockhash expires (~2 minutes)
  • Get a fresh blockhash just before sending the transaction:
    tx.recentBlockhash = (await connection.getLatestBlockhash('finalized')).blockhash;

Connection errors to Paymaster Server

  • Ensure server is running at baseUrl
  • Check CORS configuration on server
  • Verify network connectivity
  • Example debug:
    const isHealthy = await sdk.healthCheck();
    if (!isHealthy) {
      console.error('Cannot connect to Paymaster server');
    }

TypeScript compilation errors

  • Ensure TypeScript 5.9+ is installed
  • Check tsconfig.json has correct settings
  • Run npm run build to verify compilation
  • Use npm run dev:loader for development with automatic recompilation

Support & Documentation

API Docs

  • Full API documentation is available in the API Reference section above

Example Code

  • See examples/example.ts for a complete working example
  • Run it with: npm run dev:loader

Issues

For bugs and feature requests, please open an issue on GitHub: Harmori-Finance/paymaster-sdk

License

ISC

Contributing

Contributions are welcome! Please ensure:

  1. Code follows TypeScript strict mode
  2. All types are properly defined
  3. Error handling is comprehensive
  4. Examples are updated if adding new features
  5. Run npm run build to verify compilation

Development workflow

# Install dependencies
npm install

# Make changes in src/
# Edit example in examples/example.ts

# Test with dev:loader
npm run dev:loader

# Build when ready
npm run build

# Commit changes
git add .
git commit -m "Description of changes"

Paymaster SDK | Solana | TypeScript
Last Updated: November 11, 2025
Version: 1.0.0
Maintained by: Harmori Finance