npm package: @paul.lumberwork/bonding-curve-sdk
Description: SDK for Kick.fun clone - Solana bonding curve launchpad
Latest version: 1.8.1

# Kick.fun SDK

Solana program with Linear Bonding Curve for token launches. SDK package: `@paul.lumberwork/bonding-curve-sdk`

## Overview

- **Program ID**: `6o7oTqg2CfvcMCJTLNEJsef7c875zGpTvcnFctNAjudL`
- **Admin Wallet**: `7eGpbyRpcM7WpNKQtd6XkteNQWHbWXP7icZjKzNK2aTk`
- **Network**: Devnet / Localnet

## Token Allocation

| Allocation | Percent | Description |
|------------|---------|-------------|
| Bonding Curve | 70% | Available for trading |
| LP Reserve | 20% | Reserved for DEX migration |
| Treasury | 10% | Platform treasury |

## Trading Fees (Hardcoded)

| Fee | Percent | Recipient |
|-----|---------|-----------|
| Accumulated Fee | 0.5% | Accumulated in curve (withdrawn by admin) |
| Admin Fee | 0.5% | Platform admin (immediate) |
| **Total** | **1%** | |

## Bonding Curve Formula

```
Price = K x sold_supply
Cost  = K x (s2² - s1²) / 2 / K_SCALE
```

Where `K_SCALE = 10^22`

### K Value Reference

| K Value | Target SOL | Use Case |
|---------|------------|----------|
| 60 | ~2 SOL | Testing |
| 312 | ~10 SOL | Small launch |
| 2156 | ~69 SOL | pump.fun style |
| 3125 | ~100 SOL | Large launch |

**Formula**: `Target SOL = K x 0.032`

> **Note:** `total_supply` and `k` are configured by admin in the Launchpad contract. Creators do not set these values.

---

## SDK Usage

### Installation

```bash
npm install @paul.lumberwork/bonding-curve-sdk
```

### Setup

```typescript
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import { KickFunSDK, formatTokens, formatSol, PROGRAM_ID } from "@paul.lumberwork/bonding-curve-sdk";
import idl from "@paul.lumberwork/bonding-curve-sdk/idl/kick_fun_program.json";

// Setup connection and wallet
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const wallet = new anchor.Wallet(keypair);
const provider = new anchor.AnchorProvider(connection, wallet, { commitment: "confirmed" });

// Create program instance
const program = new Program(idl as anchor.Idl, provider);

// Create SDK instance
const sdk = new KickFunSDK(program, wallet);
```

### 1. Initialize Launchpad (one-time, admin only)

```typescript
const result = await sdk.initializeLaunchpad();
// Returns: "already_initialized" or transaction signature
```

### 2. Create Token Launch

```typescript
const { txSignature, addresses, mintKeypair } = await sdk.createTokenLaunch({
  name: "My Token",
  symbol: "MTK",
  uri: "https://example.com/metadata.json",
});

const mint = mintKeypair.publicKey;
console.log("Mint:", mint.toString());
console.log("Token Launch PDA:", addresses.tokenLaunch.toString());
```

**With Custom Creator Wallet:**
```typescript
const { txSignature, addresses, mintKeypair } = await sdk.createTokenLaunch({
  name: "My Token",
  symbol: "MTK",
  uri: "https://example.com/metadata.json",
  creatorWallet: new PublicKey("YourCreatorWalletAddress..."),
});
```

**Parameters:**
- `name`: Token name
- `symbol`: Token symbol
- `uri`: Metadata URI (Metaplex standard)
- `creatorWallet`: (Optional) Custom creator wallet. Defaults to SDK wallet.

**Returns:**
- `txSignature`: Transaction hash
- `addresses.tokenLaunch`: Token launch PDA
- `addresses.bondingCurve`: Bonding curve PDA
- `addresses.vault`: SOL vault PDA
- `addresses.curveTokenAccount`: Token account for curve
- `addresses.metadata`: Metaplex metadata PDA
- `mintKeypair`: Keypair of the new token

### 3. Buy Tokens

```typescript
const result = await sdk.buy(mint, 0.5, { computeUnits: 400_000 });

console.log("Tokens received:", formatTokens(result.tokensTraded));
console.log("Progress:", result.newProgress.toFixed(2) + "%");
console.log("Curve complete:", result.isCurveComplete);
```

