> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Token-2022 support matrix

> Which Raydium programs support Token-2022, the explicit extension allow-list each program enforces at pool creation, and the one remaining bypass — the admin-managed per-mint registry PDA — that admits otherwise-rejected mints.

<Info>
  Raydium does **not** accept arbitrary Token-2022 mints. CPMM and CLMM both run a strict **allow-list mode**: only a small set of extensions pass by default; everything else is rejected at pool creation. Each program admits an individual mint through one mechanism — an admin-managed per-mint registry PDA. Both programs also carried hardcoded bypasses at various points; all of those have now been deleted. This page is the single-page reference for what's enforced and where, with citations into the program source.
</Info>

## Program-level support

| Program         | Token-2022 base/quote mints?              | Mode                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| --------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **CPMM**        | Yes — gated                               | Strict extension allow-list + admin per-mint registry. Its 4-mint static whitelist was removed in the 2026-09 upgrade.                                                                                                                                                                                                                                                                                                                                     |
| **CLMM**        | Yes — gated                               | Strict extension allow-list + admin per-mint registry. Its 6-mint static whitelist and its Superstate detection were both removed in July 2026.                                                                                                                                                                                                                                                                                                            |
| **AMM v4**      | No                                        | Program predates Token-2022; both mints must be classic SPL Token.                                                                                                                                                                                                                                                                                                                                                                                         |
| **Stable AMM**  | No                                        | Same constraints as AMM v4.                                                                                                                                                                                                                                                                                                                                                                                                                                |
| **Farm v6**     | Yes, partial                              | Reward mint can be Token-2022 (subject to the same allow-list when a farm wraps a CPMM/CLMM LP); user-stake LP mints inherit from the wrapped pool.                                                                                                                                                                                                                                                                                                        |
| **LaunchLab**   | Yes — base program-managed, quote ungated | Base mint can be Token-2022, but only via the dedicated `initialize_with_token_2022` instruction; the program itself creates the mint with `MetadataPointer` (always) and optionally `TransferFeeConfig` (rate ≤ 5%). Pre-existing Token-2022 mints with arbitrary extensions cannot be used as the base. The **quote** mint may be any Token-2022 mint an admin binds to a `GlobalConfig`, with no extension check — see [below](#launchlab-quote-mints). |
| **Burn & Earn** | Mirrors pool program                      | Inherits CPMM / CLMM gating.                                                                                                                                                                                                                                                                                                                                                                                                                               |

The allow-list checks live in:

* **CPMM**: `is_supported_mint` in [`raydium-cp-swap/programs/cp-swap/src/utils/token.rs`](https://github.com/raydium-io/raydium-cp-swap/blob/master/programs/cp-swap/src/utils/token.rs), called from `Initialize` and `InitializeWithPermission`.
* **CLMM**: `is_supported_mint` in [`raydium-clmm/programs/amm/src/util/token.rs`](https://github.com/raydium-io/raydium-clmm/blob/master/programs/amm/src/util/token.rs), called from `CreatePool`.
* **LaunchLab**: `initialize_with_token_2022` in `raydium-launchpad/programs/launchpad/src/instructions/initialize_with_token_2022.rs` (source not publicly available).

There is **no** swap-time mint check on CPMM or CLMM — the gate fires only at pool creation. Once a pool exists, swaps just trust that the mints didn't change, which is correct for the immutable parts of Token-2022 mint state.

## CLMM position NFT freezing for restricted issuers

Position NFT accounts remain unfrozen by default. CLMM freezes one only when the position uses a V2 open path **and** at least one vault mint's current `freeze_authority` appears in the hardcoded `frozen_position_nft_authorities::IDS` list. That list is what replaced CLMM's earlier Superstate-token detection, and it carries the same issuer authority the old heuristic matched on. Setting the pool PDA as the position NFT mint's freeze authority is not itself a freeze. This is a position-custody rule, not a pool-creation allow-list:

* The pool can already exist and remain swappable.
* The position NFT mint uses the CLMM pool PDA as its own freeze authority; the underlying issuer does not control the position NFT.
* `OpenPositionV2` covers classic SPL position NFTs over Token-2022 pool assets. `OpenPositionWithToken22Nft` covers Token-2022 position NFTs.
* `OpenPosition` V1 does not inspect vault mints and cannot serve the Token-2022 restricted assets targeted by the shipped list.
* Existing positions are unchanged.

Frozen positions remain manageable by their owner but cannot be transferred. `ClosePosition` thaws and burns atomically when the client passes the pool as the first remaining account. See [`products/clmm/ticks-and-positions`](/products/clmm/ticks-and-positions#restricted-issuer-positions).

## LaunchLab quote mints

LaunchLab's two mints are gated very differently, and the asymmetry is easy to miss.

* **Base mint** — LaunchLab creates it. A Token-2022 base mint is only reachable through `initialize_with_token_2022`, and the program will only attach `MetadataPointer` and (optionally) `TransferFeeConfig`. Anything else returns `NoSupportExtension`. A pre-existing Token-2022 mint cannot be supplied as the base at all. When `TransferFeeConfig` is attached, the mint's `transfer_fee_config_authority` is the launch authority PDA until graduation, while its `withdraw_withheld_authority` is the platform's configured `transfer_fee_extension_auth` from mint creation onward — the launchpad program itself has no withdraw-withheld instruction. See [`products/launchlab/platform-config`](/products/launchlab/platform-config#token-2022-transfer-fee-authorities).
* **Quote mint** — LaunchLab does not create it and does not screen it. `CreateConfig` accepts the mint account as given, so **there is no `is_supported_mint` equivalent on the quote side**. The only gate is which mints an admin chooses to bind to a `GlobalConfig`.

That makes binding a Token-2022 quote mint a high-trust action, for the same reason registering a mint below is: a `TransferHook` quote mint would run its hook on every buy, sell, and fee claim in every pool quoted in it, and a `PermanentDelegate` quote mint would let the delegate sweep those pools' quote vaults. Neither is blocked by the program.

What LaunchLab does handle correctly once a quote mint is bound:

* The quote vault, both fee vaults, and the share-fee receiver's token account are created on the quote mint's own program.
* `TransferFeeConfig` on the quote side is priced into all four trade instructions, and the slippage bound is checked against the payer's net amount rather than the vault's gross movement. See [`products/launchlab/instructions`](/products/launchlab/instructions#quote-side-transfer-fees).
* `PoolState.token_program_flag` records both mints' programs — bit0 for the base mint, bit1 for the quote mint. Decode it per bit; the byte is not a boolean. See [`products/launchlab/accounts`](/products/launchlab/accounts#poolstate).

The deprecated `Initialize` instruction still takes a legacy-only quote program, so a config with a Token-2022 quote mint is reachable only through `InitializeV2` and `InitializeWithToken2022`.

## CPMM and CLMM extension allow-list

After the two short-circuits covered [below](#the-one-bypass-the-per-mint-registry), the program iterates the mint's extensions and rejects the mint if it carries **any** extension other than these five:

| Extension               | Reason it's allowed                                                                                                                                                      |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `TransferFeeConfig`     | Pool math subtracts the inbound fee; the Token-2022 program handles the outbound fee. See [`algorithms/token-2022-transfer-fees`](/algorithms/token-2022-transfer-fees). |
| `MetadataPointer`       | Decorative — points at on-chain metadata.                                                                                                                                |
| `TokenMetadata`         | Decorative — inline metadata.                                                                                                                                            |
| `InterestBearingConfig` | Pool sees the principal amount; UI multiplier is decorator-only and the underlying balance is preserved.                                                                 |
| `ScaledUiAmount`        | Same shape as interest-bearing — scale factor applies to UI display only.                                                                                                |

Anything not in this list — `TransferHook`, `NonTransferable`, `ConfidentialTransferMint`, `PermanentDelegate`, `MintCloseAuthority`, `DefaultAccountState`, `GroupPointer`, `GroupMemberPointer`, `MemberPointer`, `Pausable`, etc. — causes `is_supported_mint` to return `false` and pool creation to revert.

The relevant lines (CPMM, identical shape in CLMM):

```rust theme={null}
for e in extensions {
    if e != ExtensionType::TransferFeeConfig
        && e != ExtensionType::MetadataPointer
        && e != ExtensionType::TokenMetadata
        && e != ExtensionType::InterestBearingConfig
        && e != ExtensionType::ScaledUiAmount
    {
        return Ok(false);
    }
}
Ok(true)
```

— [`cp-swap/src/utils/token.rs`](https://github.com/raydium-io/raydium-cp-swap/blob/master/programs/cp-swap/src/utils/token.rs)

## Bypass paths

A Token-2022 mint that doesn't fit the allow-list can still be admitted, through one mechanism in each program. Both programs also carried hardcoded bypasses in the past; none of them survive.

| Bypass                               | CPMM                          | CLMM                          |
| ------------------------------------ | ----------------------------- | ----------------------------- |
| Per-mint registry PDA                | Yes — the only bypass         | Yes — the only bypass         |
| Static `MINT_WHITELIST`              | Removed 2026-09 (4 addresses) | Removed 2026-07 (6 addresses) |
| Superstate authority-shape detection | Never had one                 | Removed 2026-07               |

`is_supported_mint` is now byte-for-byte the same function in both programs: legacy SPL Token mints pass, a mint with a registry PDA passes, and everything else must carry only allow-listed extensions.

## The one bypass: the per-mint registry

Both programs consult a `SupportMintAssociated` PDA at seed `[b"support_mint", mint]`. If that PDA exists for the mint, the mint is admitted regardless of its extension set.

Each program has its own copy of the PDA (they derive under different program IDs), its own `CreateSupportMintAssociated` / `CloseSupportMintAssociated` pair, and its own dedicated authority alongside the shared admin:

| Program | Dedicated support-mint authority (mainnet)    | Dedicated support-mint authority (devnet)      |
| ------- | --------------------------------------------- | ---------------------------------------------- |
| CPMM    | `Rayv2LG4tFSMizZhMP8aSUYxDPjV8qJtx2NQY9RKYZy` | `DRaypyeDL6y1dUusMgwyeDM5JebjhsSi8aRXobKQ9DcQ` |
| CLMM    | `RayVyjyJQz9vAi126A4sGexKnSU1XeZaHTRcM1mZMPY` | `rayf3nEbb3bnfN6RDGFpqPbjc5uUa3tRUzu6UVYrRx5`  |

In both programs the instruction accepts either `crate::admin::ID` or that program's dedicated authority, and requires the mint to be owned by Token-2022. Effect: a specific Token-2022 mint can be opted into pool creation without a program upgrade — which is why the hardcoded lists could go.

Each program consults the registry from **every** pool-creation path it has: CPMM from `Initialize` and `InitializeWithPermission` (the latter is what LaunchLab graduations use, so a registered mint graduates as well as it creates), CLMM from `CreatePool`, `CreateCustomizablePool` and `CreatePermissionedPool`.

## The removed bypasses

Both hardcoded mechanisms are gone from the deployed programs. They are documented here only because integrations written against the older behaviour may still assume them.

### Static `MINT_WHITELIST` — removed

A constant array of base58 mint addresses used to short-circuit `is_supported_mint` before the extension iteration. CLMM's held six addresses and was deleted on 2026-07-24; CPMM's held the first four of the same set and was deleted in the [2026-09-09 upgrade](/reference/changelog/2026-09-09-cpmm-anchor-1-and-excess-lamports).

| Formerly whitelisted in | Mints                                                                                                                                                                                          |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CPMM and CLMM           | `HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM`, `Crn4x1Y2HUKko7ox2EZMT6N2t2ZyH7eKtwkBGVnhEq1g`, `FrBfWJ4qE5sCzKm3k3JaAtqZcXUh4LvJygDeketsrsH4`, `2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo` |
| CLMM only               | `DAUcJBg4jSpVoEzASxYzdqHMUN8vuTpQyG2TvDcCHfZg`, `AUSD1jCcCyPLybk1YnvPWsHQSrZ46dxwoMniN4N2UEB9`                                                                                                 |

**A pool that already exists for one of these mints keeps trading** — the mint check runs only at pool creation. Creating a *new* pool for one now requires a registry PDA for it instead.

### Superstate authority-shape detection — removed

CLMM briefly identified Superstate's tokenized assets by their authority shape rather than by address: a Token-2022 mint whose `freeze_authority` **and** permanent delegate both equalled `superstate_allowlist::ID`, with `DefaultAccountState` set to `Frozen`, was admitted. It was a heuristic, so any future mint with the same shape would have been admitted automatically.

It was deleted on 2026-07-31, together with the `superstate_allowlist` module. What replaced it is narrower and serves a different purpose: `frozen_position_nft_authorities::IDS`, which does not admit anything — it decides whether a **position NFT** gets frozen, and is described [above](#clmm-position-nft-freezing-for-restricted-issuers). The one issuer authority the old heuristic matched is the one entry in that list.

## What the bypasses do not waive

The bypasses skip the extension allow-list, but the program still enforces:

* The mint is owned by either `Token` or `Token-2022`. A custom token program is rejected upstream.
* The pool vaults are created with the right ATA extensions for Token-2022 pools (`ImmutableOwner`, etc.).
* All transfers go through `transfer_checked` — fee-bearing mints land the right amount in vault.

A whitelisted or PDA-registered mint that, e.g., adds a `TransferHook` later does not gain a swap-time check; the hook would simply run on every transfer and could brick swaps. Registering a mint is therefore a high-trust action.

## "Blocked" semantics

When `is_supported_mint` returns `false`, pool creation reverts with `ErrorCode::NotSupportMint` (CPMM) / `ErrorCode::NotSupportMint` (CLMM). See [`reference/error-codes`](/reference/error-codes) for the numeric codes.

Existing pools cannot retroactively fail this check — the gate runs only at creation. Mint extensions are immutable for the categories Raydium rejects (transfer hook, non-transferable, confidential transfer can't be added post-creation), so the static check is sufficient.

## Why each excluded extension is excluded

* **TransferHook** — invokes a custom program on every transfer, with arbitrary CU consumption, arbitrary failure conditions, and the ability to reenter the calling program. No safe sandbox exists. Some DEXes maintain hook allow-lists; Raydium does not.
* **NonTransferable** — `Transfer` always fails. A pool cannot take custody.
* **ConfidentialTransfer** — transfer amounts are encrypted; the curve cannot price the swap.
* **PermanentDelegate** — a holder of the delegate can sweep any token account, including the pool vault. Admissible only by registering the mint, which is how a trusted issuer (e.g. a regulated stablecoin) is onboarded case by case.
* **MintCloseAuthority** — the mint can be closed; existing pools become unusable. Disallowed by default.
* **DefaultAccountState (Frozen)** — pool ATAs would land in `Frozen` state and require thawing per account. Admissible only by registering the mint, which assumes the issuer thaws institutional accounts on enrollment.
* **Group/Member pointers** — not actively harmful, but unreviewed. Disallowed by default to keep the surface narrow.

## Transfer-fee accounting

For mints carrying `TransferFeeConfig`, every swap, deposit, and withdraw moves less than the nominal amount. Two separate numbers are involved, and the SDK keeps them apart:

* **The pool fee** (LP + protocol + fund + creator) comes from the curve. `raydium.cpmm.computeSwapAmount({ ... })` returns it as `fee`, alongside `amountIn`, `amountOut`, `minAmountOut`, `executionPrice`, `priceImpact` and the raw `swapResult`.
* **The Token-2022 transfer fee** comes from the mint, not the pool. It is computed by the `getTransferAmountFee` helpers in `@raydium-io/raydium-sdk-v2`, which return a `GetTransferAmountFee`:

```ts theme={null}
export interface GetTransferAmountFee {
  amount: BN;                  // the amount that actually moves
  fee: BN | undefined;         // withheld by the mint; undefined when the mint has no TransferFeeConfig
  expirationTime: number | undefined;  // set while a scheduled fee change is pending
}
```

Deposit and withdraw surface it directly: `computePairAmount` returns `inputAmountFee` and `anotherAmount` as `GetTransferAmountFee` values.

A correct UI shows:

* the input amount **plus** its transfer `fee` as "you send"
* the output amount **minus** its transfer `fee` as "you receive"
* the pool `fee` as a separate line — it is not the Token-2022 fee

A naive UI that shows only `amountIn → amountOut` understates costs. Pass `epochInfo` from the cluster into these helpers rather than caching it; the epoch is what selects between a mint's `older` and `newer` fee config.

### `maximumFee` cap

Token-2022 transfer fees are capped per transfer. For a 1 % mint with a 10,000-token cap, a 100,000,000-token transfer pays only 10,000 in fee. The SDK's `computeSwapAmount` applies the cap; direct program callers must replicate it.

### Epoch transition

A mint authority can schedule a fee-rate change that activates at the next epoch. During the transition window, two configs (`older`, `newer`) live on the mint at once and `TransferChecked` selects by current epoch. CPMM `SwapV2` and CLMM `SwapV2` both pass the full mint account in `accounts`, so the program reads the right config without an extra lookup.

If you quote more than one epoch in advance via the Trade API or SDK, executed fee can differ from quoted fee — bounded by the older config's `maximum_fee_basis_points`.

## Interest-bearing and ScaledUiAmount

The pool holds the principal amount; the "UI amount" is the principal multiplied by a time-dependent or admin-set scale factor. Swap math operates on principal:

```
principal_in = ui_amount_in / ui_multiplier(now)
```

The SDK converts automatically. Direct RPC readers should treat `pool.token0Vault.amount` as principal.

## "Token-2022 pool" definition

A pool is a Token-2022 pool if **either** mint has `programId == TokenzQdB...`. The API surfaces this:

```
GET /pools/info/ids?ids=<POOL_ID>
```

```json theme={null}
{
  "data": [{
    "mintA": { "programId": "TokenzQdB...", "hasTransferFee": true, ... },
    "mintB": { "programId": "Tokenkeg...", ... }
  }]
}
```

Use `programId` to dispatch, and `hasTransferFee` to surface a UI warning.

## SDK helpers

```ts theme={null}
import { Raydium, TOKEN_2022_PROGRAM_ID } from "@raydium-io/raydium-sdk-v2";
import { ExtensionType, getExtensionTypes, unpackMint } from "@solana/spl-token";

const raydium = await Raydium.load({ owner, connection });

const acct = await connection.getAccountInfo(mintPubkey);
if (!acct) throw new Error("mint not found");
const mint = unpackMint(mintPubkey, acct, acct.owner);

if (acct.owner.equals(TOKEN_2022_PROGRAM_ID)) {
  const extensions = getExtensionTypes(mint.tlvData);
  const accepted = new Set([
    ExtensionType.TransferFeeConfig,
    ExtensionType.MetadataPointer,
    ExtensionType.TokenMetadata,
    ExtensionType.InterestBearingConfig,
    ExtensionType.ScaledUiAmount,
  ]);
  const rejecting = extensions.filter(e => !accepted.has(e));
  if (rejecting.length) {
    console.warn("Pool creation will revert unless this mint is whitelisted:", rejecting);
  }
}
```

## Common integration mistakes

* **Pre-flighting only the program ID.** A mint can be Token-2022 *and* unsupported. Walk the extension list against the allow-list, and check for the mint's registry PDA, before allowing pool creation.
* **Trusting the SDK's quote when the mint isn't accepted at all.** The quote API doesn't refuse to quote — pool creation is what reverts. Confirm `is_supported_mint` semantics off-chain before exposing pool creation in your UI.
* **Quoting without the transfer-fee haircut.** A 1% transfer-fee mint on both sides of a 0.25% CPMM pool has an effective fee around 2.25%, not 0.25%. Use the SDK quote or Trade API quote — never compute fee manually from the pool's fee tier alone.
* **Calling the legacy `Swap` instruction on a Token-2022 pool.** `Swap` predates Token-2022. Use `SwapV2` whenever either mint is Token-2022.
* **Auto-listing new Token-2022 mints.** Wallets and aggregators should check for `TransferHook` and `NonTransferable` before surfacing a mint to users; both are Raydium-hostile.

## Future work

Solana ecosystem and protocol roadmap items that would change this matrix:

* Allow-listed transfer-hook programs at the Solana level (ecosystem convention evolving).
* Confidential-transfer-compatible AMMs (research stage).
* Broader CPMM per-mint registry (parity with CLMM).
* A public read path for the registry, so a UI can tell "unsupported" from "unsupported but registered" without deriving the PDA itself.

This page will be updated when any land.

## Pointers

* [`algorithms/token-2022-transfer-fees`](/algorithms/token-2022-transfer-fees) — fee math in swaps.
* [`products/cpmm/instructions`](/products/cpmm/instructions) — `SwapV2`, `Initialize`.
* [`products/clmm/instructions`](/products/clmm/instructions) — `SwapV2`, `CreatePool`, `CreateSupportMintAssociated`.
* [`reference/error-codes`](/reference/error-codes) — `NotSupportMint` numeric codes per program.

Sources:

* `raydium-cp-swap/programs/cp-swap/src/utils/token.rs` — `is_supported_mint`, `support_mint_associated_is_initialized`.
* `raydium-clmm/programs/amm/src/util/token.rs` — `is_supported_mint`, `support_mint_associated_is_initialized`, `frozen_position_nft_authorities`, `position_nft_must_freeze`.
* `raydium-clmm/programs/amm/src/instructions/admin/create_support_mint_associated.rs` — per-mint registry instruction.
* `raydium-launchpad/programs/launchpad/src/instructions/initialize_with_token_2022.rs` — LaunchLab Token-2022 base-mint creation.
* `raydium-launchpad/programs/launchpad/src/instructions/admin/create_config.rs` — LaunchLab quote-mint binding (no extension check).
