From db1b73e5ecdee6af60ad32d545d2a56944fc410f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Von=C3=A1=C5=A1ek?= Date: Wed, 15 Jan 2025 15:01:10 +0100 Subject: [PATCH] Money Market user data on wallet page --- src/api/borrow.ts | 74 +++++++++++++++++++ src/api/provider.ts | 4 + src/providers/rpcProvider.tsx | 5 ++ .../wallet/assets/WalletAssets.utils.ts | 9 ++- .../assets/header/WalletAssetsHeader.tsx | 32 ++++++-- src/utils/provider.ts | 28 +++++++ src/utils/queryKeys.ts | 1 + 7 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 src/api/borrow.ts create mode 100644 src/utils/provider.ts diff --git a/src/api/borrow.ts b/src/api/borrow.ts new file mode 100644 index 000000000..11afe753d --- /dev/null +++ b/src/api/borrow.ts @@ -0,0 +1,74 @@ +import { UiPoolDataProvider } from "@aave/contract-helpers" +import { formatReserves, formatUserSummary } from "@aave/math-utils" +import { useQuery } from "@tanstack/react-query" +import { isTestnetRpcUrl } from "api/provider" +import { useRpcProvider } from "providers/rpcProvider" +import { + AaveV3HydrationMainnet, + AaveV3HydrationTestnet, +} from "sections/lending/ui-config/addresses" +import { useEvmAccount } from "sections/web3-connect/Web3Connect.utils" +import { QUERY_KEYS } from "utils/queryKeys" + +export const useUserBorrowSummary = () => { + const { api, evm, isLoaded } = useRpcProvider() + const { account } = useEvmAccount() + + const address = account?.address ?? "" + + return useQuery( + QUERY_KEYS.borrowUserSummary(address), + async () => { + const isTestnet = isTestnetRpcUrl(evm.connection.url) + + const addresses = isTestnet + ? AaveV3HydrationTestnet + : AaveV3HydrationMainnet + + const poolDataContract = new UiPoolDataProvider({ + uiPoolDataProviderAddress: addresses.UI_POOL_DATA_PROVIDER, + provider: evm, + chainId: parseFloat(import.meta.env.VITE_EVM_CHAIN_ID), + }) + + const [reserves, user, timestamp] = await Promise.all([ + poolDataContract.getReservesHumanized({ + lendingPoolAddressProvider: addresses.POOL_ADDRESSES_PROVIDER, + }), + poolDataContract.getUserReservesHumanized({ + lendingPoolAddressProvider: addresses.POOL_ADDRESSES_PROVIDER, + user: address, + }), + api.query.timestamp.now(), + ]) + + const { baseCurrencyData, reservesData } = reserves + const { userEmodeCategoryId, userReserves } = user + + const currentTimestamp = timestamp.toNumber() / 1000 + + const formattedReserves = formatReserves({ + currentTimestamp, + reserves: reservesData, + marketReferencePriceInUsd: + baseCurrencyData.marketReferenceCurrencyPriceInUsd, + marketReferenceCurrencyDecimals: + baseCurrencyData.marketReferenceCurrencyDecimals, + }) + + return formatUserSummary({ + currentTimestamp, + marketReferencePriceInUsd: + baseCurrencyData.marketReferenceCurrencyPriceInUsd, + marketReferenceCurrencyDecimals: + baseCurrencyData.marketReferenceCurrencyDecimals, + userReserves, + formattedReserves, + userEmodeCategoryId, + }) + }, + { + enabled: isLoaded && !!address, + }, + ) +} diff --git a/src/api/provider.ts b/src/api/provider.ts index a3596b215..3d314903e 100644 --- a/src/api/provider.ts +++ b/src/api/provider.ts @@ -21,6 +21,7 @@ import { identity, undefinedNoop } from "utils/helpers" import { ExternalAssetCursor } from "@galacticcouncil/apps" import { getExternalId } from "utils/externalAssets" import { pingRpc } from "utils/rpc" +import { PolkadotEvmRpcProvider } from "utils/provider" export type TEnv = "testnet" | "mainnet" export type ProviderProps = { @@ -273,8 +274,11 @@ export const useProviderData = () => { const balanceClient = new BalanceClient(api) + const evm = new PolkadotEvmRpcProvider(api) + return { api, + evm, tradeRouter, poolService, balanceClient, diff --git a/src/providers/rpcProvider.tsx b/src/providers/rpcProvider.tsx index 049e9f5f1..04029012a 100644 --- a/src/providers/rpcProvider.tsx +++ b/src/providers/rpcProvider.tsx @@ -18,9 +18,11 @@ import { useDisplayAssetStore } from "utils/displayAsset" import { useShareTokens } from "api/xyk" import { AssetsProvider } from "./assets" import { differenceInHours } from "date-fns" +import { PolkadotEvmRpcProvider } from "utils/provider" type TProviderContext = { api: ApiPromise + evm: PolkadotEvmRpcProvider tradeRouter: TradeRouter poolService: PoolService balanceClient: BalanceClient @@ -30,6 +32,7 @@ type TProviderContext = { const ProviderContext = createContext({ isLoaded: false, api: {} as TProviderContext["api"], + evm: {} as TProviderContext["evm"], tradeRouter: {} as TradeRouter, featureFlags: {} as TProviderContext["featureFlags"], poolService: {} as TProviderContext["poolService"], @@ -103,6 +106,7 @@ export const RpcProvider = ({ children }: { children: ReactNode }) => { return { poolService: providerData.poolService, api: providerData.api, + evm: providerData.evm, tradeRouter: providerData.tradeRouter, balanceClient: providerData.balanceClient, featureFlags: providerData.featureFlags, @@ -113,6 +117,7 @@ export const RpcProvider = ({ children }: { children: ReactNode }) => { return { isLoaded: false, api: {} as TProviderContext["api"], + evm: {} as TProviderContext["evm"], tradeRouter: {} as TradeRouter, balanceClient: {} as BalanceClient, featureFlags: { diff --git a/src/sections/wallet/assets/WalletAssets.utils.ts b/src/sections/wallet/assets/WalletAssets.utils.ts index 8ce9807f4..82798e05e 100644 --- a/src/sections/wallet/assets/WalletAssets.utils.ts +++ b/src/sections/wallet/assets/WalletAssets.utils.ts @@ -8,6 +8,7 @@ import { useDisplayShareTokenPrice } from "utils/displayAsset" import { useAssetsData } from "./table/data/WalletAssetsTableData.utils" import { useAccountAssets } from "api/deposits" import BigNumber from "bignumber.js" +import { useUserBorrowSummary } from "api/borrow" type AssetCategory = "all" | "assets" | "liquidity" | "farming" @@ -46,6 +47,7 @@ export const useWalletAssetsTotals = ({ }: { address?: string } = {}) => { + const borrows = useUserBorrowSummary() const assets = useAssetsData({ isAllAssets: false, address }) const lpPositions = useOmnipoolPositionsData({ address }) const farmsTotal = useFarmDepositsTotal(address) @@ -103,17 +105,21 @@ export const useWalletAssetsTotals = ({ }, "0") }, [shareTokenBalances, spotPrices.data]) + const borrowsTotal = borrows.data?.totalBorrowsUSD ?? "0" + const balanceTotal = useMemo( () => BigNumber(assetsTotal) .plus(farmsTotal.value) .plus(lpTotal) .plus(xykTotal) + .plus(borrowsTotal) .toString(), - [assetsTotal, farmsTotal.value, lpTotal, xykTotal], + [assetsTotal, farmsTotal.value, lpTotal, xykTotal, borrowsTotal], ) const isLoading = + borrows.isLoading || assets.isLoading || lpPositions.isLoading || farmsTotal.isLoading || @@ -125,6 +131,7 @@ export const useWalletAssetsTotals = ({ farmsTotal: farmsTotal.value, lpTotal: BigNumber(lpTotal).plus(xykTotal).toString(), balanceTotal, + borrowsTotal: borrows.data?.totalBorrowsUSD ?? "0", isLoading, } } diff --git a/src/sections/wallet/assets/header/WalletAssetsHeader.tsx b/src/sections/wallet/assets/header/WalletAssetsHeader.tsx index 05a195bbb..7e9b9f72d 100644 --- a/src/sections/wallet/assets/header/WalletAssetsHeader.tsx +++ b/src/sections/wallet/assets/header/WalletAssetsHeader.tsx @@ -8,11 +8,18 @@ type Props = { disconnected?: boolean } export const WalletAssetsHeader = ({ disconnected }: Props) => { const { t } = useTranslation() - const { isLoading, balanceTotal, assetsTotal, farmsTotal, lpTotal } = - useWalletAssetsTotals() + const { + isLoading, + balanceTotal, + assetsTotal, + farmsTotal, + borrowsTotal, + lpTotal, + } = useWalletAssetsTotals() + return ( { content: ( ), @@ -32,7 +39,7 @@ export const WalletAssetsHeader = ({ disconnected }: Props) => { content: ( ), @@ -43,7 +50,7 @@ export const WalletAssetsHeader = ({ disconnected }: Props) => { content: ( ), @@ -54,11 +61,22 @@ export const WalletAssetsHeader = ({ disconnected }: Props) => { content: ( ), }, + { + label: "Borrows", + disconnected: disconnected, + content: ( + + ), + }, ]} /> ) diff --git a/src/utils/provider.ts b/src/utils/provider.ts new file mode 100644 index 000000000..09d39cbb2 --- /dev/null +++ b/src/utils/provider.ts @@ -0,0 +1,28 @@ +import { getNetwork, JsonRpcProvider, Network } from "@ethersproject/providers" +import { ApiPromise, WsProvider } from "@polkadot/api" + +export class PolkadotEvmRpcProvider extends JsonRpcProvider { + provider: WsProvider + + constructor(api: ApiPromise) { + const provider = PolkadotEvmRpcProvider.getProviderInstance(api) + const path = provider.endpoint + super(path) + this.provider = provider + } + + async _uncachedDetectNetwork(): Promise { + const chainId = await this.send("eth_chainId", []) + return getNetwork(parseInt(chainId, 16)) + } + + static getProviderInstance(api: ApiPromise) { + // @ts-expect-error Property '_options' is protected + const options = api?._options + return options?.provider as WsProvider + } + + send(method: string, params: Array = []): Promise { + return this.provider.send(method, params) + } +} diff --git a/src/utils/queryKeys.ts b/src/utils/queryKeys.ts index 543292c84..35729c7ce 100644 --- a/src/utils/queryKeys.ts +++ b/src/utils/queryKeys.ts @@ -331,4 +331,5 @@ export const QUERY_KEYS = { ) => ["xcmTransfer", asset, srcAddr, srcChain, dstAddr, dstChain], externalApi: (chain: string) => ["externalApi", chain], bifrostVDotApy: ["bifrostVDotApy"], + borrowUserSummary: (address: string) => ["borrowUserSummary", address], } as const