Solana's Token-2022 program introduced a feature called extensions — modular add-ons that change how a token behaves. Most of them are useful: metadata pointers, non-transferability for soulbound tokens, mint close authority. One of them is dangerous in the wrong hands: TransferFeeConfig.
A token with TransferFeeConfig deducts a fee on every transfer. Up to 100%. Set by an authority that can change the rate anytime. And most Solana DEX integrations don't handle it correctly — which means a fee-bearing token can quote one price on Dexscreener, then deliver something dramatically different when you actually swap.
This post explains how the extension works, where AMMs break, the empirical evidence from real Solana pool data, and a checklist for spotting these tokens before you trade them.
What TransferFeeConfig actually does
When a Token-2022 mint has TransferFeeConfig, every Transfer instruction on that mint runs through a fee deduction:
- Sender intends to send
N tokens
- Program calculates
fee = min(N × basis_points / 10000, maximum_fee)
- Recipient receives
N - fee
- The
fee accumulates as withheld_amount on each affected account
- The fee authority can later
Harvest the withheld amounts and WithdrawWithheld to a treasury wallet
The fee is structurally part of the token. It's not a contract that runs on transfers — it's a behavior the Token-2022 program enforces at the lowest level. There's no way around it for the holder. Even sending to yourself or your own ATA pays the fee.
The fields stored on the mint (offsets within the TransferFeeConfig extension TLV):
| Field | Type | Purpose |
|---|
transfer_fee_config_authority | OptionalNonZeroPubkey | Can change fee parameters |
withdraw_withheld_authority | OptionalNonZeroPubkey | Can collect accumulated fees |
withheld_amount | u64 | Total fees withheld on the mint itself |
older_transfer_fee | TransferFee | Active fee before the most recent change |
newer_transfer_fee | TransferFee | The fee scheduled to become active next epoch |
Each TransferFee struct has epoch, maximum_fee, and transfer_fee_basis_points. The "newer" fee becomes "older" on the configured epoch, and a fresh newer one can be queued. This is meant for graceful fee changes — but a malicious authority can queue 9999 bps (99.99%) and rugpull holders in one slot.
Why AMMs misprice fee-bearing tokens
Most Solana AMM pools — Raydium, Orca, Meteora, PumpSwap — read vault balances directly via SPL TokenAccount.amount at offset 64 of the 165-byte token account. That works perfectly for regular SPL tokens. For Token-2022 with TransferFeeConfig, it stops being the whole picture.
When liquidity gets deposited to a fee-bearing pool, the deposit transfer itself is fee-taxed. The vault receives N - fee tokens, not N. The pool state records the pre-fee amount; the vault holds the post-fee amount. The discrepancy is the withheld_amount sitting on the vault account, unspendable until harvested.
A naive price calculation says:
price_per_token = quote_reserve / token_reserve
If token_reserve is read as the pre-fee deposit amount but the actual usable balance is the post-fee amount, the price ratio is wrong. Worse, the divergence compounds with every swap, since each swap re-taxes the vault.
We hit this directly while auditing our own market-cap engine. Token 33eum82LaAhtv5YkUq1BdwEviSErH5CnFxqVNLT5pump is a Token-2022 pump.fun graduate. Dexscreener priced it at $0.002 per token; our pool readings were quoting it at $1.30 — a 618× discrepancy. After tracing the mint, the issue turned out to be Meteora's DLMM SDK miscounting the reserve adjustment for the fee-bearing path. (We fixed our integration; not every Solana product has.)
The three failure modes you'll actually encounter
1. Quoted price doesn't match received tokens. You see "1 SOL = 1,000 TOKEN" in the wallet preview, hit swap, receive 800 tokens. Frustrating, but rarely a true rug — the AMM just didn't account for the transfer fee in its quote. The pool state was approximately right; the wallet UI was wrong.
2. Market cap inflates beyond reality. AMM pools quote a price; aggregators multiply by reported supply for "market cap." If the price is wrong by 600×, the market cap is wrong by 600×. We've seen Token-2022 memecoins show up on aggregators with $10M+ "market cap" when the real circulating-supply-times-real-price was under $50k.
3. Aggregator routes into honeypots. A high-fee mint (50%+ bps) can pass naive listing filters because it looks like any other Token-2022 token. Users hit the aggregator, the aggregator routes through the fee-bearing pool, the user gets half (or less) of the expected output. The mint owner harvests withheld fees and wins.
The last one is the genuine danger. Failure modes 1 and 2 are accounting bugs. Failure mode 3 is theft with a regulatory veneer — the token has perfect on-chain transparency about its fee, but the user never sees it.
How to actually check a Token-2022 mint
Before trading any Token-2022 token, run the following:
On Solscan or Solana Explorer:
- Open the mint account page
- Check the
Token Program — if it's TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb, it's Token-2022 (not regular SPL)
- Look for the
Extensions section — TransferFeeConfig is what you're checking for
- Find the active
transfer_fee_basis_points. Anything over 100 (1%) is unusual; anything over 1000 (10%) is hostile
On a programmatic level:
import { Connection, PublicKey } from "@solana/web3.js";
async function getTransferFeeBps(c: Connection, mint: string): Promise<number | null> {
const acc = await c.getAccountInfo(new PublicKey(mint));
if (!acc || acc.data.length < 166) return null; // not Token-2022 with extensions
// TLV starts at offset 166 (1 byte for AccountType marker at offset 165)
let off = 166;
while (off + 4 <= acc.data.length) {
const type = acc.data.readUInt16LE(off);
const len = acc.data.readUInt16LE(off + 2);
if (type === 1) {
// TransferFeeConfig — newer_transfer_fee at end of struct, bps at last 2 bytes
const bps = acc.data.readUInt16LE(off + 4 + len - 2);
return bps;
}
off += 4 + len;
if (type === 0 && len === 0) break;
}
return 0; // Token-2022 but no fee extension
}
If the result is over a few hundred basis points, walk away. If it's null, it's a regular SPL token — no fee to worry about.
On RugCheck and SolSniffer:
Both RugCheck and SolSniffer check for Token-2022 extensions and flag high transfer fees. They're not infallible — particularly for fees that just got increased via the older-to-newer transition — but they catch the obvious traps. If you have not used it before, our walkthrough on how to use RugCheck to verify any Solana token in 30 seconds shows where these extension flags surface in the report.
Worth noting: TransferFeeConfig isn't the only Token-2022 extension that traders should watch for.
TransferHook (extension 14) lets the mint delegate transfer logic to a custom program. That program runs on every transfer and can do arbitrary things — block transfers from specific wallets, charge dynamic fees, redirect funds. Tokens with active transfer hooks are even riskier than fee-bearing tokens because the hook code can change anything.