Lookup tables
Launch with up to 16 buyers in one block: a lookup table lets several wallets buy in one transaction.
A Jito bundle holds 5 transactions, so a plain launch bundle is the create plus 4 buyers. An address lookup table lists the accounts a coin's buys touch (its curve accounts, each wallet's token account), so one transaction can carry about 4 buyers instead of one, and a bundle about 16. The table lives on chain under your wallet; its rent (about 0.002 SOL plus 32 bytes per address) comes back when you close it after the launch.
buyers entries that name the table → send the bundle → later, deactivate and close the table to get the rent back.1. Create the table
| Field | Type | Description |
|---|---|---|
| authority | string | The wallet that owns the table and pays its rent (the dev wallet is a good choice). |
| mint | string | The coin (it may not exist yet: pass the new mint's public key). |
| wallets | string[] | Every wallet that will buy (up to 60). |
| creator | string | The coin's creator (defaults to authority); needed when the coin does not exist yet. |
You get the table's address and one or more unsigned transactions: the first creates the table and adds the first addresses, the others add more. Sign each with authority and send them in order with /api/sol/send, waiting for the first to confirm.
const { lookupTable, transactions, rentSol } = await fetch(`${BASE}/api/sol/lut?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ authority: dev.publicKey.toBase58(), mint: mint.publicKey.toBase58(), wallets: buyers.map((b) => b.publicKey.toBase58()) }),
}).then((r) => r.json())
for (const b64 of transactions) {
const tx = VersionedTransaction.deserialize(Buffer.from(b64, 'base64'))
tx.sign([dev])
const { signature } = await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }),
}).then((r) => r.json())
// wait for it: GET /api/sol/tx/:signature until confirmed
}
// then poll GET /api/sol/lut/:lookupTable until ready === true
{ "lookupTable": "…", "addresses": 14, "rentSol": 0.0043, "transactions": ["base64…"], "encoding": "base64" }
2. Check it is ready
curl "https://api.trenchy.tools/api/sol/lut/LOOKUP_TABLE?api-key=YOUR_API_KEY"
{ "lookupTable": "…", "authority": "…", "addresses": ["…"], "lastExtendedSlot": 3812, "ready": true, "deactivated": false }
3. Build the bundle with buyers entries
In /api/sol/trade-local a bundle entry may carry buyers: [{ publicKey, amount }, …] plus lookupTable instead of a single publicKey. Up to 6 buyers per entry are accepted; about 4 fit in 1232 bytes (every signer still costs an address and a signature). Each buyer signs; the first one pays the network fee. Buyers are priced in order on the same curve.
const txs = await fetch(`${BASE}/api/sol/trade-local?format=json&api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([
{ publicKey: dev.publicKey.toBase58(), action: 'create', mint: mint.publicKey.toBase58(),
tokenMetadata: { name: 'My Coin', symbol: 'MYC', uri: metadataUri }, amount: 0.5, denominatedInSol: 'true', slippage: 10, priorityFee: 0.002 },
{ action: 'buy', mint: mint.publicKey.toBase58(), lookupTable, denominatedInSol: 'true', slippage: 25,
buyers: buyers.slice(0, 4).map((b) => ({ publicKey: b.publicKey.toBase58(), amount: 0.2 })) },
{ action: 'buy', mint: mint.publicKey.toBase58(), lookupTable, denominatedInSol: 'true', slippage: 25,
buyers: buyers.slice(4, 8).map((b) => ({ publicKey: b.publicKey.toBase58(), amount: 0.2 })) },
]),
}).then((r) => r.json()) // { transactions: [{ transaction, expected, perBuyer: [{ publicKey, tokens, sol }] }, …] }
const signed = txs.transactions.map((t, i) => {
const tx = VersionedTransaction.deserialize(bs58.decode(t.transaction))
tx.sign(i === 0 ? [dev, mint] : buyers.slice((i - 1) * 4, i * 4))
return bs58.encode(tx.serialize())
})
// POST /api/sol/bundle { transactions: signed }
4. Get the rent back
POST /api/sol/lut/:address/deactivate { authority } returns a transaction to sign and send. About four minutes later (513 slots), POST /api/sol/lut/:address/close { authority, recipient? } returns the one that closes the table and refunds the rent. Too early gets 409 cooling_down with the seconds left.