feat: replace graphql requests with cosmwasm queries (#225)

* feat: replace graphql requests with cosmwasm queries

* feat: concurent price and market fetching
This commit is contained in:
Yusuf Seyrek 2023-05-30 12:16:03 +03:00 committed by GitHub
parent de89ecb7ed
commit 0c959d5097
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 96 additions and 240 deletions

View File

@ -20,7 +20,6 @@
"@tippyjs/react": "^4.2.6",
"bignumber.js": "^9.1.1",
"classnames": "^2.3.2",
"graphql-request": "^6.0.0",
"moment": "^2.29.4",
"next": "^13.4.3",
"react": "^18.2.0",

View File

@ -7,7 +7,7 @@ let _cosmWasmClient: CosmWasmClient
const getClient = async () => {
try {
if (!_cosmWasmClient) {
_cosmWasmClient = await CosmWasmClient.connect(ENV.URL_RPC || '')
_cosmWasmClient = await CosmWasmClient.connect(ENV.URL_RPC)
}
return _cosmWasmClient

View File

@ -1,29 +0,0 @@
import { gql, request as gqlRequest } from 'graphql-request'
import { ENV } from 'constants/env'
export default async function getBalances() {
const result = await gqlRequest<Result>(
ENV.URL_GQL,
gql`
query RedbankBalances {
bank {
balance(
address: "${ENV.ADDRESS_RED_BANK}"
) {
amount
denom
}
}
}
`,
)
return result.bank.balance
}
interface Result {
bank: {
balance: Coin[]
}
}

View File

@ -1,54 +1,24 @@
import { gql, request as gqlRequest } from 'graphql-request'
import { ENV } from 'constants/env'
import { denomToKey, getContractQuery, keyToDenom } from 'utils/query'
import getMarkets from 'api/markets/getMarkets'
import { getClient } from 'api/cosmwasm-client'
export default async function getMarketDebts(): Promise<Coin[]> {
try {
const markets: Market[] = await getMarkets()
const client = await getClient()
let query = ''
markets.forEach((asset) => {
query += getContractQuery(
denomToKey(asset.denom),
ENV.ADDRESS_RED_BANK || '',
`
{
const debtQueries = markets.map((asset) =>
client.queryContractSmart(ENV.ADDRESS_RED_BANK, {
underlying_debt_amount: {
denom: "${asset.denom}"
amount_scaled: "${asset.debtTotalScaled}"
}
}`,
denom: asset.denom,
amount_scaled: asset.debtTotalScaled,
},
}),
)
})
const debtsResults = await Promise.all(debtQueries)
const result = await gqlRequest<DebtsQuery>(
ENV.URL_GQL,
gql`
query RedbankBalances {
debts: wasm {
${query}
}
}
`,
)
if (result) {
const debts = Object.keys(result.debts).map((key) => {
return {
denom: keyToDenom(key),
amount: result.debts[key],
}
})
return debts
}
return new Promise((_, reject) => reject('No data'))
}
interface DebtsQuery {
debts: {
[key: string]: string
return debtsResults.map<Coin>((debt, index) => ({ denom: markets[index].denom, amount: debt }))
} catch (ex) {
throw ex
}
}

View File

@ -1,54 +1,27 @@
import { gql, request as gqlRequest } from 'graphql-request'
import { ENV } from 'constants/env'
import { denomToKey, getContractQuery, keyToDenom } from 'utils/query'
import getMarkets from 'api/markets/getMarkets'
import { getClient } from 'api/cosmwasm-client'
export default async function getMarketDeposits(): Promise<Coin[]> {
const markets = await getMarkets()
try {
const markets: Market[] = await getMarkets()
const client = await getClient()
let query = ''
markets.forEach((market: Market) => {
query += getContractQuery(
denomToKey(market.denom),
ENV.ADDRESS_RED_BANK || '',
`
{
const depositQueries = markets.map((asset) =>
client.queryContractSmart(ENV.ADDRESS_RED_BANK, {
underlying_liquidity_amount: {
denom: "${market.denom}"
amount_scaled: "${market.collateralTotalScaled}"
}
}`,
denom: asset.denom,
amount_scaled: asset.collateralTotalScaled,
},
}),
)
})
const depositsResults = await Promise.all(depositQueries)
const result = await gqlRequest<DepositsQuery>(
ENV.URL_GQL,
gql`
query RedbankBalances {
deposits: wasm {
${query}
}
}
`,
)
if (result) {
const deposits = Object.keys(result.deposits).map((key) => {
return {
denom: keyToDenom(key),
amount: result.deposits[key],
}
})
return deposits
}
return new Promise((_, reject) => reject('No data'))
}
interface DepositsQuery {
deposits: {
[key: string]: string
return depositsResults.map<Coin>((deposit, index) => ({
denom: markets[index].denom,
amount: deposit,
}))
} catch (ex) {
throw ex
}
}

