Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Money Market user data on wallet page #2041

Merged
merged 11 commits into from
Jan 23, 2025
85 changes: 85 additions & 0 deletions src/api/borrow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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 { useMemo } from "react"
import {
AaveV3HydrationMainnet,
AaveV3HydrationTestnet,
} from "sections/lending/ui-config/addresses"
import { useAccount } from "sections/web3-connect/Web3Connect.utils"
import { H160, isEvmAccount } from "utils/evm"
import { QUERY_KEYS } from "utils/queryKeys"

export const useUserBorrowSummary = (givenAddress?: string) => {
const { account } = useAccount()
const { api, evm, isLoaded } = useRpcProvider()

const address = givenAddress || account?.address

const isEvm = isEvmAccount(address)

const evmAddress = useMemo(() => {
if (!address) return ""
if (isEvm) return H160.fromAccount(address)
return H160.fromSS58(address)
}, [isEvm, address])

return useQuery(
QUERY_KEYS.borrowUserSummary(evmAddress),
async () => {
const isTestnet = isTestnetRpcUrl(evm.connection.url)

const contracts = isTestnet
? AaveV3HydrationTestnet
: AaveV3HydrationMainnet

const poolDataContract = new UiPoolDataProvider({
uiPoolDataProviderAddress: contracts.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: contracts.POOL_ADDRESSES_PROVIDER,
}),
poolDataContract.getUserReservesHumanized({
lendingPoolAddressProvider: contracts.POOL_ADDRESSES_PROVIDER,
user: evmAddress,
}),
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,
})
},
{
retry: false,
enabled: isLoaded && !!evmAddress,
},
)
}
23 changes: 16 additions & 7 deletions src/api/external/bifrost.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useQuery } from "@tanstack/react-query"
import { useQuery, UseQueryOptions } from "@tanstack/react-query"
import { QUERY_KEYS } from "utils/queryKeys"

