Account balances endpoint (#147)

* add accounts/positions to api

* strong type apis
This commit is contained in:
Bob van der Helm 2023-04-03 13:30:16 +02:00 committed by GitHub
parent 7efb7908c0
commit 8e0bb97839
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 117 additions and 37 deletions

View File

@ -9,10 +9,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const accountId = req.query.id const accountId = req.query.id
const account = await (await fetch(`${ENV.URL_API}/accounts/${accountId}${VERCEL_BYPASS}`)).json() const position: Position = await (
await fetch(`${ENV.URL_API}/accounts/${accountId}${VERCEL_BYPASS}`)
).json()
if (account) { if (position) {
return res.status(200).json(account.debts) return res.status(200).json(position.debts)
} }
return res.status(404) return res.status(404)

View File

@ -9,10 +9,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const accountId = req.query.id const accountId = req.query.id
const account = await (await fetch(`${ENV.URL_API}/accounts/${accountId}${VERCEL_BYPASS}`)).json() const position: Position = await (
await fetch(`${ENV.URL_API}/accounts/${accountId}${VERCEL_BYPASS}`)
).json()
if (account) { if (position) {
return res.status(200).json(account.deposits) return res.status(200).json(position.deposits)
} }
return res.status(404) return res.status(404)

View File

@ -2,6 +2,7 @@ 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 { resolvePositionResponse } from 'utils/resolvers'
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) {
@ -12,14 +13,14 @@ 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 data: PositionResponse = await client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER, {
positions: { positions: {
account_id: accountId, account_id: accountId,
}, },
}) })
if (data) { if (data) {
return res.status(200).json(data) return res.status(200).json(resolvePositionResponse(data))
} }
return res.status(404) return res.status(404)

View File

