本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
Raydium 不接受任意的 Token-2022 铸币。CPMM 和 CLMM 都运行严格的允许列表模式:默认只有一小组扩展通过;其他所有扩展在池创建时被拒绝。每个程序通过一种机制接纳单个铸币——一个管理员管理的按铸币注册表 PDA。两个程序在不同位置都曾有硬编码的绕过;现在全部已删除。本页是关于强制执行内容和位置的单页参考,并附带程序源代码的引用。
程序级支持
允许列表检查位于:
- CPMM:
raydium-cp-swap/programs/cp-swap/src/utils/token.rs中的is_supported_mint,从Initialize和InitializeWithPermission调用。 - CLMM:
raydium-clmm/programs/amm/src/util/token.rs中的is_supported_mint,从CreatePool调用。 - LaunchLab:
raydium-launchpad/programs/launchpad/src/instructions/initialize_with_token_2022.rs中的initialize_with_token_2022(源代码不公开)。
CLMM 位置 NFT 冻结(受限发行者)
位置 NFT 账户默认保持解冻。CLMM 仅在位置使用 V2 开放路径且至少一个金库铸币的当前freeze_authority 出现在硬编码的 frozen_position_nft_authorities::IDS 列表中时才冻结一个。该列表是替代 CLMM 早期 Superstate 令牌检测的内容,它携带与旧启发式匹配的相同发行者权限。将池 PDA 设置为位置 NFT 铸币的冻结权限本身不是冻结。这是位置托管规则,不是池创建允许列表:
- 池可以已存在并保持可交换。
- 位置 NFT 铸币使用 CLMM 池 PDA 作为其自己的冻结权限;底层发行者不控制位置 NFT。
OpenPositionV2覆盖 Token-2022 池资产上的经典 SPL 位置 NFT。OpenPositionWithToken22Nft覆盖 Token-2022 位置 NFT。OpenPositionV1 不检查金库铸币,无法服务于已发布列表针对的 Token-2022 受限资产。- 现有位置保持不变。
ClosePosition 在客户端将池作为第一个剩余账户传递时原子地解冻和销毁。见products/clmm/ticks-and-positions。
LaunchLab 报价铸币
LaunchLab 的两个铸币受限方式非常不同,这种不对称性很容易被忽视。- 基础铸币——LaunchLab 创建它。Token-2022 基础铸币仅通过
initialize_with_token_2022可达,程序只会附加MetadataPointer和(可选)TransferFeeConfig。任何其他内容都返回NoSupportExtension。预先存在的 Token-2022 铸币根本无法作为基础提供。当附加TransferFeeConfig时,铸币的transfer_fee_config_authority是启动权限 PDA(直到毕业),而其withdraw_withheld_authority是平台从铸币创建开始配置的transfer_fee_extension_auth——launchpad 程序本身没有提取扣留指令。见products/launchlab/platform-config。 - 报价铸币——LaunchLab 不创建它,也不筛选它。
CreateConfig接受给定的铸币账户,所以报价端没有is_supported_mint等价物。唯一的限制是管理员选择绑定到GlobalConfig的铸币。
TransferHook 报价铸币会在每次买入、卖出和费用索取时在每个以它报价的池中运行其钩子,而 PermanentDelegate 报价铸币会让委托人扫空这些池的报价金库。两者都不被程序阻止。
LaunchLab 一旦绑定报价铸币就正确处理的内容:
- 报价金库、两个费用金库和分享费用接收者的令牌账户都在报价铸币自己的程序上创建。
- 报价端的
TransferFeeConfig被计入所有四个交易指令,滑点界限针对支付者的净额而非金库的总移动进行检查。见products/launchlab/instructions。 PoolState.token_program_flag记录两个铸币的程序——bit0 用于基础铸币,bit1 用于报价铸币。按位解码;字节不是布尔值。见products/launchlab/accounts。
Initialize 指令仍接受仅限遗留的报价程序,所以具有 Token-2022 报价铸币的配置仅通过 InitializeV2 和 InitializeWithToken2022 可达。
CPMM 和 CLMM 扩展允许列表
在下面涵盖的两个短路之后,程序迭代铸币的扩展,如果它携带任何不在这五个扩展之外的扩展,则拒绝铸币:
不在此列表中的任何内容——
TransferHook、NonTransferable、ConfidentialTransferMint、PermanentDelegate、MintCloseAuthority、DefaultAccountState、GroupPointer、GroupMemberPointer、MemberPointer、Pausable 等——导致 is_supported_mint 返回 false 并且池创建恢复。
相关行(CPMM,CLMM 中形状相同):
cp-swap/src/utils/token.rs
绕过路径
不符合允许列表的 Token-2022 铸币仍可通过每个程序中的一种机制被接纳。两个程序过去都有硬编码的绕过;现在都不存在。is_supported_mint 现在在两个程序中是字节对字节相同的函数:遗留 SPL Token 铸币通过,具有注册表 PDA 的铸币通过,其他所有内容必须仅携带允许列表扩展。
唯一的绕过:按铸币注册表
两个程序都查询种子为[b"support_mint", mint] 的 SupportMintAssociated PDA。如果该 PDA 对铸币存在,则无论其扩展集如何,铸币都被接纳。
每个程序都有自己的 PDA 副本(它们在不同的程序 ID 下派生)、自己的 CreateSupportMintAssociated / CloseSupportMintAssociated 对,以及自己的专用权限与共享管理员:
四个密钥(CPMM 和 CLMM × 主网和开发网)列在支持铸币注册表权限下。
在两个程序中,指令接受 crate::admin::ID 或该程序的专用权限,并要求铸币由 Token-2022 拥有。效果:特定的 Token-2022 铸币可以选择加入池创建而无需程序升级——这就是硬编码列表可以删除的原因。
每个程序从它拥有的每个池创建路径查询注册表:CPMM 从 Initialize 和 InitializeWithPermission(后者是 LaunchLab 毕业使用的,所以注册的铸币也毕业和创建),CLMM 从 CreatePool、CreateCustomizablePool 和 CreateCustomizablePoolV2。
已移除的绕过机制
两种硬编码机制都已从已部署的程序中移除。之所以在此记录,仅因为针对旧版行为编写的集成可能仍然假设它们存在。静态 MINT_WHITELIST — 已移除
一个 base58 铸币地址常量数组,用于在扩展迭代之前短路 is_supported_mint。CLMM 曾包含六个地址,于 2026-07-24 删除;CPMM 曾包含相同集合中的前四个,于 2026-09-09 升级时删除。
已存在于这些铸币之一的流动性池继续交易 — 铸币检查仅在池创建时运行。现在为其中之一创建新池需要为其注册一个 PDA。
Superstate 权限形状检测 — 已移除
CLMM 曾通过权限形状而非地址来识别 Superstate 的代币化资产:一个 Token-2022 铸币,其freeze_authority 和 永久委托都等于 superstate_allowlist::ID,且 DefaultAccountState 设置为 Frozen,会被接受。这是一种启发式方法,因此任何具有相同形状的未来铸币都会被自动接受。
它于 2026-07-31 与 superstate_allowlist 模块一起删除。替代它的是更窄的机制,用途不同:frozen_position_nft_authorities::IDS,它不接受任何东西 — 它决定头寸 NFT 是否被冻结,详见上文。旧启发式匹配的唯一发行者权限是该列表中的唯一条目。
绕过机制不豁免的内容
绕过机制跳过扩展允许列表,但程序仍然强制执行:- 铸币由
Token或Token-2022拥有。自定义代币程序会在上游被拒绝。 - 池金库使用正确的 ATA 扩展创建,用于 Token-2022 池(
ImmutableOwner等)。 - 所有转账都通过
transfer_checked— 带费用的铸币在金库中落地正确的金额。
TransferHook,不会获得交换时检查;该钩子会在每次转账时运行,可能会破坏交换。因此,注册铸币是一个高信任操作。
“被阻止”语义
当is_supported_mint 返回 false 时,池创建会以 ErrorCode::NotSupportMint(CPMM)/ ErrorCode::NotSupportMint(CLMM)回滚。参见 reference/error-codes 了解数字代码。
现有池无法追溯性地失败此检查 — 门控仅在创建时运行。Raydium 拒绝的类别的铸币扩展是不可变的(转账钩子、不可转账、机密转账无法在创建后添加),因此静态检查是充分的。
每个被排除扩展的排除原因
- TransferHook — 在每次转账时调用自定义程序,具有任意 CU 消耗、任意失败条件,以及重新进入调用程序的能力。不存在安全沙箱。某些 DEX 维护钩子允许列表;Raydium 不维护。
- NonTransferable —
Transfer总是失败。池无法获得托管权。 - ConfidentialTransfer — 转账金额被加密;曲线无法为交换定价。
- PermanentDelegate — 委托的持有者可以扫除任何代币账户,包括池金库。仅通过注册铸币才可接受,这是如何逐案例载入受信发行者(例如受监管稳定币)的方式。
- MintCloseAuthority — 铸币可以关闭;现有池变得不可用。默认不允许。
- DefaultAccountState (Frozen) — 池 ATA 会落地在
Frozen状态,需要按账户解冻。仅通过注册铸币才可接受,这假设发行者在注册时解冻机构账户。 - Group/Member pointers — 不是主动有害的,但未经审查。默认不允许以保持表面狭窄。
转账费用会计
对于携带TransferFeeConfig 的铸币,每次交换、存入和提取都会移动少于名义金额的数量。涉及两个独立的数字,SDK 将它们分开:
- 池费用(LP + 协议 + 基金 + 创建者)来自曲线。
raydium.cpmm.computeSwapAmount({ ... })将其作为fee返回,与amountIn、amountOut、minAmountOut、executionPrice、priceImpact和原始swapResult一起。 - Token-2022 转账费用来自铸币,而非池。它由
@raydium-io/raydium-sdk-v2中的getTransferAmountFee辅助函数计算,返回GetTransferAmountFee:
computePairAmount 返回 inputAmountFee 和 anotherAmount 作为 GetTransferAmountFee 值。
正确的 UI 显示:
- 输入金额加上其转账
fee作为”你发送” - 输出金额减去其转账
fee作为”你接收” - 池
fee作为单独的行 — 它不是 Token-2022 费用
amountIn → amountOut 的天真 UI 会低估成本。将集群中的 epochInfo 传递给这些辅助函数,而不是缓存它;epoch 是选择铸币 older 和 newer 费用配置之间的内容。
maximumFee 上限
Token-2022 转账费用按转账上限。对于 1% 铸币,上限为 10,000 代币,100,000,000 代币转账仅支付 10,000 费用。SDK 的 computeSwapAmount 应用上限;直接程序调用者必须复制它。
Epoch 转换
铸币权限可以计划在下一个 epoch 激活的费率变更。在转换窗口期间,两个配置(older、newer)同时存在于铸币上,TransferChecked 按当前 epoch 选择。CPMM SwapV2 和 CLMM SwapV2 都在 accounts 中传递完整的铸币账户,因此程序读取正确的配置而无需额外查询。
如果你通过 Trade API 或 SDK 提前引用多个 epoch,执行费用可能与报价费用不同 — 受旧配置的 maximum_fee_basis_points 限制。
计息和 ScaledUiAmount
池持有本金金额;“UI 金额”是本金乘以时间相关或管理员设置的缩放因子。交换数学在本金上运行:pool.token0Vault.amount 视为本金。
“Token-2022 池”定义
如果任一铸币具有programId == TokenzQdB...,则池是 Token-2022 池。API 显示这一点:
programId 进行分派,使用 hasTransferFee 显示 UI 警告。
SDK 辅助函数
常见集成错误
- 仅预检程序 ID。 铸币可以是 Token-2022 且不受支持。在允许池创建之前,针对允许列表检查扩展列表,并检查铸币的注册 PDA。
- 当铸币根本不被接受时信任 SDK 的报价。 报价 API 不会拒绝报价 — 池创建是会回滚的。在你的 UI 中公开池创建之前,确认
is_supported_mint语义离链。 - 报价时不考虑转账费用扣除。 一个 0.25% CPMM 池两侧都有 1% 转账费用铸币,有效费用约为 2.25%,而非 0.25%。使用 SDK 报价或 Trade API 报价 — 永远不要仅从池的费用等级手动计算费用。
- 在 Token-2022 池上调用旧版
Swap指令。Swap早于 Token-2022。只要任一铸币是 Token-2022,就使用SwapV2。 - 自动列出新的 Token-2022 铸币。 钱包和聚合器应在向用户显示铸币之前检查
TransferHook和NonTransferable;两者都对 Raydium 不利。
未来工作
Solana 生态系统和协议路线图项目,会改变此矩阵:- Solana 级别的允许列表转账钩子程序(生态系统约定不断演变)。
- 机密转账兼容的 AMM(研究阶段)。
- 更广泛的 CPMM 按铸币注册表(与 CLMM 奇偶)。
- 注册表的公共读取路径,以便 UI 可以判断”不受支持”与”不受支持但已注册”,而无需自己推导 PDA。
指针
algorithms/token-2022-transfer-fees— 交换中的费用数学。products/cpmm/instructions—SwapV2、Initialize。products/clmm/instructions—SwapV2、CreatePool、CreateSupportMintAssociated。reference/error-codes— 每个程序的NotSupportMint数字代码。
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— 按铸币注册表指令。raydium-launchpad/programs/launchpad/src/instructions/initialize_with_token_2022.rs— LaunchLab Token-2022 基础铸币创建。raydium-launchpad/programs/launchpad/src/instructions/admin/create_config.rs— LaunchLab 报价铸币绑定(无扩展检查)。

