initialize_pool

Creates a new launchpad pool, mints the sale token (PDA mint), assigns authorities, creates vaults, and sets on-chain metadata via the Metaplex Token Metadata program.

Program: Tradersdex Curve (Launchpad) Instruction: initialize_pool


What it does

  • Creates pool PDA: ["pool", mint]

  • Creates PDA mint for the sale token (authority = ["token_authority"])

  • Creates vaults:

    • Token vault = ATA(mint, pool)

    • Quote vault = ATA(quote_mint, pool) (if quote != native SOL)

  • Creates MPL metadata with name, symbol, and uri

  • Stores curve parameters and sale targets

  • Emits PoolCreatedEvent for indexers

URI enforcement: If a non-empty uri is provided, it must begin with with a valid ipfs gateway:

"https://ipfs.io/ipfs/"
"https://gateway.pinata.cloud/ipfs/"
"https://cloudflare-ipfs.com/ipfs/"

otherwise the transaction fails. Name and symbol must each be at least 1 characters long.


Accounts (in order)

#
Name
Writable
Seeds / Constraints
Notes

0

pool_signer

["token_authority"]

Program authority for mint & vaults

1

payer

Funds rent & creates accounts

2

mint

Created in handler

PDA mint for sale token

3

quote_mint

Native SOL or SPL quote asset

4

pool

["pool", mint] (PDA), space = Pool::size_for_type(args.curve_type)

Pool state

5

token_vault

ATA(mint, pool)

Base token vault

6

quote_vault

ATA(quote_mint, pool) or WSOL vault

Quote token vault

7

metadata

MPL PDA: ["metadata", mpl_token_metadata::ID, mint]

Token metadata

8

metadata_program

= mpl_token_metadata::ID

MPL Token Metadata program

9

token_program

= anchor_spl::token::ID

SPL Token

10

associated_token_program

For ATA creation

11

system_program

12

tradersdex_curve

= crate::ID

Self-check

13

event_authority

["event_authority"]

Event signer for Anchor events


Arguments

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct InitializePoolArgs {
    /// Curve type 
    /// 0: SOL / NO creator fee | 1: SOL /creator fee | 2: WSOL / no cf | 3: WSOL / cf
    pub curve_type: PoolType,
    /// Total quote tokens to raise before finalize/migrate
    /// must be between 60 and 100, will round to 0 decimals
    pub target_raised: u64,
    /// PDA salt for mint derivation
    pub salt: u128,
    /// Fixed 80 for now
    pub bonding_token_share: u8, 
    /// must be between 20 and 255
    pub slope_multiplier: u8, 
    /// Currently only 6 supported
    pub decimals: u8,
    /// MPL metadata
    pub name: String,   // >= 1 chars
    pub symbol: String, // >= 1 chars
    pub uri: String,    // must start with valid ipfs gateway
}

Events

#[event]
pub struct PoolCreatedEvent {
    pub pool: Pubkey,
    pub mint: Pubkey,
    pub creator: Pubkey,
    pub quote_mint: Pubkey,
    pub target_raised: u64,
    pub total_supply: u64,
    pub virtual_token_reserves: u64,
    pub virtual_sol_reserves: u64,
    pub curve_type: PoolType,
    pub timestamp: i64,
}

PDA Derivation

  • Pool = PDA(["pool", mint])

  • Program signer = PDA(["token_authority"])

  • Mint = PDA(["mint", le_u128(salt)])

  • Vaults:

    • Token vault = ATA(mint, pool)

    • Quote vault = ATA(quote_mint, pool) or pool for SOL

  • Metadata = PDA(["metadata", MPL_ID, mint], program = MPL_ID)


Validation rules

  • decimals must equal 6

  • name.len() >= 1 and symbol.len() >= 1

  • quote_mint must be either wrapped SOL mint or the native SOL mint

  • PDA derivations must match

  • Adequate lamports for rent-exempt accounts


Client example (TypeScript)

import { AnchorProvider, Program, BN } from "@coral-xyz/anchor";
import { PublicKey, SystemProgram } from "@solana/web3.js";
import { NATIVE_MINT } from "@solana/spl-token";
import crypto from "crypto";

const TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
const ATOKEN_PROGRAM_ID = new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
const LAUNCHPAD_ID = new PublicKey("TDEXxqqkyJ4Jh8UtLB5BzYoxbJKHwo3FFGKc5GeS1H6");
const MPL_ID = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");

/** random u128 as bigint */
function randomU128(): bigint {
  return BigInt("0x" + crypto.randomBytes(16).toString("hex"));
}

function le128(n: bigint): Buffer {
  const b = Buffer.alloc(16);
  let x = n;
  for (let i = 0; i < 16; i++) { b[i] = Number(x & 0xffn); x >>= 8n; }
  return b;
}

/** derive mint PDA from salt: seeds ["mint", le_u128(salt)] */
function deriveMintPda(programId: PublicKey, salt: bigint): PublicKey {
  const [mint] = PublicKey.findProgramAddressSync(
    [Buffer.from("mint"), le128(salt)],
    programId
  );
  return mint;
}

function deriveEventAuthority(programId: PublicKey): PublicKey {
  return PublicKey.findProgramAddressSync([Buffer.from("event_authority")], programId)[0];
}

