Mp 2539 vaults api (#156)
* add basic endpoint for vaultConfigs * implement apy for vaults
This commit is contained in:
parent
51543504f9
commit
4847121180
@ -3,8 +3,11 @@ import { Suspense } from 'react'
|
|||||||
import Card from 'components/Card'
|
import Card from 'components/Card'
|
||||||
import Loading from 'components/Loading'
|
import Loading from 'components/Loading'
|
||||||
import { Text } from 'components/Text'
|
import { Text } from 'components/Text'
|
||||||
|
import { getVaults } from 'utils/api'
|
||||||
|
|
||||||
async function Content(props: PageProps) {
|
async function Content(props: PageProps) {
|
||||||
|
const vaults = await getVaults()
|
||||||
|
|
||||||
const address = props.params.address
|
const address = props.params.address
|
||||||
|
|
||||||
if (!address)
|
if (!address)
|
||||||
|
38
src/constants/vaults.ts
Normal file
38
src/constants/vaults.ts
Normal 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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
@ -2,6 +2,12 @@ import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
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) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER) {
|
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 client = await CosmWasmClient.connect(ENV.URL_RPC)
|
||||||
|
|
||||||
const data = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
|
const $vaultConfigs = getVaultConfigs(client)
|
||||||
vaults_info: { limit: 5, start_after: undefined },
|
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) {
|
if (vaults) {
|
||||||
return res.status(200).json(data)
|
return res.status(200).json(vaults)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(404)
|
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
34
src/types/interfaces/vaults.d.ts
vendored
Normal 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
|
||||||
|
}
|
@ -9,6 +9,7 @@ export enum Endpoints {
|
|||||||
ACCOUNT_DEBTS = '/accounts/{accountId}/debts',
|
ACCOUNT_DEBTS = '/accounts/{accountId}/debts',
|
||||||
MARKETS_BORROW = '/markets/borrow',
|
MARKETS_BORROW = '/markets/borrow',
|
||||||
PRICES = '/prices',
|
PRICES = '/prices',
|
||||||
|
VAULTS = '/vaults',
|
||||||
WALLET_BALANCES = '/wallets/{address}/balances',
|
WALLET_BALANCES = '/wallets/{address}/balances',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,9 +27,12 @@ export function getEndpoint(endpoint: Endpoints, props?: ParamProps) {
|
|||||||
return returnEndpoint
|
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}`, {
|
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
|
return response.json() as T
|
||||||
@ -60,6 +64,10 @@ export async function getPrices() {
|
|||||||
return callAPI<Coin[]>(getEndpoint(Endpoints.PRICES))
|
return callAPI<Coin[]>(getEndpoint(Endpoints.PRICES))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getVaults() {
|
||||||
|
return callAPI<Coin[]>(getEndpoint(Endpoints.VAULTS), 'default')
|
||||||
|
}
|
||||||
|
|
||||||
export async function getWalletBalancesSWR(url: string) {
|
export async function getWalletBalancesSWR(url: string) {
|
||||||
return callAPI<Coin[]>(url)
|
return callAPI<Coin[]>(url)
|
||||||
}
|
}
|
||||||
|
@ -9,3 +9,7 @@ export function isNumber(value: unknown) {
|
|||||||
|
|
||||||
return false
|
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
5
src/utils/vaults.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { VAULTS } from 'constants/vaults'
|
||||||
|
|
||||||
|
export function getVaultMetaData(address: string) {
|
||||||
|
return VAULTS.find((vault) => vault.address === address)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user