import type { Account, Address } from "@solana/kit"; import { fetchRequestWithdrawVaultReceipt, fetchVault, findRequestWithdrawVaultReceiptPda, findVaultLpMintPda, VOLTR_VAULT_PROGRAM_ADDRESS, type RequestWithdrawVaultReceipt, type Vault, } from "../generated"; import { fetchAllRequestWithdrawVaultReceiptsOfVault } from "./batchFetchers"; import { convertDecimalBitsToNumber } from "./decimal"; import { getTotalManagementFeeBps } from "./lpEconomics"; import { calculateAssetsForWithdrawAmount } from "./math"; import type { ExtensionsRpc } from "./rpc"; export type PendingWithdrawal = { user: Address; amountAssetToWithdrawEffective: number; amountAssetToWithdrawAtRequest: number; amountAssetToWithdrawAtPresent: number; amountLpEscrowed: bigint; withdrawableFromTs: bigint; }; function processWithdrawalReceipt( receipt: RequestWithdrawVaultReceipt, vault: Vault, lpSupply: bigint, ): PendingWithdrawal { const amountAssetToWithdrawAtRequest = convertDecimalBitsToNumber( receipt.amountAssetToWithdrawDecimalBits, ); const amountAssetToWithdrawAtPresent = Number( calculateAssetsForWithdrawAmount({ vaultTotalValue: vault.asset.totalValue, vaultLastUpdatedLockedProfit: vault.lockedProfitState.lastUpdatedLockedProfit, vaultLastReport: vault.lockedProfitState.lastReport, vaultLockedProfitDegradationDuration: vault.vaultConfiguration.lockedProfitDegradationDuration, vaultAccumulatedLpAdminFees: vault.feeState.accumulatedLpAdminFees, vaultAccumulatedLpManagerFees: vault.feeState.accumulatedLpManagerFees, vaultAccumulatedLpProtocolFees: vault.feeState.accumulatedLpProtocolFees, vaultDeadWeight: vault.deadWeight, vaultRedemptionFeeBps: vault.feeConfiguration.redemptionFee, vaultManagementFeeBps: getTotalManagementFeeBps(vault.feeConfiguration), vaultLastManagementFeeUpdateTs: vault.feeUpdate.lastManagementFeeUpdateTs, lpSupply, lpAmount: receipt.amountLpEscrowed, }), ); return { user: receipt.user, amountAssetToWithdrawEffective: Math.min( amountAssetToWithdrawAtPresent, amountAssetToWithdrawAtRequest, ), amountAssetToWithdrawAtRequest, amountAssetToWithdrawAtPresent, amountLpEscrowed: receipt.amountLpEscrowed, withdrawableFromTs: receipt.withdrawableFromTs, }; } async function fetchVaultWithLpSupply( rpc: ExtensionsRpc, vault: Address, ): Promise<{ data: Vault; lpSupply: bigint }> { const account = await fetchVault(rpc, vault); const [lpMint] = await findVaultLpMintPda( { vault }, { programAddress: VOLTR_VAULT_PROGRAM_ADDRESS }, ); const supplyResp = await rpc.getTokenSupply(lpMint).send(); return { data: account.data, lpSupply: BigInt(supplyResp.value.amount), }; } export async function getPendingWithdrawalForUser( rpc: ExtensionsRpc, vault: Address, user: Address, ): Promise { const { data, lpSupply } = await fetchVaultWithLpSupply(rpc, vault); const [receiptAddress] = await findRequestWithdrawVaultReceiptPda( { vault, userTransferAuthority: user }, { programAddress: VOLTR_VAULT_PROGRAM_ADDRESS }, ); const receipt = await fetchRequestWithdrawVaultReceipt(rpc, receiptAddress); return processWithdrawalReceipt(receipt.data, data, lpSupply); } export async function getAllPendingWithdrawalsForVault( rpc: ExtensionsRpc, vault: Address, ): Promise { const [{ data, lpSupply }, receipts] = await Promise.all([ fetchVaultWithLpSupply(rpc, vault), fetchAllRequestWithdrawVaultReceiptsOfVault(rpc, vault), ]); return receipts.map((account: Account) => processWithdrawalReceipt(account.data, data, lpSupply), ); } export { processWithdrawalReceipt };