diff --git a/centrifuge-app/src/pages/Pools.tsx b/centrifuge-app/src/pages/Pools.tsx index c00410707..d1432ee86 100644 --- a/centrifuge-app/src/pages/Pools.tsx +++ b/centrifuge-app/src/pages/Pools.tsx @@ -1,7 +1,7 @@ import { formatBalance } from '@centrifuge/centrifuge-react' -import { Box, IconArrowUpRight, Stack, StatusChip, Text } from '@centrifuge/fabric' +import { Box, IconArrowDown, IconArrowUpRight, Stack, StatusChip, Text } from '@centrifuge/fabric' import * as React from 'react' -import { useListedPools } from '../../src/utils/useListedPools' +import { getYearOverYearGrowth, useListedPools } from '../../src/utils/useListedPools' import { LayoutSection } from '../components/LayoutBase/LayoutSection' import { PoolList } from '../components/PoolList' import { prefetchRoute } from '../components/Root' @@ -10,6 +10,9 @@ import { Dec } from '../utils/Decimal' export default function PoolsPage() { const [, listedTokens] = useListedPools() + const { totalValueLockedGrowth, isLoading } = getYearOverYearGrowth() + const isPositiveYoy = totalValueLockedGrowth > 0 + const IconComponent = isPositiveYoy ? IconArrowUpRight : IconArrowDown const totalValueLocked = React.useMemo(() => { return ( @@ -41,12 +44,16 @@ export default function PoolsPage() { Total value locked (TVL) - - - - 24% YoY - - + {!isLoading && ( + + + + + {formatBalance(totalValueLockedGrowth ?? 0, '', 2)} YoY + + + + )} {formatBalance(totalValueLocked ?? 0, config.baseCurrency)} diff --git a/centrifuge-app/src/utils/useListedPools.ts b/centrifuge-app/src/utils/useListedPools.ts index 7f03ea92d..6645f512e 100644 --- a/centrifuge-app/src/utils/useListedPools.ts +++ b/centrifuge-app/src/utils/useListedPools.ts @@ -1,12 +1,20 @@ -import { PoolMetadata } from '@centrifuge/centrifuge-js' +import { CurrencyBalance, PoolMetadata } from '@centrifuge/centrifuge-js' import BN from 'bn.js' import * as React from 'react' +import { useMemo } from 'react' import { useAddress } from '../utils/useAddress' import { useMetadataMulti } from '../utils/useMetadata' import { usePermissions } from '../utils/usePermissions' import { usePools } from '../utils/usePools' +import { Dec } from './Decimal' import { getPoolTVL } from './getPoolTVL' import { useTinlakePools } from './tinlake/useTinlakePools' +import { useSubquery } from './useSubquery' + +type FlattenedDataItem = { + netAssetValue: string + decimals: number +} const sign = (n: BN) => (n.isZero() ? 0 : n.isNeg() ? -1 : 1) @@ -40,3 +48,84 @@ export function useListedPools() { return [listedPools, listedTokens, isLoading] as const } + +export function getYearOverYearGrowth() { + const [listedPools] = useListedPools() + + const { oneDayAgoFromOneYearAgo, nextDay } = useMemo(() => { + const today = new Date() + const oneYearAgo = new Date(Date.UTC(today.getUTCFullYear() - 1, today.getUTCMonth(), today.getUTCDate(), 0, 0, 0)) + + const addOneDay = (date: Date): Date => { + const newDate = new Date(date) + newDate.setDate(date.getDate() + 1) + return newDate + } + + return { + oneDayAgoFromOneYearAgo: oneYearAgo, + nextDay: addOneDay(oneYearAgo), + } + }, []) + + const { data, isLoading } = useSubquery( + `query ($oneDayAgoFromOneYearAgo: Datetime!, $nextDay: Datetime!) { + pools { + nodes { + currency { + decimals + } + poolSnapshots( + filter: { + timestamp: { + greaterThan: $oneDayAgoFromOneYearAgo, + lessThan: $nextDay + } + }, + ) { + nodes { + netAssetValue + timestamp + } + } + } + } + }`, + { + oneDayAgoFromOneYearAgo, + nextDay, + }, + { + enabled: !!oneDayAgoFromOneYearAgo, + } + ) + + const flattenedData = + data?.pools?.nodes.flatMap((pool: any) => + pool.poolSnapshots.nodes.map((snapshot: any) => ({ + netAssetValue: snapshot.netAssetValue, + decimals: pool.currency.decimals, + })) + ) || [] + + // Aggregate NAV from last year + const aggregatedNetAssetValue = flattenedData.reduce((accumulator: any, item: FlattenedDataItem) => { + const netAssetValue = new CurrencyBalance(item.netAssetValue, item.decimals) + return accumulator.add(netAssetValue.toDecimal()) + }, Dec(0)) + + const aggregatedListedPoolsNav = listedPools.reduce((accumulator, pool) => { + const decimal = pool.currency?.decimals ?? 0 + const navTotal = new CurrencyBalance(pool.nav.total, decimal) + return accumulator.add(navTotal.toDecimal()) + }, Dec(0)) + + const lastYearNAV = aggregatedNetAssetValue.toNumber() + const currentYearNAV = aggregatedListedPoolsNav.toNumber() + + // YoY growth + const totalValueLockedGrowth = + lastYearNAV && currentYearNAV ? ((currentYearNAV - lastYearNAV) / lastYearNAV) * 100 : 0 + + return { totalValueLockedGrowth, isLoading } +}