**Parameters:**
- `mint`: Token mint address (PublicKey)
- `solAmount`: SOL to spend (number, e.g., 0.5 for 0.5 SOL)
- `options.computeUnits`: Optional compute budget (default: none)

### 4. Sell Tokens

```typescript
const result = await sdk.sell(mint, 10_000_000, 0, { computeUnits: 400_000 });

console.log("SOL received:", formatSol(result.solTraded));
console.log("Progress:", result.newProgress.toFixed(2) + "%");
```

**Parameters:**
- `mint`: Token mint address (PublicKey)
- `tokenAmount`: Tokens to sell (number, e.g., 10_000_000 for 10M)
- `minSolOut`: Minimum SOL to receive, slippage protection (default: 0)
- `options.computeUnits`: Optional compute budget

### 5. Estimate Buy (SOL -> Tokens)

```typescript
const estimate = await sdk.estimateBuy(mint, 0.5);

console.log("Tokens out:", formatTokens(estimate.tokensOut));
console.log("SOL in (total):", formatSol(estimate.solIn));
console.log("SOL after fee:", formatSol(estimate.solAfterFee));
console.log("Fee:", formatSol(estimate.fee));
console.log("Price impact:", estimate.priceImpactBps, "bps");
```

### 6. Estimate Buy by Tokens (Tokens -> SOL needed)

```typescript
const estimate = await sdk.estimateBuyByTokens(mint, 1_000_000); // 1M tokens

console.log("SOL needed:", formatSol(estimate.solIn));
console.log("SOL to curve:", formatSol(estimate.solAfterFee));
console.log("Fee:", formatSol(estimate.fee));
console.log("Tokens out:", formatTokens(estimate.tokensOut));
```

### 7. Estimate Sell (Tokens -> SOL)

```typescript
const estimate = await sdk.estimateSell(mint, 10_000_000);

console.log("SOL before fee:", formatSol(estimate.solBeforeFee));
console.log("SOL out (net):", formatSol(estimate.solOut));
console.log("Fee:", formatSol(estimate.fee));
console.log("Tokens in:", formatTokens(estimate.tokensIn));
```

### 8. Estimate Sell by SOL (SOL desired -> Tokens to sell)

```typescript
const estimate = await sdk.estimateSellBySol(mint, 0.5); // want 0.5 SOL

console.log("Tokens to sell:", formatTokens(estimate.tokensIn));
console.log("SOL before fee:", formatSol(estimate.solBeforeFee));
console.log("SOL out (net):", formatSol(estimate.solOut));
console.log("Fee:", formatSol(estimate.fee));
```

### 9. Get Progress

```typescript
const progress = await sdk.getProgress(mint);

console.log("Progress:", progress.percent + "%");
console.log("SOL raised:", formatSol(progress.solRaised));
console.log("Current price:", progress.currentPrice.toString(), "lamports/token");
console.log("Complete:", progress.isComplete);
```

### 10. Get Curve State

```typescript
const curve = await sdk.getCurveState(mint);

console.log("K:", curve.k.toString());
console.log("Sold:", formatTokens(curve.soldSupply));
console.log("For curve:", formatTokens(curve.tokensForCurve));
console.log("SOL reserves:", formatSol(curve.realSolReserves));
console.log("Complete:", curve.complete);
```

### 11. Get Token Launch State

```typescript
const launch = await sdk.getTokenLaunchState(mint);

console.log("Name:", launch.name, `(${launch.symbol})`);
console.log("Creator:", launch.creator.toString());
console.log("Trading active:", launch.tradingActive);
console.log("Migrated:", launch.migrated);
```

### 12. Admin Withdraw (Admin Only)

```typescript
if (wallet.publicKey.equals(ADMIN_WALLET)) {
  const result = await sdk.adminWithdraw(mint);
  console.log("SOL withdrawn:", formatSol(result.solWithdrawn));
  console.log("LP tokens:", formatTokens(result.tokensWithdrawn));
  console.log("Fees to treasury:", formatSol(result.feesToTreasury));
  console.log("Treasury tokens:", formatTokens(result.treasuryTokens));
}
```

