Chuyển đến nội dung chính

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.

Trang này được dịch tự động bằng AI. Phiên bản tiếng Anh là bản chính thức.Xem bản tiếng Anh →
Khi tích hợp Raydium, ví thường cần trả lời bốn câu hỏi cho mỗi người dùng: người dùng này có LP trong những pool nào? họ nắm giữ những position nào (CLMM NFT)? họ stake trong những farm nào? tất cả có giá trị bao nhiêu? Trang này tài liệu hóa từng câu hỏi.

Phát hiện vị trí Raydium

Token LP cổ điển (CPMM, AMM v4)

Chúng giống như bất kỳ token SPL nào khác: ATA của người dùng nắm giữ một số dư. Ví mặc định hiển thị điều này như một token khác. Để tiết lộ nó là một vị trí Raydium LP:
  1. Liệt kê các tài khoản token của người dùng: connection.getParsedTokenAccountsByOwner(user, { programId: TOKEN_PROGRAM_ID }).
  2. Với mỗi mint, kiểm tra danh sách mint của Raydium: GET https://api-v3.raydium.io/pools/info/lps?lps=<LP_MINT>,... (nhóm lên đến ~50 LP mints mỗi lệnh gọi).
  3. Với các mint khớp, API trả về tham chiếu pool. Sử dụng nó để tính giá trị vị trí được mệnh danh bằng token:
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
Hiển thị cả số dư LP và các số tiền chưa được bao bọc — người dùng nghĩ theo token cơ sở, không phải đơn vị LP.

CLMM position NFTs

Các vị trí CLMM là NFT. PDA PersonalPositionState của mỗi vị trí được derived từ mint NFT. Để phát hiện:
  1. Liệt kê NFT của người dùng. Đối với NFT Metaplex cũ: lọc các tài khoản token để những cái có supply 1 và decimals 0.
  2. Với mỗi mint NFT, hãy cố derive 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;
// Đó là một vị trí CLMM của Raydium — decode.
  1. Decode thông qua raydium.clmm.getPositionInfo({ positionPda }) để lấy:
    • poolId → fetch pool để resolve mints
    • tickLower, tickUpper → hiển thị phạm vi
    • liquidity, tokensOwedA/B → tính giá trị vị trí + phí chưa nhận
    • rewardInfos → phần thưởng chưa nhận theo mỗi stream
  2. Đối với position-NFT được phát hành theo Token-2022 (OpenPositionWithToken22Nft), program của mint NFT là Token-2022 chứ không phải SPL Token. Liệt kê cả hai khi quét.

Farm stakes

Farm v3 / v5 / v6 mỗi cái có một PDA ledger cho mỗi người dùng. Derivations:
// Thử cả ba phiên bản farm cho mỗi tương tác farm tiềm năng của người dùng.
// Cách tiếp cận rẻ nhất: hỏi API trước, nó đã lập chỉ mục tất cả vị trí người dùng.

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, ...
}
Đối với ví thích phát hiện hoàn toàn on-chain: lặp các PDA UserLedger có thể có bằng cách hash người dùng với danh sách được tuyrate của “farm ID có khả năng”. Liệt kê toàn bộ farm ID một cách cặn kẽ là không thực tế (hàng ngàn tồn tại); sử dụng API.

Tính giá trị vị trí

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);
Sau đó nhân từng cái với giá USD của mint (từ raydium.token hoặc một price oracle).

CLMM position

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,  // for display, no slippage
});

// Phí chưa nhận — hiển thị riêng biệt dưới dạng "chưa thu thập"
const fees = {
  A: position.tokenFeesOwedA,
  B: position.tokenFeesOwedB,
};

// Phần thưởng chưa nhận — pool CLMM có 0–3 reward stream.
const rewards = position.rewardInfos.map(r => ({
  mint:          r.rewardMint,
  rewardAmount:  r.rewardAmountOwed,
}));
Render như sau:
  • Giá trị thanh khoản (giá hiện tại)
  • Phí chưa thu thập
  • Phần thưởng chưa nhận mỗi stream
  • Phạm vi: [tickLower_price, tickUpper_price] với thanh trực quan cho thấy liệu giá hiện tại có nằm trong phạm vi hay không

Farm stake

