Get All Deposits for a User

Fetch all deposits for a liquidity provider on Meteora DLMM

Whenever a user deposits some token to a decentralized exchange's pool, they gain a position, which signifies how much of the pool the user owns. With SHYFT's graphQL APIs, we can find out how much liquidity one particular liquidity provider has deposited. The process however involves two simple steps:

  1. Suppose we have a liquidity provider (user) address, we fetch all the positions on Meteora that the provider holds, and their lbPair details. This step should give us the position address(es) for the provider, and the tokens involved for each of the related pool . (A liquidity provider may have positions for multiple pools)

  2. Once we have the above details, we get all the transactions for the position address received. The liquidity deposited details are stored in the add liquidity transactions for positions, and as token transfers for positionsV2 (with receiver being the pool address). Owing to SHYFT's efficient transaction parsing, we can easily identify transactions of "ADD_LIQUIDITY" and "TOKEN_TRANSFER" type.

You can directly copy paste this code on replit and see it in action.

import { ShyftSdk, Network } from "@shyft-to/js";

const SHYFT_API_KEY = "YOUR_SHYFT_API_KEY";

const shyft = new ShyftSdk({ apiKey: SHYFT_API_KEY, network: Network.Mainnet });

async function getUserDepositsByPositionV1(positionAddress, tokenX, tokenY) {
    let addLiquidityFound = false;
    let genesisTxnReached = false;
    let addLiquidity = [];
    let lastSignature = undefined;

    while (!addLiquidityFound || !genesisTxnReached) {
        const transactions = await shyft.transaction.history({
            account: positionAddress,
            network: Network.Mainnet,
            txNum: 10,
            beforeTxSignature: lastSignature,
        });
        transactions.map((txn) => {
            if (txn.type === "ADD_LIQUIDITY") addLiquidity.push(txn);
            addLiquidityFound = true;
        });

        if (transactions.length < 10) {
            genesisTxnReached = true;
            break;
        }
        lastSignature = transactions[transactions.length - 1].signatures[0];
    }
    const amountAddedDetails = [];
    addLiquidity.map((addLiquidityTxn) => {
        addLiquidityTxn.actions.map((action) => {
            let eachAddedTxn = {
                txn_id: addLiquidityTxn.signatures[0],
                onchain_timestamp: addLiquidityTxn.timestamp,
            };
            if (action.type === "ADD_LIQUIDITY") {
                action.info.liquidity_added.map((liquidityDetails) => {
                    if (liquidityDetails.token_address === tokenX) {
                        eachAddedTxn = {
                            ...eachAddedTxn,
                            tokenX_amount: liquidityDetails.amount_raw,
                            tokenX_address: liquidityDetails.token_address,
                        };
                    }
                    if (liquidityDetails.token_address === tokenY) {
                        eachAddedTxn = {
                            ...eachAddedTxn,
                            tokenY_amount: liquidityDetails.amount_raw,
                            tokenY_address: liquidityDetails.token_address,
                        };
                    }
                });
                amountAddedDetails.push(eachAddedTxn);
            }
        });
    });
    console.log(amountAddedDetails);
}

async function getUserDepositsByPositionV2(
    ownerAddress,
    positionAddress,
    lbPair,
    tokenX,
    tokenY,
) {
    let genesisTxnReached = false;
    let tokenTransferTxns = [];
    let lastSignature = undefined;

    while (!genesisTxnReached) {
        const transactions = await shyft.transaction.history({
            account: positionAddress,
            network: Network.Mainnet,
            txNum: 10,
            beforeTxSignature: lastSignature,
        });
        
        transactions.map((txn) => {
            if (txn.type === "TOKEN_TRANSFER") {
                txn.actions.map((action) => {
                    if (
                        action.type === "TOKEN_TRANSFER" &&
                        action.info.sender === ownerAddress &&
                        action.info.receiver === lbPair
                    ) {
                        tokenTransferTxns.push(txn);
                    }
                });
            }
            if (txn.type === "INITIALIZEPOSITION") tokenTransferTxns.push(txn);
            // addLiquidity = txn;
        });

        if (transactions.length < 10) {
            genesisTxnReached = true;
            break;
        }
        lastSignature = transactions[transactions.length - 1].signatures[0];
    }
    
    const addedLiquidityDetails = [];

    if (tokenTransferTxns.length > 0) {
        tokenTransferTxns.forEach((tokenTransferTxn) => {
            let eachAddedTxn = {
                txn_id: tokenTransferTxn.signatures[0],
                onchain_timestamp: tokenTransferTxn.timestamp,
            };
            tokenTransferTxn.actions.map((action) => {
                if (action.type === "TOKEN_TRANSFER") {
                    if (action.info.token_address === tokenX) {
                        eachAddedTxn = {
                            ...eachAddedTxn,
                            tokenX_amount: action.info.amount_raw,
                            tokenX_address: action.info.token_address,
                        };
                    }
                    if (action.info.token_address === tokenY) {
                        eachAddedTxn = {
                            ...eachAddedTxn,
                            tokenY_amount: action.info.amount_raw,
                            tokenY_address: action.info.token_address,
                        };
                    }
                }
            });
            if (
                addedLiquidityDetails.length === 0 ||
                addedLiquidityDetails
                    .map((txn) => txn.txn_id)
                    .includes(eachAddedTxn.txn_id) === false
            )
                addedLiquidityDetails.push(eachAddedTxn);
        });
    }
    console.log(addedLiquidityDetails);
}

async function getLbPairDetails(lbPairAddress) {
    const LbPairDetails = await fetch(
        `https://programs.shyft.to/v0/graphql/accounts?api_key=${SHYFT_API_KEY}&network=mainnet-beta`, //SHYFT's GQL endpoint
        {
            method: "POST",
            body: JSON.stringify({
                query: `
                query MyQuery {
                    meteora_dlmm_LbPair(
                        where: {pubkey: {_eq: ${JSON.stringify(lbPairAddress)}}}
                ) {
                    pubkey
                    tokenXMint
                    tokenYMint
                    }
                }
          `, //querying the LB pair details
                variables: {},
                operationName: "MyQuery",
            }),
        },
    );
    const LBPairResponse = await LbPairDetails.json();

    return {
        pubkey: LBPairResponse.data.meteora_dlmm_LbPair[0].pubkey,
        tokenY: LBPairResponse.data.meteora_dlmm_LbPair[0].tokenYMint,
        tokenX: LBPairResponse.data.meteora_dlmm_LbPair[0].tokenXMint,
    };
}

async function getPositionLiquidityDetails(ownerAddress) {
    //getting all the positions the user holds
    const operationsDoc = `
    query MyQuery {
      meteora_dlmm_PositionV2(
        where: {owner: {_eq: ${JSON.stringify(ownerAddress)}}}
      ) {
        upperBinId
        lowerBinId
        totalClaimedFeeYAmount
        totalClaimedFeeXAmount
        lastUpdatedAt
        lbPair
        owner
        pubkey
      }
      meteora_dlmm_Position(
        where: {owner: {_eq: ${JSON.stringify(ownerAddress)}}}
      ) {
        lastUpdatedAt
        lbPair
        lowerBinId
        upperBinId
        totalClaimedFeeYAmount
        totalClaimedFeeXAmount
        owner
        pubkey
      }
    }
`; //you can cherrypick the fields as per your requirement
    const result = await fetch(
        `https://programs.shyft.to/v0/graphql/accounts?api_key=${SHYFT_API_KEY}&network=mainnet-beta`, //SHYFT's GQL endpoint
        {
            method: "POST",
            body: JSON.stringify({
                query: operationsDoc,
                variables: {},
                operationName: "MyQuery",
            }),
        },
    );

    const { errors, data } = await result.json();

    //adding a delay of 2 seconds to avoid rate limiting, only for free API Keys.
    //await new Promise((resolve) => setTimeout(resolve, 2000));

    if (data.meteora_dlmm_Position.length > 0) {
        for (
            let index = 0;
            index < data.meteora_dlmm_Position.length;
            index++
        ) {
            const position = data.meteora_dlmm_Position[index];

            //get all Lb pair details for the position
            const LbPairDetails = await getLbPairDetails(position.lbPair);
            //fetch and display all the liquidity details
            await getUserDepositsByPositionV1(
                position.pubkey,
                LbPairDetails.tokenX,
                LbPairDetails.tokenY,
            );
        }
    }

    //adding a delay of 2 seconds to avoid rate limiting, only for free API Keys.
    //await new Promise((resolve) => setTimeout(resolve, 2000));

    if (data.meteora_dlmm_PositionV2.length > 0) {
        for (
            let index = 0;
            index < data.meteora_dlmm_PositionV2.length;
            index++
        ) {
            const position = data.meteora_dlmm_PositionV2[index];

            //get all Lb pair details for the positionV2
            const LbPairDetails = await getLbPairDetails(position.lbPair);
            //fetch and display all the liquidity details for the provider
            await getUserDepositsByPositionV2(
                position.owner,
                position.pubkey,
                position.lbPair,
                LbPairDetails.tokenX,
                LbPairDetails.tokenY,
            );
        }
    }
}

getPositionLiquidityDetails("9gM46puuLU26Z9W8krXDjDMdrFnvvtvK1eNQcgvuMTr9");
//liquidity provider wallet.

Please note that liquidity can also be fetched for particular positions and liquidity provider, for that we would have to skip the first step, and directly call the getUserDepositsByPositionV1 or getUserDepositsByPositionV2 functions.

Last updated