**Requirements:**
- Caller must be `ADMIN_WALLET`
- Curve must be complete (70% sold)
- Token not already migrated

---

## Estimate Return Types

### EstimateBuyResult

```typescript
interface EstimateBuyResult {
  solIn: bigint;          // Total SOL user pays (including fee)
  solAfterFee: bigint;    // SOL that goes into the curve (after fee)
  tokensOut: bigint;      // Tokens user receives
  fee: bigint;            // Fee in SOL (lamports)
  pricePerToken: bigint;
  priceImpactBps: number;
}
```

Used by: `estimateBuy()`, `estimateBuyByTokens()`

### EstimateSellResult

```typescript
interface EstimateSellResult {
  tokensIn: bigint;       // Tokens user sells
  solBeforeFee: bigint;   // Gross SOL from curve (before fee)
  solOut: bigint;         // Net SOL user receives (after fee)
  fee: bigint;            // Fee in SOL (lamports)
  pricePerToken: bigint;
  priceImpactBps: number;
}
```

Used by: `estimateSell()`, `estimateSellBySol()`

> **Note:** Fee is always denominated in SOL (lamports).

---

## Event Listeners

Subscribe to real-time events from the program.

### Available Events

| Event | Description |
|-------|-------------|
| `LaunchCreated` | New token launch created |
| `TokensPurchased` | Tokens bought on bonding curve |
| `TokensSold` | Tokens sold on bonding curve |
| `CurveComplete` | Bonding curve reached 100% |
| `AdminWithdraw` | Admin withdrew SOL + LP tokens |

### Listen for Token Purchases

```typescript
const listenerId = sdk.onTokensPurchased((event, slot, signature) => {
  console.log("Buy Event:", {
    buyer: event.buyer.toString(),
    mint: event.mint.toString(),
    solAmount: formatSol(event.solAmount),
    solAfterFees: formatSol(event.solAfterFees),
    tokensReceived: formatTokens(event.tokensReceived),
    curveFee: formatSol(event.curveFee),
    adminFee: formatSol(event.adminFee),
    progress: event.progress + "%",
    complete: event.isCurveComplete,
  });
});

// Unsubscribe when done
await sdk.removeEventListener(listenerId);
```

### Listen for Token Sales

```typescript
const listenerId = sdk.onTokensSold((event, slot, signature) => {
  console.log("Sell Event:", {
    seller: event.seller.toString(),
    tokensSold: formatTokens(event.tokensSold),
    grossSolRefund: formatSol(event.grossSolRefund),
    netSolRefund: formatSol(event.netSolRefund),
    curveFee: formatSol(event.curveFee),
    adminFee: formatSol(event.adminFee),
    progress: event.progress + "%",
  });
});
```

### Listen for New Launches

```typescript
sdk.onLaunchCreated((event, slot, signature) => {
  console.log("New Launch:", {
    name: event.name,
    symbol: event.symbol,
    mint: event.mint.toString(),
    creator: event.creator.toString(),
    totalSupply: formatTokens(event.totalSupply),
    curveAllocation: formatTokens(event.curveAllocation),
    lpAllocation: formatTokens(event.lpAllocation),
    treasuryAllocation: formatTokens(event.treasuryAllocation),
    initialK: event.initialK.toString(),
  });
});
```

### Listen for Curve Completion

```typescript
sdk.onCurveComplete((event, slot, signature) => {
  console.log("Curve Complete!", {
    mint: event.mint.toString(),
    totalSolRaised: formatSol(event.totalSolRaised),
    finalPrice: event.finalPrice.toString(),
  });
});
```

### Listen for Admin Withdrawals

```typescript
sdk.onAdminWithdraw((event, slot, signature) => {
  console.log("Admin Withdrawal:", {
    mint: event.mint.toString(),
    solWithdrawn: formatSol(event.solWithdrawn),
    tokensWithdrawn: formatTokens(event.tokensWithdrawn),
    feesToTreasury: formatSol(event.feesToTreasury),
    treasuryTokens: formatTokens(event.treasuryTokens),
  });
});
```

---

## Utility Functions

