Get All User Positions and Withdrawals for a Pool
Fetch all user positions for a pool for Meteora DLMM and their withdrawn amounts
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 getPoolDetails(poolAddress) {
//querying pool details with LbPair address
const operationsDoc = `
query MyQuery {
meteora_dlmm_LbPair(
where: {pubkey: {_eq: ${JSON.stringify(poolAddress)}}}
) {
activeId
tokenXMint
tokenYMint
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();
if (errors) {
return {
meteora_dlmm_LbPair: [],
};
}
return data;
}
async function getPositionsByPool(poolAddress) {
//querying pool details with LbPair address
const operationsDoc = `
query MyQuery {
meteora_dlmm_Position(
where: {lbPair: {_eq: ${JSON.stringify(poolAddress)}}}
) {
_lamports
lbPair
owner
pubkey
}
meteora_dlmm_PositionV2(
where: {lbPair: {_eq: ${JSON.stringify(poolAddress)}}}
) {
_lamports
lbPair
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();
if (errors) {
return {
positionsV1: [],
positionV2: [],
};
}
let positionsV1 = [];
let positionsV2 = [];
if (data.meteora_dlmm_Position.length > 0) {
positionsV1 = data.meteora_dlmm_Position;
}
if (data.meteora_dlmm_PositionV2.length > 0) {
positionsV2 = data.meteora_dlmm_PositionV2;
}
return {
positionsV1: positionsV1,
positionsV2: positionsV2,
};
}
async function getUserWithdrawsByPositionV1(positionAddress,tokenX,tokenY) {
let genesisTxnReached = false;
let removeLiquidityTxns = [];
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 === "REMOVE_LIQUIDITY")
removeLiquidityTxns.push(txn);
});
if(transactions.length < 10){
genesisTxnReached = true;
break;
}
lastSignature = transactions[transactions.length - 1].signatures[0];
}
const removedLiquidityDetails = [];
if(removeLiquidityTxns.length > 0){
removeLiquidityTxns.forEach((removeLiquidityTxn) => {
let eachRemovedTxn = {
"txn_id":removeLiquidityTxn.signatures[0],
"onchain_timestamp": removeLiquidityTxn.timestamp,
};
removeLiquidityTxn.actions.map((action) => {
if(action.type === "REMOVE_LIQUIDITY"){
action.info.liquidity_removed.map((liquidityDetails) => {
if(liquidityDetails.token_address === tokenX){
eachRemovedTxn = {
...eachRemovedTxn,
"tokenX_amount":liquidityDetails.amount_raw,
"tokenX_address":liquidityDetails.token_address
}
}
if(liquidityDetails.token_address === tokenY){
eachRemovedTxn = {
...eachRemovedTxn,
"tokenY_amount":liquidityDetails.amount_raw,
"tokenY_address":liquidityDetails.token_address
}
}
})
}
})
removedLiquidityDetails.push(eachRemovedTxn);
});
}
return removedLiquidityDetails;
}
async function getUserWithdrawsByPositionV2(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 === lbPair && action.info.receiver === ownerAddress){
tokenTransferTxns.push(txn)
}
})
}
});
if(transactions.length < 10){
genesisTxnReached = true;
break;
}
lastSignature = transactions[transactions.length - 1].signatures[0];
}
const removedLiquidityDetails = [];
if(tokenTransferTxns.length > 0){
tokenTransferTxns.forEach((tokenTransferTxn) => {
let eachRemovedTxn = {
"txn_id":tokenTransferTxn.signatures[0],
"onchain_timestamp": tokenTransferTxn.timestamp,
};
tokenTransferTxn.actions.map((action) => {
if(action.type === "TOKEN_TRANSFER"){
if(action.info.token_address === tokenX){
eachRemovedTxn = {
...eachRemovedTxn,
"tokenX_amount":action.info.amount_raw,
"tokenX_address":action.info.token_address
}
}
if(action.info.token_address === tokenY){
eachRemovedTxn = {
...eachRemovedTxn,
"tokenY_amount":action.info.amount_raw,
"tokenY_address":action.info.token_address
}
}
}
})
if(removedLiquidityDetails.length === 0 || removedLiquidityDetails.map((txn) => txn.txn_id).includes(eachRemovedTxn.txn_id) === false)
removedLiquidityDetails.push(eachRemovedTxn);
});
}
return removedLiquidityDetails;
}
async function getUserPoolDetails(poolAddress) {
let allPoolPositionDetails = [];
let allPoolPositionDetailsV1 = [];
const poolDetails = await getPoolDetails(poolAddress);
const tokenMintX = poolDetails.meteora_dlmm_LbPair[0].tokenXMint;
const tokenMintY = poolDetails.meteora_dlmm_LbPair[0].tokenYMint;
console.log("Tokens involved in this pool");
console.log("TokenX:", tokenMintX);
console.log("TokenY:", tokenMintY);
const positions = await getPositionsByPool(poolAddress);
for (let i = 0; i < positions.positionsV1.length; i++) {
const currentPositionV1 = positions.positionsV1[i];
const liquidityDetails = await getUserWithdrawsByPositionV1(
currentPositionV1.pubkey,
tokenMintX,
tokenMintY,
);
allPoolPositionDetailsV1.push({
owner: currentPositionV1.owner,
positionAddress: currentPositionV1.pubkey,
poolAddress: poolAddress,
liquidityWithdrawDetails: liquidityDetails,
});
}
for (let i = 0; i < positions.positionsV2.length; i++) {
const currentPositionV2 = positions.positionsV2[i];
const liquidityDetails = await getUserWithdrawsByPositionV2(
currentPositionV2.owner,
currentPositionV2.pubkey,
poolAddress,
tokenMintX,
tokenMintY,
);
allPoolPositionDetails.push({
owner: currentPositionV2.owner,
positionAddress: currentPositionV2.pubkey,
poolAddress: poolAddress,
liquidityWithdrawDetails: liquidityDetails,
});
}
console.dir({
poolAddress: poolAddress,
tokenXMint: tokenMintX,
tokenYMint: tokenMintY,
allPoolPositionV1Details: allPoolPositionDetailsV1,
allPoolPositionV2Details: allPoolPositionDetails
},{depth:null})
}
getUserPoolDetails("FFpF3BNiBvtewyACdxvvedLDdbZ3einkkpJ7NnknpeqU");
//replace with your pool addressLast updated