View File

@ -1,41 +1,24 @@
import { gql, request as gqlRequest } from 'graphql-request'
import { ENV } from 'constants/env'
import { getMarketAssets } from 'utils/assets'
import { denomToKey } from 'utils/query'
import { getEnabledMarketAssets } from 'utils/assets'
import { resolveMarketResponses } from 'utils/resolvers'
import { getClient } from 'api/cosmwasm-client'
export default async function getMarkets(): Promise<Market[]> {
const marketAssets = getMarketAssets()
try {
const enabledAssets = getEnabledMarketAssets()
const client = await getClient()
const marketQueries = marketAssets.map(
(asset: Asset) =>
`${denomToKey(asset.denom)}: contractQuery(
contractAddress: "${ENV.ADDRESS_RED_BANK}"
query: { market: { denom: "${asset.denom}" } }
)`,
const marketQueries = enabledAssets.map((asset) =>
client.queryContractSmart(ENV.ADDRESS_RED_BANK, {
market: {
denom: asset.denom,
},
}),
)
const marketResults = await Promise.all(marketQueries)
const result = await gqlRequest<RedBankData>(
ENV.URL_GQL,
gql`
query RedbankQuery {
rbwasmkey: wasm {
${marketQueries}
}
}
`,
)
const markets = marketAssets.map((asset) => {
const market = result.rbwasmkey[`${denomToKey(asset.denom)}`]
return market
})
return resolveMarketResponses(markets)
}
interface RedBankData {
rbwasmkey: {
[key: string]: MarketResponse
return resolveMarketResponses(marketResults)
} catch (ex) {
throw ex
}
}

View File

@ -1,59 +1,36 @@
import { gql, request as gqlRequest } from 'graphql-request'
import { ASSETS } from 'constants/assets'
import { ENV } from 'constants/env'
import { getMarketAssets } from 'utils/assets'
import { getEnabledMarketAssets } from 'utils/assets'
import { BN } from 'utils/helpers'
import { getClient } from 'api/cosmwasm-client'
export default async function getPrices(): Promise<Coin[]> {
const marketAssets = getMarketAssets()
try {
const enabledAssets = getEnabledMarketAssets()
const client = await getClient()
const baseCurrency = ASSETS[0]
const result = await gqlRequest<TokenPricesResult>(
ENV.URL_GQL,
gql`
query PriceOracle {
prices: wasm {
${marketAssets.map((asset) => {
return `${asset.id}: contractQuery(
contractAddress: "${ENV.ADDRESS_ORACLE}"
query: {
const priceQueries = enabledAssets.map((asset) =>
client.queryContractSmart(ENV.ADDRESS_ORACLE, {
price: {
denom: "${asset.denom}"
}
}
)`
})}
}
}
`,
)
const data: Coin[] = Object.values(result?.prices).reduce((acc: Coin[], curr) => {
const asset = marketAssets.find((asset) => asset.denom === curr.denom)
const additionalDecimals = asset
? asset.decimals > baseCurrency.decimals
? asset.decimals - baseCurrency.decimals
: 0
: 0
return [
...acc,
{
denom: curr.denom,
amount: BN(curr.price).shiftedBy(additionalDecimals).toString(),
denom: asset.denom,
},
] as Coin[]
}, [])
}),
)
const priceResults: PriceResult[] = await Promise.all(priceQueries)
return data
}
const assetPrices = priceResults.map(({ denom, price }, index) => {
const asset = enabledAssets[index]
const decimalDiff = asset.decimals - baseCurrency.decimals
interface TokenPricesResult {
prices: {
[key: string]: {
denom: string
price: string
return {
denom,
amount: BN(price).shiftedBy(decimalDiff).toString(),
}
})
return assetPrices
} catch (ex) {
throw ex
}
}

View File

@ -2,7 +2,7 @@ import { Row } from '@tanstack/react-table'
import Button from 'components/Button'
import useStore from 'store'
import { getMarketAssets } from 'utils/assets'
import { getEnabledMarketAssets } from 'utils/assets'
type AssetRowProps = {
row: Row<BorrowAsset | BorrowAssetActive>
@ -12,7 +12,7 @@ type AssetRowProps = {
}
export default function AssetExpanded(props: AssetRowProps) {
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
const asset = marketAssets.find((asset) => asset.denom === props.row.original.denom)
let isActive: boolean = false

View File

@ -1,7 +1,7 @@
import { flexRender, Row } from '@tanstack/react-table'
import classNames from 'classnames'
import { getMarketAssets } from 'utils/assets'
import { getEnabledMarketAssets } from 'utils/assets'
type AssetRowProps = {
row: Row<BorrowAsset | BorrowAssetActive>
@ -9,7 +9,7 @@ type AssetRowProps = {
}
export const AssetRow = (props: AssetRowProps) => {
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
const asset = marketAssets.find((asset) => asset.denom === props.row.original.denom)
if (!asset) return null

View File

@ -17,7 +17,7 @@ import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons'
import Loading from 'components/Loading'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { getMarketAssets } from 'utils/assets'
import { getEnabledMarketAssets } from 'utils/assets'
import { formatPercent } from 'utils/formatters'
type Props = {
@ -26,7 +26,7 @@ type Props = {
export const BorrowTable = (props: Props) => {
const [sorting, setSorting] = React.useState<SortingState>([])
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
const columns = React.useMemo<ColumnDef<BorrowAsset | BorrowAssetActive>[]>(
() => [

View File

@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { useParams } from 'react-router-dom'
import Card from 'components/Card'
import { getMarketAssets } from 'utils/assets'
import { getEnabledMarketAssets } from 'utils/assets'
import { BorrowTable } from 'components/Borrow/BorrowTable'
import useAccountDebts from 'hooks/useAccountDebts'
import useMarketBorrowings from 'hooks/useMarketBorrowings'
@ -16,7 +16,7 @@ function Content(props: Props) {
const { data: debtData } = useAccountDebts(accountId)
const { data: borrowData } = useMarketBorrowings()
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
function getBorrowAssets() {
return marketAssets.reduce(
@ -60,7 +60,7 @@ function Content(props: Props) {
}
function Fallback() {
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
const available: BorrowAsset[] = marketAssets.reduce((prev: BorrowAsset[], curr) => {
prev.push({ ...curr, borrowRate: null, liquidity: null })

View File

@ -18,7 +18,7 @@ import Text from 'components/Text'
import { IS_TESTNET } from 'constants/env'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getBaseAsset, getMarketAssets } from 'utils/assets'
import { getBaseAsset, getEnabledMarketAssets } from 'utils/assets'
import { formatValue, truncate } from 'utils/formatters'
import useWalletBalances from 'hooks/useWalletBalances'
@ -26,7 +26,7 @@ export default function ConnectedButton() {
// ---------------
// EXTERNAL HOOKS
// ---------------
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
const { disconnect } = useWallet()
const { disconnect: terminate } = useWalletManager()
const address = useStore((s) => s.address)

View File

@ -29,3 +29,8 @@ interface MarketResponse {
borrow_enabled: boolean
deposit_cap: string
}
interface PriceResult {
denom: string
price: string
}

View File

@ -8,7 +8,7 @@ export function getAssetBySymbol(symbol: string) {
return ASSETS.find((asset) => asset.symbol === symbol)
}
export function getMarketAssets(): Asset[] {
export function getEnabledMarketAssets(): Asset[] {
return ASSETS.filter((asset) => asset.isEnabled && asset.isMarket)
}

View File

@ -1,6 +1,6 @@
import BigNumber from 'bignumber.js'
import { getMarketAssets } from 'utils/assets'
import { getEnabledMarketAssets } from 'utils/assets'
import { BN } from 'utils/helpers'
export function truncate(text = '', [h, t]: [number, number] = [6, 6]): string {
@ -126,7 +126,7 @@ export function formatPercent(percent: number | string, minDecimals?: number) {
}
export function formatAmountWithSymbol(coin: Coin) {
const marketAssets = getMarketAssets()
const marketAssets = getEnabledMarketAssets()
const asset = marketAssets.find((asset) => asset.denom === coin.denom)
@ -157,7 +157,7 @@ export function demagnify(amount: number | string | BigNumber, asset: Asset) {
export function convertToDisplayAmount(coin: Coin, displayCurrency: Asset, prices: Coin[]) {
const price = prices.find((price) => price.denom === coin.denom)
const asset = getMarketAssets().find((asset) => asset.denom === coin.denom)
const asset = getEnabledMarketAssets().find((asset) => asset.denom === coin.denom)
const displayPrice = prices.find((price) => price.denom === displayCurrency.denom)
if (!price || !asset || !displayPrice) return '0'

View File

@ -2059,7 +2059,7 @@
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0":
"@graphql-typed-document-node/core@^3.1.1":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
@ -4719,13 +4719,6 @@ create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
@ -6072,14 +6065,6 @@ graphemer@^1.4.0:
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
graphql-request@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.0.0.tgz#9c8b6a0c341f289e049936d03cc9205300faae1c"
integrity sha512-2BmHTuglonjZvmNVw6ZzCfFlW/qkIPds0f+Qdi/Lvjsl3whJg2uvHmSvHnLWhUTEw6zcxPYAHiZoPvSVKOZ7Jw==
dependencies:
"@graphql-typed-document-node/core" "^3.2.0"
cross-fetch "^3.1.5"
graphql-tag@^2.12.6:
version "2.12.6"
resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz"
@ -7532,13 +7517,6 @@ node-addon-api@^5.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz"
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
node-fetch@2.6.7:
version "2.6.7"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.7:
version "2.6.9"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz"