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.
Bunu ne yapar? Seçtiğiniz işlem ücreti katmanında yeni bir CLMM havuzu oluşturur, sonra ilk yoğunlaştırılmış pozisyonu açar. İki işlem, bir betik. Kod, raydium-sdk-V2-demo/src/clmm klasöründeki resmi demolardan alınmış ve tek bir Node çalıştırılabilir dosyaya uyarlanmıştır.
Kurulum
Hızlı başlangıç ön koşullarını okuduğunuzdan emin olun ve RPC_URL, KEYPAIR ve bağımlılıkların yüklü olduğundan emin olun.
CLMM havuzu oluşturma işleminin bir kerelik ücreti ve ilk pozisyon için tick array kirası vardır. Ayrıca her iki seed mint’in de cüzdanınızda olması gerekir — seçilen aralık içinde fiyat oturduğunda bir pozisyon açmak her iki tarafta da likidite gerektirir.
Adım 1 — config.ts
config.ts olarak kaydedin. Bu, demo repo’sunun src/config.ts.template ile aynı yapıdadır — disableFeatureCheck zorunlu olarak true değerine ayarlanmıştır (SDK başlangıçta özellik tespitinde engellenmemesi için herhangi bir önemsiz entegrasyon için önerilir):
// config.ts
import { Raydium, TxVersion, parseTokenAccountResp } from "@raydium-io/raydium-sdk-v2";
import { Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
import bs58 from "bs58";
import fs from "node:fs";
export const owner: Keypair = Keypair.fromSecretKey(
// accept either a JSON-array keypair file (same shape Solana CLI writes) or a bs58 secret in env
process.env.KEYPAIR_BS58
? bs58.decode(process.env.KEYPAIR_BS58)
: new Uint8Array(JSON.parse(fs.readFileSync(process.env.KEYPAIR!, "utf8"))),
);
export const connection = new Connection(
process.env.RPC_URL ?? clusterApiUrl("mainnet-beta"),
"confirmed",
);
export const txVersion = TxVersion.V0;
const cluster = "mainnet" as "mainnet" | "devnet";
let raydium: Raydium | undefined;
export const initSdk = async (params?: { loadToken?: boolean }) => {
if (raydium) return raydium;
raydium = await Raydium.load({
owner,
connection,
cluster,
disableFeatureCheck: true,
disableLoadToken: !params?.loadToken,
blockhashCommitment: "finalized",
});
return raydium;
};
export const fetchTokenAccountData = async () => {
const solAccountResp = await connection.getAccountInfo(owner.publicKey);
const tokenAccountResp = await connection.getTokenAccountsByOwner(owner.publicKey, {
programId: TOKEN_PROGRAM_ID,
});
const token2022Req = await connection.getTokenAccountsByOwner(owner.publicKey, {
programId: TOKEN_2022_PROGRAM_ID,
});
return parseTokenAccountResp({
owner: owner.publicKey,
solAccountResp,
tokenAccountResp: {
context: tokenAccountResp.context,
value: [...tokenAccountResp.value, ...token2022Req.value],
},
});
};
Adım 2 — createPool.ts
config.ts ile birlikte kaydedin. Kaynak: src/clmm/createPool.ts.
// createPool.ts
import { CLMM_PROGRAM_ID, DEVNET_PROGRAM_ID } from "@raydium-io/raydium-sdk-v2";
import { PublicKey } from "@solana/web3.js";
import Decimal from "decimal.js";
import { initSdk, txVersion } from "./config";
export const createPool = async () => {
const raydium = await initSdk({ loadToken: true });
// RAY
const mint1 = await raydium.token.getTokenInfo("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R");
// USDT
const mint2 = await raydium.token.getTokenInfo("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
// Fee tiers come from the live API. On devnet the published `id` field is wrong;
// re-derive the PDA before passing it to the SDK.
const clmmConfigs = await raydium.api.getClmmConfigs();
const { execute } = await raydium.clmm.createPool({
programId: CLMM_PROGRAM_ID,
// programId: DEVNET_PROGRAM_ID.CLMM_PROGRAM_ID,
mint1,
mint2,
ammConfig: {
...clmmConfigs[0],
id: new PublicKey(clmmConfigs[0].id),
fundOwner: "",
description: "",
},
initialPrice: new Decimal(1),
txVersion,
// optional: set up priority fee here
// computeBudgetConfig: { units: 600000, microLamports: 46591500 },
});
const { txId } = await execute({ sendAndConfirm: true });
console.log("clmm pool created:", { txId: `https://explorer.solana.com/tx/${txId}` });
process.exit();
};
createPool();
Adım 3 — createPosition.ts
Kaynak: src/clmm/createPosition.ts.
// createPosition.ts
import {
ApiV3PoolInfoConcentratedItem,
TickUtils,
PoolUtils,
ClmmKeys,
} from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";
import Decimal from "decimal.js";
import { initSdk, txVersion } from "./config";
import { isValidClmm } from "./utils";
export const createPosition = async () => {
const raydium = await initSdk();
let poolInfo: ApiV3PoolInfoConcentratedItem;
// RAY-USDC pool
const poolId = "61R1ndXxvsWXXkWSyNkCxnzwd3zUNB8Q2ibmkiLPC8ht";
let poolKeys: ClmmKeys | undefined;
if (raydium.cluster === "mainnet") {
const data = await raydium.api.fetchPoolById({ ids: poolId });
poolInfo = data[0] as ApiV3PoolInfoConcentratedItem;
if (!isValidClmm(poolInfo.programId)) throw new Error("target pool is not CLMM pool");
} else {
const data = await raydium.clmm.getPoolInfoFromRpc(poolId);
poolInfo = data.poolInfo;
poolKeys = data.poolKeys;
}
// Optional: pull on-chain real-time price to avoid slippage errors from a stale API quote.
// const rpcData = await raydium.clmm.getRpcClmmPoolInfo({ poolId: poolInfo.id });
// poolInfo.price = rpcData.currentPrice;
const inputAmount = 0.000001; // RAY amount
const [startPrice, endPrice] = [0.000001, 100000];
const { tick: lowerTick } = TickUtils.getPriceAndTick({
poolInfo,
price: new Decimal(startPrice),
baseIn: true,
});
const { tick: upperTick } = TickUtils.getPriceAndTick({
poolInfo,
price: new Decimal(endPrice),
baseIn: true,
});
const epochInfo = await raydium.fetchEpochInfo();
const res = await PoolUtils.getLiquidityAmountOutFromAmountIn({
poolInfo,
slippage: 0,
inputA: true,
tickUpper: Math.max(lowerTick, upperTick),
tickLower: Math.min(lowerTick, upperTick),
amount: new BN(new Decimal(inputAmount || "0").mul(10 ** poolInfo.mintA.decimals).toFixed(0)),
add: true,
amountHasFee: true,
epochInfo,
});
const { execute, extInfo } = await raydium.clmm.openPositionFromBase({
poolInfo,
poolKeys,
tickUpper: Math.max(lowerTick, upperTick),
tickLower: Math.min(lowerTick, upperTick),
base: "MintA",
ownerInfo: { useSOLBalance: true },
baseAmount: new BN(new Decimal(inputAmount || "0").mul(10 ** poolInfo.mintA.decimals).toFixed(0)),
otherAmountMax: res.amountSlippageB.amount,
txVersion,
computeBudgetConfig: { units: 600000, microLamports: 100000 },
});
const { txId } = await execute({ sendAndConfirm: true });
console.log("clmm position opened:", { txId, nft: extInfo.nftMint.toBase58() });
process.exit();
};
createPosition();
Adım 4 — utils.ts
Kaynak: src/clmm/utils.ts.
// utils.ts
import { CLMM_PROGRAM_ID, DEVNET_PROGRAM_ID } from "@raydium-io/raydium-sdk-v2";
const VALID_PROGRAM_IDS = new Set<string>([
CLMM_PROGRAM_ID.toBase58(),
DEVNET_PROGRAM_ID.CLMM_PROGRAM_ID.toBase58(),
]);
export const isValidClmm = (programId: string) => VALID_PROGRAM_IDS.has(programId);
Çalıştırın
# create the pool first
RPC_URL="https://api.mainnet-beta.solana.com" \
KEYPAIR="$HOME/.config/solana/id.json" \
npx tsx createPool.ts
# then open an initial position against the new pool id
# (edit poolId at the top of createPosition.ts to point at your new pool)
RPC_URL="https://api.mainnet-beta.solana.com" \
KEYPAIR="$HOME/.config/solana/id.json" \
npx tsx createPosition.ts
Az önce ne oldu
İşlem 1 — raydium.clmm.createPool başlatıldı:
(mint1, mint2, ammConfig) için kanonik PDA’da havuz durumu,
token_0_vault ve token_1_vault (mint byte sırasına göre sıralanmış),
observation halka arabelleği,
- satır içi tick-array bitmap,
ve ilk sqrt_price_x64’ü initialPrice’inizden ayarladı.
İşlem 2 — raydium.clmm.openPositionFromBase yoğunlaştırılmış bir pozisyon açtı:
- cüzdanınıza bir pozisyon NFT’si bastı (NFT pozisyondur; onu aktarmak pozisyonu aktarır),
- alt ve üst sınırlarda tick dizileri tahsis etti (bu aralıklardaki ilk pozisyon için bir kerelik kira; tick dizileri program tarafından asla kapatılmaz, bu nedenle aynı dizilerdeki sonraki pozisyonlar fazladan kira ödemez),
inputAmount mint1 ve mint2’nin eşleşen çift miktarını yatırdı (PoolUtils.getLiquidityAmountOutFromAmountIn tarafından hesaplandı),
- pozisyonu aralık genişliğiyle orantılı likidite ile krediye aldı.
Aralık ne kadar dar olursa, TVL başına sermaye verimliliği o kadar yüksek — ve fiyat aralığın dışına çıktığında kalıcı olmayan kayıp o kadar acı verici. Yukarıda kullanılan aralık ([0.000001, 100000]) etkili olarak tam aralık; mevcut spot’a yakın ücretleri yoğunlaştırmak için sıkılaştırın.
İşlem ücreti katmanı seçme
clmmConfigs[0] en düşük işlem ücreti katmanıdır. Tam set GET https://api-v3.raydium.io/main/clmm-config adresinde yayınlanır:
| İndeks | tradeFeeRate | Tick aralığı | Kullanım zamanı |
|---|
| 0 | 100 (1bp) | 1 | Sabit/sabit, çok düşük kalıcı olmayan kayıp beklenir |
| 1 | 500 (5bp) | 10 | Yüksek ilişkili varlıklar (ör. likit ödün veren vs temel) |
| 2 | 2_500 (25bp) | 60 | Standart token çifti, mavi çip + sabit |
| 3 | 10_000 (1.00%) | 120 | Kalıcı olmayan kayıp riskinin yüksek olduğu değişken veya ince çift |
Tam karar matrisini user-flows/choosing-a-pool-type adresinde görün.
Sık karşılaşılan hatalar
Pool already exists for this config — Bu (mint1, mint2, ammConfig) üçlüsü için zaten bir CLMM havuzu mevcut. Mevcut havuz ID’sini arayın ve Adım 2’yi atlayın.
Insufficient funds for amount B — Cüzdanınızda istenen mintA miktarı var ama eşleşen mintB yok. Fiyat aralık içinde oturduğunda bir pozisyon açmak her iki tarafta da likidite gerektirir.
Tick out of range — lowerPrice veya upperPrice’ınız temsil edilebilir fiyat aralığının dışında. Mevcut fiyata göre daha makul bir aralık kullanın.
- Eski fiyat — API’den bir fiyat teklifi 5–60 saniye eski olabilir.
executePosition slippage’de başarısız olursa, imzalamadan hemen önce canlı fiyatı yeniden getirmek için createPosition.ts’de getRpcClmmPoolInfo bloğunun açıklamasını kaldırın.
Uyarılar
- Pozisyon NFT’si tek tutamağınız. NFT’yi kaybederseniz veya aktarırsanız, pozisyona erişimi kaybedersiniz. Bunu bir anahtar gibi ele alın.
- Aralık dışı pozisyonlar ücret kazanmaz. Fiyat
[lowerPrice, upperPrice] aralığının dışına çıkarsa, pozisyonunuz tamamen bir varlıkta park edilir ve yeniden dengelenene kadar hiçbir şey kazanmaz.
- Tick array kirası tek yönlüdür. Daha önce başlatılmayan bir tick dizisine temas eden ilk pozisyon kirasını öder; program, tick dizilerini kapatmanın bir yolunu ortaya koymaz, bu nedenle bu kira kalıcı. Aynı dizideki sonraki pozisyonlar ücretsizdir.
Sonra
Kaynaklar: