Mp 2539 vaults api (#156)

* add basic endpoint for vaultConfigs

* implement apy for vaults
This commit is contained in:
Bob van der Helm 2023-04-14 14:36:49 +02:00 committed by GitHub
parent 51543504f9
commit 4847121180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 218 additions and 6 deletions

View File

@ -3,8 +3,11 @@ import { Suspense } from 'react'
import Card from 'components/Card'
import Loading from 'components/Loading'
import { Text } from 'components/Text'
import { getVaults } from 'utils/api'
async function Content(props: PageProps) {
const vaults = await getVaults()
const address = props.params.address
if (!address)

38
src/constants/vaults.ts Normal file
View File

@ -0,0 +1,38 @@
export const VAULTS: VaultMetaData[] = [
{
address: 'osmo108q2krqr0y9g0rtesenvsw68sap2xefelwwjs0wedyvdl0cmrntqvllfjk',
name: 'OSMO-ATOM',
lockup: {
duration: 14,
timeframe: 'day',
},
provider: 'Apollo',
denoms: {
primary: 'uosmo',
secondary: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
lp: 'gamm/pool/1',
},
symbols: {
primary: 'OSMO',
secondary: 'ATOM',
},
},
{
address: 'osmo1g5hryv0gp9dzlchkp3yxk8fmcf5asjun6cxkvyffetqzkwmvy75qfmeq3f',
name: 'OSMO - JUNO',
lockup: {
duration: 14,
timeframe: 'day',
},
provider: 'Apollo',
denoms: {
primary: 'uosmo',
secondary: 'ibc/46B44899322F3CD854D2D46DEEF881958467CDD4B3B10086DA49296BBED94BED',
lp: 'gamm/pool/497',
},
symbols: {
primary: 'OSMO',
secondary: 'JUNO',
},
},
]

View File

@ -2,6 +2,12 @@ import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { NextApiRequest, NextApiResponse } from 'next'
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
import {
ArrayOfVaultInfoResponse,
VaultBaseForString,
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { VAULTS } from 'constants/vaults'
import { convertAprToApy } from 'utils/parsers'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER) {
@ -9,13 +15,127 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
const client = await CosmWasmClient.connect(ENV.URL_RPC)
const data = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
vaults_info: { limit: 5, start_after: undefined },
const $vaultConfigs = getVaultConfigs(client)
const $aprs = getAprs()
const vaults: Vault[] = await Promise.all([$vaultConfigs, $aprs]).then(([vaultConfigs, aprs]) => {
return vaultConfigs.map((vaultConfig) => {
const apr = aprs.find((apr) => apr.address === vaultConfig.address)
if (apr) {
return {
...vaultConfig,
apy: convertAprToApy(apr.apr, 365),
}
}
return {
...vaultConfig,
apy: null,
}
})
})
if (data) {
return res.status(200).json(data)
if (vaults) {
return res.status(200).json(vaults)
}
return res.status(404)
}
async function getVaultConfigs(client: CosmWasmClient, startAfter?: VaultBaseForString) {
let data: VaultConfig[] = []
const getBatch = async (startAfter?: VaultBaseForString) => {
if (!ENV.ADDRESS_CREDIT_MANAGER) return
const batch: ArrayOfVaultInfoResponse = await client.queryContractSmart(
ENV.ADDRESS_CREDIT_MANAGER,
{
vaults_info: { limit: 4, start_after: startAfter },
},
)
const batchProcessed = batch?.map((vaultInfo) => {
return {
address: vaultInfo.vault.address,
cap: {
denom: vaultInfo.config.deposit_cap.denom,
used: Number(vaultInfo.utilization.amount),
max: Number(vaultInfo.config.deposit_cap.amount),
},
ltv: {
max: Number(vaultInfo.config.max_ltv),
liq: Number(vaultInfo.config.liquidation_threshold),
},
} as VaultConfig
})
data = [...data, ...batchProcessed]
if (batch.length === 4) {
await getBatch({
address: batchProcessed[batchProcessed.length - 1].address,
} as VaultBaseForString)
}
}
await getBatch()
return VAULTS.map((vaultMetaData) => {
const vaultInfo = data.find((vault) => vault.address === vaultMetaData.address)
return {
...vaultMetaData,
...vaultInfo,
} as VaultConfig
})
}
interface FlatApr {
contract_address: string
apr: { type: string; value: number | string }[]
fees: { type: string; value: number | string }[]
}
interface NestedApr {
contract_address: string
apr: {
aprs: { type: string; value: number | string }[]
fees: { type: string; value: number | string }[]
}
}
async function getAprs() {
const APOLLO_URL = 'https://api.apollo.farm/api/vault_infos/v2/osmo-test-4'
try {
const response = await fetch(APOLLO_URL)
if (response.ok) {
const data: FlatApr[] | NestedApr[] = await response.json()
const newAprs = data.map((aprData) => {
try {
const apr = aprData as FlatApr
const aprTotal = apr.apr.reduce((prev, curr) => Number(curr.value) + prev, 0)
const feeTotal = apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0)
const finalApr = aprTotal + feeTotal
return { address: aprData.contract_address, apr: finalApr }
} catch {
const apr = aprData as NestedApr
const aprTotal = apr.apr.aprs.reduce((prev, curr) => Number(curr.value) + prev, 0)
const feeTotal = apr.apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0)
const finalApr = aprTotal + feeTotal
return { address: aprData.contract_address, apr: finalApr }
}
})
return newAprs
}
return []
} catch {
return []
}
}

34
src/types/interfaces/vaults.d.ts vendored Normal file
View File

@ -0,0 +1,34 @@
interface VaultMetaData {
address: string
name: string
lockup: {
duration: number
timeframe: string
}
provider: string
denoms: {
primary: string
secondary: string
lp: string
}
symbols: {
primary: string
secondary: string
}
}
interface VaultConfig extends VaultMetaData {
ltv: {
max: number
liq: number
}
cap: {
denom: string
used: numnber
max: number
}
}
interface Vault extends VaultConfig {
apy: number | null
}

View File

@ -9,6 +9,7 @@ export enum Endpoints {
ACCOUNT_DEBTS = '/accounts/{accountId}/debts',
MARKETS_BORROW = '/markets/borrow',
PRICES = '/prices',
VAULTS = '/vaults',
WALLET_BALANCES = '/wallets/{address}/balances',
}
@ -26,9 +27,12 @@ export function getEndpoint(endpoint: Endpoints, props?: ParamProps) {
return returnEndpoint
}
export async function callAPI<T>(endpoint: string): Promise<T> {
export async function callAPI<T>(endpoint: string, cache?: RequestCache): Promise<T> {
const response = await fetch(`${ENV.URL_API}${endpoint}${VERCEL_BYPASS}`, {
cache: 'no-store',
cache: cache ? cache : 'no-store',
next: {
revalidate: 30,
},
})
return response.json() as T
@ -60,6 +64,10 @@ export async function getPrices() {
return callAPI<Coin[]>(getEndpoint(Endpoints.PRICES))
}
export async function getVaults() {
return callAPI<Coin[]>(getEndpoint(Endpoints.VAULTS), 'default')
}
export async function getWalletBalancesSWR(url: string) {
return callAPI<Coin[]>(url)
}

View File

@ -9,3 +9,7 @@ export function isNumber(value: unknown) {
return false
}
export const convertAprToApy = (apr: number, numberOfCompoundingPeriods: number): number => {
return ((1 + apr / 100 / numberOfCompoundingPeriods) ** numberOfCompoundingPeriods - 1) * 100
}

5
src/utils/vaults.ts Normal file
View File

@ -0,0 +1,5 @@
import { VAULTS } from 'constants/vaults'
export function getVaultMetaData(address: string) {
return VAULTS.find((vault) => vault.address === address)
}