export async function initializePoolInstruction(program: Program, args: {
  payer: PublicKey;
  quoteMint: PublicKey;
  creatorFeeEnabled: bool;
  targetRaised: bigint;
  salt?: bigint;
  bondingTokenShare: number;
  slopeMultiplier: number;
  decimals: number;
  name: string;
  symbol: string;
  uri: string;
}) {

  const isWSOL = args.quoteMint.equals(NATIVE_MINT);
  const curveType = (isWSOL ? 2 : 0) + (args.creatorFeeEnabled ? 1 : 0);

  const [poolSigner] = PublicKey.findProgramAddressSync(
    [Buffer.from("token_authority")],
    program.programId
  );
  
  const [metadata] = PublicKey.findProgramAddressSync(
    [Buffer.from("metadata"), MPL_ID.toBuffer(), args.mint.toBuffer()],
    MPL_ID
  );
  
  let salt = args.salt ?? randomU128();
  const derivedMint = deriveMintPda(program.programId, salt);
  
  const [pool] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool"), derivedMint.toBuffer()],
    program.programId
  );
  
  // Off-curve owner (PDA), so allowOwnerOffCurve=true
  const tokenVault = getAssociatedTokenAddressSync(mint, pool, true); // ATA(mint, pool)
  // For WSOL we also use the NATIVE_MINT ATA; program will wrap/unwrap as needed
  const quoteVault = isWSOL
    ? getAssociatedTokenAddressSync(NATIVE_MINT, pool, true)
    : pool;

  const ix = await program.methods
    .initializePool({
      curveType,
      targetRaised: new BN(args.targetRaised.toString()),
      salt: new BN(salt.toString()),
      bondingTokenShare: args.bondingTokenShare,
      slopeMultiplier: args.slopeMultiplier,
      decimals: args.decimals,
      name: args.name,
      symbol: args.symbol,
      uri: args.uri,
    })
    .accounts({
      poolSigner,
      payer: args.payer,
      mint: derivedMint,
      quoteMint: args.quoteMint,
      pool,
      tokenVault,
      quoteVault,
      metadata,
      metadataProgram: MPL_ID,
      tokenProgram: TOKEN_PROGRAM_ID,
      associatedTokenProgram: ATOKEN_PROGRAM_ID,
      systemProgram: SystemProgram.programId,
      tradersdex_curve: program.programId,
      eventAuthority: deriveEventAuthority(program.programId),
    })
    .instruction();

  return {
    ix,
    mint: derivedMint,
    saltLeHex: le128(salt).toString("hex"),
    curveType,
    accounts: { poolSigner, pool, tokenVault, quoteVault, metadata },
  };
}

Vanity mint grinder (TypeScript)

Finds one salt that produces a mint PDA (seed: ["mint", le_u128(salt)]) ending with a desired Base58 suffix.

grinder_one.ts

import { PublicKey } from "@solana/web3.js";
import crypto from "crypto";

// --- configuration ---
const PROGRAM_ID = new PublicKey("TDEXxqqkyJ4Jh8UtLB5BzYoxbJKHwo3FFGKc5GeS1H6");
const SUFFIX = "TDEX";

// --- helpers ---
function le128(n: bigint): Buffer {
  const buf = Buffer.alloc(16);
  let x = n;
  for (let i = 0; i < 16; i++) {
    buf[i] = Number(x & 0xffn);
    x >>= 8n;
  }
  return buf;
}

function randomU128(): bigint {
  return BigInt("0x" + crypto.randomBytes(16).toString("hex"));
}

// --- main ---
async function main() {
  console.log(`Searching for mint PDA ending with '${SUFFIX}'`);
  console.log(`Program ID: ${PROGRAM_ID.toBase58()}`);
  console.log("====================================");

  let salt = randomU128();
  let attempts = 0n;
  const startTime = Date.now();

  while (true) {
    const saltLe = le128(salt);
    const [pda] = PublicKey.findProgramAddressSync(
      [Buffer.from("mint"), saltLe],
      PROGRAM_ID
    );
    const base58 = pda.toBase58();
    attempts++;

    if (base58.endsWith(SUFFIX)) {
      const elapsed = (Date.now() - startTime) / 1000;
      console.log("\n✅ Found vanity mint!");
      console.log(`Mint PDA:  ${base58}`);
      console.log(`Salt (LE u128 hex): ${saltLe.toString("hex")}`);
      console.log(`Attempts:  ${attempts}`);
      console.log(`Elapsed:   ${elapsed.toFixed(2)}s`);
      break;
    }

    if (attempts % 100000n === 0n) {
      const elapsed = (Date.now() - startTime) / 1000;
      process.stdout.write(
        `Attempts: ${attempts.toString()} | ${(Number(attempts) / Math.max(elapsed, 1)).toFixed(0)}/s | last: ${base58}\r`
      );
    }

    salt = (salt + 1n) & ((1n << 128n) - 1n);
  }
}

main().catch((e) => console.error("Error:", e));

Usage:

ts-node grinder_one.ts

Output:

Searching for mint PDA ending with 'TDEX'
Program ID: TDEXxqq...
====================================
✅ Found vanity mint!
Mint PDA:  3Myg7TDEX
Salt (LE u128 hex): e4f7c0000021f0f4a9c3...
Attempts:  693452
Elapsed:   10.21s

Use the found salt (in LE hex) as your InitializePoolArgs.salt.

Last updated