npm package: @maithinh/paymaster-sdk
Description: Paymaster SDK for Solana
Latest version: 1.1.2

# 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

```bash
npm install paymaster-sdk
```

Or from git:

```bash
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

```typescript
import { PaymasterSDK } from 'paymaster-sdk';

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

### 2. Check Server Health

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

### 3. Get Token Price

```typescript
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

```typescript
// 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)

```typescript
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

```typescript
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:**
```typescript
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:**
```typescript
interface TokenPriceInfo {
  price: number;
  decimals: number;
}
```

**Example:**
```typescript
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:**
```typescript
interface SwapFeePayload {
  base64Tx: string;      // Base64 encoded transaction (unsigned)
  token: string;          // Token mint address
  userAddress: string;    // User's Solana wallet address
}
```

**Returns:**
```typescript
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:**
```typescript
interface VersionedSwapFeePayload {
  base64Tx: string;      // Base64 encoded VersionedTransaction
  token: string;          // Token mint address
  userAddress: string;    // User's Solana wallet address
}
```

**Returns:**
```typescript
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:

```typescript
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:

```typescript
// 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

```typescript
/**
 * 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

```bash
# 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

```typescript
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

```typescript
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:

```bash
# 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

```bash
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:

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

## Environment Variables

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

```bash
# 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:

```typescript
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

```typescript
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

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

### Decoding secret key

```typescript
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

```typescript
// ✅ 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:
```typescript
// ✅ 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:

```typescript
// ✅ 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:

```typescript
// 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:

```bash
# 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:

```bash
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:
  ```typescript
  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:
  ```typescript
  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](#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](https://github.com/Harmori-Finance/paymaster-sdk/issues)

## 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

```bash
# 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](https://harmori.finance)