import moment from 'moment' import { getClient, getCreditManagerQueryClient, getVaultQueryClient } from 'api/cosmwasm-client' import getPrice from 'api/prices/getPrice' import getVaults from 'api/vaults/getVaults' import { VaultStatus } from 'types/enums/vault' import { VaultPosition, VaultPositionAmount, } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { BN } from 'utils/helpers' async function getUnlocksAtTimestamp(unlockingId: number, vaultAddress: string) { try { const client = await getClient() const vaultExtension = (await client.queryContractSmart(vaultAddress, { vault_extension: { lockup: { unlocking_position: { lockup_id: unlockingId } } }, })) as VaultExtensionResponse return Number(vaultExtension.release_at.at_time) / 1e6 } catch (ex) { throw ex } } async function getVaultPositionStatusAndUnlockIdAndUnlockTime( vaultPosition: VaultPosition, ): Promise<[VaultStatus, number | undefined, number | undefined]> { const amount = vaultPosition.amount if (VaultStatus.UNLOCKED in amount) return [VaultStatus.UNLOCKED, undefined, undefined] if (amount.locking.unlocking.length) { const unlockId = amount.locking.unlocking[0].id const unlocksAtTimestamp = await getUnlocksAtTimestamp(unlockId, vaultPosition.vault.address) if (moment(unlocksAtTimestamp).isBefore(new Date())) { return [VaultStatus.UNLOCKED, unlockId, unlocksAtTimestamp] } return [VaultStatus.UNLOCKING, unlockId, unlocksAtTimestamp] } else { return [VaultStatus.ACTIVE, undefined, undefined] } } function flatVaultPositionAmount( vaultPositionAmount: VaultPositionAmount, ): VaultPositionFlatAmounts { const amounts = { locked: BN(0), unlocking: BN(0), unlocked: BN(0), } if ('locking' in vaultPositionAmount) { const { locked, unlocking } = vaultPositionAmount.locking amounts.locked = BN(locked) amounts.unlocking = BN(unlocking[0]?.coin.amount ?? '0') } else if ('unlocked' in vaultPositionAmount) { amounts.unlocked = BN(vaultPositionAmount.unlocked) } return amounts } async function getLpTokensForVaultPosition( vault: Vault, vaultPosition: VaultPosition, ): Promise { try { const vaultQueryClient = await getVaultQueryClient(vault.address) const creditManagerQueryClient = await getCreditManagerQueryClient() const amounts = flatVaultPositionAmount(vaultPosition.amount) const totalAmount = BN(amounts.locked) .plus(BN(amounts.unlocked)) .plus(BN(amounts.unlocking)) .toString() const lpAmount = await vaultQueryClient.previewRedeem({ amount: totalAmount, }) const lpTokens = await creditManagerQueryClient.estimateWithdrawLiquidity({ lpToken: { amount: lpAmount, denom: vault.denoms.lp, }, }) const primaryLpToken = lpTokens.find((t) => t.denom === vault.denoms.primary) ?? { amount: '0', denom: vault.denoms.primary, } const secondaryLpToken = lpTokens.find((t) => t.denom === vault.denoms.secondary) ?? { amount: '0', denom: vault.denoms.secondary, } return [primaryLpToken, secondaryLpToken] } catch (ex) { throw ex } } async function getVaultValuesAndAmounts( vault: Vault, vaultPosition: VaultPosition, ): Promise { try { const pricesQueries = Promise.all([ getPrice(vault.denoms.primary), getPrice(vault.denoms.secondary), ]) const lpTokensQuery = getLpTokensForVaultPosition(vault, vaultPosition) const amounts = flatVaultPositionAmount(vaultPosition.amount) const [[primaryLpToken, secondaryLpToken], [primaryAsset, secondaryAsset]] = await Promise.all([ lpTokensQuery, pricesQueries, ]) return { amounts: { ...amounts, primary: BN(primaryLpToken.amount), secondary: BN(secondaryLpToken.amount), }, values: { primary: BN(primaryLpToken.amount).multipliedBy(primaryAsset), secondary: BN(secondaryLpToken.amount).multipliedBy(secondaryAsset), }, } } catch (ex) { throw ex } } async function getDepositedVaults(accountId: string): Promise { try { const creditManagerQueryClient = await getCreditManagerQueryClient() const positionsQuery = creditManagerQueryClient.positions({ accountId }) const [positions, allVaults] = await Promise.all([positionsQuery, getVaults()]) const depositedVaults = positions.vaults.map(async (vaultPosition) => { const vault = allVaults.find((v) => v.address === vaultPosition.vault.address) if (!vault) { throw 'Could not find the deposited vault among all vaults' } const [[status, unlockId, unlocksAt], valuesAndAmounts] = await Promise.all([ getVaultPositionStatusAndUnlockIdAndUnlockTime(vaultPosition), getVaultValuesAndAmounts(vault, vaultPosition), ]) return { ...vault, status, unlockId, unlocksAt, ...valuesAndAmounts, } }) return await Promise.all(depositedVaults) } catch (ex) { throw ex } } export default getDepositedVaults