diff --git a/src/components/Header/useMenu.tsx b/src/components/Header/useMenu.tsx
index 01d9e1de63..88d67a9257 100644
--- a/src/components/Header/useMenu.tsx
+++ b/src/components/Header/useMenu.tsx
@@ -295,7 +295,27 @@ const useMenu: UseMenu = () => {
}
if (featureEnabled(Feature.ANALYTICS, chainId)) {
+ if (featureEnabled(Feature.TRIDENT, chainId)) {
+ analyticsMenu.items.push({
+ key: 'trident',
+ title: 'Trident',
+ link: `/analytics/trident`,
+ })
+ }
menu.push(analyticsMenu)
+ } else if (featureEnabled(Feature.TRIDENT, chainId)) {
+ menu.push({
+ key: 'analytics',
+ title: i18n._(t`Analytics`),
+ icon: ,
+ items: [
+ {
+ key: 'trident',
+ title: 'Trident',
+ link: `/analytics/trident`,
+ },
+ ],
+ })
}
if (account) {
diff --git a/src/features/analytics/trident/SearchResultTokens.tsx b/src/features/analytics/trident/SearchResultTokens.tsx
new file mode 100644
index 0000000000..2918d0aeee
--- /dev/null
+++ b/src/features/analytics/trident/SearchResultTokens.tsx
@@ -0,0 +1,124 @@
+import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid'
+import { TablePageToggler } from 'app/features/transactions/TablePageToggler'
+import {
+ TABLE_TABLE_CLASSNAME,
+ TABLE_TBODY_TD_CLASSNAME,
+ TABLE_TBODY_TR_CLASSNAME,
+ TABLE_TR_TH_CLASSNAME,
+ TABLE_WRAPPER_DIV_CLASSNAME,
+} from 'app/features/trident/constants'
+import Link from 'next/link'
+import React, { FC } from 'react'
+// @ts-ignore TYPE NEEDS FIXING
+import { useFilters, useFlexLayout, usePagination, useSortBy, useTable } from 'react-table'
+
+import { SearchCategoryLabel } from '../tokens/SearchCategoryLabel'
+import { useInstantiateTableFeatures } from './useInstantiateTableFeatures'
+import { useTokensTableConfig } from './useTokensTableConfig'
+
+const SearchResultTokens: FC<{ chainId: number }> = ({ chainId }) => {
+ const { config } = useTokensTableConfig(chainId)
+
+ const {
+ getTableProps,
+ getTableBodyProps,
+ headerGroups,
+ rows,
+ // @ts-ignore TYPE NEEDS FIXING
+ page,
+ // @ts-ignore TYPE NEEDS FIXING
+ gotoPage,
+ // @ts-ignore TYPE NEEDS FIXING
+ canPreviousPage,
+ // @ts-ignore TYPE NEEDS FIXING
+ canNextPage,
+ // @ts-ignore TYPE NEEDS FIXING
+ prepareRow,
+ // @ts-ignore TYPE NEEDS FIXING
+ setFilter,
+ // @ts-ignore TYPE NEEDS FIXING
+ // toggleSortBy,
+ // @ts-ignore TYPE NEEDS FIXING
+ state: { pageIndex, pageSize },
+ // @ts-ignore TYPE NEEDS FIXING
+ } = useTable(config, useFlexLayout, useFilters, useSortBy, useFlexLayout, usePagination)
+ useInstantiateTableFeatures(setFilter)
+ return (
+
+
+
+
+
+ {headerGroups.map((headerGroup, i) => (
+
+ {headerGroup.headers.map((column, i) => (
+
+ {column.render('Header')}
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {column.isSorted ? (
+ // @ts-ignore TYPE NEEDS FIXING
+ column.isSortedDesc ? (
+
+ ) : (
+
+ )
+ ) : (
+ ''
+ )}
+
+ |
+ ))}
+
+ ))}
+
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {page.map((row, i) => {
+ prepareRow(row)
+ return (
+
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {row.cells.map((cell, i) => {
+ return (
+
+ {cell.render('Cell')}
+ |
+ )
+ })}
+
+
+ )
+ })}
+
+
+
+
+
+ )
+}
+
+export default SearchResultTokens
diff --git a/src/features/analytics/trident/tokenTableFilters.ts b/src/features/analytics/trident/tokenTableFilters.ts
new file mode 100644
index 0000000000..a29271513d
--- /dev/null
+++ b/src/features/analytics/trident/tokenTableFilters.ts
@@ -0,0 +1,12 @@
+// import { Fee } from '@sushiswap/trident-sdk'
+
+type FilterSymbolsFunc = (arg0: { original: { name: string; symbol: string } }[], arg1: string[], arg2: T) => any[]
+
+export const filterForSearchQuery: FilterSymbolsFunc<{ searchQuery: string }> = (rows, id, filterValue) => {
+ return rows.filter(({ original }) => {
+ // Allow searching for symbol (LINK) or name (chainlink)
+ const searchableText = original?.name?.concat(original?.symbol)?.toLowerCase()
+ // return true
+ return !filterValue.searchQuery.length || searchableText.includes(filterValue.searchQuery.toLowerCase())
+ })
+}
diff --git a/src/features/analytics/trident/tokens/Header.tsx b/src/features/analytics/trident/tokens/Header.tsx
new file mode 100644
index 0000000000..083eb7e396
--- /dev/null
+++ b/src/features/analytics/trident/tokens/Header.tsx
@@ -0,0 +1,81 @@
+import { getAddress } from '@ethersproject/address'
+import { LinkIcon } from '@heroicons/react/outline'
+import { useLingui } from '@lingui/react'
+import { ChainId, Token } from '@sushiswap/core-sdk'
+import CopyHelper from 'app/components/AccountDetails/Copy'
+import { CurrencyLogo } from 'app/components/CurrencyLogo'
+import Typography from 'app/components/Typography'
+import { formatNumber, getExplorerLink, shortenAddress } from 'app/functions'
+import { useAllTokens } from 'app/hooks/Tokens'
+import useDesktopMediaQuery from 'app/hooks/useDesktopMediaQuery'
+import Link from 'next/link'
+import { FC, useMemo } from 'react'
+
+interface HeaderProps {
+ chainId: ChainId
+ token?: any
+}
+
+export const Header: FC = ({ token, chainId }) => {
+ const { i18n } = useLingui()
+ const isDesktop = useDesktopMediaQuery()
+ const allTokens = useAllTokens()
+ const currency = useMemo(() => {
+ const address = getAddress(token.id)
+ return address in allTokens
+ ? allTokens[address]
+ : new Token(chainId, address, Number(token.decimals), token.symbol, token.name)
+ }, [token, allTokens, chainId])
+ return (
+
+
+
+
+
+ {i18n._('Price')}
+
+
+ {formatNumber(token.price.derivedUSD, true)}
+
+
+
+
+ {i18n._('Liquidity')}
+
+
+ {formatNumber(token.kpi.liquidityUSD, true)}
+
+
+
+
+ )
+}
+
+export default Header
diff --git a/src/features/analytics/trident/tokens/TokenStats.tsx b/src/features/analytics/trident/tokens/TokenStats.tsx
new file mode 100644
index 0000000000..c32b538cd8
--- /dev/null
+++ b/src/features/analytics/trident/tokens/TokenStats.tsx
@@ -0,0 +1,83 @@
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import { ChainId } from '@sushiswap/core-sdk'
+import Typography from 'app/components/Typography'
+import { classNames, formatPercent } from 'app/functions'
+import useDesktopMediaQuery from 'app/hooks/useDesktopMediaQuery'
+import { useTridentRollingTokenStats } from 'app/services/graph'
+import { FC } from 'react'
+
+interface TokenStatsProps {
+ chainId: ChainId
+ token?: any
+}
+
+const TokenStats: FC = ({ chainId, token }) => {
+ const { i18n } = useLingui()
+ const isDesktop = useDesktopMediaQuery()
+
+ const { data: stats } = useTridentRollingTokenStats({
+ chainId,
+ variables: { where: { id: token.id.toLowerCase() } },
+ shouldFetch: !!chainId && !!token && !!token.id.toLowerCase(),
+ })
+
+ const items = [
+ {
+ label: i18n._(t`Volume (24H)`),
+ value: 'volume',
+ change: 'volume24hChange',
+ },
+ {
+ label: i18n._(t`Fees (24H)`),
+ value: 'fees',
+ change: 'fees24hChange',
+ },
+ {
+ label: i18n._(t`Utilization (24H)`),
+ value: 'liquidity',
+ change: 'liquidity24hChange',
+ },
+ {
+ label: i18n._(t`Transactions (24H)`),
+ value: 'transactions',
+ change: 'transactions24hChange',
+ },
+ ]
+
+ return (
+
+ {items.map(({ label, value, change }, index) => (
+
+
+ {label}
+
+
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {stats?.[0]?.[value]}
+
+ 0 && 'text-green',
+ stats?.[0]?.[change] < 0 && 'text-red',
+ 'text-inherit'
+ )}
+ >
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {formatPercent(stats?.[0]?.[change])}
+
+
+
+ ))}
+
+ )
+}
+
+export default TokenStats
diff --git a/src/features/analytics/trident/tokens/TokenStatsChart.tsx b/src/features/analytics/trident/tokens/TokenStatsChart.tsx
new file mode 100644
index 0000000000..4829c059b6
--- /dev/null
+++ b/src/features/analytics/trident/tokens/TokenStatsChart.tsx
@@ -0,0 +1,150 @@
+import { ChainId } from '@sushiswap/core-sdk'
+import BarGraph from 'app/components/BarGraph'
+import Button from 'app/components/Button'
+import LineGraph from 'app/components/LineGraph'
+import Tabs from 'app/components/Tabs'
+import Typography from 'app/components/Typography'
+import { formatDate, formatNumber } from 'app/functions'
+import useDesktopMediaQuery from 'app/hooks/useDesktopMediaQuery'
+import { useTridentTokenDayBuckets, useTridentTokenHourBuckets } from 'app/services/graph'
+import { FC, useEffect, useMemo, useState } from 'react'
+
+interface TokenStatsChartProps {
+ chainId: ChainId
+ token?: any
+}
+
+enum ChartType {
+ Volume = 'Volume',
+ TVL = 'TVL',
+ Price = 'Price',
+}
+
+enum ChartRange {
+ '24H' = '24H',
+ '1W' = '1W',
+ '1M' = '1M',
+ '1Y' = '1Y',
+ 'ALL' = 'ALL',
+}
+
+const chartTimespans: Record = {
+ [ChartRange['24H']]: 86400,
+ [ChartRange['1W']]: 604800,
+ [ChartRange['1M']]: 2629746,
+ [ChartRange['1Y']]: 31556952,
+ [ChartRange['ALL']]: Infinity,
+}
+
+const TokenStatsChart: FC = ({ token, chainId }) => {
+ const isDesktop = useDesktopMediaQuery()
+ const [chartType, setChartType] = useState(ChartType.Volume)
+ const [chartRange, setChartRange] = useState(ChartRange['1W'])
+
+ const hourBuckets = useTridentTokenHourBuckets({
+ chainId,
+ variables: {
+ first: 168,
+ where: { token: token.id.toLowerCase() },
+ },
+ shouldFetch: !!token && chartTimespans[chartRange] < chartTimespans['1W'],
+ })
+
+ const dayBuckets = useTridentTokenDayBuckets({
+ chainId,
+ variables: {
+ where: { token: token.id.toLowerCase() },
+ },
+ shouldFetch: !!token && chartTimespans[chartRange] >= chartTimespans['1W'],
+ })
+
+ const data = chartTimespans[chartRange] < chartTimespans['1W'] ? hourBuckets : dayBuckets
+
+ const graphData = useMemo(() => {
+ const currentDate = Math.round(Date.now() / 1000)
+ return (
+ data
+ ?.reduce((acc, cur) => {
+ const x = cur.date.getTime()
+ if (Math.round(x / 1000) >= currentDate - chartTimespans[chartRange]) {
+ acc.push({
+ // @ts-ignore TYPE NEEDS FIXING
+ x,
+ // @ts-ignore TYPE NEEDS FIXING
+ y: Number(
+ chartType === ChartType.Volume
+ ? cur.volumeUSD
+ : chartType === ChartType.Price
+ ? cur.priceUSD
+ : cur.liquidityUSD
+ ),
+ })
+ }
+
+ return acc
+ }, [])
+ // @ts-ignore TYPE NEEDS FIXING
+ .sort((a, b) => a.x - b.x)
+ )
+ }, [data, chartRange, chartType])
+
+ useEffect(() => setSelectedIndex(graphData?.length - 1), [graphData])
+
+ const [selectedIndex, setSelectedIndex] = useState(graphData?.length - 1)
+
+ const chartButtons = (
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {Object.keys(chartTimespans).map((text: ChartRange) => (
+
+ ))}
+
+ )
+
+ return (
+
+
+
+ {graphData && graphData.length > 0 && (
+
+
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {formatNumber(graphData[selectedIndex]?.y, true, false, 2)}
+
+
+ {/*@ts-ignore TYPE NEEDS FIXING*/}
+ {formatDate(new Date(graphData[selectedIndex]?.x))}
+
+
+
+ {isDesktop ? (
+
+ ) : (
+
+ )}
+
+
+ )}
+
{chartButtons}
+
+ )
+}
+
+export default TokenStatsChart
diff --git a/src/features/analytics/trident/useInstantiateTableFeatures.tsx b/src/features/analytics/trident/useInstantiateTableFeatures.tsx
new file mode 100644
index 0000000000..d898f61c00
--- /dev/null
+++ b/src/features/analytics/trident/useInstantiateTableFeatures.tsx
@@ -0,0 +1,13 @@
+import { TableInstance } from 'app/features/transactions/types'
+import { useAppSelector } from 'app/state/hooks'
+import { selectTokens } from 'app/state/tokens/slice'
+import { useMemo } from 'react'
+
+const useInstantiateFilters = (setFilter: TableInstance['setFilter']) => {
+ const { searchQuery } = useAppSelector(selectTokens)
+ useMemo(() => setFilter('name', { searchQuery }), [searchQuery, setFilter])
+}
+
+export const useInstantiateTableFeatures = (setFilter: TableInstance['setFilter']) => {
+ useInstantiateFilters(setFilter)
+}
diff --git a/src/features/analytics/trident/useTokensTableConfig.tsx b/src/features/analytics/trident/useTokensTableConfig.tsx
new file mode 100644
index 0000000000..fee122cc87
--- /dev/null
+++ b/src/features/analytics/trident/useTokensTableConfig.tsx
@@ -0,0 +1,87 @@
+import { getAddress } from '@ethersproject/address'
+import { Token } from '@sushiswap/core-sdk'
+import { CurrencyLogo } from 'app/components/CurrencyLogo'
+import { formatNumber } from 'app/functions'
+import { useAllTokens } from 'app/hooks/Tokens'
+import { useTridentTokens } from 'app/services/graph'
+import { useMemo } from 'react'
+import { UsePaginationOptions, UseSortByOptions } from 'react-table'
+
+import { filterForSearchQuery } from './tokenTableFilters'
+
+export const useTokensTableConfig = (chainId: number) => {
+ const { data } = useTridentTokens({
+ chainId,
+ variables: {},
+ })
+
+ const allTokens = useAllTokens()
+ const columns = useMemo(
+ () => [
+ {
+ Header: 'Name',
+ accessor: 'name',
+ maxWidth: 100,
+ // @ts-ignore
+ Cell: (props) => {
+ const currency = useMemo(() => {
+ const token = props.row.original
+ const address = getAddress(token.id)
+ return address in allTokens
+ ? allTokens[address]
+ : new Token(chainId, address, Number(token.decimals), token.symbol, token.name)
+ }, [props])
+ return (
+
+
+ {props.row.original.symbol}
+
+ )
+ },
+ filter: filterForSearchQuery,
+ },
+ {
+ Header: 'Price',
+ accessor: 'price.derivedUSD',
+ minWidth: 150,
+ // @ts-ignore
+ Cell: (props) => formatNumber(props.value, true, undefined, 2),
+ align: 'right',
+ },
+ {
+ Header: 'Liquidity',
+ accessor: 'kpi.liquidityUSD',
+ minWidth: 150,
+ // @ts-ignore
+ Cell: (props) => formatNumber(props.value, true, false),
+ align: 'right',
+ },
+ {
+ Header: 'Volume',
+ accessor: 'volumeUSD',
+ minWidth: 150,
+ // @ts-ignore
+ Cell: (props) => formatNumber(props.value, true, false),
+ align: 'right',
+ },
+ ],
+ [chainId]
+ )
+
+ return useMemo(
+ () => ({
+ config: {
+ columns,
+ data: data ?? [],
+ initialState: {
+ sortBy: [{ id: 'kpi.liquidityUSD', desc: true }],
+ },
+ autoResetFilters: false,
+ autoResetPage: false,
+ } as UseSortByOptions & UsePaginationOptions,
+ // loading: isValidating,
+ // error,
+ }),
+ [columns, data]
+ )
+}
diff --git a/src/features/trident/pools/SearchCategoryLabel.tsx b/src/features/trident/pools/SearchCategoryLabel.tsx
index c45d4f07d8..d18a5fac2a 100644
--- a/src/features/trident/pools/SearchCategoryLabel.tsx
+++ b/src/features/trident/pools/SearchCategoryLabel.tsx
@@ -10,7 +10,6 @@ export const SearchCategoryLabel: FC = () => {
const { i18n } = useLingui()
const { searchQuery } = useAppSelector(selectTridentPools)
- console.log({ searchQuery })
return (
diff --git a/src/pages/analytics/trident/index.tsx b/src/pages/analytics/trident/index.tsx
new file mode 100644
index 0000000000..73b63b0d1d
--- /dev/null
+++ b/src/pages/analytics/trident/index.tsx
@@ -0,0 +1,72 @@
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import Typography from 'app/components/Typography'
+import TokenSearch from 'app/features/analytics/tokens/TokenSearch'
+import SearchResultTokens from 'app/features/analytics/trident/SearchResultTokens'
+import { PoolSearch } from 'app/features/trident/pools/PoolSearch'
+import { PoolSort } from 'app/features/trident/pools/PoolSort'
+import SearchResultPools from 'app/features/trident/pools/SearchResultPools'
+import { TridentBody, TridentHeader } from 'app/layouts/Trident'
+import { useRouter } from 'next/router'
+import { NextSeo } from 'next-seo'
+
+const chartTimespans = [
+ {
+ text: '1W',
+ length: 604800,
+ },
+ {
+ text: '1M',
+ length: 2629746,
+ },
+ {
+ text: '1Y',
+ length: 31556952,
+ },
+ {
+ text: 'ALL',
+ length: Infinity,
+ },
+]
+
+function Analytics() {
+ const { i18n } = useLingui()
+ const router = useRouter()
+ const chainId = Number(router.query.chainId)
+
+ return (
+ <>
+
+
+
+
+ {i18n._(t`Trident Analytics.`)}
+
+
+ {i18n._(t`Dive deeper in the analytics of Trident Pools and Tokens.`)}
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default Analytics
diff --git a/src/pages/analytics/trident/tokens/[id].tsx b/src/pages/analytics/trident/tokens/[id].tsx
new file mode 100644
index 0000000000..9d34cef176
--- /dev/null
+++ b/src/pages/analytics/trident/tokens/[id].tsx
@@ -0,0 +1,63 @@
+import { ChevronLeftIcon } from '@heroicons/react/solid'
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import { ChainId } from '@sushiswap/core-sdk'
+import Button from 'app/components/Button'
+import { Feature } from 'app/enums'
+import Header from 'app/features/analytics/trident/tokens/Header'
+import TokenStats from 'app/features/analytics/trident/tokens/TokenStats'
+import TokenStatsChart from 'app/features/analytics/trident/tokens/TokenStatsChart'
+import NetworkGuard from 'app/guards/Network'
+import TridentLayout, { TridentBody, TridentHeader } from 'app/layouts/Trident'
+import { useTridentTokens } from 'app/services/graph'
+import Link from 'next/link'
+import { useRouter } from 'next/router'
+import { NextSeo } from 'next-seo'
+
+const Token = () => {
+ const { i18n } = useLingui()
+ const router = useRouter()
+ const { query } = router
+ const chainId = Number(query.chainId) as ChainId
+ const id = query.id
+ const { data: tokens } = useTridentTokens({ chainId, variables: { where: { id } } })
+
+ if (!tokens || tokens.length === 0) return null
+
+ const token = tokens[0]
+ return (
+ <>
+
+
+
+
+ }
+ >
+ {i18n._(t`Trident Analytics`)}
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+Token.Guard = NetworkGuard(Feature.TRIDENT)
+Token.Layout = TridentLayout
+
+export default Token
diff --git a/src/services/graph/fetchers/pager.ts b/src/services/graph/fetchers/pager.ts
index 4560482ec0..59548458b7 100644
--- a/src/services/graph/fetchers/pager.ts
+++ b/src/services/graph/fetchers/pager.ts
@@ -10,21 +10,22 @@ export async function pager(endpoint, query, variables = {}) {
while (flag) {
flag = false
- const req = await request(endpoint, query, variables)
+ try {
+ const req = await request(endpoint, query, variables)
+ Object.keys(req).forEach((key) => {
+ data[key] = data[key] ? [...data[key], ...req[key]] : req[key]
+ })
- Object.keys(req).forEach((key) => {
- data[key] = data[key] ? [...data[key], ...req[key]] : req[key]
- })
+ Object.values(req).forEach((entry: any) => {
+ if (entry.length === 1000) flag = true
+ })
- Object.values(req).forEach((entry: any) => {
- if (entry.length === 1000) flag = true
- })
+ // @ts-ignore TYPE NEEDS FIXING
+ if (Object.keys(variables).includes('first') && variables['first'] !== undefined) break
- // @ts-ignore TYPE NEEDS FIXING
- if (Object.keys(variables).includes('first') && variables['first'] !== undefined) break
-
- skip += 1000
- variables = { ...variables, skip }
+ skip += 1000
+ variables = { ...variables, skip }
+ } catch (e) {}
}
return data
}
diff --git a/src/services/graph/fetchers/tokens.ts b/src/services/graph/fetchers/tokens.ts
index 6e79d12575..f27e353c45 100644
--- a/src/services/graph/fetchers/tokens.ts
+++ b/src/services/graph/fetchers/tokens.ts
@@ -2,6 +2,8 @@ import { ChainId, Currency, CurrencyAmount } from '@sushiswap/core-sdk'
import { STABLECOIN_AMOUNT_OUT } from 'app/hooks/useUSDCPrice'
import { fetcher } from 'app/services/graph'
import {
+ getTridentTokenDaySnapshotsQuery,
+ getTridentTokenHourSnapshotsQuery,
getTridentTokenPriceQuery,
getTridentTokenPricesQuery,
getTridentTokensQuery,
@@ -93,27 +95,133 @@ export const getTridentTokens = async (
return tokens.map((token) => ({
id: token.id,
price: {
- derivedNative: Number(token.price.derivedNative),
- derivedUSD: Number(token.price.derivedUSD),
+ derivedNative: token.price ? Number(token.price.derivedNative) : 0,
+ derivedUSD: token.price ? Number(token.price.derivedUSD) : 0,
},
kpi: {
- liquidity: Number(token.kpi.liquidity),
- liquidityNative: Number(token.kpi.liquidityNative),
- liquidityUSD: Number(token.kpi.liquidityUSD),
- volume: Number(token.kpi.volume),
- volumeNative: Number(token.kpi.volumeNative),
- volumeUSD: Number(token.kpi.volumeUSD),
- fees: Number(token.kpi.fees),
- feesNative: Number(token.kpi.feesNative),
- feesUSD: Number(token.kpi.feesUSD),
- transactionCount: Number(token.kpi.transactionCount),
+ liquidity: token.kpi ? Number(token.kpi.liquidity) : 0,
+ liquidityNative: token.kpi ? Number(token.kpi.liquidityNative) : 0,
+ liquidityUSD: token.kpi ? Number(token.kpi.liquidityUSD) : 0,
+ volume: token.kpi ? Number(token.kpi.volume) : 0,
+ volumeNative: token.kpi ? Number(token.kpi.volumeNative) : 0,
+ volumeUSD: token.kpi ? Number(token.kpi.volumeUSD) : 0,
+ fees: token.kpi ? Number(token.kpi.fees) : 0,
+ feesNative: token.kpi ? Number(token.kpi.feesNative) : 0,
+ feesUSD: token.kpi ? Number(token.kpi.feesUSD) : 0,
+ transactionCount: token.kpi ? Number(token.kpi.transactionCount) : 0,
},
rebase: {
- base: Number(token.rebase.base),
- elastic: Number(token.rebase.elastic),
+ base: token.rebase ? Number(token.rebase.base) : 0,
+ elastic: token.rebase ? Number(token.rebase.elastic) : 0,
},
symbol: token.symbol,
name: token.name,
decimals: Number(token.decimals),
}))
}
+
+interface TokenBucketQueryResult {
+ id: string
+ date: string
+ liquidityUSD: string
+ volumeUSD: string
+ feesUSD: string
+ priceUSD: string
+ transactionCount: string
+}
+
+export interface TokenBucket {
+ date: Date
+ liquidityUSD: number
+ volumeUSD: number
+ feesUSD: number
+ priceUSD: number
+ transactionCount: number
+}
+
+const formatBuckets = (buckets: TokenBucketQueryResult[]): TokenBucket[] =>
+ buckets.map((bucket) => ({
+ date: new Date(Number(bucket.date) * 1000),
+ liquidityUSD: Number(bucket.liquidityUSD),
+ volumeUSD: Number(bucket.volumeUSD),
+ feesUSD: Number(bucket.feesUSD),
+ priceUSD: Number(bucket.priceUSD),
+ transactionCount: Number(bucket.transactionCount),
+ }))
+
+export const getTridentTokenHourBuckets = async (
+ chainId: ChainId = ChainId.ETHEREUM,
+ variables: any
+): Promise => {
+ const result: TokenBucketQueryResult[] = Object.values(
+ await fetcher(chainId, getTridentTokenHourSnapshotsQuery, variables)
+ )?.[0] as TokenBucketQueryResult[]
+ return formatBuckets(result)
+}
+
+export const getTridentTokenDayBuckets = async (
+ chainId: ChainId = ChainId.ETHEREUM,
+ variables: any
+): Promise => {
+ const result: TokenBucketQueryResult[] = Object.values(
+ await fetcher(chainId, getTridentTokenDaySnapshotsQuery, variables)
+ )?.[0] as TokenBucketQueryResult[]
+ return formatBuckets(result)
+}
+
+export interface TokenKpiQueryResult {
+ id: string
+ fees: string
+ feesUSD: string
+ volume: string
+ volumeUSD: string
+ liquidity: string
+ liquidityUSD: string
+ transactionCount: string
+}
+
+export interface TokenKpi {
+ id: string
+ fees: number
+ feesUSD: number
+ volume: number
+ volumeUSD: number
+ liquidity: number
+ liquidityUSD: number
+ transactionCount: number
+}
+
+const formatKpi = ({
+ id,
+ fees,
+ feesUSD,
+ volume,
+ volumeUSD,
+ liquidity,
+ liquidityUSD,
+ transactionCount,
+}: TokenKpiQueryResult) => ({
+ id,
+ fees: Number(fees),
+ feesUSD: Number(feesUSD),
+ volume: Number(volume),
+ volumeUSD: Number(volumeUSD),
+ liquidity: Number(liquidity),
+ liquidityUSD: Number(liquidityUSD),
+ transactionCount: Number(transactionCount),
+})
+
+// @ts-ignore TYPE NEEDS FIXING
+export const getTridentTokenKpis = async (chainId: ChainId = ChainId.ETHEREUM, variables = {}): Promise => {
+ const result: TokenKpiQueryResult[] = Object.values(
+ await fetcher(chainId, getTridentTokenKpis, variables)
+ )?.[0] as TokenKpiQueryResult[]
+ return result.map(formatKpi)
+}
+
+export const getTridentTokenKpi = async (chainId: ChainId = ChainId.ETHEREUM, variables = {}): Promise => {
+ const result: TokenKpiQueryResult = Object.values(
+ await fetcher(chainId, getTridentTokenKpi, variables)
+ )?.[0] as TokenKpiQueryResult
+ return formatKpi(result)
+}
diff --git a/src/services/graph/hooks/tokens.ts b/src/services/graph/hooks/tokens.ts
index 62b8744a5a..d64dc7a25f 100644
--- a/src/services/graph/hooks/tokens.ts
+++ b/src/services/graph/hooks/tokens.ts
@@ -1,7 +1,18 @@
-import { getTridentTokenPrices } from 'app/services/graph'
+import { formatNumber, formatPercent } from 'app/functions'
+import { getTridentTokenPrices, useOneDayBlock, useTwoDayBlock } from 'app/services/graph'
+import stringify from 'fast-json-stable-stringify'
import useSWR from 'swr'
-import { getTridentTokenPrice, getTridentTokens } from '../fetchers'
+import {
+ getTridentTokenDayBuckets,
+ getTridentTokenHourBuckets,
+ getTridentTokenKpi,
+ getTridentTokenKpis,
+ getTridentTokenPrice,
+ getTridentTokens,
+ TokenBucket,
+} from '../fetchers'
+import { GraphProps } from '../interfaces'
// @ts-ignore TYPE NEEDS FIXING
export function useTridentTokens({ chainId, variables, shouldFetch = true, swrConfig = undefined }) {
@@ -29,3 +40,185 @@ export function useTridentTokenPrice({ chainId, variables, shouldFetch = true, s
swrConfig
)
}
+
+export function useTridentTokenHourBuckets({
+ chainId,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps): TokenBucket[] {
+ const { data } = useSWR(
+ shouldFetch && !!chainId ? ['trident-token-hour-buckets', chainId, stringify(variables)] : null,
+ () => getTridentTokenHourBuckets(chainId, variables),
+ swrConfig
+ )
+ return data
+}
+
+export function useTridentTokenDayBuckets({
+ chainId,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps): TokenBucket[] {
+ const { data } = useSWR(
+ shouldFetch && !!chainId ? ['trident-token-day-buckets', chainId, stringify(variables)] : null,
+ () => getTridentTokenDayBuckets(chainId, variables),
+ swrConfig
+ )
+ return data
+}
+
+export function useTridentTokenKpi({ chainId, variables, shouldFetch = true, swrConfig = undefined }: GraphProps) {
+ return useSWR(
+ shouldFetch && !!chainId ? ['trident-token-kpis', chainId, stringify(variables)] : null,
+ () => getTridentTokenKpi(chainId, variables),
+ swrConfig
+ )
+}
+
+// @ts-ignore TYPE NEEDS FIXING
+export function useTridentTokenKpis({ chainId, variables, shouldFetch = true, swrConfig = undefined }: GraphProps) {
+ return useSWR(
+ shouldFetch && !!chainId ? ['trident-token-kpis', chainId, stringify(variables)] : null,
+ () => getTridentTokenKpis(chainId, variables),
+ swrConfig
+ )
+}
+
+export function useTridentOneDayTokenKpis({
+ chainId,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ return useSWR(
+ shouldFetch && !!chainId ? ['trident-token-kpis', chainId, stringify(variables)] : null,
+ () => getTridentTokenKpis(chainId, variables),
+ swrConfig
+ )
+}
+
+// @ts-ignore TYPE NEEDS FIXING
+export function useTridentTwoDayTokenKpis({
+ chainId,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ return useSWR(
+ shouldFetch && !!chainId ? ['trident-token-kpis', chainId, stringify(variables)] : null,
+ () => getTridentTokenKpis(chainId, variables),
+ swrConfig
+ )
+}
+
+export function useTridentRollingTokenStats({
+ chainId,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const { data: oneDayBlock } = useOneDayBlock({ chainId, shouldFetch: !!chainId })
+ const { data: twoDayBlock } = useTwoDayBlock({ chainId, shouldFetch: !!chainId })
+
+ const {
+ data: tokenKpis,
+ isValidating: tokenKpisIsValidating,
+ error: tokenKpisError,
+ } = useTridentTokenKpis({
+ chainId,
+ shouldFetch,
+ variables,
+ swrConfig,
+ })
+ const {
+ data: oneDayTokenKpis,
+ isValidating: oneDayTokenKpisIsValidating,
+ error: oneDayTokenKpisError,
+ } = useTridentOneDayTokenKpis({
+ chainId,
+ shouldFetch,
+ variables: { ...variables, block: oneDayBlock },
+ swrConfig,
+ })
+ const {
+ data: twoDayTokenKpis,
+ isValidating: twoDayTokenKpisIsValidating,
+ error: twoDayTokenKpisError,
+ } = useTridentTwoDayTokenKpis({
+ chainId,
+ shouldFetch,
+ variables: { ...variables, block: twoDayBlock },
+ swrConfig,
+ })
+
+ return {
+ isValidating: tokenKpisIsValidating || oneDayTokenKpisIsValidating || twoDayTokenKpisIsValidating,
+ error: tokenKpisError || oneDayTokenKpisError || twoDayTokenKpisError,
+ data: tokenKpis?.map((tokenKpi: any) => {
+ const oneDayTokenKpi = oneDayTokenKpis?.find((oneDayTokenKpi: any) => oneDayTokenKpi.id === tokenKpi.id)
+ const twoDayTokenKpi = twoDayTokenKpis?.find((twoDayTokenKpi: any) => twoDayTokenKpi.id === tokenKpi.id)
+
+ const volume = formatNumber(
+ oneDayTokenKpi?.volumeUSD ? tokenKpi.volumeUSD - oneDayTokenKpi.volumeUSD : tokenKpi.volumeUSD,
+ true,
+ false
+ )
+
+ const volume24hChange =
+ ((tokenKpi?.volumeUSD - oneDayTokenKpi?.volumeUSD) / (oneDayTokenKpi?.volumeUSD - twoDayTokenKpi?.volumeUSD)) *
+ 100 -
+ 100
+
+ const fees = formatNumber(
+ oneDayTokenKpi ? tokenKpi?.feesUSD - oneDayTokenKpi?.feesUSD : tokenKpi?.feesUSD,
+ true,
+ false
+ )
+
+ const fees24hChange =
+ ((tokenKpi?.feesUSD - oneDayTokenKpi?.feesUSD) / (oneDayTokenKpi?.feesUSD - twoDayTokenKpi?.feesUSD)) * 100 -
+ 100
+
+ const liquidity = formatPercent(
+ ((oneDayTokenKpi ? tokenKpi?.volumeUSD - oneDayTokenKpi?.volumeUSD : tokenKpi?.volumeUSD) /
+ tokenKpi?.liquidityUSD) *
+ 100
+ )
+
+ const transactions = oneDayTokenKpi
+ ? tokenKpi.transactionCount - oneDayTokenKpi.transactionCount
+ : tokenKpi.transactionCount
+
+ const apy =
+ tokenKpi.liquidityUSD > 0
+ ? (Math.max(0, oneDayTokenKpi ? tokenKpi?.feesUSD - oneDayTokenKpi?.feesUSD : tokenKpi?.feesUSD) *
+ 365 *
+ 100) /
+ tokenKpi?.liquidityUSD
+ : 0
+
+ return {
+ volume,
+ volume24hChange,
+ fees,
+ fees24hChange,
+ liquidity,
+ liquidity24hChange:
+ ((tokenKpi?.volumeUSD - oneDayTokenKpi?.volumeUSD) /
+ tokenKpi?.liquidityUSD /
+ ((oneDayTokenKpi?.volumeUSD - twoDayTokenKpi?.volumeUSD) / oneDayTokenKpi?.liquidityUSD)) *
+ 100 -
+ 100,
+ transactions,
+ transactions24hChange:
+ ((tokenKpi?.transactionCount - oneDayTokenKpi?.transactionCount) /
+ (oneDayTokenKpi?.transactionCount - twoDayTokenKpi?.transactionCount)) *
+ 100 -
+ 100,
+ apy,
+ }
+ }),
+ }
+}
diff --git a/src/services/graph/queries/tokens.ts b/src/services/graph/queries/tokens.ts
index 099c373946..79ab06e737 100644
--- a/src/services/graph/queries/tokens.ts
+++ b/src/services/graph/queries/tokens.ts
@@ -51,3 +51,61 @@ export const getTridentTokensQuery = gql`
}
}
`
+
+export const getTridentTokenHourSnapshotsQuery = gql`
+ query tokenHourSnapshots($first: Int = 1000, $skip: Int = 0, $block: Block_height, $where: TokenDaySnapshot_filter) {
+ tokenHourSnapshots(first: $first, skip: $skip, block: $block, where: $where, orderBy: date, orderDirection: desc) {
+ id
+ date
+ liquidityUSD
+ volumeUSD
+ feesUSD
+ priceUSD
+ transactionCount
+ }
+ }
+`
+
+export const getTridentTokenDaySnapshotsQuery = gql`
+ query tokenDaySnapshots($first: Int = 1000, $skip: Int = 0, $block: Block_height, $where: TokenDaySnapshot_filter) {
+ tokenDaySnapshots(first: $first, skip: $skip, block: $block, where: $where, orderBy: date, orderDirection: desc) {
+ id
+ date
+ liquidityUSD
+ volumeUSD
+ feesUSD
+ priceUSD
+ transactionCount
+ }
+ }
+`
+
+export const getTridentTokenKpiQuery = gql`
+ query tridentTokenKpiQuery($id: String!, $block: Block_height, $where: PoolKpi_filter) {
+ poolKpi(id: $id, block: $block, where: $where) {
+ id
+ fees
+ feesUSD
+ volume
+ volumeUSD
+ liquidity
+ liquidityUSD
+ transactionCount
+ }
+ }
+`
+
+export const getTridentTokenKpisQuery = gql`
+ query tokenKpisQuery($first: Int = 1000, $skip: Int = 0, $block: Block_height, $where: PoolKpi_filter) {
+ tokenKpis(first: $first, skip: $skip, block: $block, where: $where) {
+ id
+ fees
+ feesUSD
+ volume
+ volumeUSD
+ liquidity
+ liquidityUSD
+ transactionCount
+ }
+ }
+`