> ## 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.

# 钱包集成

> 在钱包 UI 中显示 Raydium 头寸、流动性池份额、农场质押和待领奖励 — 检测模式、交易预览模拟和 Token-2022 显示细节。

<Info>
  **本页内容由 AI 自动翻译，所有内容以英文版本为准。**

  [查看英文版 →](/integration-guides/wallet-integration)
</Info>

<Info>
  钱包集成 Raydium 时，通常需要为每个用户回答四个问题：该用户在哪些池中有 LP？他们持有哪些头寸（CLMM NFT）？他们在哪些农场中质押？这一切的价值是多少？本页文档详细说明了每一项。
</Info>

## 检测 Raydium 头寸

### 经典 LP 代币（CPMM、AMM v4）

这些看起来像任何其他 SPL 代币：用户的 ATA 持有余额。钱包默认将其显示为另一个代币。要将其识别为 Raydium LP 头寸：

1. 枚举用户的代币账户：`connection.getParsedTokenAccountsByOwner(user, { programId: TOKEN_PROGRAM_ID })`。
2. 对于每个铸币，检查 Raydium 的铸币列表：`GET https://api-v3.raydium.io/pools/info/lps?lps=<LP_MINT>,...`（每次调用最多约 50 个 LP 铸币）。
3. 对于匹配的铸币，API 返回池引用。使用它计算头寸的代币面值：

```
token_a_owned = user_lp_balance * poolReserves.A / lpMint.supply
token_b_owned = user_lp_balance * poolReserves.B / lpMint.supply
usd_value     = token_a_owned * priceA_usd + token_b_owned * priceB_usd
```

同时显示 LP 余额和展开的金额 — 用户以底层代币而非 LP 单位思考。

### CLMM 头寸 NFT

CLMM 头寸是 NFT。每个头寸的 `PersonalPositionState` PDA 由 NFT 铸币派生。检测方式：

1. 枚举用户的 NFT。对于遗留的 Metaplex NFT：将代币账户过滤为供应量为 1、小数位为 0 的账户。
2. 对于每个 NFT 铸币，尝试派生 PersonalPositionState PDA：

```ts theme={null}
import { CLMM_PROGRAM_ID } from "@raydium-io/raydium-sdk-v2";

const [positionPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("position"), nftMint.toBuffer()],
  CLMM_PROGRAM_ID,
);

const accountInfo = await connection.getAccountInfo(positionPda);
if (!accountInfo || !accountInfo.owner.equals(CLMM_PROGRAM_ID)) return null;
// 这是一个 Raydium CLMM 头寸 — 解码。
```

3. 通过 `raydium.clmm.getPositionInfo({ positionPda })` 解码以获得：
   * `poolId` → 获取池来解析铸币
   * `tickLower`、`tickUpper` → 显示范围
   * `liquidity`、`tokensOwedA/B` → 计算头寸价值 + 待领费用
   * `rewardInfos` → 每个流的待领奖励

4. 对于在 Token-2022 下发行的头寸 NFT（`OpenPositionWithToken22Nft`），NFT 铸币的程序是 Token-2022 而非 SPL Token。扫描时枚举两者。

### 农场质押

Farm v3 / v5 / v6 各有一个按用户的账本 PDA。派生方式：

```ts theme={null}
// 对于用户的每个可能的农场交互尝试所有三个农场版本。
// 最便宜的方法：先向 API 询问，它已索引所有用户头寸。

const r = await fetch(
  `https://api-v3.raydium.io/positions/staking?wallet=${user.toBase58()}`
).then(r => r.json());

for (const s of r.data.stakings) {
  // s.farmId, s.stakedAmount, s.pendingRewards[], s.poolApr, ...
}
```

对于偏好完全链上检测的钱包：通过使用精选的"可能"农场 ID 列表对用户进行哈希来迭代可能的 `UserLedger` PDA。穷举枚举所有农场 ID 是不切实际的（存在数千个）；使用 API。

## 计算头寸价值

### CPMM / AMM v4 LP

```ts theme={null}
const poolInfo = await raydium.<type>.getPoolInfoFromRpc({ poolId });
const myShare  = userLpBalance / poolInfo.lpMint.supply;
const tokensA  = BigInt(poolInfo.mintAmountA) * BigInt(userLpBalance)
                / BigInt(poolInfo.lpMint.supply);
const tokensB  = BigInt(poolInfo.mintAmountB) * BigInt(userLpBalance)
                / BigInt(poolInfo.lpMint.supply);
```

然后将每个乘以铸币的美元价格（来自 `raydium.token` 或价格预言机）。

### CLMM 头寸

```ts theme={null}
const position = await raydium.clmm.getPositionInfo({ positionPda });
const pool     = await raydium.clmm.getPoolInfoFromRpc({ poolId: position.poolId });

const { amountA, amountB } = PoolUtils.getAmountsFromLiquidity({
  sqrtPriceX64:  pool.sqrtPriceX64,
  tickLower:     position.tickLower,
  tickUpper:     position.tickUpper,
  liquidity:     position.liquidity,
  slippage:      0,  // 显示用，无滑点
});

