@bagsfm/bags-sdk
Version:
TypeScript SDK for Bags
445 lines • 23 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFeeVaultFromVaultAuthorityAndClaimer = getFeeVaultFromVaultAuthorityAndClaimer;
exports.fetchFeeVaultData = fetchFeeVaultData;
exports.getPoolFeeMetrics = getPoolFeeMetrics;
exports.getAllPositionNftAccountByOwner = getAllPositionNftAccountByOwner;
exports.getUserPositionByPool = getUserPositionByPool;
exports.getMyMeteoraTokenLaunchesAndFees = getMyMeteoraTokenLaunchesAndFees;
exports.getFeeVaultPda = getFeeVaultPda;
const web3_js_1 = require("@solana/web3.js");
const cp_amm_sdk_1 = require("@meteora-ag/cp-amm-sdk");
const spl_token_1 = require("@solana/spl-token");
const constants_1 = require("../constants");
async function getFeeVaultFromVaultAuthorityAndClaimer(vaultAuthority, claimer, bagsMeteoraFeeClaimerProgram) {
const feeVaultResult = await bagsMeteoraFeeClaimerProgram.account.vault.all([
{
memcmp: {
offset: 8,
bytes: vaultAuthority,
},
},
{
memcmp: {
offset: 40,
bytes: claimer,
},
},
]);
if (feeVaultResult.length === 0) {
throw new Error('Custom fee vault not found');
}
return feeVaultResult;
}
async function fetchFeeVaultData(vaultAuthority, claimer, bagsMeteoraFeeClaimerProgram) {
const feeVaultResult = await getFeeVaultFromVaultAuthorityAndClaimer(vaultAuthority, claimer, bagsMeteoraFeeClaimerProgram);
if (feeVaultResult.length === 0) {
throw new Error('Custom fee vault not found');
}
return feeVaultResult;
}
async function getPoolFeeMetrics(poolAddress, meteoraDbcProgram, commitment) {
const pool = await meteoraDbcProgram.account.virtualPool.fetchNullable(poolAddress, commitment);
if (!pool) {
throw new Error(`Pool not found: ${poolAddress.toString()}`);
}
return {
current: {
partnerBaseFee: pool.partnerBaseFee,
partnerQuoteFee: pool.partnerQuoteFee,
creatorBaseFee: pool.creatorBaseFee,
creatorQuoteFee: pool.creatorQuoteFee,
},
total: {
totalTradingBaseFee: pool.metrics.totalTradingBaseFee,
totalTradingQuoteFee: pool.metrics.totalTradingQuoteFee,
},
};
}
async function getAllPositionNftAccountByOwner(connection, commitment, user) {
const tokenAccounts = await connection.getTokenAccountsByOwner(user, {
programId: spl_token_1.TOKEN_2022_PROGRAM_ID,
}, {
commitment,
});
const userPositionNftAccount = [];
for (const { account, pubkey } of tokenAccounts.value) {
const tokenAccountData = spl_token_1.AccountLayout.decode(account.data);
if (tokenAccountData.amount.toString() === '1') {
userPositionNftAccount.push({
positionNft: tokenAccountData.mint,
positionNftAccount: pubkey,
});
}
}
return userPositionNftAccount;
}
async function getUserPositionByPool(dammV2Pool, user, dammV2Program, connection, commitment) {
const userPositionAccounts = await getAllPositionNftAccountByOwner(connection, commitment, user);
if (userPositionAccounts.length === 0) {
return [];
}
const positionAddresses = userPositionAccounts.map((account) => (0, cp_amm_sdk_1.derivePositionAddress)(account.positionNft));
const positionStates = await dammV2Program.account.position.fetchMultiple(positionAddresses);
const positionResult = userPositionAccounts
.map((account, index) => {
const positionState = positionStates[index];
if (!positionState)
return null;
return {
positionNftAccount: account.positionNftAccount,
position: positionAddresses[index],
positionState,
};
})
.filter(Boolean);
positionResult.sort((a, b) => {
const totalLiquidityA = a.positionState.vestedLiquidity.add(a.positionState.permanentLockedLiquidity).add(a.positionState.unlockedLiquidity);
const totalLiquidityB = b.positionState.vestedLiquidity.add(b.positionState.permanentLockedLiquidity).add(b.positionState.unlockedLiquidity);
return totalLiquidityB.cmp(totalLiquidityA);
});
return positionResult.filter((position) => position.positionState.pool.equals(dammV2Pool));
}
async function processVirtualPool(virtualPool, creator, feeVaultTokenMints, feeVaultsAsA, feeVaultsAsB, meteoraDbcProgram, bagsMeteoraFeeClaimerProgram, dammV2Program, commitment, connection) {
try {
const poolFeeMetricsPromise = await getPoolFeeMetrics(virtualPool.publicKey, meteoraDbcProgram, commitment);
const virtualPoolClaimableAmount = poolFeeMetricsPromise.current.partnerQuoteFee.toNumber() / web3_js_1.LAMPORTS_PER_SOL;
const isCustomFeeVault = feeVaultTokenMints.includes(virtualPool.account.baseMint.toBase58());
let customFeeVaultBps = 0;
let customFeeVaultClaimOwner;
let customFeeVaultClaimerA;
let customFeeVaultClaimerB;
let customFeeVaultClaimerSide;
let customFeeVault;
let customFeeVaultBalance;
if (isCustomFeeVault) {
const customFeeVaultAsA = feeVaultsAsA.find((vault) => vault.account.mint.toBase58() === virtualPool.account.baseMint.toBase58());
const customFeeVaultAsB = feeVaultsAsB.find((vault) => vault.account.mint.toBase58() === virtualPool.account.baseMint.toBase58());
if (customFeeVaultAsA) {
customFeeVaultBps = customFeeVaultAsA.account.claimerABps;
customFeeVaultClaimOwner = customFeeVaultAsA.publicKey;
customFeeVaultClaimerA = customFeeVaultAsA.account.claimerA;
customFeeVaultClaimerB = customFeeVaultAsA.account.claimerB;
const feeVaultResult = await fetchFeeVaultData(customFeeVaultAsA.publicKey.toBase58(), customFeeVaultAsA.account.claimerA.toBase58(), bagsMeteoraFeeClaimerProgram);
if (feeVaultResult.length === 0) {
throw new Error('Custom fee vault not found');
}
customFeeVault = feeVaultResult[0].publicKey;
customFeeVaultClaimerSide = 'A';
const feeVaultBalanceRaw = (await connection.getBalance(customFeeVault, commitment)) - constants_1.BAGS_METEORA_FEE_CLAIMER_VAULT_RENT_EXCEMPT_AMOUNT;
customFeeVaultBalance = Math.max(feeVaultBalanceRaw, 0) / web3_js_1.LAMPORTS_PER_SOL;
}
else if (customFeeVaultAsB) {
customFeeVaultBps = customFeeVaultAsB.account.claimerBBps;
customFeeVaultClaimOwner = customFeeVaultAsB.publicKey;
customFeeVaultClaimerA = customFeeVaultAsB.account.claimerA;
customFeeVaultClaimerB = customFeeVaultAsB.account.claimerB;
const feeVaultResult = await fetchFeeVaultData(customFeeVaultAsB.publicKey.toBase58(), customFeeVaultAsB.account.claimerB.toBase58(), bagsMeteoraFeeClaimerProgram);
if (feeVaultResult.length === 0) {
throw new Error('Custom fee vault not found');
}
customFeeVault = feeVaultResult[0].publicKey;
customFeeVaultClaimerSide = 'B';
const feeVaultBalanceRaw = (await connection.getBalance(customFeeVault, commitment)) - constants_1.BAGS_METEORA_FEE_CLAIMER_VAULT_RENT_EXCEMPT_AMOUNT;
customFeeVaultBalance = Math.max(feeVaultBalanceRaw, 0) / web3_js_1.LAMPORTS_PER_SOL;
}
else {
throw new Error('Custom fee vault not found');
}
}
if (!virtualPool.account.isMigrated) {
if (isCustomFeeVault) {
return [
{
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
isMigrated: false,
isCustomFeeVault: true,
customFeeVaultBps: customFeeVaultBps,
customFeeVaultClaimOwner: customFeeVaultClaimOwner,
customFeeVaultClaimerA: customFeeVaultClaimerA,
customFeeVaultClaimerB: customFeeVaultClaimerB,
customFeeVaultClaimerSide: customFeeVaultClaimerSide,
claimableDisplayAmount: virtualPoolClaimableAmount * (customFeeVaultBps / 10000) + customFeeVaultBalance,
customFeeVault: customFeeVault,
customFeeVaultBalance: customFeeVaultBalance,
},
];
}
else {
return [
{
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
isMigrated: false,
isCustomFeeVault: false,
claimableDisplayAmount: virtualPoolClaimableAmount,
},
];
}
}
const dammPools = await dammV2Program.account.pool.all([
{
memcmp: {
offset: 168,
bytes: virtualPool.account.baseMint.toBase58(),
},
},
{
memcmp: {
offset: 328,
bytes: constants_1.METEORA_DBC_MIGRATION_DAMM_V2_CREATOR.toBase58(),
},
},
]);
if (!dammPools.length) {
console.error(`Damm pool not found for virtual pool that migrated ${virtualPool.publicKey.toBase58()}`);
if (isCustomFeeVault) {
return [
{
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
isMigrated: true,
isCustomFeeVault: true,
customFeeVaultBps: customFeeVaultBps,
customFeeVaultClaimOwner: customFeeVaultClaimOwner,
customFeeVaultClaimerA: customFeeVaultClaimerA,
customFeeVaultClaimerB: customFeeVaultClaimerB,
customFeeVaultClaimerSide: customFeeVaultClaimerSide,
claimableDisplayAmount: virtualPoolClaimableAmount * (customFeeVaultBps / 10000) + customFeeVaultBalance,
customFeeVault: customFeeVault,
customFeeVaultBalance: customFeeVaultBalance,
},
];
}
else {
return [
{
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
isMigrated: true,
isCustomFeeVault: false,
claimableDisplayAmount: virtualPoolClaimableAmount,
},
];
}
}
const allDammPoolClaimableAmounts = [];
for (const dammPool of dammPools) {
try {
let pool;
let userPositions;
if (isCustomFeeVault) {
[pool, userPositions] = await Promise.all([
dammV2Program.account.pool.fetchNullable(dammPool.publicKey, commitment),
getUserPositionByPool(dammPool.publicKey, customFeeVaultClaimOwner, dammV2Program, connection, commitment),
]);
}
else {
[pool, userPositions] = await Promise.all([
dammV2Program.account.pool.fetchNullable(dammPool.publicKey, commitment),
getUserPositionByPool(dammPool.publicKey, new web3_js_1.PublicKey(creator), dammV2Program, connection, commitment),
]);
}
if (!userPositions.length) {
console.error(`No user positions found for virtual pool ${virtualPool.publicKey.toBase58()}`);
if (isCustomFeeVault) {
allDammPoolClaimableAmounts.push({
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
isMigrated: true,
isCustomFeeVault: true,
customFeeVaultBps: customFeeVaultBps,
customFeeVaultClaimOwner: customFeeVaultClaimOwner,
customFeeVaultClaimerA: customFeeVaultClaimerA,
customFeeVaultClaimerB: customFeeVaultClaimerB,
customFeeVaultClaimerSide: customFeeVaultClaimerSide,
claimableDisplayAmount: (virtualPoolClaimableAmount + customFeeVaultBalance) * (customFeeVaultBps / 10000),
customFeeVault: customFeeVault,
customFeeVaultBalance: customFeeVaultBalance,
});
}
else {
allDammPoolClaimableAmounts.push({
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
isMigrated: true,
isCustomFeeVault: false,
claimableDisplayAmount: virtualPoolClaimableAmount,
});
}
}
const unClaimReward = await (0, cp_amm_sdk_1.getUnClaimReward)(pool, userPositions[0].positionState);
const dammPoolClaimableAmount = unClaimReward.feeTokenB.toNumber() / web3_js_1.LAMPORTS_PER_SOL;
if (isCustomFeeVault) {
allDammPoolClaimableAmounts.push({
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
dammPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
dammPoolAddress: dammPool.publicKey.toBase58(),
dammPositionInfo: {
owner: new web3_js_1.PublicKey(creator),
position: userPositions[0].position,
pool: userPositions[0].positionState.pool,
positionNftAccount: userPositions[0].positionNftAccount,
tokenAMint: pool.tokenAMint,
tokenBMint: pool.tokenBMint,
tokenAVault: pool.tokenAVault,
tokenBVault: pool.tokenBVault,
tokenAProgram: spl_token_1.TOKEN_PROGRAM_ID,
tokenBProgram: spl_token_1.TOKEN_PROGRAM_ID,
},
isMigrated: true,
isCustomFeeVault: true,
customFeeVaultBps: customFeeVaultBps,
customFeeVaultClaimOwner: customFeeVaultClaimOwner,
customFeeVaultClaimerA: customFeeVaultClaimerA,
customFeeVaultClaimerB: customFeeVaultClaimerB,
customFeeVaultClaimerSide: customFeeVaultClaimerSide,
claimableDisplayAmount: (virtualPoolClaimableAmount + dammPoolClaimableAmount + customFeeVaultBalance) * (customFeeVaultBps / 10000),
customFeeVault: customFeeVault,
customFeeVaultBalance: customFeeVaultBalance,
});
}
else {
allDammPoolClaimableAmounts.push({
virtualPool: virtualPool.publicKey.toBase58(),
baseMint: virtualPool.account.baseMint.toBase58(),
virtualPoolClaimableAmount,
dammPoolClaimableAmount,
virtualPoolAddress: virtualPool.publicKey.toBase58(),
dammPoolAddress: dammPool.publicKey.toBase58(),
dammPositionInfo: {
owner: new web3_js_1.PublicKey(creator),
position: userPositions[0].position,
pool: userPositions[0].positionState.pool,
positionNftAccount: userPositions[0].positionNftAccount,
tokenAMint: pool.tokenAMint,
tokenBMint: pool.tokenBMint,
tokenAVault: pool.tokenAVault,
tokenBVault: pool.tokenBVault,
tokenAProgram: spl_token_1.TOKEN_PROGRAM_ID,
tokenBProgram: spl_token_1.TOKEN_PROGRAM_ID,
},
isMigrated: true,
isCustomFeeVault: false,
claimableDisplayAmount: virtualPoolClaimableAmount + dammPoolClaimableAmount,
});
}
}
catch (error) {
console.error(`Error processing damm pool ${dammPool.publicKey.toBase58()}: ${error}`);
}
}
return allDammPoolClaimableAmounts;
}
catch (error) {
console.error(`Error processing virtual pool: ${error}`);
return null;
}
}
const mergeMeteoraLaunches = (launches) => {
const mergedMap = new Map();
for (const launch of launches) {
const existingLaunch = mergedMap.get(launch.virtualPool);
if (!existingLaunch) {
mergedMap.set(launch.virtualPool, launch);
continue;
}
if (existingLaunch.virtualPoolClaimableAmount === launch.virtualPoolClaimableAmount) {
if (launch.dammPositionInfo && !existingLaunch.dammPositionInfo) {
mergedMap.set(launch.virtualPool, launch);
}
else if (!launch.dammPositionInfo && existingLaunch.dammPositionInfo) {
continue;
}
}
else {
mergedMap.set(launch.virtualPool, launch);
}
}
return Array.from(mergedMap.values());
};
async function getMyMeteoraTokenLaunchesAndFees(creator, meteoraDbcProgram, dammV2Program, bagsMeteoraFeeClaimerProgram, commitment, connection, getPoolConfigKeysByFeeClaimerVaults, chunkSize = 5) {
if (!creator || creator.length === 0) {
throw new Error('Creator is required');
}
try {
const allConfigKeysForUser = [];
const [configsCreatedDirectlyByUser, feeVaultsForUserAsA, feeVaultsForUserAsB] = await Promise.all([
meteoraDbcProgram.account.poolConfig.all([
{
memcmp: {
offset: 40,
bytes: creator,
},
},
]),
bagsMeteoraFeeClaimerProgram.account.feeAuthority.all([
{
memcmp: {
offset: 8,
bytes: creator,
},
},
]),
bagsMeteoraFeeClaimerProgram.account.feeAuthority.all([
{
memcmp: {
offset: 40,
bytes: creator,
},
},
])
]);
const feeVaults = [...feeVaultsForUserAsA, ...feeVaultsForUserAsB];
const feeVaultTokenMints = [...feeVaults.map((vault) => vault.account.mint.toBase58())];
const allFeeVaultKeys = [...feeVaults.map((vault) => vault.publicKey)];
const configKeysForFeeVaults = await getPoolConfigKeysByFeeClaimerVaults(allFeeVaultKeys);
allConfigKeysForUser.push(...configsCreatedDirectlyByUser.map((config) => config.publicKey), ...configKeysForFeeVaults);
const allVirtualPools = [];
const configChunks = Array.from({ length: Math.ceil(allConfigKeysForUser.length / chunkSize) }, (_, i) => allConfigKeysForUser.slice(i * chunkSize, (i + 1) * chunkSize));
for (const chunk of configChunks) {
const chunkResults = await Promise.all(chunk.map(config => meteoraDbcProgram.account.virtualPool.all([
{
memcmp: {
offset: 72,
bytes: config.toBase58(),
},
},
])));
allVirtualPools.push(...chunkResults);
}
const virtualPools = allVirtualPools.flat();
const chunkedPools = Array.from({ length: Math.ceil(virtualPools.length / chunkSize) }, (_, i) => virtualPools.slice(i * chunkSize, (i + 1) * chunkSize));
const chunkResults = await Promise.all(chunkedPools.map((chunk) => Promise.all(chunk.map((pool) => processVirtualPool(pool, creator, feeVaultTokenMints, feeVaultsForUserAsA, feeVaultsForUserAsB, meteoraDbcProgram, bagsMeteoraFeeClaimerProgram, dammV2Program, commitment, connection)))));
const results = chunkResults
.flat()
.flat()
.filter((result) => result !== null);
const mergedResults = mergeMeteoraLaunches(results);
return mergedResults.sort((a, b) => b.claimableDisplayAmount - a.claimableDisplayAmount);
}
catch (error) {
console.error('Error fetching Meteora virtual pools:', error);
throw error;
}
}
function getFeeVaultPda(feeClaimer, baseMint, programId) {
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.BAGS_METEORA_FEE_CLAIMER_VAULT_PDA_SEED), feeClaimer.toBuffer(), baseMint.toBuffer()], programId)[0];
}
//# sourceMappingURL=fee-claim.js.map