# @solana-payment/sdk

TypeScript SDK for Solana Payment Contract - Build unsigned transactions for frontend applications.

## Features

- 🔐 **Wallet-agnostic**: Build unsigned transactions, let frontend handle signing
- 🎯 **Type-safe**: Full TypeScript support with comprehensive types
- 🚀 **Simple API**: Clean, intuitive methods for all contract operations
- 📦 **Zero wallet dependencies**: No wallet adapter required
- ⚡ **Anchor-based**: Built on Anchor framework for reliability

## Installation

```bash
pnpm add @solana-payment/sdk
```

Or using npm/yarn:

```bash
npm install @solana-payment/sdk
yarn add @solana-payment/sdk
```

### Browser/Frontend Setup

If you're using this SDK in a browser environment (React, Next.js, Vue, etc.), you need to configure Buffer polyfill. This SDK includes `buffer` as a dependency, but your bundler needs to be configured properly.

**Quick setup for Vite:**

```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      buffer: 'buffer',
    },
  },
  define: {
    'global': 'globalThis',
  },
});

// main.tsx
import { Buffer } from 'buffer';
window.Buffer = Buffer;
```

**For Next.js, Create React App, Webpack, and other frameworks:** See detailed setup guide in [docs/BROWSER_SETUP.md](./docs/BROWSER_SETUP.md)

## Quick Start

```typescript
import { VaultSDK, PROGRAM_ID } from '@solana-payment/sdk';
import { Connection, PublicKey } from '@solana/web3.js';

// Initialize SDK
const connection = new Connection('https://api.devnet.solana.com');
const sdk = new VaultSDK({
  connection,
  programId: PROGRAM_ID, // Optional, uses default if not provided
});

// Build a deposit transaction
const depositTx = await sdk.buildDepositTransaction({
  amount: 1000000, // Amount in raw token units
  user: userPublicKey,
  mint: tokenMintPublicKey,
});

// Frontend signs and sends (using wallet adapter or other method)
await wallet.signTransaction(depositTx);
await connection.sendRawTransaction(depositTx.serialize());
```

## API Documentation

### Constructor

```typescript
new VaultSDK(config: VaultSDKConfig)
```

**Parameters:**
- `config.connection`: Solana Connection instance
- `config.programId`: Program ID (optional, defaults to deployed program)

### Query Methods

#### `getConfig()`

Fetch vault configuration from on-chain.

```typescript
const config = await sdk.getConfig();
console.log('Admin:', config.admin.toString());
console.log('Mint:', config.mint.toString());
```

**Returns:** `Promise<VaultConfig>`

#### `getVaultBalance(mint)`

Get current vault balance for a specific token mint.

```typescript
const balance = await sdk.getVaultBalance(mintPublicKey);
console.log('Raw amount:', balance.amount);
console.log('Formatted:', balance.formatted);
console.log('Decimals:', balance.decimals);
```

**Parameters:**
- `mint`: Token mint PublicKey

**Returns:** `Promise<VaultBalanceInfo>`

#### `isInitialized()`

Check if vault has been initialized.

```typescript
const initialized = await sdk.isInitialized();
if (!initialized) {
  console.log('Vault needs to be initialized');
}
```

**Returns:** `Promise<boolean>`

### Transaction Builders

All transaction builder methods return unsigned `Transaction` objects that need to be signed by the frontend before submission.

#### `buildInitializeTransaction(params)`

Build transaction to initialize the vault (one-time setup).

```typescript
const tx = await sdk.buildInitializeTransaction({
  admin: adminPublicKey,
  mint: tokenMintPublicKey,
  payer: payerPublicKey,
});

// Sign and send
await wallet.signTransaction(tx);
await connection.sendRawTransaction(tx.serialize());
```

**Parameters:**
- `admin`: PublicKey of the admin
- `mint`: PublicKey of the SPL token mint
- `payer`: PublicKey paying for transaction fees

**Returns:** `Promise<Transaction>`

#### `buildDepositTransaction(params)`

Build transaction to deposit tokens into the vault.

```typescript
const tx = await sdk.buildDepositTransaction({
  amount: 1000000, // Raw token amount (e.g., 1 USDC = 1000000 with 6 decimals)
  user: userPublicKey,
  mint: tokenMintPublicKey,
});

// Sign and send
await wallet.signTransaction(tx);
await connection.sendRawTransaction(tx.serialize());
```

**Parameters:**
- `amount`: Amount to deposit (number or BN)
- `user`: PublicKey of the user depositing
- `mint`: PublicKey of the token mint

**Returns:** `Promise<Transaction>`

#### `buildWithdrawTransaction(params)`

Build transaction to withdraw tokens from the vault (admin only).

```typescript
const tx = await sdk.buildWithdrawTransaction({
  amount: 500000,
  admin: adminPublicKey,
  recipient: recipientPublicKey,
  mint: tokenMintPublicKey,
});

// Admin signs and sends
await adminWallet.signTransaction(tx);
await connection.sendRawTransaction(tx.serialize());
```

