UNPKG

@raydium-io/raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

304 lines (289 loc) 10.5 kB
import { Connection, PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import Decimal from "decimal.js"; import { AmmV4Keys, AmmV5Keys } from "../../api/type"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { findProgramAddress, parseSimulateLogToJson, parseSimulateValue, simulateMultipleInstruction, } from "@/common/txTool/txUtils"; import { toApiV3Token } from "../../raydium/token/utils"; import { makeSimulatePoolInfoInstruction } from "./instruction"; import { getSerumAssociatedAuthority } from "./serum"; import { StableLayout } from "./stable"; import { AmmRpcData, ComputeAmountOutParam, LiquidityPoolKeys } from "./type"; import { liquidityStateV4Layout } from "./layout"; import { splAccountLayout } from "../account"; import { SPL_MINT_LAYOUT } from "../token"; type AssociatedName = | "amm_associated_seed" | "lp_mint_associated_seed" | "coin_vault_associated_seed" | "pc_vault_associated_seed" | "lp_mint_associated_seed" | "temp_lp_token_associated_seed" | "open_order_associated_seed" | "target_associated_seed" | "withdraw_associated_seed"; interface GetAssociatedParam { name: AssociatedName; programId: PublicKey; marketId: PublicKey; } export function getAssociatedConfigId({ programId }: { programId: PublicKey }): PublicKey { const { publicKey } = findProgramAddress([Buffer.from("amm_config_account_seed", "utf-8")], programId); return publicKey; } export function getLiquidityAssociatedId({ name, programId, marketId }: GetAssociatedParam): PublicKey { const { publicKey } = findProgramAddress( [programId.toBuffer(), marketId.toBuffer(), Buffer.from(name, "utf-8")], programId, ); return publicKey; } export function getAssociatedOpenOrders({ programId, marketId }: { programId: PublicKey; marketId: PublicKey }) { const { publicKey } = findProgramAddress( [programId.toBuffer(), marketId.toBuffer(), Buffer.from("open_order_associated_seed", "utf-8")], programId, ); return publicKey; } export function getLiquidityAssociatedAuthority({ programId }: { programId: PublicKey }): { publicKey: PublicKey; nonce: number; } { return findProgramAddress([Buffer.from([97, 109, 109, 32, 97, 117, 116, 104, 111, 114, 105, 116, 121])], programId); } export function getAssociatedPoolKeys({ version, marketVersion, marketId, baseMint, quoteMint, baseDecimals, quoteDecimals, programId, marketProgramId, }: { version: 4 | 5; marketVersion: 3; marketId: PublicKey; baseMint: PublicKey; quoteMint: PublicKey; baseDecimals: number; quoteDecimals: number; programId: PublicKey; marketProgramId: PublicKey; }): LiquidityPoolKeys { const id = getLiquidityAssociatedId({ name: "amm_associated_seed", programId, marketId }); const lpMint = getLiquidityAssociatedId({ name: "lp_mint_associated_seed", programId, marketId }); const { publicKey: authority, nonce } = getLiquidityAssociatedAuthority({ programId }); const baseVault = getLiquidityAssociatedId({ name: "coin_vault_associated_seed", programId, marketId }); const quoteVault = getLiquidityAssociatedId({ name: "pc_vault_associated_seed", programId, marketId }); const lpVault = getLiquidityAssociatedId({ name: "temp_lp_token_associated_seed", programId, marketId }); const openOrders = getAssociatedOpenOrders({ programId, marketId }); const targetOrders = getLiquidityAssociatedId({ name: "target_associated_seed", programId, marketId }); const withdrawQueue = getLiquidityAssociatedId({ name: "withdraw_associated_seed", programId, marketId }); const { publicKey: marketAuthority } = getSerumAssociatedAuthority({ programId: marketProgramId, marketId, }); return { // base id, baseMint, quoteMint, lpMint, baseDecimals, quoteDecimals, lpDecimals: baseDecimals, // version version, programId, // keys authority, nonce, baseVault, quoteVault, lpVault, openOrders, targetOrders, withdrawQueue, // market version marketVersion, marketProgramId, // market keys marketId, marketAuthority, lookupTableAccount: PublicKey.default, configId: getAssociatedConfigId({ programId }), }; } let stableLayout: StableLayout | undefined; export async function fetchMultipleInfo({ connection, poolKeysList, // eslint-disable-next-line @typescript-eslint/no-unused-vars config, modelDataPubKey, }: { connection: Connection; poolKeysList: (AmmV4Keys | AmmV5Keys)[]; config: any; modelDataPubKey?: PublicKey; }): Promise< { status: BN; baseDecimals: number; quoteDecimals: number; lpDecimals: number; baseReserve: BN; quoteReserve: BN; lpSupply: BN; startTime: BN; }[] > { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const loadStable = poolKeysList.find((i) => i.modelDataAccount); if (loadStable) { if (!stableLayout) { stableLayout = new StableLayout({ connection, modelDataPubKey }); await stableLayout.initStableModelLayout(); } } return await Promise.all( poolKeysList.map(async (itemPoolKey) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (itemPoolKey.modelDataAccount) { const instructions = makeSimulatePoolInfoInstruction({ poolKeys: itemPoolKey }); const logs = await simulateMultipleInstruction(connection, [instructions.instruction], "GetPoolData"); const poolsInfo = logs.map((log) => { const json = parseSimulateLogToJson(log, "GetPoolData"); const status = new BN(parseSimulateValue(json, "status")); const baseDecimals = Number(parseSimulateValue(json, "coin_decimals")); const quoteDecimals = Number(parseSimulateValue(json, "pc_decimals")); const lpDecimals = Number(parseSimulateValue(json, "lp_decimals")); const baseReserve = new BN(parseSimulateValue(json, "pool_coin_amount")); const quoteReserve = new BN(parseSimulateValue(json, "pool_pc_amount")); const lpSupply = new BN(parseSimulateValue(json, "pool_lp_supply")); // TODO fix it when split stable let startTime = "0"; try { startTime = parseSimulateValue(json, "pool_open_time"); } catch (error) { // } return { status, baseDecimals, quoteDecimals, lpDecimals, baseReserve, quoteReserve, lpSupply, startTime: new BN(startTime), }; })[0]; return poolsInfo; } else { const [poolAcc, vaultAccA, vaultAccB, mintAccLp] = await connection.getMultipleAccountsInfo([ new PublicKey(itemPoolKey.id), new PublicKey(itemPoolKey.vault.A), new PublicKey(itemPoolKey.vault.B), new PublicKey(itemPoolKey.mintLp.address), ]); if (poolAcc === null) throw Error("fetch pool error"); if (vaultAccA === null) throw Error("fetch vaultAccA error"); if (vaultAccB === null) throw Error("fetch vaultAccB error"); if (mintAccLp === null) throw Error("fetch mintAccLp error"); const poolInfo = liquidityStateV4Layout.decode(poolAcc.data); const vaultInfoA = splAccountLayout.decode(vaultAccA.data); const vaultInfoB = splAccountLayout.decode(vaultAccB.data); const lpInfo = SPL_MINT_LAYOUT.decode(mintAccLp.data); return { status: poolInfo.status, baseDecimals: poolInfo.baseDecimal.toNumber(), quoteDecimals: poolInfo.quoteDecimal.toNumber(), lpDecimals: lpInfo.decimals, baseReserve: vaultInfoA.amount.sub(poolInfo.baseNeedTakePnl), quoteReserve: vaultInfoB.amount.sub(poolInfo.quoteNeedTakePnl), lpSupply: poolInfo.lpReserve, startTime: poolInfo.poolOpenTime, }; } }), ); } const mockRewardData = { volume: 0, volumeQuote: 0, volumeFee: 0, apr: 0, feeApr: 0, priceMin: 0, priceMax: 0, rewardApr: [], }; export const toAmmComputePoolInfo = ( poolData: Record<string, AmmRpcData>, ): Record<string, ComputeAmountOutParam["poolInfo"]> => { const data: Record<string, ComputeAmountOutParam["poolInfo"]> = {}; const tokenProgramStr = TOKEN_PROGRAM_ID.toBase58(); Object.keys(poolData).map((poolId) => { const poolInfo = poolData[poolId]; const [mintA, mintB] = [poolInfo.baseMint.toBase58(), poolInfo.quoteMint.toBase58()]; data[poolId] = { id: poolId, version: 4, status: poolInfo.status.toNumber(), programId: poolInfo.programId.toBase58(), // needed mintA: toApiV3Token({ address: mintA, // needed programId: tokenProgramStr, decimals: poolInfo.baseDecimal.toNumber(), }), mintB: toApiV3Token({ address: mintB, // needed programId: tokenProgramStr, decimals: poolInfo.quoteDecimal.toNumber(), }), rewardDefaultInfos: [], rewardDefaultPoolInfos: "Ecosystem", price: poolInfo.poolPrice.toNumber(), mintAmountA: new Decimal(poolInfo.mintAAmount.toString()).div(10 ** poolInfo.baseDecimal.toNumber()).toNumber(), mintAmountB: new Decimal(poolInfo.mintBAmount.toString()).div(10 ** poolInfo.quoteDecimal.toNumber()).toNumber(), baseReserve: poolInfo.baseReserve, // needed quoteReserve: poolInfo.quoteReserve, // needed feeRate: new Decimal(poolInfo.tradeFeeNumerator.toString()) .div(poolInfo.tradeFeeDenominator.toString()) .toNumber(), openTime: poolInfo.poolOpenTime.toString(), tvl: 0, day: mockRewardData, week: mockRewardData, month: mockRewardData, pooltype: [], farmUpcomingCount: 0, farmOngoingCount: 0, farmFinishedCount: 0, type: "Standard", marketId: poolInfo.marketId.toBase58(), configId: getAssociatedConfigId({ programId: poolInfo.programId }).toBase58(), lpPrice: 0, lpAmount: new Decimal(poolInfo.lpReserve.toString()) .div(10 ** Math.min(poolInfo.baseDecimal.toNumber(), poolInfo.quoteDecimal.toNumber())) .toNumber(), lpMint: toApiV3Token({ address: poolInfo.lpMint.toBase58(), programId: tokenProgramStr, decimals: Math.min(poolInfo.baseDecimal.toNumber(), poolInfo.quoteDecimal.toNumber()), }), burnPercent: 0, }; }); return data; };