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:
- Admin-controlled BTV reserve. BitView genesis-seeds the vault
from the Onboarding bucket (150M BTV at MVP). Only the admin can
depositoradd_allowlist. - Per-streamer cap.
add_allowlist(streamer, lb_pair, max_btv)creates anAllowlistEntryPDA that caps how much BTV any one streamer can pull, and pins whichlb_pairthey can pull it for. - 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
| PDA | Seeds | Purpose |
|---|---|---|
vault | [b"launchpad-vault"] | Singleton; holds admin pubkey, BTV mint, vault ATA reference |
vault_ata | ATA, owner = vault | Holds 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.
deposittops up the vault ATA from the admin's BTV ATA.add_allowlistrequires an explicit(streamer, lb_pair, max_btv)tuple. A second call for the same(streamer, lb_pair)fails.disable_allowlistflips a flag (preserves history);close_allowlistcloses 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):
- Account-owner inspection — the destination ATA's owner must
equal the Meteora pool PDA (DLMM
lb_pairreserve or DAMM v2 pool reserve). The PDA is re-derived on-chain. - Sibling-instruction introspection — the next instruction in
the same transaction must be:
add_liquidity_by_strategy2(DLMM v2, discriminator0x03dd95de6f8d76d5), 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:
| Route | Returns |
|---|---|
POST /launchpad-vault-api/build-initialize | unsigned tx (admin signs) |
POST /launchpad-vault-api/build-deposit | unsigned tx (admin signs) |
POST /launchpad-vault-api/build-add-allowlist | unsigned tx (admin signs) |
POST /launchpad-vault-api/build-disable-allowlist | unsigned tx (admin signs) |
POST /launchpad-vault-api/build-close-allowlist | unsigned tx (admin signs) |
POST /launchpad-vault-api/build-fund-launch-pool-seeding | atomic: vault withdraw + Meteora add-liquidity (streamer signs) |
POST /launchpad-vault-api/build-withdraw-damm-v2 | atomic: vault withdraw + DAMM v2 add-liquidity (streamer signs) |
GET /launchpad-vault-api/snapshot/{mint} | allowlist snapshot for a streamer-token mint |
Deployment
| Cluster | Program ID |
|---|---|
| Devnet | DpAbzBsenJqFaa6Bk3cZjCdapWriEPgYek17UefkwPEb |
| 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
| Surface | State |
|---|---|
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
- Streamer vault — sibling program: 85% STREAM lock with purpose-gated withdrawals
- On-chain program — merkle-distributor
- distributor (component overview)
- Tokenomics — where Identity-tier pool seeding fits
- Streamer-token economics — the 85/15 supply split and the pool seeding mechanics