메인 콘텐츠로 건너뛰기

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 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.영문판 보기 →
Raydium을 통합하는 지갑은 사용자마다 일반적으로 네 가지 질문에 답해야 합니다. 이 사용자가 LP로 보유 중인 풀은 어떤 것인가? 어떤 포지션(CLMM NFT)을 보유하고 있는가? 어느 팜에 스테이킹되어 있는가? 전체 가치는 얼마인가? 이 페이지에서는 각각을 설명합니다.

Raydium 포지션 감지

Classic LP 토큰 (CPMM, AMM v4)

이들은 다른 SPL Token처럼 보입니다. 사용자의 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를 파생시키려고 합니다:
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 → 풀을 가져와서 민트 해석
    • tickLower, tickUpper → 범위 표시
    • liquidity, tokensOwedA/B → 포지션 가치 + 대기 중인 수수료 계산
    • rewardInfos → 스트림당 대기 중인 보상
  2. Token-2022 하에서 발급된 포지션 NFT의 경우 (OpenPositionWithToken22Nft), NFT 민트의 프로그램은 SPL Token이 아닌 Token-2022입니다. 스캔할 때 둘 다를 나열합니다.

팜 스테이크

Farm v3 / v5 / v6 각각에는 사용자별 원장 PDA가 있습니다. 파생:
// 사용자의 각 잠재적 팜 상호작용에 대해 세 가지 팜 버전을 모두 시도합니다.
// 가장 저렴한 접근: 먼저 API에 문의합니다. 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

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);
그런 다음 각각에 민트의 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,  // 표시용, 슬리피지 없음
});

// 대기 중인 수수료 — "수집되지 않음"으로 별도로 표시합니다
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;      // 스테이킹 민트의 최소 단위
const rewards   = stakingFarm.pendingRewards;    // array of { mint, amount }
온체인 계산의 경우 팜의 회계를 반영합니다:
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 스왑은 유효한 틱 배열이 필요합니다. 사용자의 입력 크기가 초기화되지 않은 틱 배열로 이동하면 시뮬레이션이 실행과 마찬가지로 되돌립니다. UI에서 이를 명확하게 표시합니다.
  • 우선 수수료. 시뮬레이션은 적용된 컴퓨팅 예산 지시문 없이 실행됩니다. 기본값 200k CU를 초과하는 대규모 트랜잭션의 경우 시뮬레이션은 실패하지만 명시적 CU 한계로의 실제 실행은 성공합니다. 시뮬레이션된 트랜잭션에도 항상 CU 한계를 설정합니다.
  • 새로운 블록해시. 시뮬레이션은 현재 블록해시를 사용합니다. 서명이 60초 이상 걸리면 트랜잭션이 유효하지 않게 됩니다. 사용자가 주저하면 다시 시뮬레이션합니다.

Token-2022 표시

Token-2022 프로그램 하의 토큰은 지갑의 토큰 목록에서 그렇게 표시되어야 합니다. 위험 표면이 다르기 때문입니다:
  • 이전 수수료 민트: 현재 transferFeeBasisPoints를 잔액 옆에 “이전 수수료: X%“로 표시합니다. 수신 시 경고합니다. 사용자는 발신자가 보낸 금액보다 적게 받을 것임을 깨닫지 못할 수 있습니다.
  • 이전 훅 민트: 훅 프로그램 ID를 표시합니다. 악의적인 훅은 아웃바운드 전송을 차단할 수 있습니다. 사용자는 훅이 예상한 것인지 확인해야 합니다.
  • 이전 불가능 민트: “이전 불가능”을 표시하고 스왑/전송을 비활성화합니다. 이들은 일반적으로 영혼 바인딩 토큰 또는 자격 증명입니다.
  • 이자 베어링 민트: TokenAccount.amount에서 파생된 UI 잔액은 누적된 이자를 반영하지 않습니다. 표시된 값에 @solana/spl-tokenamountToUiAmount(스케일링 계수를 적용함)를 사용합니다.

팜 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%로 표시합니다. 스테이킹 민트가 LP 토큰인 경우 기저 LP의 기본 수수료 APR도 계산하고 합계를 “총 APR” 또는 “APR + 수수료”로 표시합니다.

포인터

출처:
  • Raydium SDK v2 — 포지션/팜 도우미.
  • api-v3.raydium.io의 사용자 포지션 엔드포인트.