2

I have a problem regarding the calculation of a Raydium pool price. (https://github.com/raydium-io/raydium-sdk)

  1. Raydium API Request for Liquid Pool V4 SOL/USDC.
import { Connection, PublicKey } from "@solana/web3.js";
import { LIQUIDITY_STATE_LAYOUT_V4 } from "@raydium-io/raydium-sdk";
import { BN } from "bn.js"

const SOL_USDC_POOL_ID = "58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2"; // Your pool ID

async function parsePoolInfo() {
  const connection = new Connection("https://api.mainnet-beta.solana.com", "recent");
  
  const info = await connection.getAccountInfo(new PublicKey(SOL_USDC_POOL_ID));
  if (!info) return;
  
  // Assuming the structure was successfully decoded
  const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(info.data);

  console.log(poolState)
}


parsePoolInfo();

1.1 Console Output of the API call

{
  status: <BN: 1>,
  nonce: <BN: fe>,
  maxOrder: <BN: 7>,
  depth: <BN: 3>,
  baseDecimal: <BN: 9>,
  quoteDecimal: <BN: 6>,
  state: <BN: 1>,
  resetFlag: <BN: 1>,
  minSize: <BN: f4240>,
  volMaxCutRatio: <BN: 1f4>,
  amountWaveRatio: <BN: 0>,
  baseLotSize: <BN: f4240>,
  quoteLotSize: <BN: f4240>,
  minPriceMultiplier: <BN: 1>,
  maxPriceMultiplier: <BN: 3b9aca00>,
  systemDecimalValue: <BN: 3b9aca00>,
  minSeparateNumerator: <BN: 5>,
  minSeparateDenominator: <BN: 2710>,
  tradeFeeNumerator: <BN: 19>,
  tradeFeeDenominator: <BN: 2710>,
  pnlNumerator: <BN: c>,
  pnlDenominator: <BN: 64>,
  swapFeeNumerator: <BN: 19>,
  swapFeeDenominator: <BN: 2710>,
  baseNeedTakePnl: <BN: 15c0b6d20>,
  quoteNeedTakePnl: <BN: 3f148c03>,
  quoteTotalPnl: <BN: cc322d0c31>,
  baseTotalPnl: <BN: 20f2cc8a6d10>,
  poolOpenTime: <BN: 0>,
  punishPcAmount: <BN: 0>,
  punishCoinAmount: <BN: 0>,
  orderbookToInitTime: <BN: 0>,
  swapBaseInAmount: <BN: 198ae479152985>,
  swapQuoteOutAmount: <BN: daf1151a5f2b>,
  swapBase2QuoteFee: <BN: 92b08e7d17>,
  swapQuoteInAmount: <BN: e533d56efeeb>,
  swapBaseOutAmount: <BN: 1a4bba4d1f08f5>,
  swapQuote2BaseFee: <BN: 1058e429cf81>,
  baseVault: PublicKey [PublicKey(DQyrAcCrDXQ7NeoqGgDCZwBvWDcYmFCjSb9JtteuvPpz)] {
    _bn: <BN: b870e12dd379891561d2e9fa8f26431834eb736f2f24fc2a2a4dff1fd5dca4df>
  },
  quoteVault: PublicKey [PublicKey(HLmqeL62xR1QoZ1HKKbXRrdN1p3phKpxRMb2VVopvBBz)] {
    _bn: <BN: f2cbb9b760eddb185706303063ad33d7b57296ea02d4e0335e31ceafa4cc42dd>
  },
  baseMint: PublicKey [PublicKey(So11111111111111111111111111111111111111112)] {
    _bn: <BN: 69b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001>
  },
  quoteMint: PublicKey [PublicKey(EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v)] {
    _bn: <BN: c6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d61>
  },
  lpMint: PublicKey [PublicKey(8HoQnePLqPj4M7PUDzfw8e3Ymdwgc7NLGnaTUapubyvu)] {
    _bn: <BN: 6c4f93d858e88ffafea08c43674497e8e6a932c0c83148262a1ae3ccc7829ec6>
  },
  openOrders: PublicKey [PublicKey(HmiHHzq4Fym9e1D4qzLS6LDDM3tNsCTBPDWHTLZ763jY)] {
    _bn: <BN: f92f390ff9609e8ad437bb8e4c1f1aa43ac05d24308cca77de8512c5509292d3>
  },
  marketId: PublicKey [PublicKey(8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6)] {
    _bn: <BN: 6ac4c3cefa9f19bf54c8dc0f5e4d1ceee5327d26482b29d2b13cbaa43447218d>
  },
  marketProgramId: PublicKey [PublicKey(srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX)] {
    _bn: <BN: d0751a8282da61305fe299c37b998e58471db1135037310f8be1045a60af6ee>
  },
  targetOrders: PublicKey [PublicKey(CZza3Ej4Mc58MnxWA385itCC9jCo3L1D7zc3LKy1bZMR)] {
    _bn: <BN: abe43c7c1e21eaa6f97c8bd355e21bd1279674756c1c8e106c6e712ba116d970>
  },
  withdrawQueue: PublicKey [PublicKey(11111111111111111111111111111111)] {
    _bn: <BN: 0>
  },
  lpVault: PublicKey [PublicKey(11111111111111111111111111111111)] {
    _bn: <BN: 0>
  },
  owner: PublicKey [PublicKey(GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ)] {
    _bn: <BN: e5b62b65cb3bbda6f56888e66fee8e64dc5560199c0f88b11fe273bd059e8aa1>
  },
  lpReserve: <BN: 41e692e5f338>,
  padding: [ <BN: 700dc7>, <BN: 0>, <BN: 0> ]
}
  1. Decode API DATA BigNumbers into decimals.
  console.log("Status:", poolState.status.toNumber());
  console.log("Nonce:", poolState.nonce.toNumber());
  console.log("Max Order:", poolState.maxOrder.toNumber());
  console.log("Depth:", poolState.depth.toNumber());
  console.log("Base Decimal:", poolState.baseDecimal.toNumber());
  console.log("Quote Decimal:", poolState.quoteDecimal.toNumber());
  console.log("State:", poolState.state.toNumber());
  console.log("Reset Flag:", poolState.resetFlag.toNumber());
  console.log("Min Size:", poolState.minSize.toNumber());
  console.log("Vol Max Cut Ratio:", poolState.volMaxCutRatio.toNumber());
  console.log("Amount Wave Ratio:", poolState.amountWaveRatio.toNumber());
  console.log("Base Lot Size:", poolState.baseLotSize.toNumber());
  console.log("Quote Lot Size:", poolState.quoteLotSize.toNumber());
  console.log("Min Price Multiplier:", poolState.minPriceMultiplier.toNumber());
  console.log("Max Price Multiplier:", poolState.maxPriceMultiplier.toNumber());
  console.log("System Decimal Value:", poolState.systemDecimalValue.toNumber());
  console.log("Min Separate Numerator:", poolState.minSeparateNumerator.toNumber());
  console.log("Min Separate Denominator:", poolState.minSeparateDenominator.toNumber());
  console.log("Trade Fee Numerator:", poolState.tradeFeeNumerator.toNumber());
  console.log("Trade Fee Denominator:", poolState.tradeFeeDenominator.toNumber());
  console.log("PnL Numerator:", poolState.pnlNumerator.toNumber());
  console.log("PnL Denominator:", poolState.pnlDenominator.toNumber());
  console.log("Swap Fee Numerator:", poolState.swapFeeNumerator.toNumber());
  console.log("Swap Fee Denominator:", poolState.swapFeeDenominator.toNumber());
  console.log("Base Need Take PnL:", poolState.baseNeedTakePnl.toNumber());
  console.log("Quote Need Take PnL:", poolState.quoteNeedTakePnl.toNumber());
  console.log("Quote Total PnL:", poolState.quoteTotalPnl.toNumber());
  console.log("Base Total PnL:", poolState.baseTotalPnl.toNumber());
  console.log("Pool Open Time:", poolState.poolOpenTime.toNumber());
  console.log("Punish Pc Amount:", poolState.punishPcAmount.toNumber());
  console.log("Punish Coin Amount:", poolState.punishCoinAmount.toNumber());
  console.log("Orderbook To Init Time:", poolState.orderbookToInitTime.toNumber());
  console.log("Swap Base In Amount:", poolState.swapBaseInAmount.toNumber());
  console.log("Swap Quote Out Amount:", poolState.swapQuoteOutAmount.toNumber());
  console.log("Swap Base2Quote Fee:", poolState.swapBase2QuoteFee.toNumber());
  console.log("Swap Quote In Amount:", poolState.swapQuoteInAmount.toNumber());
  console.log("Swap Base Out Amount:", poolState.swapBaseOutAmount.toNumber());
  console.log("Swap Quote2Base Fee:", poolState.swapQuote2BaseFee.toNumber());

  // For PublicKey fields, you can directly log them as they are:
  console.log("Base Vault:", poolState.baseVault.toString());
  console.log("Quote Vault:", poolState.quoteVault.toString());
  console.log("Base Mint:", poolState.baseMint.toString());
  console.log("Quote Mint:", poolState.quoteMint.toString());
  console.log("LP Mint:", poolState.lpMint.toString());
  console.log("Open Orders:", poolState.openOrders.toString());
  console.log("Market ID:", poolState.marketId.toString());
  console.log("Market Program ID:", poolState.marketProgramId.toString());
  console.log("Target Orders:", poolState.targetOrders.toString());
  console.log("Withdraw Queue:", poolState.withdrawQueue.toString());
  console.log("LP Vault:", poolState.lpVault.toString());
  console.log("Owner:", poolState.owner.toString());
  console.log("LP Reserve:", poolState.lpReserve.toNumber());

2.2 Console Output decoded BigNumbers in decimals

Status: 1
Nonce: 254
Max Order: 7
Depth: 3
Base Decimal: 9
Quote Decimal: 6
State: 1
Reset Flag: 1
Min Size: 1000000
Vol Max Cut Ratio: 500
Amount Wave Ratio: 0
Base Lot Size: 1000000
Quote Lot Size: 1000000
Min Price Multiplier: 1
Max Price Multiplier: 1000000000
System Decimal Value: 1000000000
Min Separate Numerator: 5
Min Separate Denominator: 10000
Trade Fee Numerator: 25
Trade Fee Denominator: 10000
PnL Numerator: 12
PnL Denominator: 100
Swap Fee Numerator: 25
Swap Fee Denominator: 10000
Base Need Take PnL: 5839220000
Quote Need Take PnL: 1058311171
Quote Total PnL: 877015141425
Base Total PnL: 36227185798416
Pool Open Time: 0
Punish Pc Amount: 0
Punish Coin Amount: 0
Orderbook To Init Time: 0
Swap Base In Amount: 7189588306372997
Swap Quote Out Amount: 240728976023339
Swap Base2Quote Fee: 630027353367
Swap Quote In Amount: 252010786914027
Swap Base Out Amount: 7401612924356853
Swap Quote2Base Fee: 17973971111809
Base Vault: DQyrAcCrDXQ7NeoqGgDCZwBvWDcYmFCjSb9JtteuvPpz
Quote Vault: HLmqeL62xR1QoZ1HKKbXRrdN1p3phKpxRMb2VVopvBBz
Base Mint: So11111111111111111111111111111111111111112
Quote Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
LP Mint: 8HoQnePLqPj4M7PUDzfw8e3Ymdwgc7NLGnaTUapubyvu
Open Orders: HmiHHzq4Fym9e1D4qzLS6LDDM3tNsCTBPDWHTLZ763jY
Market ID: 8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6
Market Program ID: srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX
Target Orders: CZza3Ej4Mc58MnxWA385itCC9jCo3L1D7zc3LKy1bZMR
Withdraw Queue: 11111111111111111111111111111111
LP Vault: 11111111111111111111111111111111
Owner: GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ
LP Reserve: 72458562827064

Here is where I struggle
3. Calculation of the Price of one SOL. ( 1 SOL / (X) USDC )

**//ATTEMP NUMBER 1:**

// Just for clarification where I got these values!

// from decoded data: Swap Quote Out Amount: 240728976023339, Swap Base Out Amount: 7401612924356853

function calculatePricePerSOLInUSDC(): string {
    return (Number(BigInt("252010786914027")) / Math.pow(10, 6) / (Number(BigInt("7401612924356853")) / Math.pow(10, 9))).toFixed(6);
  }
  
  console.log(`Price per 1 SOL in USDC: ${calculatePricePerSOLInUSDC()}`);


**//ATTEMPT 2**
//Standard calculation of quote amount / base amount
// Got values of these two "VAULTS"
//Base Vault: DQyrAcCrDXQ7NeoqGgDCZwBvWDcYmFCjSb9JtteuvPpz
//Quote Vault: HLmqeL62xR1QoZ1HKKbXRrdN1p3phKpxRMb2VVopvBBz

// at the current timestamp the values are:
//23,177.61 SOL
//5,700,097.46 USDC

5700097.46 / 23177.61 = 245,931

3.1 Output of Calculation (which obviously can´t be right)

**ATTEMPT 1* Price per 1 SOL in USDC: 34.048091
**ATTEMPT 2* Price per 1 SOl in USDC: 245,931

If any additional information is needed please feel free to leave a comment!

1
  • Has any solution been found for this? We know how to obtain the liquidity of the pool and the market value of those currencies? Commented Jun 6 at 10:10

4 Answers 4

1

This Hybrid AMM pool is sharing idle liquidity with OpenBook, the value in the base and quote vault doesn't account for the orders in the event q of the CLOB.

Divergences between quoteVault and real quote amount can occur.

pool_vault_amount = amm_vault_amount + open_orders.native_coin_total + part_filled_amount - need_take_pnl_coin

You can refer to this program to calculate the amounts in event_q: https://github.com/openbook-dex/program/blob/master/dex/src/state.rs#L2975

Not certain how to interpret, the 34$ result though.

1
  • wheres amm_vault_amount?
    – AdityaDees
    Commented May 10 at 21:27
0

Were you able to calculate a price using the swap amounts? I believe you have to fetch the vault amounts and use those...

baseVaultAmount / quoteVaultAmount

0

I calculate price using following code

market.ts

import { Commitment, Connection, PublicKey } from '@solana/web3.js';
import { GetStructureSchema, MARKET_STATE_LAYOUT_V3, publicKey, struct } from '@raydium-io/raydium-sdk';

export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([publicKey('eventQueue'), publicKey('bids'), publicKey('asks')]);
export type MinimalMarketStateLayoutV3 = typeof MINIMAL_MARKET_STATE_LAYOUT_V3;
export type MinimalMarketLayoutV3 = GetStructureSchema<MinimalMarketStateLayoutV3>;

export async function getMinimalMarketV3(
  connection: Connection,
  marketId: PublicKey,
  commitment?: Commitment,
): Promise<MinimalMarketLayoutV3> {
  const marketInfo = await connection.getAccountInfo(marketId, {
    commitment,
    dataSlice: {
      offset: MARKET_STATE_LAYOUT_V3.offsetOf('eventQueue'),
      length: 32 * 3,
    },
  });

  return MINIMAL_MARKET_STATE_LAYOUT_V3.decode(marketInfo!.data);
}

export function createPoolKeys(
  id: PublicKey,
  accountData: LiquidityStateV4,
  minimalMarketLayoutV3: MinimalMarketLayoutV3,
): LiquidityPoolKeys {
  return {
    id,
    baseMint: accountData.baseMint,
    quoteMint: accountData.quoteMint,
    lpMint: accountData.lpMint,
    baseDecimals: accountData.baseDecimal.toNumber(),
    quoteDecimals: accountData.quoteDecimal.toNumber(),
    lpDecimals: 5,
    version: 4,
    programId: MAINNET_PROGRAM_ID.AmmV4,
    authority: Liquidity.getAssociatedAuthority({
      programId: MAINNET_PROGRAM_ID.AmmV4,
    }).publicKey,
    openOrders: accountData.openOrders,
    targetOrders: accountData.targetOrders,
    baseVault: accountData.baseVault,
    quoteVault: accountData.quoteVault,
    marketVersion: 3,
    marketProgramId: accountData.marketProgramId,
    marketId: accountData.marketId,
    marketAuthority: Market.getAssociatedAuthority({
      programId: accountData.marketProgramId,
      marketId: accountData.marketId,
    }).publicKey,
    marketBaseVault: accountData.baseVault,
    marketQuoteVault: accountData.quoteVault,
    marketBids: minimalMarketLayoutV3.bids,
    marketAsks: minimalMarketLayoutV3.asks,
    marketEventQueue: minimalMarketLayoutV3.eventQueue,
    withdrawQueue: accountData.withdrawQueue,
    lpVault: accountData.lpVault,
    lookupTableAccount: PublicKey.default,
  };
}

// then you get pool keys
const poolKeys: LiquidityPoolKeysV4 = createPoolKeys(accountId, poolState, market);

    // then you can get current price 
    const poolInfo = await Liquidity.fetchInfo({
      connection: this.connection,
      poolKeys,
    });

    const amountOut = Liquidity.computeAmountOut({
      poolKeys,
      poolInfo,
      amountIn: amountIn,
      currencyOut: this.config.quoteToken,
      slippage,
    });

    console.log(Current Price: ${amountOut.currentPrice.toFixed()})

i hope that help you

0

Has any solution been found for this? We know how to obtain the liquidity of the pool and the market value of those currencies?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.