**Parameters:**
- `amount`: Amount to withdraw (number or BN)
- `admin`: PublicKey of the admin (must match vault config)
- `recipient`: PublicKey receiving the tokens
- `mint`: PublicKey of the token mint

**Returns:** `Promise<Transaction>`

#### `buildUpdateAdminTransaction(params)`

Build transaction to update vault admin (current admin only).

```typescript
const tx = await sdk.buildUpdateAdminTransaction({
  currentAdmin: currentAdminPublicKey,
  newAdmin: newAdminPublicKey,
});

// Current admin signs and sends
await adminWallet.signTransaction(tx);
await connection.sendRawTransaction(tx.serialize());
```

**Parameters:**
- `currentAdmin`: PublicKey of current admin (must match vault config)
- `newAdmin`: PublicKey of new admin

**Returns:** `Promise<Transaction>`

## Factory Pattern (Singleton)

For applications that need a single SDK instance:

```typescript
import { initVaultSDK, getVaultSDK } from '@solana-payment/sdk';

// Initialize once
initVaultSDK({ connection, programId });

// Use anywhere in your app
const sdk = getVaultSDK();
const config = await sdk.getConfig();
```

## Advanced Usage

### Working with Token Amounts

The SDK provides utilities for handling token amounts with decimals:

```typescript
import { formatTokenAmount, validateAmount } from '@solana-payment/sdk';
import { BN } from '@coral-xyz/anchor';

// Format raw amount with decimals
const formatted = formatTokenAmount(new BN(1000000), 6);
console.log(formatted); // "1"

// Validate and convert to BN
const amount = validateAmount(1000000);
```

### PDA Derivation

Manually derive PDAs if needed:

```typescript
import {
  deriveConfigPDA,
  deriveVaultPDA,
  findVaultTokenAccount,
  findUserTokenAccount,
} from '@solana-payment/sdk';

const { publicKey: configPDA, bump } = await deriveConfigPDA();
const { publicKey: vaultPDA } = await deriveVaultPDA(configPDA);
const vaultATA = await findVaultTokenAccount(vaultPDA, mint);
const userATA = await findUserTokenAccount(userPubkey, mint);
```

## Error Handling

```typescript
try {
  const tx = await sdk.buildDepositTransaction({
    amount: 0, // Invalid: will throw
    user: userPublicKey,
    mint: mintPublicKey,
  });
} catch (error) {
  console.error('Failed to build transaction:', error.message);
  // Error: Amount must be greater than 0
}
```

Common errors:
- `"Amount must be greater than 0"` - Invalid amount parameter
- `"Missing required parameters"` - Required parameters not provided
- `"Config account not found"` - Vault not initialized
- `"New admin must be different from current admin"` - Same admin provided

## Integration Examples

### React with Wallet Adapter

```typescript
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { VaultSDK } from '@solana-payment/sdk';

function DepositButton() {
  const { connection } = useConnection();
  const { publicKey, signTransaction } = useWallet();

  const handleDeposit = async () => {
    if (!publicKey || !signTransaction) return;

    const sdk = new VaultSDK({ connection });
    const tx = await sdk.buildDepositTransaction({
      amount: 1000000,
      user: publicKey,
      mint: USDC_MINT,
    });

    const signed = await signTransaction(tx);
    const signature = await connection.sendRawTransaction(signed.serialize());
    await connection.confirmTransaction(signature);
  };

  return <button onClick={handleDeposit}>Deposit</button>;
}
```

### Next.js API Route

```typescript
// pages/api/build-transaction.ts
import { Connection, PublicKey } from '@solana/web3.js';
import { VaultSDK } from '@solana-payment/sdk';

export default async function handler(req, res) {
  const { amount, user, mint } = req.body;

  const connection = new Connection(process.env.RPC_URL);
  const sdk = new VaultSDK({ connection });

  const tx = await sdk.buildDepositTransaction({
    amount: Number(amount),
    user: new PublicKey(user),
    mint: new PublicKey(mint),
  });

  // Serialize and send to frontend
  const serialized = tx.serialize({ requireAllSignatures: false });
  res.json({ transaction: serialized.toString('base64') });
}
```

## Constants

```typescript
import {
  PROGRAM_ID,
  TOKEN_PROGRAM_ID,
  ASSOCIATED_TOKEN_PROGRAM_ID,
  RPC_ENDPOINTS,
} from '@solana-payment/sdk';

console.log('Program ID:', PROGRAM_ID.toString());
console.log('Devnet RPC:', RPC_ENDPOINTS.devnet);
```

## TypeScript Types

The SDK exports all TypeScript types for use in your application:

```typescript
import type {
  VaultSDKConfig,
  VaultConfig,
  InitializeParams,
  DepositParams,
  WithdrawParams,
  UpdateAdminParams,
  VaultBalanceInfo,
} from '@solana-payment/sdk';
```

## Development

```bash
# Install dependencies
pnpm install

# Build SDK
pnpm build

# Watch mode
pnpm watch
```

## License

MIT

## Support

For issues and questions, please open an issue on GitHub.

