跳转到主要内容

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.

本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
Solana 上的每一种可交易资产(包括每个 Raydium 池的基础资产和报价资产)都是由两个程序之一铸造的代币:遗留的 SPL Token 程序或其继任者 Token-2022。它们是在不同地址的独立程序,具有不同的账户布局和扩展语义。Raydium 支持两者,但并非处处都支持:CPMM、CLMM 和 Farm v6 接受 Token-2022 铸币;AMM v4 则不接受。在与任何池集成前,理解这种分化至关重要。

两个程序

SPL TokenToken-2022
程序 IDTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DATokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
发布时间20202022
账户大小(代币账户)165 B165 B + 扩展(可变)
扩展是 —— 17+ 官方扩展
遗留兼容性完全按铸币可选
两者均由 Solana Labs 团队(现为 Anza)维护,并存放在 solana-program-library 仓库下。

为何有两个程序?

SPL Token 被冻结以保证向前兼容性 —— 其字节码实际上是不可变的,为整个生态系统提供了一个清晰的基线。随着用例的增长(稳定币需要转账费、机构铸币需要精细化的冻结权限、NFT 需要元数据指针),Solana 团队引入了 Token-2022 作为一个独立的、可扩展的程序,而非升级 SPL Token。这保留了现有集成,并让每个铸币只需选择它需要的确切扩展。 Token-2022 在功能上是严格的超集,但在地址空间上不是:两个程序共存,给定地址的铸币恰好属于其中之一。

账户结构

铸币账户

定义代币的身份。 SPL Token 铸币(82 字节):
u32   mint_authority_option
Pubkey mint_authority
u64   supply
u8    decimals
bool  is_initialized
u32   freeze_authority_option
Pubkey freeze_authority
Token-2022 铸币:相同的基础布局,加上零个或多个追加在基础后的 扩展 TLV(类型-长度-值)记录。

代币账户

保有特定铸币对特定所有者的余额。 SPL Token 账户(165 字节):
Pubkey mint
Pubkey owner
u64    amount
u32    delegate_option
Pubkey delegate
u8     state         // initialized, frozen
u32    is_native_option
u64    is_native
u64    delegated_amount
u32    close_authority_option
Pubkey close_authority
Token-2022 账户:相同的基础,加上任何活跃扩展的扩展 TLV 记录。

Token-2022 扩展

扩展是可附加到铸币或账户的模块化特性。每个扩展都是一条单独的 TLV 记录。对 Raydium 至关重要的包括:

转账费

铸币可以对每次转账收取百分比费用。费用流向配置的提取权限持有者。通过 SwapV2 被 Raydium CPMM 和 CLMM 支持 —— 程序在计算汇率时考虑该费用,以便池的数学保持一致。
let extension = TransferFeeConfig {
    transfer_fee_config_authority,
    withdraw_withheld_authority,
    withheld_amount: 0,
    older_transfer_fee: ...,
    newer_transfer_fee: ...,
};

转账钩子

铸币指向一个程序,运行时在每次转账时调用该程序。钩子程序可以拒绝转账或执行副作用(更新合规状态、记录日志等)。Raydium CPMM/CLMM 通过 SwapV2 调用钩子 —— 交易包含钩子程序和它需要的任何额外账户。

生息

链上余额以配置的利率产生利息。仅限显示(余额随时间增加)而非实际铸币;底层供应量不变。

铸币关闭权限

允许在供应量达到零后关闭铸币。

永久委托

指定的钱包可以无条件地从任何账户转账或销毁代币。Raydium 阻止为具有此扩展的铸币创建池 —— 这与”池储备不能被夺取”的不变量不兼容。

不可转账

代币无法从铸造它们的账户移出。Raydium 阻止池创建 —— 不可交易的资产不能作为 LP 池的基础或报价资产。

默认账户状态

此铸币的新代币账户默认被冻结,必须由冻结权限解冻。可用但罕见。

机密转账

余额和转账金额被加密。Raydium 不支持机密转账铸币(池数学需要明文余额)。

元数据指针 + 代币元数据

为 Token-2022 铸币替代 Metaplex 元数据。支持 Raydium 池列表。

组 / 成员指针

声明铸币属于一个组(例如 NFT 集合)。仅供信息使用;Raydium 将其用于显示。 查看官方 Token-2022 扩展页面获取完整列表。

Raydium 产品支持情况

产品SPL TokenToken-2022备注
AMM v4OpenBook 集成需要 SPL Token
CPMMToken-2022 池需要 SwapV2
CLMMToken-2022 池需要 SwapV2
Farm v6支持质押铸币和奖励铸币
LaunchLab毕业的 CPMM 池继承 Token-2022 支持
Raydium 池的铸币资格 —— 除非列出,否则所有扩展都允许:
  • 被阻止:不可转账、永久委托、机密转账、默认账户状态(在被拒配置中)。
  • 条件允许(LP 必须接受风险):转账费、转账钩子、冻结权限活跃。
  • 完全允许:生息、元数据指针、组指针、铸币关闭权限。
