UNPKG

@bagsfm/bags-sdk

Version:

TypeScript SDK for Bags

445 lines 23 kB
"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