@paul.lumberwork/bonding-curve-sdk
v1.5.0
Published
SDK for Kick.fun clone - Solana bonding curve launchpad
Downloads
664
Maintainers
Readme
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_SCALEWhere 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_supplyandkare configured by admin in the Launchpad contract. Creators do not set these values.
SDK Usage
Installation
npm install @paul.lumberwork/bonding-curve-sdkSetup
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)
const result = await sdk.initializeLaunchpad();
// Returns: "already_initialized" or transaction signature2. Create Token Launch
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:
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 namesymbol: Token symboluri: Metadata URI (Metaplex standard)creatorWallet: (Optional) Custom creator wallet. Defaults to SDK wallet.
Returns:
txSignature: Transaction hashaddresses.tokenLaunch: Token launch PDAaddresses.bondingCurve: Bonding curve PDAaddresses.vault: SOL vault PDAaddresses.curveTokenAccount: Token account for curveaddresses.metadata: Metaplex metadata PDAmintKeypair: Keypair of the new token
3. Buy Tokens
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
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)
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)
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)
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)
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
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
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
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)
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
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
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
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
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
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
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
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
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
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 // 10PDA 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
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
# 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.tsLicense
MIT