// 待领费用 — 单独显示为"未领取"
const fees = {
  A: position.tokenFeesOwedA,
  B: position.tokenFeesOwedB,
};

// 待领奖励 — 一个 CLMM 池有 0–3 个奖励流。
const rewards = position.rewardInfos.map(r => ({
  mint:          r.rewardMint,
  rewardAmount:  r.rewardAmountOwed,
}));
```

呈现为：

* 流动性价值（当前价格）
* 未领费用
* 每个流的待领奖励
* 范围：`[tickLower_price, tickUpper_price]`，带有视觉条形显示当前价格是否在范围内

### 农场质押

```ts theme={null}
// API 响应已包含待领奖励；直接使用。
const apr       = stakingFarm.apr;               // %，年化
const staked    = stakingFarm.stakedAmount;      // 质押铸币的最小单位
const rewards   = stakingFarm.pendingRewards;    // 数组 { mint, amount }
```

对于链上计算，镜像农场的记账：

```
pending_reward_i = user.deposited
                 * farm.reward_per_share_x64[i] / 2^64
                 - user.reward_debts[i]
```

确保在计算前使用延迟更新公式刷新 `reward_per_share_x64`（经过时间 × 发行速率 ÷ 总质押）。

## 交易预览模拟

在用户签署前，钱包通常会预览余额变化。使用 `simulateTransaction`：

```ts theme={null}
const sim = await connection.simulateTransaction(tx, {
  sigVerify: false,
  commitment: "confirmed",
  accounts: {
    encoding: "base64",
    addresses: [userTokenAtaA, userTokenAtaB, userLpAta].map(a => a.toBase58()),
  },
});

// 解码每个返回账户的余额并与交易前余额进行比较。
for (const [i, acctData] of sim.value.accounts!.entries()) {
  const newBalance = decodeTokenAccount(acctData!.data[0]).amount;
  // 与交易前余额比较，显示 Δ
}
```

`accounts` 参数要求验证器为列出的地址返回模拟后账户状态。比尝试从指令形状单独预测余额变化准确得多。

### 模拟陷阱

* **CLMM 交换需要有效的 tick 数组。** 如果用户的输入大小会跨越未初始化的 tick 数组，模拟会还原（与执行相同）。在 UI 中清楚地显示这一点。
* **优先费。** 模拟运行时未应用计算预算指令。对于会超过默认 200k CU 的大型交易，模拟失败但使用显式 CU 限制的实际执行成功。在模拟交易上也设置 CU 限制。
* **新鲜的区块哈希。** 模拟使用当前区块哈希；如果签署耗时 >60 秒，交易将变为无效。如果用户犹豫，重新模拟。

## Token-2022 显示

Token-2022 程序下的代币应在钱包的代币列表中标记为此，因为它们有不同的风险表面：

* **转账费用铸币**：在余额旁显示当前的 `transferFeeBasisPoints` 为"转账费用：X%"。在接收时警告 — 用户可能没有意识到他们将收到少于发送者发送的金额。
* **转账钩子铸币**：显示钩子程序 ID。恶意钩子可能会阻止出站转账；用户应验证钩子是他们期望的。
* **不可转账铸币**：显示"不可转账"并禁用交换/发送。这些通常是灵魂绑定代币或凭证。
* **生息铸币**：从 `TokenAccount.amount` 派生的 UI 余额**不**反映应计利息。使用 `@solana/spl-token` 中的 `amountToUiAmount`（应用缩放因子）获得显示值。

## 农场 APR 显示

向用户显示的 APR 应结合所有实时奖励流、转换为美元并年化：

```ts theme={null}
let apr = 0;
for (const r of farm.rewardInfos) {
  if (r.rewardState !== 1) continue;   // 跳过未运行的
  const annualRewardTokens = Number(r.emissionPerSecond) * 86400 * 365 / 1e<decimals>;
  const annualRewardValue  = annualRewardTokens * priceUsd(r.rewardMint);
  const tvlValue           = Number(farm.totalStaked) * priceUsd(farm.stakingMint) / 1e<decimals>;
  apr += annualRewardValue / tvlValue;
}
```

显示为 `APR: X.Y%`。如果质押铸币是 LP 代币，也计算底层 LP 的基础费用 APR 并标记总和为"总 APR"或"APR + 费用"。

## 指针

* [`products/clmm/ticks-and-positions`](/zh/products/clmm/ticks-and-positions) — 头寸价值派生。
* [`products/farm-staking/accounts`](/zh/products/farm-staking/accounts) — 农场状态字段。
* [`algorithms/token-2022-transfer-fees`](/zh/algorithms/token-2022-transfer-fees) — 转账费用代币的显示语义。

来源：

* Raydium SDK v2 — 头寸/农场助手。
* `api-v3.raydium.io` 上的用户头寸端点。
