Mp 2565 api for active vaults (#239)

* feat: get deposited vaults functions

* feat: functional improvements

* cleanup: logging

* cleanup: remove unused import

* update: naming conventions
This commit is contained in:
Yusuf Seyrek 2023-06-05 09:31:46 +03:00 committed by GitHub
parent 9d09ebdf77
commit 2156431355
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 235 additions and 7 deletions

View File

@ -0,0 +1,16 @@
import { getClient } from 'api/cosmwasm-client'
import { ENV } from 'constants/env'
export default async function getPrice(denom: string): Promise<PriceResult> {
try {
const client = await getClient()
return await client.queryContractSmart(ENV.ADDRESS_ORACLE, {
price: {
denom,
},
})
} catch (ex) {
throw ex
}
}

View File

@ -0,0 +1,183 @@
import moment from 'moment'
import { getClient } from 'api/cosmwasm-client'
import { ENV } from 'constants/env'
import getVaults from 'api/vaults/getVaults'
import {
Positions,
VaultPosition,
VaultPositionAmount,
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { BN } from 'utils/helpers'
import getPrice from 'api/prices/getPrice'
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 getVaultPositionStatusAndUnlockTime(
vaultPosition: VaultPosition,
): Promise<[VaultStatus, number | undefined]> {
const amount = vaultPosition.amount
if ('unlocked' in amount) return ['unlocked', undefined]
if (amount.locking.unlocking.length) {
const unlocksAtTimestamp = await getUnlocksAtTimestamp(
amount.locking.unlocking[0].id,
vaultPosition.vault.address,
)
if (moment(unlocksAtTimestamp).isBefore(new Date())) {
return ['unlocked', unlocksAtTimestamp]
}
return ['unlocking', unlocksAtTimestamp]
} else {
return ['active', undefined]
}
}
function flatVaultPositionAmount(
vaultPositionAmount: VaultPositionAmount,
): VaultPositionFlatAmounts {
const amounts = {
locked: '0',
unlocking: '0',
unlocked: '0',
}
if ('locking' in vaultPositionAmount) {
const { locked, unlocking } = vaultPositionAmount.locking
amounts.locked = locked
amounts.unlocking = unlocking[0]?.coin.amount ?? '0'
} else if ('unlocked' in vaultPositionAmount) {
amounts.unlocked = vaultPositionAmount.unlocked
}
return amounts
}
async function getLpTokensForVaultPosition(
vault: Vault,
vaultPosition: VaultPosition,
): Promise<Coin[]> {
try {
const client = await getClient()
const amounts = flatVaultPositionAmount(vaultPosition.amount)
const totalAmount = BN(amounts.locked)
.plus(BN(amounts.unlocked))
.plus(BN(amounts.unlocking))
.toString()
const lpAmount = await client.queryContractSmart(vault.address, {
preview_redeem: {
amount: totalAmount,
},
})
const lpTokens: Coin[] = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
estimate_withdraw_liquidity: {
lp_token: {
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<VaultValuesAndAmounts> {
try {
const pricesQueries = Promise.all([
getPrice(vault.denoms.primary),
getPrice(vault.denoms.secondary),
])
const lpTokensQuery = getLpTokensForVaultPosition(vault, vaultPosition)
const [[primaryLpToken, secondaryLpToken], [primaryAsset, secondaryAsset]] = await Promise.all([
lpTokensQuery,
pricesQueries,
])
return {
amounts: {
primary: BN(primaryLpToken.amount),
secondary: BN(secondaryLpToken.amount),
},
values: {
primary: BN(primaryLpToken.amount).multipliedBy(BN(primaryAsset.price)),
secondary: BN(secondaryLpToken.amount).multipliedBy(BN(secondaryAsset.price)),
},
}
} catch (ex) {
throw ex
}
}
async function getDepositedVaults(accountId: string): Promise<DepositedVault[]> {
try {
const client = await getClient()
const positionsQuery = client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
positions: {
account_id: accountId,
},
}) as Promise<Positions>
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, unlocksAt], valuesAndAmounts] = await Promise.all([
getVaultPositionStatusAndUnlockTime(vaultPosition),
getVaultValuesAndAmounts(vault, vaultPosition),
])
return {
...vault,
status,
unlocksAt,
...valuesAndAmounts,
}
})
return await Promise.all(depositedVaults)
} catch (ex) {
throw ex
}
}
export default getDepositedVaults

View File

@ -5,7 +5,6 @@ import { getClient } from 'api/cosmwasm-client'
export default async function getAccounts(address: string): Promise<Account[]> {
const accountIds: string[] = await getWalletAccountIds(address)
const client = await getClient()
const $accounts: Promise<AccountResponse>[] = accountIds.map((accountId) =>

View File

@ -0,0 +1,9 @@
import useSWR from 'swr'
import getDepositedVaults from 'api/vaults/getDepositedVaults'
export default function useDepositedVaults(accountId: string) {
return useSWR(`depositedVaultsByAccount-${accountId}`, () => getDepositedVaults(accountId), {
suspense: true,
})
}

View File

@ -1,3 +1,4 @@
type BigNumber = import('bignumber.js').BigNumber
interface VaultMetaData {
address: string
name: string
@ -37,15 +38,34 @@ interface Vault extends VaultConfig {
apy: number | null
}
interface ActiveVault extends Vault {
status: 'active' | 'unlocking' | 'unlocked'
interface VaultValuesAndAmounts {
amounts: {
primary: number
secondary: number
primary: BigNumber
secondary: BigNumber
}
values: {
primary: number
secondary: number
primary: BigNumber
secondary: BigNumber
}
}
type VaultStatus = 'active' | 'unlocking' | 'unlocked'
interface DepositedVault extends Vault, VaultValuesAndAmounts {
status: VaultStatus
unlocksAt?: number
}
interface VaultExtensionResponse {
base_token_amount: string
id: number
owner: string
release_at: {
at_time: string
}
}
interface VaultPositionFlatAmounts {
locked: string
unlocking: string
unlocked: string
}

View File

@ -1,5 +1,6 @@
import BigNumber from 'bignumber.js'
BigNumber.config({ EXPONENTIAL_AT: 1e9 })
export function BN(n: BigNumber.Value) {
return new BigNumber(n)
}