getPoolInfoFromRpc 响应包含铸币的扩展标志 —— 客户端在成为 LP 前应检查。

代币账户标准

关联代币账户(ATA)

两个程序共享 关联代币账户 约定:通过关联代币程序 (ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL) 从 [owner, programId, mint] 派生的 PDA。Solana 上几乎每个用户代币账户都是 ATA。
import { getAssociatedTokenAddressSync } from "@solana/spl-token";

// SPL Token
const ata = getAssociatedTokenAddressSync(mint, owner);

// Token-2022
const ata22 = getAssociatedTokenAddressSync(
  mint,
  owner,
  false,               // allowOwnerOffCurve
  TOKEN_2022_PROGRAM_ID,
);
ATA 程序根据哪个程序拥有铸币来创建由相应代币程序拥有的账户。

非 ATA 代币账户

钱包可以为单个铸币拥有多个代币账户;ATA 只是约定。例如,池金库不是 ATA —— 它们是池程序的 PDA,持有池的储备。

检测铸币属于哪个程序

每个铸币的账户都有一个 owner 字段,指向 SPL Token 或 Token-2022:
const mintInfo = await connection.getAccountInfo(mintPubkey);
if (mintInfo.owner.equals(TOKEN_PROGRAM_ID)) {
  console.log("SPL Token mint");
} else if (mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID)) {
  console.log("Token-2022 mint");
}
Raydium SDK 会自动处理此检测 —— getPoolInfoFromRpc 为每个代币返回相应的 programId,以便客户端可以构建正确的 ATA。

按程序划分的交换指令

Raydium 的 CPMM 和 CLMM 各有两个交换指令:
指令支持的铸币
Swap / SwapBaseInput(遗留)仅 SPL Token
SwapV2 / SwapBaseInputV2SPL Token 和 Token-2022
SwapV2 接受额外的账户:两侧的铸币账户、每侧的代币程序(因为它们可能不同),以及 —— 对于转账钩子铸币 —— 钩子程序和其所需账户。 当至少一侧是 Token-2022 时,客户端应始终使用 SwapV2SwapV2 也适用于仅 SPL 的池,但遗留的 Swap 在计算上更便宜。 SDK 会自动选择正确的变体。

将 SPL Token 项目迁移到 Token-2022

Token-2022 在铸币级别上不是即插即用的替代品 —— 地址 X 处的铸币要么是 SPL,要么是 Token-2022,这在创建时就固定了。要”迁移”你必须:
  1. 在 Token-2022 下创建一个新铸币,配置你需要的扩展。
  2. 为旧 SPL 铸币的持有者提供交换/包装机制以换取新铸币。
  3. 更新所有 LP 池、农场和集成以引用新铸币。
这很繁重。大多数在 SPL 下推出的项目除非特定扩展需求强制迁移,否则会保持在 SPL。

实战例子:创建带转账费的 Token-2022 铸币

import {
  Connection, Keypair, SystemProgram, Transaction, sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
  TOKEN_2022_PROGRAM_ID, ExtensionType, createInitializeMintInstruction,
  getMintLen, createInitializeTransferFeeConfigInstruction,
} from "@solana/spl-token";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const payer      = Keypair.generate();
const mint       = Keypair.generate();

const extensions    = [ExtensionType.TransferFeeConfig];
const mintLen       = getMintLen(extensions);
const rentLamports  = await connection.getMinimumBalanceForRentExemption(mintLen);

const tx = new Transaction().add(
  SystemProgram.createAccount({
    fromPubkey:       payer.publicKey,
    newAccountPubkey: mint.publicKey,
    space:            mintLen,
    lamports:         rentLamports,
    programId:        TOKEN_2022_PROGRAM_ID,
  }),
  createInitializeTransferFeeConfigInstruction(
    mint.publicKey,
    payer.publicKey,                  // transfer fee authority
    payer.publicKey,                  // withdraw-withheld authority
    50,                               // 50 bps = 0.5%
    BigInt(1_000_000),                // max fee per transfer (smallest units)
    TOKEN_2022_PROGRAM_ID,
  ),
  createInitializeMintInstruction(
    mint.publicKey,
    9,                                // decimals
    payer.publicKey,                  // mint authority
    null,                             // freeze authority
    TOKEN_2022_PROGRAM_ID,
  ),
);

await sendAndConfirmTransaction(connection, tx, [payer, mint]);
这个铸币可以被 LP 到 Raydium CPMM 池;交换者除了池的交换费外还需支付 0.5% 的转账费。

安全考虑

在 LP 到或通过 Token-2022 铸币交换之前:
  • 检查 freeze_authority。如果非空且由中心化方控制,他们可以冻结你的 ATA(以及可能的池金库)。
  • 检查 transfer_hook。钩子程序可以任意阻止转账 —— 对钩子的源代码进行尽职调查。
  • 检查 transfer_fee。在预期交换输出中考虑费用。
  • 检查 permanent_delegatenon_transferable。Raydium 的程序会拒绝这些,但如果构建自定义集成需验证。
查看 security/oracle-and-token-risks 以了解完整的风险接受框架。

相关指南

参考资料: