In this guide, you’ll learn how to launch a Solana token using the Bags TypeScript SDK with Node.js. This updated script uses the new SDK API with improved configuration management and simplified token launching.

Prerequisites

Before starting, make sure you have:
  • Completed our TypeScript and Node.js Setup Guide.
  • Got your API key from the Bags Developer Portal.
  • A Solana wallet with some SOL for transactions.
  • A token image file ready for upload.
  • Installed the additional dependencies for this guide:
    npm install @solana/web3.js bs58
    

1. Set Up Environment Variables

This guide requires your wallet’s private key. Add it to your base .env file:
# .env
BAGS_API_KEY=your_api_key_here
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
PRIVATE_KEY=your_base58_encoded_private_key_here  # Required for this guide
You can export your private key from wallets like Bags, Phantom, or Backpack.

2. The Token Launch Script

Here is the complete script for launching a token. Save it as launch-token.ts. This updated script uses the new SDK API that simplifies the token launch process by automatically handling configuration management, using image URLs instead of local files, and providing better error handling with emojis for clearer status updates.
import dotenv from "dotenv";
dotenv.config({ quiet: true });

import { BagsSDK } from "@bagsfm/bags-sdk";
import { Keypair, LAMPORTS_PER_SOL, PublicKey, Connection } from "@solana/web3.js";
import bs58 from "bs58";

// Initialize SDK
const BAGS_API_KEY = process.env.BAGS_API_KEY;
const SOLANA_RPC_URL = process.env.SOLANA_RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;

if (!BAGS_API_KEY || !SOLANA_RPC_URL || !PRIVATE_KEY) {
    throw new Error("BAGS_API_KEY, SOLANA_RPC_URL, and PRIVATE_KEY are required");
}

const connection = new Connection(SOLANA_RPC_URL);
const sdk = new BagsSDK(BAGS_API_KEY, connection, "processed");

async function launchToken(launchParams: {
    imageUrl: string;
    name: string;
    symbol: string;
    description: string;
    twitterUrl: string;
    websiteUrl: string;
    initialBuyAmountLamports: number;
}) {
    try {
        const PRIVATE_KEY = process.env.PRIVATE_KEY;

        if (!PRIVATE_KEY) {
            throw new Error("PRIVATE_KEY is not set");
        }

        const keypair = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));

        console.log(`🚀 Creating token $${launchParams.symbol} with wallet ${keypair.publicKey.toBase58()}`);

        const connection = sdk.state.getConnection();
        const commitment = sdk.state.getCommitment();

        console.log("⚙️  Fetching configuration...");

        // Get existing config or config creation TX
        const configResponse = await sdk.config.getOrCreateConfig(keypair.publicKey);

        // Config does not exist, create it
        if (configResponse.transaction) { 
            console.log("🔧 Config does not exist, creating new configuration...");
            configResponse.transaction.sign([keypair]);

            const blockhash = await connection.getLatestBlockhash(commitment);

            const txSignature = await connection.sendTransaction(configResponse.transaction, { maxRetries: 0, skipPreflight: true });

            const confirmed = await connection.confirmTransaction({
                blockhash: blockhash.blockhash,
                lastValidBlockHeight: blockhash.lastValidBlockHeight,
                signature: txSignature,
            }, commitment);

            if (confirmed.value.err) { 
                console.error("❌ Error creating config:", confirmed.value.err);

                throw new Error("Error creating config");
            }
            else {
                console.log("✅ Configuration successfully created!");
            }
        }
        else {
            console.log("♻️  Config already exists, reusing config key:", configResponse.configKey.toString());
        }

        console.log("📝 Creating token info and metadata...");

        // Fetch image url to file like object
        const imageBlob = await fetch(launchParams.imageUrl).then(res => res.blob());

        // Create token info and metadata
        const tokenInfoResponse = await sdk.tokenLaunch.createTokenInfoAndMetadata({
            image: imageBlob,
            name: launchParams.name,
            description: launchParams.description,
            symbol: launchParams.symbol?.toUpperCase()?.replace("$", ""),
            twitter: launchParams.twitterUrl,
            website: launchParams.websiteUrl,
        });

        console.log("✨ Successfully created token info and metadata!");
        console.log("🪙 Token mint:", tokenInfoResponse.tokenMint);
        console.log("🎯 Creating token launch transaction...");

        // Create token launch
        const tokenLaunchTransaction = await sdk.tokenLaunch.createLaunchTransaction({
            metadataUrl: tokenInfoResponse.tokenMetadata,
            tokenMint: new PublicKey(tokenInfoResponse.tokenMint),
            launchWallet: keypair.publicKey,
            initialBuyLamports: launchParams.initialBuyAmountLamports,
            configKey: configResponse.configKey,
        });

        tokenLaunchTransaction.sign([keypair]);

        const blockhash = await connection.getLatestBlockhash(commitment);

        const txSignature = await connection.sendTransaction(tokenLaunchTransaction, { maxRetries: 0, skipPreflight: true });

        console.log("🔑 Confirming transaction signature:", txSignature);

        const confirmed = await connection.confirmTransaction({
            blockhash: blockhash.blockhash,
            lastValidBlockHeight: blockhash.lastValidBlockHeight,
            signature: txSignature,
        }, commitment);

        if (confirmed.value.err) {
            console.error("💥 Error creating token launch:", confirmed.value.err);

            throw new Error("Error creating token launch");
        }

        console.log("🎉 Token launched successfully!");
        console.log(`🌐 View your token at: https://bags.fm/${tokenInfoResponse.tokenMint}`);
    }
    catch (error) {
        console.error("🚨 Unexpected error occurred:", error);
    }
}

launchToken({
    imageUrl: "https://img.freepik.com/premium-vector/white-abstract-vactor-background-design_665257-153.jpg",
    name: "TEST",
    symbol: "TEST",
    description: "TEST",
    twitterUrl: "https://x.com/test",
    websiteUrl: "https://test.com",
    initialBuyAmountLamports: 0.01 * LAMPORTS_PER_SOL, // 0.01 SOL
});

3. Run Your Script

To launch your token, edit the launchToken function call at the bottom of launch-token.ts with your token’s details, especially the imageUrl. Then, run the script from your terminal:
npx ts-node launch-token.ts

Troubleshooting

The script includes comprehensive error handling. Common issues include:
  • API Key Issues: Ensure your API key is valid.
  • Private Key Format: Your private key must be base58 encoded.
  • Insufficient SOL: Your wallet needs SOL for transaction fees.
  • Image URL: The URL to your token image must be accessible and valid.
For more details, see the API Reference.