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 自动翻译,所有内容以英文版本为准。查看英文版 →
PDA(程序派生地址)和 CPI(跨程序调用)是使 Raydium 成为可能的两个基础原语。PDA 让程序能够”拥有”确定的地址而无需私钥——这是资金池授权和金库工作的原理。CPI 让一个程序调用另一个程序——这是 Raydium 如何通过 SPL Token 程序交换代币,以及集成者如何将 Raydium 组合到自己流程中的方式。在阅读 Raydium 源代码之前,值得理解这两者。
PDA:无需密钥的地址
程序派生地址是一个公钥,它:- 不在 ed25519 曲线上(不存在其私钥)。
- 由程序 ID 和一组种子确定派生。
- 只能由派生程序通过
invoke_signed签署。
派生
PDA 通过用种子哈希程序 ID,然后找到一个”bump”字节来强制结果离开曲线而计算得出。第一个产生离曲线地址的 bump(通常从 255 开始并递减)胜出;这就是规范 bump。u64 值作为小端字节。Raydium 的惯例是人类可读的前缀后跟唯一标识符。
Raydium PDA 模式
Raydium 程序中常见的 PDA:| PDA | 种子 | 程序 |
|---|---|---|
| AMM 授权(AMM v4) | [b"amm authority"] + bump | AMM v4 |
| 资金池状态(CPMM) | [b"pool", amm_config, mint_a, mint_b] | CPMM |
| 资金池金库(CPMM) | [b"pool_vault", pool, mint] | CPMM |
| 授权(CPMM) | [b"vault_and_lp_mint_auth_seed"] | CPMM |
| 资金池状态(CLMM) | [b"pool", amm_config, mint_0, mint_1] | CLMM |
| Tick 数组(CLMM) | [b"tick_array", pool, start_tick_index] | CLMM |
| 观察(CLMM) | [b"observation", pool] | CLMM |
| 个人头寸(CLMM) | [b"position", position_nft_mint] | CLMM |
| 农场状态(Farm v6) | [b"pool_farm_state", farm_id] | Farm v6 |
| 用户账本(Farm v6) | [b"user_ledger", farm, user] | Farm v6 |
规范 bump
虽然原则上可能存在多个产生离曲线地址的 bump,Raydium 的程序始终使用规范 bump(通过从 255 递减找到)。这存储在 PDA 的账户数据中,以便后续交易可以传入它并跳过(昂贵的)派生循环:CPI:调用其他程序
跨程序调用让程序在单个交易内内联调用另一个程序的指令。Raydium 广泛使用 CPI:- 交换指令调用 SPL Token 程序来移动代币。
- CLMM 调用 Metaplex 来铸造头寸 NFT。
- 资金池创建调用系统程序来分配账户。
- Farm v6 调用 SPL Token 来转移奖励。
integration-guides/cpi-integration。
invoke 与 invoke_signed
Solana 运行时提供两个 CPI 原语:invoke:调用另一个程序;被调用程序继承外层交易的签署者。invoke_signed:代表 PDA 调用另一个程序;运行时验证 PDA 的种子并授权签名。
invoke_signed 是让程序在不管理私钥的情况下拥有账户权限的魔法。
示例:Raydium 从资金池金库转账
资金池金库是一个代币账户,其权限是资金池程序的 PDA。为了在交换期间转出代币,资金池程序必须作为该 PDA 签署:invoke_signed 由 CPMM 程序调用,验证 vault_and_lp_mint_auth_seed + bump 与 CPMM 程序 ID 一起哈希时派生到 pool_authority 的地址,并允许代币转账上的权限签名。不涉及私钥。
示例:集成者调用 Raydium CPMM
集成者程序(例如,托管合约)可以通过 CPI 调用 Raydium 的swap_base_input:
integration-guides/cpi-integration 获取完整的托管合约示例。
CPI 深度限制
Solana 将 CPI 深度上限设置为 4 级。交易的顶级指令计为深度 0;每次 CPI 调用增加一级深度。 实际影响:Raydium 自己的交换已经使用 1-2 级 CPI(Raydium → SPL Token)。集成者调用 Raydium 使用 2 级。如果该集成者被另一个集成者调用,则为 3 级。第 4 级是限制。 大多数组合都轻松保持在此下,但深层嵌套(聚合器→路由器→Raydium→钩子)可能会触发它。设计应该扁平而非深层。剩余账户
当 Raydium 指令需要可变数量的账户(例如,CLMM 交换跨越未知数量的 tick 数组)时,额外账户作为剩余账户传递——附加到固定账户列表,按位置解释。 CPMM 的SwapV2 使用剩余账户来存放转账钩子程序的额外所需账户。客户端获取所需账户并附加它们:
PDA 陷阱
错误的种子 → 错误的地址
种子顺序错误、编码错误或包含/排除额外字节的 bug 会默默产生不同的 PDA。交易以模糊的方式失败(程序尝试读取不存在的账户)。始终对照已知的黄金值进行单元测试种子派生。不存储 bump
如果你在每笔交易上重新派生 bump,你会为派生循环支付计算费用。在 PDA 的数据中存储规范 bump 并从那里读取它。混淆规范 vs 非规范 bump
非规范 bump(如果有人找到产生离曲线的)被invoke_signed 允许但被 Raydium 的程序通过 assert_eq!(bump, canonical_bump) 拒绝。如果有人尝试使用非规范 bump 声称 PDA,tx 失败。
当你不是拥有程序时将 PDA 作为签署者传递
只有程序 ID 在 PDA 派生中的程序可以invoke_signed 其种子。如果你尝试,运行时拒绝。
CPI 陷阱
忘记转发 remaining_accounts
如果你的外层指令在 remaining_accounts 中传递转账钩子账户但进入 Raydium 的 CPI 没有转发它们,Raydium 失败因为它找不到钩子账户。始终在需要它们的 CPI 中包含 with_remaining_accounts。
可写标志不匹配
外层指令标记为可写的账户如果被调用程序打算写入,也必须在 CPI 调用中可写。不匹配 → 运行时拒绝。未考虑租金
对创建账户的程序的 CPI(例如,ATA 创建)要求支付者有足够的 SOL 来支付租金。失败的租金检查表现为晦涩的错误。实际示例:计算 Raydium CPMM PDA
getPoolInfoFromRpc({ poolId }) 时底层所做的——它派生关联的 PDA 而无需往返。
相关指南
solana-fundamentals/account-model— PDA 如何适配账户模型。solana-fundamentals/programs-and-anchor— Anchor 用于声明 PDA 的助手。integration-guides/cpi-integration— 构建调用 Raydium 的 CPI 集成。sdk-api/rust-cpi— Raydium 的 Rust CPI 类型。


