Solana Transfer Hooks and the Accounts You Must Pass

A hook turns every transfer into a CPI to someone else's program — which means extra accounts, extra compute, and a revert path you do not control.

BoltTx Team··9 min read
solanatransfer-hooktoken-2022cpicomputetransaction-landing

The transfer fee extension changes an amount. ★The transfer hook extension changes what a transfer is.★

Every transfer of a hooked token calls out to a third-party program, inside your transaction, with logic the token author wrote and you cannot see from the instruction list.

What a Hook Actually Does

your swap instruction
  └─ token program: transfer
      └─ ★hook program: whatever the author wrote★
          └─ can read accounts, enforce rules, ★revert your transaction★

★The hook runs on every transfer, and it can fail.★ When it does, your transaction reverts — for a reason that appears in the logs under a program you never referenced.

Common legitimate uses: allowlists, transfer limits, on-chain royalty enforcement, compliance checks. ★All of them share the property that your transaction now depends on someone else's code.★

The Accounts Problem

This is where hooked tokens break bots that otherwise handle Token-2022 correctly.

★A program cannot invent accounts for a CPI.★ Every account the hook touches must be present in your original transaction — but you do not know what those accounts are, because the hook author decided them.

The extension solves this with an on-chain list:

import { getExtraAccountMetaAddress } from "@solana/spl-token";

// ★The hook program publishes which extra accounts it needs.★
const metaListPda = getExtraAccountMetaAddress(mint, hookProgramId);
const metaAccount = await connection.getAccountInfo(metaListPda);

The SDK can assemble this for you:

import { createTransferCheckedWithTransferHookInstruction } from "@solana/spl-token";

const ix = await createTransferCheckedWithTransferHookInstruction(
  connection, source, mint, destination, owner,
  amount, decimals, [], "confirmed",
  TOKEN_2022_PROGRAM_ID,
);

★That call reads the extra account meta list from chain and appends the accounts automatically★ — which is why it is async, unlike every other instruction builder.

Building the instruction by hand is where this goes wrong. A plain createTransferCheckedInstruction on a hooked mint compiles fine, submits fine, and reverts with NotEnoughAccountKeys naming an account you have never heard of.

If your hook handling is correct and transactions still miss, a free BoltTx key is one line to test the submission path.

The Costs You Inherit

Compute. The hook consumes units from your transaction's budget. ★You are paying for code you did not write and cannot measure in advance★ — the only way to size it is to simulate the actual transfer.

const sim = await connection.simulateTransaction(tx, {
  replaceRecentBlockhash: true, sigVerify: false,
});
const limit = Math.ceil((sim.value.unitsConsumed ?? 200_000) * 1.3);

★A wider margin than usual is justified here★, because the hook's consumption can vary with its own internal state — an allowlist lookup on a longer list costs more than on a shorter one.

Transaction size. Extra accounts at 32 bytes each, against the 1232-byte limit. A multi-hop swap already near the ceiling can be pushed over it by a hook's account requirements.

CPI depth. The hook adds a level. Your instruction, the aggregator, the pool program, the token program, then the hook — ★that fills all four nested levels, and a hooked token inside a deeper aggregator route can exceed the ceiling.★

Detecting a Hook Before You Trade

import { getTransferHook, getMint } from "@solana/spl-token";

const mint = await getMint(connection, mintAddress, "confirmed", TOKEN_2022_PROGRAM_ID);
const hook = getTransferHook(mint);

if (hook && !hook.programId.equals(PublicKey.default)) {
  // ★This token calls out on every transfer.★
}

★A hook configured with the default (all-zero) program ID is effectively disabled★, which is a distinction worth handling — the extension can be present while doing nothing.

What to check beyond existence:

Who holds the authority? The hook program can be changed after launch by whoever controls it. ★A token that is fine to trade today can have a hook pointed at different code tomorrow.★

Is the hook program verifiable? If its source is not published, you are executing unknown logic inside your transaction on every trade.

Does it revert selectively? A hook can allow buys and block sells. That is indistinguishable from a normal transfer until you try to exit.

Why Simulation Matters More Here

For most Token-2022 features, reading the mint configuration tells you what will happen. ★For hooks it does not — the hook is arbitrary code, and only running it reveals the behaviour.★

