Skip to main content

Launchpad vault program

Admin-controlled BTV escrow that match-funds Identity-tier streamer token launches. The streamer brings the STREAM side of the STREAM/BTV pool (via the streamer-vault); the launchpad vault brings the BTV side. Both sides withdraw atomically with the Meteora add_liquidity_by_strategy2 (DLMM v2) or DAMM v2 add-liquidity instruction in the same transaction.

  • Program path: distributor/launchpad-vault/ in this workspace
  • Anchor: 1.0.2
  • Solana: 3.0
  • Token program: classic SPL Token (BTV is a classic SPL mint; not Token-2022)

Why a separate program

Pre-launchpad-vault, Identity-tier streamer tokens had no canonical liquidity source. Streamers couldn't seed a meaningful pool because they didn't yet hold BTV; BitView didn't want to send BTV to streamer wallets ahead of time (the streamer could just withdraw to a personal ATA). The launchpad vault solves this in three properties:

  1. Admin-controlled BTV reserve. BitView genesis-seeds the vault from the Onboarding bucket (150M BTV at MVP). Only the admin can deposit or add_allowlist.
  2. Per-streamer cap. add_allowlist(streamer, lb_pair, max_btv) creates an AllowlistEntry PDA that caps how much BTV any one streamer can pull, and pins which lb_pair they can pull it for.
  3. Atomic with Meteora. Withdrawals are gated by both account-owner inspection (destination ATA is owned by the Meteora pool PDA) and sibling-instruction introspection (next ix in the same transaction must be the Meteora add-liquidity discriminator).

If the streamer's pool-seeding tx fails, no BTV moves. If the BTV moves, the Meteora liquidity provision succeeded.

PDAs

PDASeedsPurpose
vault[b"launchpad-vault"]Singleton; holds admin pubkey, BTV mint, vault ATA reference
vault_ataATA, owner = vaultHolds the BTV balance
allowlist_entry[b"allowlist", streamer, lb_pair]Per-streamer-per-pool authorization. Tracks max_btv, btv_used, disabled

The vault is a singleton — only one ever exists, created by initialize_vault.

Instructions

initialize_vault()
deposit(amount: u64)
add_allowlist(streamer: Pubkey, lb_pair: Pubkey, max_btv: u64)
disable_allowlist(streamer: Pubkey, lb_pair: Pubkey)
close_allowlist(streamer: Pubkey, lb_pair: Pubkey)
withdraw_for_launch_seeding(amount: u64)
withdraw_for_damm_v2_launch(amount: u64)

Admin instructions

initialize_vault, deposit, add_allowlist, disable_allowlist, close_allowlist — all gated on signer == vault.admin.

  • deposit tops up the vault ATA from the admin's BTV ATA.
  • add_allowlist requires an explicit (streamer, lb_pair, max_btv) tuple. A second call for the same (streamer, lb_pair) fails.
  • disable_allowlist flips a flag (preserves history); close_allowlist closes the PDA and refunds rent.

Streamer instructions

withdraw_for_launch_seeding (Meteora DLMM v2) and withdraw_for_damm_v2_launch (Meteora DAMM v2) — gated on a valid, non-disabled AllowlistEntry for (signer, lb_pair) with sufficient remaining cap.

Purpose-gating (dual check):

  1. Account-owner inspection — the destination ATA's owner must equal the Meteora pool PDA (DLMM lb_pair reserve or DAMM v2 pool reserve). The PDA is re-derived on-chain.
  2. Sibling-instruction introspection — the next instruction in the same transaction must be:
    • add_liquidity_by_strategy2 (DLMM v2, discriminator 0x03dd95de6f8d76d5), or
    • DAMM v2 add-liquidity (discriminator 0x14a1f118bddb4002)
    • …pinned to the canonical Meteora program ID.

If both checks pass, the vault PDA signs transfer_checked from vault_ata to the Meteora destination ATA. allowlist_entry.btv_used is incremented.

Discriminator pinning

Both sibling-ix discriminators are unit-tested at build time against sha256("global:<name>")[..8]. A downstream Meteora rename fails the build before deploy, never silently.

Off-chain client

bitview-launchpad-vault mirrors each instruction with an unsigned-tx builder. The bot's /launchpad-vault-api/* HTTP routes are thin wrappers:

RouteReturns
POST /launchpad-vault-api/build-initializeunsigned tx (admin signs)
POST /launchpad-vault-api/build-depositunsigned tx (admin signs)
POST /launchpad-vault-api/build-add-allowlistunsigned tx (admin signs)
POST /launchpad-vault-api/build-disable-allowlistunsigned tx (admin signs)
POST /launchpad-vault-api/build-close-allowlistunsigned tx (admin signs)
POST /launchpad-vault-api/build-fund-launch-pool-seedingatomic: vault withdraw + Meteora add-liquidity (streamer signs)
POST /launchpad-vault-api/build-withdraw-damm-v2atomic: vault withdraw + DAMM v2 add-liquidity (streamer signs)
GET /launchpad-vault-api/snapshot/{mint}allowlist snapshot for a streamer-token mint

Deployment

ClusterProgram ID
DevnetDpAbzBsenJqFaa6Bk3cZjCdapWriEPgYek17UefkwPEb
Mainnet— pending audit + redeploy

Identical IDs across Anchor.toml cluster entries; production mainnet needs the BPFLoaderUpgradeable upgrade authority transferred to the Squads multisig (or burned) before launch.

Status

SurfaceState
Program — initialize_vault, deposit, allowlist CRUD✅ Built (devnet)
Program — withdraw_for_launch_seeding (DLMM v2)✅ Built (devnet)
Program — withdraw_for_damm_v2_launch✅ Built (devnet)
Dual purpose-gating (owner + sibling-ix)✅ Built
Discriminator pinning unit tests✅ Built
bitview-launchpad-vault CPI client✅ Built
/launchpad-vault-api/* bot routes✅ Built
Admin allowlist editor (/launchpad-vault page in admin-web)✅ Built
Mainnet audit + redeploy🔴 Pending

Cross-references