@ -10,7 +10,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(404).json(ENV_MISSING_MESSAGE) return res.status(404).json(ENV_MISSING_MESSAGE)
} }
const marketAssets = getMarketAssets()
const $liquidity = fetch(`${ENV.URL_API}/markets/liquidity${VERCEL_BYPASS}`) const $liquidity = fetch(`${ENV.URL_API}/markets/liquidity${VERCEL_BYPASS}`)
const $markets = fetch(`${ENV.URL_API}/markets${VERCEL_BYPASS}`) const $markets = fetch(`${ENV.URL_API}/markets${VERCEL_BYPASS}`)
const $prices = fetch(`${ENV.URL_API}/prices${VERCEL_BYPASS}`) const $prices = fetch(`${ENV.URL_API}/prices${VERCEL_BYPASS}`)
@ -18,16 +17,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const borrow: BorrowAsset[] = await Promise.all([$liquidity, $markets, $prices]).then( const borrow: BorrowAsset[] = await Promise.all([$liquidity, $markets, $prices]).then(
async ([$liquidity, $markets, $prices]) => { async ([$liquidity, $markets, $prices]) => {
const liquidity: Coin[] = await $liquidity.json() const liquidity: Coin[] = await $liquidity.json()
const markets: Market[] = await $markets.json() const borrowEnabledMarkets: Market[] = (await $markets.json()).filter(
(market: Market) => market.borrowEnabled,
)
const prices: Coin[] = await $prices.json() const prices: Coin[] = await $prices.json()
return marketAssets.map((asset) => { return borrowEnabledMarkets.map((market) => {
const currentMarket = markets.find((market) => market.denom === asset.denom) const price = prices.find((coin) => coin.denom === market.denom)?.amount ?? '1'
const price = prices.find((coin) => coin.denom === asset.denom)?.amount ?? '1' const amount = liquidity.find((coin) => coin.denom === market.denom)?.amount ?? '0'
const amount = liquidity.find((coin) => coin.denom === asset.denom)?.amount ?? '0'
return { return {
denom: asset.denom, denom: market.denom,
borrowRate: currentMarket?.borrow_rate ?? '0', borrowRate: market.borrowRate ?? 0,
liquidity: { liquidity: {
amount: amount, amount: amount,
value: new BigNumber(amount).times(price).toString(), value: new BigNumber(amount).times(price).toString(),

View File

@ -21,7 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
{ {
underlying_debt_amount: { underlying_debt_amount: {
denom: "${asset.denom}" denom: "${asset.denom}"
amount_scaled: "${asset.debt_total_scaled}" amount_scaled: "${asset.debtTotalScaled}"
} }
}`, }`,
) )

View File

@ -13,15 +13,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
let query = '' let query = ''
markets.forEach((asset: any) => { markets.forEach((market: Market) => {
query += getContractQuery( query += getContractQuery(
denomToKey(asset.denom), denomToKey(market.denom),
ENV.ADDRESS_RED_BANK || '', ENV.ADDRESS_RED_BANK || '',
` `
{ {
underlying_liquidity_amount: { underlying_liquidity_amount: {
denom: "${asset.denom}" denom: "${market.denom}"
amount_scaled: "${asset.collateral_total_scaled}" amount_scaled: "${market.collateralTotalScaled}"
} }
}`, }`,
) )

View File

@ -4,6 +4,7 @@ import { NextApiRequest, NextApiResponse } from 'next'
import { ENV, ENV_MISSING_MESSAGE } from 'constants/env' import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
import { getMarketAssets } from 'utils/assets' import { getMarketAssets } from 'utils/assets'
import { denomToKey } from 'utils/query' import { denomToKey } from 'utils/query'
import { resolveMarketResponses } from 'utils/resolvers'
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK || !ENV.ADDRESS_INCENTIVES) { if (!ENV.URL_GQL || !ENV.ADDRESS_RED_BANK || !ENV.ADDRESS_INCENTIVES) {
@ -35,11 +36,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const market = result.rbwasmkey[`${denomToKey(asset.denom)}`] const market = result.rbwasmkey[`${denomToKey(asset.denom)}`]
return market return market
}) })
return res.status(200).json(markets) return res.status(200).json(resolveMarketResponses(markets))
} }
interface RedBankData { interface RedBankData {
rbwasmkey: { rbwasmkey: {
[key: string]: Market [key: string]: MarketResponse
} }
} }

View File

@ -0,0 +1,35 @@
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { NextApiRequest, NextApiResponse } from 'next'
import { ENV, ENV_MISSING_MESSAGE, VERCEL_BYPASS } from 'constants/env'
import { resolvePositionResponses } from 'utils/resolvers'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER || !ENV.URL_API) {
return res.status(404).json(ENV_MISSING_MESSAGE)
}
const address = req.query.address
const accounts: string[] = await (
await fetch(`${ENV.URL_API}/wallets/${address}/accounts${VERCEL_BYPASS}`)
).json()
const client = await CosmWasmClient.connect(ENV.URL_RPC)
const $positions: Promise<PositionResponse>[] = accounts.map((account) =>
client.queryContractSmart(ENV.ADDRESS_CREDIT_MANAGER!, {
positions: {
account_id: `${account}`,
},
}),
)
const positions = await Promise.all($positions).then((positions) => positions)
if (positions) {
return res.status(200).json(resolvePositionResponses(positions))
}
return res.status(404)
}

6
src/types/interfaces/account.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
interface Position {
account: string
deposits: import('@cosmjs/stargate').Coin[]
debts: import('@cosmjs/stargate').Coin[]
lends: import('@cosmjs/stargate').Coin[]
}

View File

@ -18,7 +18,7 @@ interface OtherAsset extends Omit<Asset, 'symbol'> {
interface BorrowAsset { interface BorrowAsset {
denom: string denom: string
borrowRate: string | null borrowRate: number | null
liquidity: { liquidity: {
amount: string amount: string
value: string value: string

9
src/types/interfaces/market.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
interface Market {
denom: string
borrowRate: number
debtTotalScaled: number
collateralTotalScaled: number
depositEnabled: boolean
borrowEnabled: boolean
depositCap: number
}

View File

@ -1,4 +1,11 @@
interface Market { interface PositionResponse {
account_id: string
deposits: Coin[]
debts: Coin[]
lends: Coin[]
}
interface MarketResponse {
denom: string denom: string
max_loan_to_value: string max_loan_to_value: string
liquidation_threshold: string liquidation_threshold: string
@ -21,11 +28,3 @@ interface Market {
borrow_enabled: boolean borrow_enabled: boolean
deposit_cap: string deposit_cap: string
} }
interface MarketResult {
wasm: MarketData
}
interface MarketData {
[key: string]: Market
}

View File

@ -11,13 +11,12 @@ export async function callAPI<T>(endpoint: string): Promise<T> {
} }
export async function getBorrowData() { export async function getBorrowData() {
await sleep()
return callAPI<BorrowAsset[]>('/markets/borrow') return callAPI<BorrowAsset[]>('/markets/borrow')
} }
export async function getCreditAccounts(address: string) { export async function getCreditAccounts(address: string) {
if (!address) return [] if (!address) return []
return callAPI<any[]>(`/wallets/${address}/accounts`) return callAPI<string[]>(`/wallets/${address}/accounts`)
} }
export async function getMarkets() { export async function getMarkets() {
@ -41,11 +40,13 @@ export async function getAccountDeposits(account: string) {
if (!account) return [] if (!account) return []
return callAPI<Coin[]>(`/accounts/${account}/deposits`) return callAPI<Coin[]>(`/accounts/${account}/deposits`)
} }
export async function getWalletBalances(wallet: string) { export async function getWalletBalances(wallet: string) {
if (!wallet) return [] if (!wallet) return []
return callAPI<Coin[]>(`/wallets/${wallet}/balances`) return callAPI<Coin[]>(`/wallets/${wallet}/balances`)
} }
async function sleep() { export async function getAccountsPositions(wallet: string) {
return new Promise((resolve) => setTimeout(resolve, 2500)) if (!wallet) return []
return callAPI<Position[]>(`/wallets/${wallet}/accounts/positions`)
} }

24
src/utils/resolvers.ts Normal file
View File

@ -0,0 +1,24 @@
export function resolvePositionResponses(responses: PositionResponse[]): Position[] {
return responses.map(resolvePositionResponse)
}
export function resolvePositionResponse(response: PositionResponse): Position {
return {
account: response.account_id,
deposits: response.deposits,
debts: response.debts,
lends: response.lends,
}
}
export function resolveMarketResponses(responses: MarketResponse[]): Market[] {
return responses.map((response) => ({
denom: response.denom,
borrowRate: Number(response.borrow_rate),
debtTotalScaled: Number(response.debt_total_scaled),
collateralTotalScaled: Number(response.collateral_total_scaled),
depositEnabled: response.deposit_enabled,
borrowEnabled: response.borrow_enabled,
depositCap: Number(response.deposit_cap),
}))
}