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:
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)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 forpositionsV2
(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.
[
{
"txn_id": "2s4KcYLb1cGJud2bmu6th8xvXRQNKurWmozUsZFBVZqTfmN3ne9RhkgCcgq7it5LMDuMucS3ZRmMvh6ihuwECCAq",
"onchain_timestamp": "2024-05-07T05:01:11.000Z",
"tokenX_amount": 283493113,
"tokenX_address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"tokenY_amount": 2308927352,
"tokenY_address": "So11111111111111111111111111111111111111112"
}
]
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