```typescript
import { formatTokens, formatSol, parseSol, parseTokens } from "@paul.lumberwork/bonding-curve-sdk";

// Format raw values to readable strings
formatTokens(1000000000000n);  // "1,000,000" (1M tokens)
formatSol(500000000n);          // "0.5000" (0.5 SOL)

// Parse human values to raw
parseSol(0.5);                  // 500000000n (lamports)
parseTokens(1000000);           // 1000000000000n (raw with 6 decimals)
```

---

## Constants

```typescript
import { ADMIN_WALLET, K_SCALE, TOKEN_DECIMALS, FEE_BPS, PROGRAM_ID } from "@paul.lumberwork/bonding-curve-sdk";

PROGRAM_ID      // PublicKey: "6o7oTqg2CfvcMCJTLNEJsef7c875zGpTvcnFctNAjudL"
ADMIN_WALLET    // PublicKey: "7eGpbyRpcM7WpNKQtd6XkteNQWHbWXP7icZjKzNK2aTk"
K_SCALE         // 10^22 (bigint)
TOKEN_DECIMALS  // 6
FEE_BPS         // 100 (1%)
CURVE_PERCENT   // 70
LP_PERCENT      // 20
TREASURY_PERCENT // 10
```

---

## PDA Seeds

| PDA | Seeds |
|-----|-------|
| Launchpad | `["launchpad"]` |
| Token Launch | `["token_launch", mint]` |
| Bonding Curve | `["bonding_curve", token_launch]` |
| Vault | `["vault", token_launch]` |
| User Position | `["user_position", token_launch, user]` |

---

## Full Example

```typescript
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Connection, Keypair } from "@solana/web3.js";
import { KickFunSDK, formatTokens, formatSol, ADMIN_WALLET } from "@paul.lumberwork/bonding-curve-sdk";
import idl from "@paul.lumberwork/bonding-curve-sdk/idl/kick_fun_program.json";

async function main() {
  const connection = new Connection("https://api.devnet.solana.com", "confirmed");
  const wallet = new anchor.Wallet(keypair);
  const provider = new anchor.AnchorProvider(connection, wallet, { commitment: "confirmed" });
  const program = new Program(idl as anchor.Idl, provider) as any;
  const sdk = new KickFunSDK(program, wallet);

  // 1. Initialize launchpad (admin only, one-time)
  await sdk.initializeLaunchpad();

  // 2. Create token (total_supply and k are set by admin)
  const { addresses, mintKeypair } = await sdk.createTokenLaunch({
    name: "Test Token",
    symbol: "TEST",
    uri: "https://example.com/metadata.json",
  });
  const mint = mintKeypair.publicKey;

  // 3. Estimate before buying
  const buyEstimate = await sdk.estimateBuy(mint, 0.5);
  console.log("Will receive:", formatTokens(buyEstimate.tokensOut), "tokens");
  console.log("Fee:", formatSol(buyEstimate.fee));

  // 4. Buy tokens
  const buy = await sdk.buy(mint, 0.5, { computeUnits: 400_000 });
  console.log("Bought:", formatTokens(buy.tokensTraded));

  // 5. Estimate sell by desired SOL
  const sellEstimate = await sdk.estimateSellBySol(mint, 0.3);
  console.log("Need to sell:", formatTokens(sellEstimate.tokensIn), "tokens to get 0.3 SOL");

  // 6. Sell tokens
  const sell = await sdk.sell(mint, 10_000_000, 0, { computeUnits: 400_000 });
  console.log("Sold for:", formatSol(sell.solTraded), "SOL");

  // 7. Check progress
  const progress = await sdk.getProgress(mint);
  console.log("Progress:", progress.percent + "%");

  // 8. Admin withdraw (when curve complete)
  if (progress.isComplete && wallet.publicKey.equals(ADMIN_WALLET)) {
    const withdraw = await sdk.adminWithdraw(mint);
    console.log("Withdrawn:", formatSol(withdraw.solWithdrawn), "SOL");
  }
}

main().catch(console.error);
```

---

## Running Tests

```bash
# Start local validator
solana-test-validator --reset

# In another terminal
cd kick_fun_program
anchor build && anchor deploy

# Run test
ANCHOR_PROVIDER_URL=http://127.0.0.1:8899 \
ANCHOR_WALLET=./test-keypair.json \
npx ts-node test-sdk.ts
```

---

## License

MIT