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

# Интеграция кошелька

> Отображение позиций Raydium, доли в пулах, ставок в фермах и ожидающих вознаграждений в интерфейсе кошелька — паттерны детектирования, симуляция для предпросмотра транзакций и специфика отображения Token-2022.

<Info>
  **Эта страница переведена с помощью ИИ. За эталон принимается английская версия.**

  [Открыть английскую версию →](/integration-guides/wallet-integration)
</Info>

<Info>
  При интеграции Raydium в кошелек обычно нужно ответить на четыре вопроса для каждого пользователя: в каких пулах у пользователя есть LP? какие позиции (CLMM NFT) он держит? в каких фермах он застейкан? какова общая стоимость? На этой странице рассмотрено каждое из этих вопросов.
</Info>

## Детектирование позиций Raydium

### Классические LP-токены (CPMM, AMM v4)

Они выглядят как любые другие SPL-токены: баланс пользователя находится на его ATA. Кошелек по умолчанию показывает это как обычный токен. Чтобы выявить это как позицию Raydium:

1. Получите все токен-счета пользователя: `connection.getParsedTokenAccountsByOwner(user, { programId: TOKEN_PROGRAM_ID })`.
2. Для каждого mint проверьте список mint'ов Raydium: `GET https://api-v3.raydium.io/pools/info/lps?lps=<LP_MINT>,...` (до \~50 LP mint'ов за вызов).
3. Для совпавших mint'ов 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. PDA каждой `PersonalPositionState` позиции выводится из mint'а NFT. Для детектирования:

1. Получите все NFT пользователя. Для унаследованных Metaplex NFT: отфильтруйте токен-счета по supply равному 1 и decimals равному 0.
2. Для каждого mint'а NFT попробуйте вывести PDA PersonalPositionState:

```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` → получите пул для разрешения mint'ов
   * `tickLower`, `tickUpper` → отобразите диапазон
   * `liquidity`, `tokensOwedA/B` → вычислите стоимость позиции + ожидающие комиссии
   * `rewardInfos` → ожидающие вознаграждения по потокам

4. Для позиций-NFT выпущенных под Token-2022 (`OpenPositionWithToken22Nft`), программой mint'а 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, ...
}
```

Для кошельков, предпочитающих полностью on-chain детектирование: переберите возможные PDA `UserLedger` хешированием пользователя с отобранным списком «вероятных» ID ферм. Перечисление всех 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);
```

Затем умножьте каждое значение на цену mint'а в USD (из `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,  // для отображения, без slippage
});

// Ожидающие комиссии — отобразите отдельно как «неснятые»
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;      // наименьшие единицы mint'а ставки
const rewards   = stakingFarm.pendingRewards;    // массив { mint, amount }
```

Для on-chain расчета воспроизведите бухгалтерию фермы:

```
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'ов, симуляция откатится (как и реальное выполнение). Отчетливо отобразите это в интерфейсе.
* **Приоритетная комиссия.** Симуляция выполняется без применения инструкций бюджета вычислений. Для большой транзакции, которая превысила бы стандартные 200k CU, симуляция падает, но реальное выполнение с явным лимитом CU проходит успешно. Также установите лимит CU для симулируемой транзакции.
* **Свежий блокхеш.** Симуляция использует текущий блокхеш; если подписание займет >60s, транзакция станет недействительной. Пересимулируйте, если пользователь колеблется.

## Отображение Token-2022

Токены под программой Token-2022 должны быть помечены как таковые в списке токенов кошелька, поскольку они имеют разные профили рисков:

* **Transfer-fee mint'ы**: отобразите текущий `transferFeeBasisPoints` как «Transfer fee: X%» рядом с балансом. Предупредите при получении — пользователи могут не заметить, что получат меньше, чем отправитель отправил.
* **Transfer-hook mint'ы**: отобразите ID программы хука. Вредоносный хук может заблокировать исходящие переводы; пользователи должны проверить, что это ожидаемый хук.
* **Non-transferable mint'ы**: отобразите «Non-transferable» и деактивируйте своп/отправку. Обычно это soulbound-токены или учетные данные.
* **Interest-bearing mint'ы**: баланс интерфейса, выведенный из `TokenAccount.amount`, **не** отражает накопленные проценты. Используйте `amountToUiAmount` из `@solana/spl-token` (который применяет коэффициент масштабирования) для отображаемого значения.

## Отображение APR фермы

APR, отображаемый пользователям, должен объединять все активные потоки вознаграждений, конвертированные в USD и в расчете на год:

```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%`. Если mint ставки является LP-токеном, также вычислите базовый APR комиссии базового LP и обозначьте сумму как «Total APR» или «APR + fees».

## Ссылки

* [`products/clmm/ticks-and-positions`](/ru/products/clmm/ticks-and-positions) — вывод стоимости позиции.
* [`products/farm-staking/accounts`](/ru/products/farm-staking/accounts) — поля состояния фермы.
* [`algorithms/token-2022-transfer-fees`](/ru/algorithms/token-2022-transfer-fees) — семантика отображения для transfer-fee токенов.

Источники:

* Raydium SDK v2 — вспомогательные функции для позиций/ферм.
* Endpoints позиций пользователя на `api-v3.raydium.io`.