// Phản hồi API đã bao gồm phần thưởng chưa nhận; sử dụng nó trực tiếp.
const apr       = stakingFarm.apr;               // %, hàng năm
const staked    = stakingFarm.stakedAmount;      // đơn vị nhỏ nhất của staking mint
const rewards   = stakingFarm.pendingRewards;    // mảng { mint, amount }
Để tính toán on-chain, sao chép kế toán của farm:
pending_reward_i = user.deposited
                 * farm.reward_per_share_x64[i] / 2^64
                 - user.reward_debts[i]
Đảm bảo refresh reward_per_share_x64 bằng công thức lazy-update trước khi tính toán (thời gian trôi qua × tốc độ phát hành ÷ tổng_staked).

Mô phỏng giao dịch để xem trước

Trước khi người dùng ký, ví thường xem trước các thay đổi số dư. Sử dụng simulateTransaction:
const sim = await connection.simulateTransaction(tx, {
  sigVerify: false,
  commitment: "confirmed",
  accounts: {
    encoding: "base64",
    addresses: [userTokenAtaA, userTokenAtaB, userLpAta].map(a => a.toBase58()),
  },
});

// Decode số dư của mỗi tài khoản được trả về và so sánh với số dư trước tx.
for (const [i, acctData] of sim.value.accounts!.entries()) {
  const newBalance = decodeTokenAccount(acctData!.data[0]).amount;
  // so sánh với số dư trước tx, hiển thị Δ
}
Tham số accounts yêu cầu validator trả về trạng thái tài khoản sau mô phỏng cho các địa chỉ được liệt kê. Chính xác hơn nhiều so với việc cố gắng dự đoán thay đổi số dư từ hình dạng hướng dẫn một mình.

Các bẫy mô phỏng

  • CLMM swaps cần tick arrays hợp lệ. Nếu kích thước đầu vào của người dùng sẽ vượt qua một tick array chưa được khởi tạo, mô phỏng revert (giống như thực thi). Bề mặt này rõ ràng trong giao diện người dùng.
  • Priority fee. Mô phỏng chạy mà không áp dụng các hướng dẫn compute-budget. Đối với một giao dịch lớn vượt quá 200k CU mặc định, mô phỏng thất bại nhưng thực thi thực tế với giới hạn CU rõ ràng thành công. Luôn đặt giới hạn CU trên tx được mô phỏng.
  • Blockhash tươi. Mô phỏng sử dụng blockhash hiện tại; nếu ký mất >60 giây, tx trở nên không hợp lệ. Mô phỏng lại nếu người dùng do dự.

Hiển thị Token-2022

Các token theo chương trình Token-2022 nên được gắn nhãn như vậy trong danh sách token của ví, vì chúng có các bề mặt rủi ro khác nhau:
  • Transfer-fee mints: hiển thị transferFeeBasisPoints hiện tại dưới dạng “Transfer fee: X%” bên cạnh số dư. Cảnh báo khi nhận — người dùng có thể không nhận ra họ sẽ nhận được ít hơn người gửi đã gửi.
  • Transfer-hook mints: bề mặt ID chương trình hook. Một hook độc hại có thể chặn chuyển gửi; người dùng nên xác minh hook là cái họ mong đợi.
  • Non-transferable mints: hiển thị “Non-transferable” và tắt swap/send. Đây thường là token soulbound hoặc thông tin xác thực.
  • Interest-bearing mints: số dư giao diện người dùng được derived từ TokenAccount.amount không phản ánh lãi suất đã tích lũy. Sử dụng amountToUiAmount từ @solana/spl-token (áp dụng hệ số tỷ lệ) cho giá trị hiển thị.

Hiển thị Farm APR

APR được hiển thị cho người dùng nên kết hợp tất cả các reward stream trực tiếp, được chuyển đổi thành USD và được hàng năm hóa:
let apr = 0;
for (const r of farm.rewardInfos) {
  if (r.rewardState !== 1) continue;   // bỏ qua không chạy
  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;
}
Hiển thị dưới dạng APR: X.Y%. Nếu staking mint là token LP, cũng tính APR phí cơ sở của LP cơ sở và gắn nhãn tổng dưới dạng “Total APR” hoặc “APR + fees”.

Con trỏ

Nguồn:
  • Raydium SDK v2 — position/farm helpers.
  • User-position endpoints trên api-v3.raydium.io.