Перейти к основному содержанию

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

Детектирование позиций 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:
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 — декодируйте.
  1. Декодируйте через raydium.clmm.getPositionInfo({ positionPda }) для получения:
    • poolId → получите пул для разрешения mint’ов
    • tickLower, tickUpper → отобразите диапазон
    • liquidity, tokensOwedA/B → вычислите стоимость позиции + ожидающие комиссии
    • rewardInfos → ожидающие вознаграждения по потокам
  2. Для позиций-NFT выпущенных под Token-2022 (OpenPositionWithToken22Nft), программой mint’а NFT является Token-2022, а не SPL Token. При сканировании перечисляйте оба.

Ставки в фермах

Farm v3 / v5 / v6 имеют по одному PDA бухгалтерской книги для каждого пользователя. Вывод:
// Попробуйте все три версии фермы для каждого из потенциальных взаимодействий пользователя с фермой.
// Наиболее экономный подход: сначала запросите 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

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 позиция

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] с визуальной полосой, показывающей находится ли текущая цена в диапазоне

Ставка в ферме

// Ответ 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:
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 и в расчете на год:
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».

Ссылки

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