Architecture
Components
- BrowserYour wallet signs every transaction
- build txsLatch webBuilds the transactions and reads chain state through an RPC
- sends txsLatch vault programHolds the position NFT, so only the vault can claim fees
- CPIMeteora DAMM v2The pool and its position
- CPISPL Token / MetaplexThe mint and its metadata
- permitClaim signerSite routes: check the X login, sign an ed25519 permit. Needs X credentials
- IndexerNot built. The pages read the chain directlyNot built
The claim signer and X login are routes on the Latch site (see X claims). There is no separate indexer: the token page reads the chain directly.
Accounts
All are program-derived addresses (PDAs) owned by the Latch vault program. This is the layout that is built and tested in vault/programs/ember-vault/src/state.rs.
// seeds: ["config"]
pub struct Config {
pub admin: Pubkey, // can change the treasury and the claim signer
pub treasury: Pubkey, // receives the protocol share
pub pending_admin: Pubkey, // set by propose_admin, cleared by accept_admin
pub claim_signer: Pubkey, // key whose signatures authorise X claims (zero = off)
pub bump: u8,
}
// seeds: ["launch", creator, position_nft_mint]
pub struct Launch {
pub creator: Pubkey,
pub recipient: Pubkey, // the creator wallet, or the escrow PDA for an X-account launch
pub pool: Pubkey,
pub position: Pubkey,
pub position_nft_mint: Pubkey,
pub token_mint: Pubkey, // the launched token
pub creator_bps: u16, // creator share, max 9500, can only go down
pub creator_x_id: u64, // numeric X user id, 0 = none
pub metadata_delay_secs: i64, // 0 = the vault does not manage the name
pub pending_executable_at: i64,
pub pending_name: String, // a queued rename
pub pending_symbol: String,
pub pending_uri: String,
pub bump: u8,
}
// seeds: ["escrow", launch]
pub struct Escrow {
pub launch: Pubkey,
pub claim_nonce: u64, // each successful claim adds one, so a permit works once
pub bump: u8,
}
The launch account's address holds the position NFT. That is what lets the vault, and only the vault, claim fees. When the creator named an X account, the creator share is paid into token accounts owned by the escrow instead of a wallet.
Instructions
| Instruction | Who can call | What it does |
|---|---|---|
initialize_config |
the program's upgrade authority | Sets the admin and the treasury. Only the upgrade authority can call it, so nobody can take the config by front-running deployment. |
set_treasury |
admin | Changes where the protocol share goes. Never touches a creator's share. |
propose_admin, accept_admin |
admin, then the new admin | Two-step handover of the admin role. |
set_claim_signer |
admin | Sets the key that authorises X-account claims. Zero turns them off. |
register_launch |
creator | Checks the position is fully locked and belongs to the pool, both mints are plain SPL Token mints, and the launch address holds the NFT. Records the creator share, the optional X account and the optional rename wait. If a wait is chosen, the vault must already hold the token's update authority. |
crank_fees |
anyone | Claims fees from Meteora (the launch address signs) and splits them: the creator share to the recipient or the escrow, the rest to the treasury. |
lower_creator_bps |
creator | Lowers the creator share. Raising it fails. |
set_recipient |
creator | Points the creator share at another wallet. Refused for X-account launches. |
queue_metadata, cancel_metadata |
creator | Queue a new name, symbol and link, or drop the queued change. |
apply_metadata |
anyone | After the launch's wait, writes the queued change into the token's metadata. |
claim_creator_fees |
the claimant | Pays an X-account launch's escrow to the claimant, only with a valid permit from the claim signer in the same transaction. |
All of these are built and covered by the integration suite. See X claims for the permit and Upgradeability for the rename wait.
Launch transaction sequence
A single Solana transaction is limited to 1232 bytes, so a launch takes three. Sizes are checked offline by scripts/verify-launch.mts.
- Create the token (about 600 bytes). Create the mint, mint the full supply to the creator's wallet, write the metadata, and revoke the mint authority.
- Open and lock the pool (about 1050 bytes). The creator's wallet creates the DAMM v2 pool with a single-sided deposit over
[Pa, Pb]and permanently locks the position in the same transaction. If the creator chose a first buy, the same transaction then wraps that SOL, swaps it for the token from the creator wallet and unwraps what is left (about 1,120 bytes in total), so nobody can trade between the pool opening and that buy. - Hand the position to the vault (about 500 to 720 bytes). In one atomic transaction: create the launch account's Token-2022 account for the position NFT, move the NFT into it, optionally hand the token's update authority to the launch address, and call
register_launch.
The vault cannot be the creator of the pool. The permanent lock instruction needs the creator to sign, and a PDA cannot sign outside a program. So the creator's wallet creates and locks the pool, and the NFT moves to the vault afterwards. The liquidity is already locked when it moves, so only the right to claim fees changes hands.
If step 2 or 3 fails, the supply stays in the creator's wallet (step 2) or the NFT stays in the creator's wallet (step 3), and the launch page retries from that step. Until step 3 lands, the creator can still claim fees directly, and nobody else can.
Why the vault owns the position
DAMM v2 positions are NFTs. Claiming fees needs the position owner's signature. If a person owned it, one wallet would control fees and could transfer the position away. If the launch account owns it, the vault is the only thing that can claim, and its code decides the split. Liquidity removal is impossible because of the permanent lock. This is also the reason the vault is the weakest link: see Security.
Off-chain pieces
- Web. Next.js app that builds unsigned transactions. Users sign in their own wallet.
- Indexer. Not built. The token page and the fees pages read accounts and program events straight from an RPC.
- Claim signer. Built into the site as API routes. It checks the X login and signs a permit the vault verifies. See X claims.