type BifrostAPY = {
Expand All @@ -7,10 +7,19 @@ type BifrostAPY = {
apyReward: string
}

export const useBifrostVDotApy = () => {
return useQuery(QUERY_KEYS.bifrostVDotApy, async () => {
const res = await fetch("https://dapi.bifrost.io/api/site")
const data = await res.json()
return data["vDOT"] as BifrostAPY
})
export const useBifrostVDotApy = (
options: UseQueryOptions<BifrostAPY> = {},
) => {
return useQuery<BifrostAPY>(
QUERY_KEYS.bifrostVDotApy,
async () => {
const res = await fetch("https://dapi.bifrost.io/api/site")
const data = await res.json()
return data["vDOT"] as BifrostAPY
},
{
refetchOnWindowFocus: false,
...options,
},
)
}
4 changes: 4 additions & 0 deletions src/api/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -273,8 +274,11 @@ export const useProviderData = () => {

const balanceClient = new BalanceClient(api)

const evm = new PolkadotEvmRpcProvider(api)

return {
api,
evm,
tradeRouter,
poolService,
balanceClient,
Expand Down
12 changes: 11 additions & 1 deletion src/components/DataValue/DataValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type DataValueProps = {
font?: TextProps["font"]
labelColor?: TextProps["color"]
isLoading?: boolean
disableSkeletonAnimation?: boolean
}

export const DataValue: React.FC<DataValueProps> = ({
Expand All @@ -65,6 +66,7 @@ export const DataValue: React.FC<DataValueProps> = ({
font = "GeistMedium",
labelColor = "white",
isLoading = false,
disableSkeletonAnimation = false,
}) => {
return (
<div className={className}>
Expand All @@ -84,7 +86,15 @@ export const DataValue: React.FC<DataValueProps> = ({
)}
</SText>
<SText as="div" font={font} {...VALUE_SIZES[size]}>
{isLoading ? <Skeleton width={80} height="1em" /> : children}
{isLoading ? (
<Skeleton
width={80}
height="1em"
enableAnimation={!disableSkeletonAnimation}
/>
) : (
children
)}
</SText>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,11 @@
"wallet.assets.transfer.position.toast.onLoading": "<0>Sending </0><1>{{value}}</1><0> to </0><1>{{address}}</1>",
"wallet.assets.transfer.position.toast.onSuccess": "<0>Sent </0><1>{{value}}</1><0> to </0><1>{{address}}</1>",
"wallet.assets.header.balance": "Total balance",
"wallet.assets.header.networth": "Net worth",
"wallet.assets.header.assetsBalance": "Assets balance",
"wallet.assets.header.assetsBorrowed": "of which {{ value, bignumber(numberPrefix: '$'; type: 'dollar') }} is borrowed",
"wallet.assets.header.liquidityBalance": "Liquidity balance",
"wallet.assets.header.farmsBalance": "Farms balance",
"wallet.assets.header.farmsBalance": "of which {{ value, bignumber(numberPrefix: '$'; type: 'dollar') }} is in farms",
"wallet.assets.header.positions": "Value in Omnipool",
"wallet.assets.header.balance.tooltip.title": "Sum of assets on wallet, assets in liquidity mining and assets in farm.",
"wallet.assets.header.balance.tooltip.assets": "Assets value:",
Expand Down
5 changes: 5 additions & 0 deletions src/providers/rpcProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,6 +32,7 @@ type TProviderContext = {
const ProviderContext = createContext<TProviderContext>({
isLoaded: false,
api: {} as TProviderContext["api"],
evm: {} as TProviderContext["evm"],
tradeRouter: {} as TradeRouter,
featureFlags: {} as TProviderContext["featureFlags"],
poolService: {} as TProviderContext["poolService"],
Expand Down Expand Up @@ -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,
Expand All @@ -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: {
Expand Down
4 changes: 3 additions & 1 deletion src/sections/lending/ui/header/DashboardHeaderValues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ export const DashboardHeaderValues: FC<{
(BN(underlyingBalance).gt(0) || BN(totalBorrows).gt(0)),
)

const { data: vDotApy, isLoading: isVDotApyLoading } = useBifrostVDotApy()
const { data: vDotApy, isLoading: isVDotApyLoading } = useBifrostVDotApy({
enabled: vDotSuppliedOrBorrowed,
})

return (
<>
Expand Down
25 changes: 16 additions & 9 deletions src/sections/transaction/ReviewTransaction.utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,26 @@ import {
useEvmAccount,
useWallet,
} from "sections/web3-connect/Web3Connect.utils"
import {
EthereumSigner,
PermitResult,
} from "sections/web3-connect/signer/EthereumSigner"
import { PermitResult } from "sections/web3-connect/signer/EthereumSigner"
import { useSettingsStore } from "state/store"
import { useToast } from "state/toasts"
import {
CALL_PERMIT_ABI,
CALL_PERMIT_ADDRESS,
H160,
getEvmChainById,
getEvmTxLink,
isEvmAccount,
isEvmAddress,
isEvmWalletExtension,
} from "utils/evm"
import { isAnyParachain, Maybe } from "utils/helpers"
import { createSubscanLink } from "utils/formatting"
import { QUERY_KEYS } from "utils/queryKeys"
import { useIsTestnet } from "api/provider"
import { tags } from "@galacticcouncil/xcm-cfg"
import { Contract } from "ethers"
import BN from "bignumber.js"
import { SolanaChain } from "@galacticcouncil/xcm-core"
import { SignatureStatus } from "@solana/web3.js"
import { getSolanaTxLink } from "utils/solana"
Expand Down Expand Up @@ -332,17 +334,22 @@ export const useSendSolanaTransactionMutation = (
}

export function useNextEvmPermitNonce(account: Maybe<AccountId32 | string>) {
const { wallet } = useWallet()
const { evm } = useRpcProvider()

return useQuery(
QUERY_KEYS.nextEvmPermitNonce(account),
async () => {
if (!account) throw new Error("Missing address")
if (!wallet?.signer) throw new Error("Missing wallet signer")
if (!(wallet?.signer instanceof EthereumSigner))
throw new Error("Invalid signer")

return wallet.signer.getPermitNonce()
const callPermit = new Contract(CALL_PERMIT_ADDRESS, CALL_PERMIT_ABI, evm)

const nonce: BN = await callPermit.nonces(
isEvmAddress(account.toString())
? account
: H160.fromAccount(account.toString()),
)

return nonce.toNumber()
},
{
refetchInterval: EVM_PERMIT_BLOCKTIME,
Expand Down
4 changes: 3 additions & 1 deletion src/sections/transaction/ReviewTransactionEvmTxForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useAssets } from "providers/assets"
import { FC } from "react"
import { useTranslation } from "react-i18next"
import Skeleton from "react-loading-skeleton"
import { useNextEvmPermitNonce } from "sections/transaction/ReviewTransaction.utils"
import { ReviewTransactionData } from "sections/transaction/ReviewTransactionData"
import { useEvmTxFee } from "sections/transaction/ReviewTransactionEvmTxForm.utils"
import {
Expand Down Expand Up @@ -57,6 +58,7 @@ export const ReviewTransactionEvmTxForm: FC<Props> = ({

const shouldUsePermit = feePaymentAssetId !== NATIVE_EVM_ASSET_ID

const { data: nonce } = useNextEvmPermitNonce(account?.address)
const { data: feeWETH, isLoading: isFeeLoading } = useEvmTxFee(tx.data)

const { mutate: signTx, isLoading } = useMutation(async () => {
Expand All @@ -66,7 +68,7 @@ export const ReviewTransactionEvmTxForm: FC<Props> = ({

if (wallet?.signer instanceof EthereumSigner) {
if (shouldUsePermit) {
const nonce = await wallet.signer.getPermitNonce()
if (typeof nonce === "undefined") throw new Error("Missing nonce")
const permit = await wallet.signer.getPermit(tx.data, nonce)
return onPermitDispatched({ permit })
}
Expand Down
5 changes: 3 additions & 2 deletions src/sections/transaction/ReviewTransactionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
useWeb3ConnectStore,
WalletMode,
} from "sections/web3-connect/store/useWeb3ConnectStore"
import { BN_0 } from "utils/constants"

type TxProps = Omit<Transaction, "id" | "tx" | "xcall"> & {
tx: SubmittableExtrinsic<"promise">
Expand Down Expand Up @@ -121,7 +120,9 @@ export const ReviewTransactionForm: FC<Props> = (props) => {
const txData = tx.method.toHex()

if (shouldUsePermit) {
const nonce = customNonce ? BN(customNonce) : permitNonce ?? BN_0
const nonce = customNonce
? parseFloat(customNonce)
: permitNonce ?? 0
const permit = await wallet.signer.getPermit(txData, nonce)
return props.onPermitDispatched({
permit,
Expand Down
14 changes: 12 additions & 2 deletions src/sections/wallet/assets/WalletAssets.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -46,6 +47,7 @@ export const useWalletAssetsTotals = ({
}: {
address?: string
} = {}) => {
const borrows = useUserBorrowSummary(address)
const assets = useAssetsData({ isAllAssets: false, address })
const lpPositions = useOmnipoolPositionsData({ address })
const farmsTotal = useFarmDepositsTotal(address)
Expand Down Expand Up @@ -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)
.minus(borrowsTotal)
.toString(),
[assetsTotal, farmsTotal.value, lpTotal, xykTotal],
[assetsTotal, farmsTotal.value, lpTotal, xykTotal, borrowsTotal],
)

const isLoading =
borrows.isLoading ||
assets.isLoading ||
lpPositions.isLoading ||
farmsTotal.isLoading ||
Expand All @@ -123,8 +129,12 @@ export const useWalletAssetsTotals = ({
return {
assetsTotal,
farmsTotal: farmsTotal.value,
lpTotal: BigNumber(lpTotal).plus(xykTotal).toString(),
lpTotal: BigNumber(lpTotal)
.plus(xykTotal)
.plus(farmsTotal.value)
.toString(),
balanceTotal,
borrowsTotal,
isLoading,
}
}
Loading
Loading