Hooks for your coin
Stoke v3: a program a creator adds to their coin at launch. What a hook can do, the ones available, and how to write one.
A hook is a small program a creator adds to their coin when they launch it. Stoke calls it before every trade of that coin, on the curve and in the pool. This is Stoke v3: anyone can write a hook, and creators add one to a coin from the list Stoke makes available.
What a hook can do
- Set a trade's fee, anywhere from 0 to 10% of its SOL side. The fee splits like every Stoke fee: 30% to the creator, 30% to holders, 40% into the coin's reserves.
- Refuse a trade.
That is all. A hook never holds or moves anyone's money, can't touch a coin's liquidity or change where fees go, and never receives a wallet's signature. The program enforces this: a hook gets one account when it's called, and it can't write to it.
The hooks available
| Hook | What it does | Program |
|---|---|---|
| Launch tax | A higher fee at launch that falls to your coin's own fee. | 3zbuMvvfy73QL6U8hsceeZxCpju2mDNed3YaQfTcdtjS |
| Max buy | No single buy above a size you set, for the coin's first minutes. | AXDdW1eY1YcxrxsrR6wGnj45hAFNtGtHrXCRsxZzKMqo |
Launch tax
- The creator sets it for buys, sells or both: where the fee starts (above the coin's own, up to 10%) and how long it takes to fall (1 minute to 7 days).
- It falls in a straight line to the coin's own fee. A 10% sell tax over 60 minutes is 5.5% at 30 minutes on a 1% coin. After that the hook changes nothing.
- The creator's dev buy, in the launch transaction, is exempt. Their later trades pay like anyone's.
Max buy
- For the coin's first minutes (1 minute to 24 hours), no single buy can be larger than the size the creator set (0.05 SOL or more).
- It applies to everyone, the creator's dev buy included.
- It caps one buy, not a wallet: someone can still buy more than once. Sells are never limited.
- While the cap lasts, a buy has to spend an exact SOL amount, which is how every app buys.
Adding a hook to your coin
- Pick it under Hook on the launch form and set it up. It goes on in the launch transaction, before the first trade.
- It can't be changed, swapped or removed afterwards, by you or by Stoke. Nobody ever buys a coin whose hook could still change.
- One hook per coin. For now a coin launches with a hook or with fee zones, not both: together they don't fit in one launch transaction.
- The hook checks your settings itself, in the launch transaction. If it refuses them, the coin doesn't launch.
Trading a coin with a hook
A coin's page shows its hook, the creator's settings in plain words, and what it does to a trade right now. Quotes on the site and in the open API include it.
- A fee the hook sets replaces the coin's own fee for that trade. It applies to everyone, the creator included: their fee-free sells don't apply to it.
- If a sell is also inside a fee zone, it pays the higher of the two.
- In a coin's first second, a hook's fee on a buy stops where the anti-snipe tax would leave the buyer nothing.
Writing a hook
A hook is any Solana program with two instructions. Both are Anchor-compatible: an 8-byte discriminator (the first 8 bytes of sha256("global:on_attach") or sha256("global:on_trade")), then the Borsh-encoded argument. The types, and helpers to decode them without Anchor, are in protocol/crates/meridian-core/src/coin_hook.rs.
| Instruction | When | What it does |
|---|---|---|
| on_attach(AttachArgs) | Once, when a creator adds the hook | Checks the creator's settings. An error fails the launch. |
| on_trade(TradeArgs) → TradeReturn | Before every trade of the coin | Returns nothing to leave the trade as it is, a fee to set it, or an error to refuse it. |
| Field | Meaning |
|---|---|
| coin | The coin: mint, creator, its own fee (bps), and when it launched (unix seconds and slot). |
| config | The creator's settings for this coin, up to 64 bytes, exactly as they gave them at launch. |
| trader | The wallet trading. |
| is_buy, exact_in | The side, and whether the trader fixed what goes in or what comes out. |
| sol, tokens | The trade's size before fees, in lamports and token units. The side the trader fixed is exact; the other is valued at the price before the trade. |
| graduated | False on the curve, true in the pool. |
| mcap | The coin's market cap in lamports, before the trade. |
| now, slot | The cluster's clock. |
TradeReturn is one field, fee_bps: Option<u16>. Anything above 1000 is cut to 1000.
use anchor_lang::prelude::Pubkey;
use anchor_lang::solana_program::{account_info::AccountInfo, entrypoint::ProgramResult,
program::set_return_data, program_error::ProgramError};
use meridian_core::coin_hook::{discriminator, verify_coin_hook_call, TradeArgs, TradeReturn};
anchor_lang::solana_program::entrypoint!(process);
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
if data.len() < 8 || accounts.is_empty() {
return Err(ProgramError::InvalidInstructionData);
}
// Only Stoke Launch can call a hook: the one account is this hook's listing, and it signed.
verify_coin_hook_call(program_id, &accounts[0])?;
let (disc, body) = data.split_at(8);
if disc == discriminator::ON_ATTACH {
return Ok(()); // no settings to check
}
let t = TradeArgs::parse(body).ok_or(ProgramError::InvalidInstructionData)?;
if t.is_buy && t.age() < 600 && t.sol > 1_000_000_000 {
return Err(ProgramError::Custom(1)); // refuse the trade
}
let (ret, len) = TradeReturn::default().encode(); // leave it as it is
set_return_data(&ret[..len]);
Ok(())
}- One account. A hook is called with a single account: its own listing, a Stoke Launch account that only Stoke Launch can sign with.
verify_coin_hook_callchecks it, so nobody else can call your hook. Hooks are stateless in this version: everything they need arrives in the arguments. - Stay small and cheap. A hook runs inside every trade of its coins. The first two hooks are about 20 KB each and add about 13,000 compute units to a trade.
- Never fail by accident. An error from
on_traderefuses the trade. Check settings inon_attach, and treat anything unexpected inon_tradeas "leave the trade as it is". - Getting listed. Send the source to @use_stoke on X. A hook is listed once Stoke has read it, built it and tested it, and its settings can be shown to traders in plain words. The program has to be immutable, or upgradeable by Stoke's admin only: the launch program refuses to list one whose code someone else could still change.
On-chain
Everything lives in Stoke Launch (CAcC7pz43rLg14jAE8iR3w3sgXcd2GGtoeFWUBB1SGVq).
| Account | Seeds | Holds |
|---|---|---|
| Hook listing | ["hook-listing", hook program] | A hook creators can add: its program, whether it's paused, and how many coins launched with it. |
| Coin hook | ["coin-hook", launch] | A coin's hook: the program, its listing, and the creator's settings. |
| Instruction | Who | Does |
|---|---|---|
| attach_hook | Creator | Adds a listed hook to the coin with their settings. In the launch transaction only, before any trade. |
| list_hook | Admin | Makes a hook program available to creators. Refused unless the program is immutable or upgradeable by the admin only. |
| set_hook_paused | Admin | Pauses a listed hook (its coins trade without it) or unpauses it. |
A coin with a hook publishes three more accounts in its pool's hook account list (["hook-accounts", pool]): its coin hook account (writable), the hook's listing and the hook program. Every swap of it has to pass them, in any order after the launch account, or it fails with HookAccountsMissing. Clients that already read that list, as Stoke v1 for terminals describes, need no change. Events: HookListed, HookPaused, HookAttached.
One rule for anyone trading a hooked coin's pool directly: use the amount limit for slippage, not the price limit. When the trader fixes the SOL side, the fee is taken up front on all of it, so a swap its price limit cuts short fails with SwapCutShort instead of paying a fee on SOL that never traded.