diff --git a/apps/evm/src/clients/api/queries/useGetPools/__testUtils__/fakeData.ts b/apps/evm/src/clients/api/queries/useGetPools/__testUtils__/fakeData.ts index b4b96ee00b..9e276be01f 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/__testUtils__/fakeData.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/__testUtils__/fakeData.ts @@ -59,14 +59,14 @@ const userIsolatedCollateralizedVTokenAddresses = [ '0xee543d5de2dbb5b07675fc72831a2f1812428393', ]; -const userCollateralizedVTokenAddresses = [ +const userCollateralVTokenAddresses = [ ...userLegacyCollateralizedVTokenAddresses, ...userIsolatedCollateralizedVTokenAddresses, ]; const vTokenBalancesAllMock = async (vTokenAddresses: string[]) => vTokenAddresses.map(vTokenAddress => { - const isUserCollateral = userCollateralizedVTokenAddresses.some( + const isUserCollateral = userCollateralVTokenAddresses.some( a => a.toLowerCase() === vTokenAddress.toLowerCase(), ); diff --git a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.prime.spec.ts.snap b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.prime.spec.ts.snap index 1fa10961a9..0e838709ba 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.prime.spec.ts.snap +++ b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.prime.spec.ts.snap @@ -1075,15 +1075,6 @@ exports[`useGetPools > does not fetch Prime distributions if user is not Prime 1 "userSupplyBalanceCents": "199992566000000", }, ], - "primeVTokenAddresses": [ - "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A", - "0x08e0A5575De71037aE36AbfAfb516595fE68e5e4", - "0x74469281310195A04840Daf6EdF576F559a3dE80", - "0x3338988d0beb4419Acb8fE624218754053362D06", - "0x2197d02cC9cd1ad51317A0a85A656a0c82383A7c", - "0x712774CBFFCBD60e9825871CcEFF2F917442b2c3", - ], } `; @@ -2282,15 +2273,6 @@ exports[`useGetPools > fetches and formats Prime distributions and Prime distrib "userSupplyBalanceCents": "199992566000000", }, ], - "primeVTokenAddresses": [ - "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A", - "0x08e0A5575De71037aE36AbfAfb516595fE68e5e4", - "0x74469281310195A04840Daf6EdF576F559a3dE80", - "0x3338988d0beb4419Acb8fE624218754053362D06", - "0x2197d02cC9cd1ad51317A0a85A656a0c82383A7c", - "0x712774CBFFCBD60e9825871CcEFF2F917442b2c3", - ], } `; @@ -3309,14 +3291,5 @@ exports[`useGetPools > filters out Prime simulations that are 0 1`] = ` "userSupplyBalanceCents": "199992566000000", }, ], - "primeVTokenAddresses": [ - "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", - "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A", - "0x08e0A5575De71037aE36AbfAfb516595fE68e5e4", - "0x74469281310195A04840Daf6EdF576F559a3dE80", - "0x3338988d0beb4419Acb8fE624218754053362D06", - "0x2197d02cC9cd1ad51317A0a85A656a0c82383A7c", - "0x712774CBFFCBD60e9825871CcEFF2F917442b2c3", - ], } `; diff --git a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap index 40f07122ff..e05a20c58d 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap +++ b/apps/evm/src/clients/api/queries/useGetPools/__tests__/__snapshots__/index.spec.ts.snap @@ -893,7 +893,6 @@ exports[`useGetPools > returns pools in the correct format 1`] = ` "userSupplyBalanceCents": "0", }, ], - "primeVTokenAddresses": [], } `; @@ -1790,7 +1789,6 @@ exports[`useGetPools > returns pools with time based reward rates in the correct "userSupplyBalanceCents": "0", }, ], - "primeVTokenAddresses": [], } `; @@ -2687,6 +2685,5 @@ exports[`useGetPools > returns pools with user data in the correct format 1`] = "userSupplyBalanceCents": "199992566000000", }, ], - "primeVTokenAddresses": [], } `; diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/formatDistributions/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/formatDistributions/index.ts index 9bbfbc7452..816fe857ed 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/formatDistributions/index.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/formatDistributions/index.ts @@ -1,12 +1,13 @@ import type BigNumber from 'bignumber.js'; -import type { AssetDistribution, PrimeApy, Token } from 'types'; +import type { AssetDistribution, Token } from 'types'; import { calculateDailyTokenRate } from 'utilities/calculateDailyTokenRate'; import findTokenByAddress from 'utilities/findTokenByAddress'; import formatRewardDistribution from 'utilities/formatRewardDistribution'; import type { ApiRewardDistributor } from 'clients/api/queries/useGetPools/getPools/getApiPools'; import { convertPriceMantissaToDollars } from 'utilities'; +import type { PrimeApy } from '../../../types'; import { isDistributingRewards } from './isDistributingRewards'; export type FormatDistributionsInput = { diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts index 1774c91976..47f1a8a6c4 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/formatOutput/index.ts @@ -4,7 +4,7 @@ import BigNumber from 'bignumber.js'; import { NATIVE_TOKEN_ADDRESS } from 'constants/address'; import { COMPOUND_DECIMALS } from 'constants/compoundMantissa'; import type { PoolLens } from 'libs/contracts'; -import type { Asset, ChainId, Pool, PrimeApy, Token, TokenBalance, VToken } from 'types'; +import type { Asset, ChainId, Pool, Token, TokenBalance, VToken } from 'types'; import { areAddressesEqual, areTokensEqual, @@ -16,7 +16,7 @@ import { getDisabledTokenActions, isPoolIsolated, } from 'utilities'; -import type { MarketParticipantsCounts } from '../../types'; +import type { MarketParticipantsCounts, PrimeApy } from '../../types'; import type { ApiPool } from '../getApiPools'; import { formatDistributions } from './formatDistributions'; @@ -26,19 +26,19 @@ export const formatOutput = ({ tokens, currentBlockNumber, isolatedPoolParticipantsCountMap, - primeApyMap, + userPrimeApyMap, userVTokenBalances = [], userTokenBalances = [], - userCollateralizedVTokenAddresses = [], + userCollateralVTokenAddresses = [], userVaiBorrowBalanceMantissa, }: { chainId: ChainId; tokens: Token[]; currentBlockNumber: number; apiPools: ApiPool[]; - primeApyMap: Map; - isolatedPoolParticipantsCountMap: Map; - userCollateralizedVTokenAddresses?: string[]; + isolatedPoolParticipantsCountMap?: Map; + userPrimeApyMap?: Map; + userCollateralVTokenAddresses?: string[]; userVTokenBalances?: Awaited>; userTokenBalances?: TokenBalance[]; userVaiBorrowBalanceMantissa?: BigNumber; @@ -139,7 +139,7 @@ export const formatOutput = ({ blocksPerDay, underlyingToken: vToken.underlyingToken, underlyingTokenPriceDollars: tokenPriceDollars, - primeApy: primeApyMap.get(vToken.address), + primeApy: userPrimeApyMap?.get(vToken.address), tokens, supplyBalanceTokens, borrowBalanceTokens, @@ -187,7 +187,7 @@ export const formatOutput = ({ const userBorrowBalanceCents = userBorrowBalanceTokens.multipliedBy(tokenPriceCents); const userWalletBalanceCents = userWalletBalanceTokens.multipliedBy(tokenPriceCents); - const isCollateralOfUser = !!userCollateralizedVTokenAddresses.some(address => + const isCollateralOfUser = !!userCollateralVTokenAddresses.some(address => areAddressesEqual(address, vToken.address), ); @@ -201,11 +201,11 @@ export const formatOutput = ({ } const supplierCount = isIsolated - ? isolatedPoolParticipantsCountMap.get(vToken.address.toLowerCase())?.supplierCount ?? 0 + ? isolatedPoolParticipantsCountMap?.get(vToken.address.toLowerCase())?.supplierCount ?? 0 : market.supplierCount; const borrowerCount = isIsolated - ? isolatedPoolParticipantsCountMap.get(vToken.address.toLowerCase())?.borrowerCount ?? 0 + ? isolatedPoolParticipantsCountMap?.get(vToken.address.toLowerCase())?.borrowerCount ?? 0 : market.borrowerCount; const asset: Asset = { diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/getIsolatedPoolParticipantCounts/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/getIsolatedPoolParticipantCounts/index.ts new file mode 100644 index 0000000000..859b899fa3 --- /dev/null +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/getIsolatedPoolParticipantCounts/index.ts @@ -0,0 +1,19 @@ +import { getIsolatedPoolParticipantsCount } from 'clients/subgraph'; +import type { ChainId } from 'types'; +import type { MarketParticipantsCounts } from '../../types'; + +export const getIsolatedPoolParticipantCounts = async ({ chainId }: { chainId: ChainId }) => { + const isolatedPoolParticipantsCount = await getIsolatedPoolParticipantsCount({ chainId }); + + const isolatedPoolParticipantsCountMap = new Map(); + (isolatedPoolParticipantsCount?.pools || []).forEach(pool => + pool.markets.forEach(market => { + isolatedPoolParticipantsCountMap.set(market.id.toLowerCase(), { + borrowerCount: +market.borrowerCount, + supplierCount: +market.supplierCount, + }); + }), + ); + + return { isolatedPoolParticipantsCountMap }; +}; diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserCollateralAddresses/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserCollateralAddresses/index.ts new file mode 100644 index 0000000000..1856002e34 --- /dev/null +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserCollateralAddresses/index.ts @@ -0,0 +1,53 @@ +import { + type IsolatedPoolComptroller, + type LegacyPoolComptroller, + getIsolatedPoolComptrollerContract, +} from 'libs/contracts'; +import type { Provider } from 'libs/wallet'; +import type { ChainId } from 'types'; +import { isPoolIsolated } from 'utilities'; +import type { ApiPool } from '../getApiPools'; + +export const getUserCollateralAddresses = async ({ + accountAddress, + apiPools, + chainId, + provider, + legacyPoolComptrollerContract, +}: { + accountAddress: string; + apiPools: ApiPool[]; + chainId: ChainId; + provider: Provider; + legacyPoolComptrollerContract?: LegacyPoolComptroller; +}) => { + const getAssetsInPromises: ReturnType[] = []; + + apiPools.forEach(pool => { + const isIsolated = isPoolIsolated({ + chainId, + comptrollerAddress: pool.address, + }); + + if (!isIsolated) { + return; + } + + const comptrollerContract = getIsolatedPoolComptrollerContract({ + signerOrProvider: provider, + address: pool.address, + }); + + if (accountAddress) { + getAssetsInPromises.push(comptrollerContract.getAssetsIn(accountAddress)); + } + }); + + if (accountAddress && legacyPoolComptrollerContract) { + getAssetsInPromises.push(legacyPoolComptrollerContract.getAssetsIn(accountAddress)); + } + + const results = await Promise.all(getAssetsInPromises); + + return { userCollateralAddresses: results.flat() }; +}; diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserPrimeApys/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserPrimeApys/index.ts new file mode 100644 index 0000000000..5d4d31bfd5 --- /dev/null +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserPrimeApys/index.ts @@ -0,0 +1,27 @@ +import type { Prime } from 'libs/contracts'; +import { convertAprBipsToApy } from 'utilities'; +import type { PrimeApy } from '../../types'; + +export const getUserPrimeApys = async ({ + primeContract, + accountAddress, + primeVTokenAddresses, +}: { primeContract: Prime; accountAddress: string; primeVTokenAddresses: string[] }) => { + const primeAprs = await Promise.all( + primeVTokenAddresses.map(primeVTokenAddress => + primeContract.calculateAPR(primeVTokenAddress, accountAddress), + ), + ); + + const userPrimeApyMap = new Map(); + primeAprs.forEach((primeApr, index) => { + const apys: PrimeApy = { + borrowApy: convertAprBipsToApy({ aprBips: primeApr.borrowAPR.toString() || '0' }), + supplyApy: convertAprBipsToApy({ aprBips: primeApr.supplyAPR.toString() || '0' }), + }; + + userPrimeApyMap.set(primeVTokenAddresses[index], apys); + }); + + return { userPrimeApyMap }; +}; diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserTokenBalances/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserTokenBalances/index.ts new file mode 100644 index 0000000000..b7662c8003 --- /dev/null +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserTokenBalances/index.ts @@ -0,0 +1,96 @@ +import { getTokenBalances } from 'clients/api'; +import { NATIVE_TOKEN_ADDRESS } from 'constants/address'; +import type { PoolLens, VenusLens } from 'libs/contracts'; +import type { Provider } from 'libs/wallet'; +import type { ChainId, Token } from 'types'; +import { findTokenByAddress, isPoolIsolated } from 'utilities'; +import type { ApiPool } from '../getApiPools'; + +export const getUserTokenBalances = async ({ + accountAddress, + apiPools, + chainId, + tokens, + provider, + poolLensContract, + venusLensContract, +}: { + accountAddress: string; + apiPools: ApiPool[]; + chainId: ChainId; + tokens: Token[]; + provider: Provider; + poolLensContract: PoolLens; + venusLensContract?: VenusLens; +}) => { + // Extract token records and addresses + const [legacyPoolVTokenAddresses, isolatedPoolsVTokenAddresses, underlyingTokens] = + apiPools.reduce<[string[], string[], Token[]]>( + (acc, pool) => { + const newLegacyPoolVTokenAddresses: string[] = []; + const newIsolatedPoolsVTokenAddresses: string[] = []; + const newUnderlyingTokens: Token[] = []; + const newUnderlyingTokenAddresses: string[] = []; + + pool.markets.forEach(market => { + const isIsolated = isPoolIsolated({ + chainId, + comptrollerAddress: pool.address, + }); + + // VToken addresses are unique + if (isIsolated) { + newIsolatedPoolsVTokenAddresses.push(market.address.toLowerCase()); + } else { + newLegacyPoolVTokenAddresses.push(market.address.toLowerCase()); + } + + const underlyingToken = findTokenByAddress({ + address: market.underlyingAddress || NATIVE_TOKEN_ADDRESS, + tokens, + }); + + if ( + underlyingToken && + !newUnderlyingTokenAddresses.includes(underlyingToken.address.toLowerCase()) + ) { + newUnderlyingTokens.push(underlyingToken); + newUnderlyingTokenAddresses.push(underlyingToken.address.toLowerCase()); + } + }); + + return [ + acc[0].concat(newLegacyPoolVTokenAddresses), + acc[1].concat(newIsolatedPoolsVTokenAddresses), + acc[2].concat(newUnderlyingTokens), + ]; + }, + [[], [], []], + ); + + const [userIsolatedPoolVTokenBalances, userLegacyPoolVTokenBalances, userTokenBalances] = + await Promise.all([ + accountAddress + ? poolLensContract.callStatic.vTokenBalancesAll( + isolatedPoolsVTokenAddresses, + accountAddress, + ) + : undefined, + accountAddress && venusLensContract + ? venusLensContract.callStatic.vTokenBalancesAll(legacyPoolVTokenAddresses, accountAddress) + : undefined, + accountAddress + ? getTokenBalances({ + accountAddress, + tokens: underlyingTokens, + provider, + }) + : undefined, + ]); + + return { + userIsolatedPoolVTokenBalances, + userLegacyPoolVTokenBalances, + userTokenBalances, + }; +}; diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserVaiBorrowBalance/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserVaiBorrowBalance/index.ts new file mode 100644 index 0000000000..4f04e59c43 --- /dev/null +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/getUserVaiBorrowBalance/index.ts @@ -0,0 +1,21 @@ +import BigNumber from 'bignumber.js'; +import type { VaiController } from 'libs/contracts'; + +export const getUserVaiBorrowBalance = async ({ + accountAddress, + vaiControllerContract, +}: { accountAddress: string; vaiControllerContract?: VaiController }) => { + const [_accrueVaiInterest, vaiRepayAmountMantissa] = await Promise.all([ + // Call (statically) accrueVAIInterest to calculate past accrued interests before fetching all + // interests. Since multicall will batch these requests, the call to accrueVAIInterest and + // getVAIRepayAmount will happen in the same request (thus making the accrual possible) + vaiControllerContract ? vaiControllerContract.callStatic.accrueVAIInterest() : undefined, + vaiControllerContract ? vaiControllerContract.getVAIRepayAmount(accountAddress) : undefined, + ]); + + const userVaiBorrowBalanceMantissa = vaiRepayAmountMantissa + ? new BigNumber(vaiRepayAmountMantissa.toString()) + : undefined; + + return { userVaiBorrowBalanceMantissa }; +}; diff --git a/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts b/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts index ff5565ffb4..4bf5867436 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/getPools/index.ts @@ -1,23 +1,31 @@ import BigNumber from 'bignumber.js'; -import { getBlockNumber, getTokenBalances } from 'clients/api'; -import { getIsolatedPoolParticipantsCount } from 'clients/subgraph'; -import { NATIVE_TOKEN_ADDRESS } from 'constants/address'; -import { type IsolatedPoolComptroller, getIsolatedPoolComptrollerContract } from 'libs/contracts'; -import type { Asset, PrimeApy, Token } from 'types'; -import { - convertAprBipsToApy, - extractSettledPromiseValue, - findTokenByAddress, - isPoolIsolated, -} from 'utilities'; -import removeDuplicates from 'utilities/removeDuplicates'; +import { getBlockNumber } from 'clients/api'; +import type { GetIsolatedPoolParticipantsCountInput } from 'clients/subgraph'; +import type { PoolLens } from 'libs/contracts'; +import type { Asset, TokenBalance } from 'types'; import { logError } from 'libs/errors'; -import type { GetPoolsInput, GetPoolsOutput, MarketParticipantsCounts } from '../types'; +import type { GetPoolsInput, GetPoolsOutput, PrimeApy } from '../types'; import { appendPrimeSimulationDistributions } from './appendPrimeSimulationDistributions'; import { formatOutput } from './formatOutput'; import { getApiPools } from './getApiPools'; +import { getIsolatedPoolParticipantCounts } from './getIsolatedPoolParticipantCounts'; +import { getUserCollateralAddresses } from './getUserCollateralAddresses'; +import { getUserPrimeApys } from './getUserPrimeApys'; +import { getUserTokenBalances } from './getUserTokenBalances'; +import { getUserVaiBorrowBalance } from './getUserVaiBorrowBalance'; + +const safeGetIsolatedPoolParticipantCount = async ( + input: GetIsolatedPoolParticipantsCountInput, +) => { + try { + const result = await getIsolatedPoolParticipantCounts(input); + return result; + } catch (error) { + logError(error); + } +}; export const getPools = async ({ chainId, @@ -31,252 +39,95 @@ export const getPools = async ({ tokens, }: GetPoolsInput) => { const [ - apiPoolsResult, - isolatedPoolParticipantsCountResult, - currentBlockNumberResult, - primeVTokenAddressesResult, - primeMinimumXvsToStakeResult, - userPrimeTokenResult, - _accrueVaiInterestResult, - vaiRepayAmountResult, - ] = await Promise.allSettled([ + { pools: apiPools }, + isolatedPoolParticipantCounts, + { blockNumber: currentBlockNumber }, + unsafePrimeVTokenAddresses, + primeMinimumXvsToStakeMantissa, + userPrimeToken, + ] = await Promise.all([ getApiPools({ chainId }), - getIsolatedPoolParticipantsCount({ chainId }), + safeGetIsolatedPoolParticipantCount({ chainId }), // Fetch current block number getBlockNumber({ provider }), // Prime related calls primeContract?.getAllMarkets(), // TODO: get from API primeContract?.MINIMUM_STAKED_XVS(), // TODO: get from API accountAddress ? primeContract?.tokens(accountAddress) : undefined, - // Call (statically) accrueVAIInterest to calculate past accrued interests before fetching all - // interests. Since multicall will batch these requests, the call to accrueVAIInterest and - // getVAIRepayAmount will happen in the same request (thus making the accrual possible) - accountAddress && vaiControllerContract - ? vaiControllerContract.callStatic.accrueVAIInterest() - : undefined, - accountAddress && vaiControllerContract - ? vaiControllerContract.getVAIRepayAmount(accountAddress) - : undefined, - ]); - - if (apiPoolsResult.status === 'rejected') { - throw new Error(apiPoolsResult.reason); - } - - if (currentBlockNumberResult.status === 'rejected') { - throw new Error(currentBlockNumberResult.reason); - } - - const apiPools = apiPoolsResult.value.pools; - - // Extract token records and addresses - const [legacyPoolVTokenAddresses, isolatedPoolsVTokenAddresses, underlyingTokens] = - apiPools.reduce<[string[], string[], Token[]]>( - (acc, pool) => { - const newLegacyPoolVTokenAddresses: string[] = []; - const newIsolatedPoolsVTokenAddresses: string[] = []; - const newUnderlyingTokens: Token[] = []; - const newUnderlyingTokenAddresses: string[] = []; - - pool.markets.forEach(market => { - const isIsolated = isPoolIsolated({ - chainId, - comptrollerAddress: pool.address, - }); - - // VToken addresses are unique - if (isIsolated) { - newIsolatedPoolsVTokenAddresses.push(market.address.toLowerCase()); - } else { - newLegacyPoolVTokenAddresses.push(market.address.toLowerCase()); - } - - const underlyingToken = findTokenByAddress({ - address: market.underlyingAddress || NATIVE_TOKEN_ADDRESS, - tokens, - }); - - if ( - underlyingToken && - !newUnderlyingTokenAddresses.includes(underlyingToken.address.toLowerCase()) - ) { - newUnderlyingTokens.push(underlyingToken); - newUnderlyingTokenAddresses.push(underlyingToken.address.toLowerCase()); - } - }); - - return [ - acc[0].concat(newLegacyPoolVTokenAddresses), - acc[1].concat(newIsolatedPoolsVTokenAddresses), - acc[2].concat(newUnderlyingTokens), - ]; - }, - [[], [], []], - ); - - // Fetch addresses of user collaterals - const getAssetsInPromises: ReturnType[] = []; - apiPools.forEach(pool => { - const isIsolated = isPoolIsolated({ - chainId, - comptrollerAddress: pool.address, - }); - - if (!isIsolated) { - return; - } - - const comptrollerContract = getIsolatedPoolComptrollerContract({ - signerOrProvider: provider, - address: pool.address, - }); - - if (accountAddress) { - getAssetsInPromises.push(comptrollerContract.getAssetsIn(accountAddress)); - } - }); - - if (accountAddress && legacyPoolComptrollerContract) { - getAssetsInPromises.push(legacyPoolComptrollerContract.getAssetsIn(accountAddress)); - } - - const settledGetAssetsInPromises = Promise.allSettled(getAssetsInPromises); - - // Fetch user token balances - const tokenBalancesPromises = Promise.allSettled([ - accountAddress - ? poolLensContract.callStatic.vTokenBalancesAll(isolatedPoolsVTokenAddresses, accountAddress) - : undefined, - accountAddress && venusLensContract - ? venusLensContract.callStatic.vTokenBalancesAll(legacyPoolVTokenAddresses, accountAddress) - : undefined, - accountAddress - ? getTokenBalances({ - accountAddress, - tokens: underlyingTokens, - provider, - }) - : undefined, ]); - // Fetch Prime distributions - const primeVTokenAddresses = extractSettledPromiseValue(primeVTokenAddressesResult) || []; - const isUserPrime = extractSettledPromiseValue(userPrimeTokenResult)?.exists || false; - - const settledPrimeAprPromises = - primeContract && isUserPrime - ? Promise.allSettled( - accountAddress - ? primeVTokenAddresses.map(primeVTokenAddress => - primeContract.calculateAPR(primeVTokenAddress, accountAddress), - ) - : [], - ) - : undefined; - - const [ - userIsolatedPoolVTokenBalancesResult, - userLegacyPoolVTokenBalancesResult, - userTokenBalancesResult, - ] = await tokenBalancesPromises; - const userAssetsInResults = await settledGetAssetsInPromises; - const primeAprResults = (await settledPrimeAprPromises) || []; - - if (userIsolatedPoolVTokenBalancesResult?.status === 'rejected') { - throw new Error(userIsolatedPoolVTokenBalancesResult.reason); - } - - if (userLegacyPoolVTokenBalancesResult?.status === 'rejected') { - throw new Error(userLegacyPoolVTokenBalancesResult.reason); - } - - if (userTokenBalancesResult?.status === 'rejected') { - throw new Error(userTokenBalancesResult.reason); - } - - // Extract user vToken balances - const userIsolatedPoolVTokenBalances = extractSettledPromiseValue( - userIsolatedPoolVTokenBalancesResult, - ); - const userLegacyPoolVTokenBalances = extractSettledPromiseValue( - userLegacyPoolVTokenBalancesResult, - ); - const userVTokenBalances = [ - ...(userIsolatedPoolVTokenBalances || []), - ...(userLegacyPoolVTokenBalances || []), - ]; - - // Extract addresses of user collaterals - const userCollateralizedVTokenAddresses = removeDuplicates( - userAssetsInResults.reduce((acc, userAssetsInResult) => { - if (userAssetsInResult.status === 'rejected') { - throw new Error(userAssetsInResult.reason); - } - - return acc.concat(userAssetsInResult.value); - }, []), - ); - - // Extract Prime APYs - const primeApyMap = new Map(); - primeAprResults.forEach((primeAprResult, index) => { - if (primeAprResult.status === 'rejected') { - throw new Error(primeAprResult.reason); - } - - const primeApr = primeAprResult.value; - - const apys: PrimeApy = { - borrowApy: convertAprBipsToApy({ aprBips: primeApr?.borrowAPR.toString() || '0' }), - supplyApy: convertAprBipsToApy({ aprBips: primeApr?.supplyAPR.toString() || '0' }), - }; - - primeApyMap.set(primeVTokenAddresses[index], apys); - }); - - const vaiRepayAmountMantissa = extractSettledPromiseValue(vaiRepayAmountResult); - - // Extract isolated pool participants count - if (isolatedPoolParticipantsCountResult.status === 'rejected') { - // Log error without throwing so assets can still be displayed - logError(isolatedPoolParticipantsCountResult.reason); + const primeVTokenAddresses = unsafePrimeVTokenAddresses ?? []; + const isUserPrime = userPrimeToken?.exists || false; + + let userCollateralVTokenAddresses: string[] | undefined; + let userVTokenBalances: + | Awaited> + | undefined; + let userTokenBalances: TokenBalance[] | undefined; + let userVaiBorrowBalanceMantissa: BigNumber | undefined; + let userPrimeApyMap: Map | undefined; + + if (accountAddress) { + const [userCollaterals, userBalances, userVaiBorrowBalance, userPrimeApys] = await Promise.all([ + getUserCollateralAddresses({ + chainId, + accountAddress, + legacyPoolComptrollerContract, + apiPools, + provider, + }), + getUserTokenBalances({ + accountAddress, + apiPools, + chainId, + tokens, + provider, + poolLensContract, + venusLensContract, + }), + getUserVaiBorrowBalance({ + accountAddress, + vaiControllerContract, + }), + isUserPrime && primeContract + ? getUserPrimeApys({ + accountAddress, + primeContract, + primeVTokenAddresses, + }) + : undefined, + ]); + + userCollateralVTokenAddresses = userCollaterals.userCollateralAddresses; + + userVTokenBalances = [ + ...(userBalances.userIsolatedPoolVTokenBalances || []), + ...(userBalances.userLegacyPoolVTokenBalances || []), + ]; + + userTokenBalances = userBalances.userTokenBalances?.tokenBalances; + + userVaiBorrowBalanceMantissa = userVaiBorrowBalance.userVaiBorrowBalanceMantissa; + + userPrimeApyMap = userPrimeApys?.userPrimeApyMap; } - const isolatedPoolParticipantsCount = extractSettledPromiseValue( - isolatedPoolParticipantsCountResult, - ); - - const isolatedPoolParticipantsCountMap = new Map(); - (isolatedPoolParticipantsCount?.pools || []).forEach(pool => - pool.markets.forEach(market => { - isolatedPoolParticipantsCountMap.set(market.id.toLowerCase(), { - borrowerCount: +market.borrowerCount, - supplierCount: +market.supplierCount, - }); - }), - ); - const pools = formatOutput({ chainId, tokens, - currentBlockNumber: currentBlockNumberResult.value.blockNumber, - apiPools: apiPoolsResult.value.pools, - userCollateralizedVTokenAddresses, + currentBlockNumber, + apiPools, + isolatedPoolParticipantsCountMap: + isolatedPoolParticipantCounts?.isolatedPoolParticipantsCountMap, + userPrimeApyMap, + userCollateralVTokenAddresses, userVTokenBalances, - userTokenBalances: extractSettledPromiseValue(userTokenBalancesResult)?.tokenBalances, - userVaiBorrowBalanceMantissa: vaiRepayAmountMantissa - ? new BigNumber(vaiRepayAmountMantissa.toString()) - : undefined, - primeApyMap, - isolatedPoolParticipantsCountMap, + userTokenBalances, + userVaiBorrowBalanceMantissa, }); // Add Prime simulations // TODO: get Prime simulations from API - const primeMinimumXvsToStakeValue = extractSettledPromiseValue(primeMinimumXvsToStakeResult); - const primeMinimumXvsToStakeMantissa = - primeMinimumXvsToStakeValue && new BigNumber(primeMinimumXvsToStakeValue.toString()); - const xvs = tokens.find(token => token.symbol === 'XVS'); if (primeContract && primeMinimumXvsToStakeMantissa && xvs) { await appendPrimeSimulationDistributions({ @@ -291,7 +142,6 @@ export const getPools = async ({ const output: GetPoolsOutput = { pools, - primeVTokenAddresses, }; return output; diff --git a/apps/evm/src/clients/api/queries/useGetPools/types.ts b/apps/evm/src/clients/api/queries/useGetPools/types.ts index 2ab28fd23f..5c093e4c1c 100644 --- a/apps/evm/src/clients/api/queries/useGetPools/types.ts +++ b/apps/evm/src/clients/api/queries/useGetPools/types.ts @@ -1,3 +1,4 @@ +import type BigNumber from 'bignumber.js'; import type { LegacyPoolComptroller, PoolLens, @@ -13,6 +14,10 @@ export interface MarketParticipantsCounts { supplierCount: number; } +export interface PrimeApy { + borrowApy: BigNumber; + supplyApy: BigNumber; +} export interface GetPoolsInput { chainId: ChainId; tokens: Token[]; @@ -27,5 +32,4 @@ export interface GetPoolsInput { export interface GetPoolsOutput { pools: Pool[]; - primeVTokenAddresses: string[]; } diff --git a/apps/evm/src/types/index.ts b/apps/evm/src/types/index.ts index 34cb57e922..1200aea0f9 100644 --- a/apps/evm/src/types/index.ts +++ b/apps/evm/src/types/index.ts @@ -457,11 +457,6 @@ export type SwapError = export type PSTokenCombination = [PSToken, PSToken]; -export interface PrimeApy { - borrowApy: BigNumber; - supplyApy: BigNumber; -} - export type ContractTxData< TContract extends BaseContract, TMethodName extends keyof TContract['functions'],