// ★Simulate a small transfer in both directions before committing size.★
const buySim  = await connection.simulateTransaction(buyTx,  {...});
const sellSim = await connection.simulateTransaction(sellTx, {...});

Simulating the sell is the part people skip, and it is the one that matters. A hook that permits acquisition and blocks disposal produces a position you cannot exit — and the buy simulation would have looked perfectly healthy.

When to Simply Skip the Token

★Not every token is worth the integration effort, and a hook is a reasonable filter.★

Skip when: the hook program is unverified, the authority is a single unknown key, or a sell simulation fails while a buy succeeds.

Handle when: the hook is from a known protocol, the source is published, and both directions simulate cleanly.

For a bot trading new launches at speed, ★the honest answer is usually to skip★ — the extra accounts, extra compute, and unknown revert conditions cost more time than the opportunity is worth, and the failure mode is holding something you cannot sell.

What Landing Looks Like

Real transactions through our delivery nodes: median confirmation 336ms — under one slot.

★A hook revert lands and costs you the base fee, exactly like any other revert.★ Since the cause is in a program you do not control, simulation is the only place to catch it cheaply.

Where BoltTx Fits

We handle submission. Whether a token has a hook, and what accounts it needs, are properties of your instruction construction.

Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so a transaction is not observable in transit before it lands. We never modify transaction contents, which includes never adding or removing accounts from your instructions.

You sign locally. We never hold funds and never sign. The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.

Get a free API key. No monthly fee:

const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");

FAQ

What is a Solana transfer hook? A Token-2022 extension that calls a third-party program on every transfer of that token. The hook can read accounts, enforce rules, and revert your transaction.

Why does my transfer fail with NotEnoughAccountKeys? The hook needs extra accounts that your instruction did not include. Use createTransferCheckedWithTransferHookInstruction, which reads the required accounts from chain and appends them.

How do I know which accounts a transfer hook needs? They are published on chain in an extra account meta list, derived from the mint and hook program. The SDK helper resolves it for you, which is why that builder is async.

How do I detect whether a token has a transfer hook? Call getTransferHook on the mint. Note that a hook configured with the default all-zero program ID is effectively disabled, so check the program ID rather than just presence.

Does a transfer hook cost compute? Yes, from your transaction's budget. Since the consumption depends on the hook's own logic and state, simulate the actual transfer and use a wider margin than usual.

Can a transfer hook block me from selling? Yes. A hook can permit buys and reject sells, which is indistinguishable from a normal token until you try to exit. Simulate the sell before taking a position.

Do transfer hooks affect CPI depth? Yes, they add a level. Inside an aggregator route that already nests several programs, a hooked token can exceed the four-level nesting ceiling.

Can the hook program be changed after launch? If an authority is set, yes. A token that trades cleanly today can have its hook repointed at different code, so the authority matters as much as the current program.

Should my bot support transfer hook tokens? Only when the hook is from a known protocol with published source and both trade directions simulate cleanly. For fast launch sniping, skipping is usually the better trade.

Why is the transfer hook instruction builder async? Because it reads the extra account meta list from chain to determine which accounts to append. Other instruction builders are pure functions and need no network access.

What happens if the hook program reverts? Your entire transaction reverts, since it is atomic. It landed, changed nothing, and cost the base fee — with the failure appearing under a program you never referenced.

Do transfer hooks work with all DEXes? Not universally. Support requires the protocol to assemble the extra accounts, so verify with a small trade rather than assuming a route will handle it.

How much compute margin should I add for a hook? More than the usual twenty percent, since hook cost can vary with its internal state. An allowlist check against a longer list consumes more than against a shorter one.

Can I see what a transfer hook does before trading? Only if the program source is published. Otherwise you are executing unknown logic in your transaction, which is itself a reason to skip the token.

Is a transfer hook the same as a transfer fee? No. A fee changes the amount arriving and is enforced by the token program. A hook runs arbitrary third-party code and can reject the transfer entirely.

How do I test hook handling safely? Simulate both a buy and a sell at small size before committing capital. The sell simulation is the one that reveals a hook designed to trap positions.

Back to all posts