diff --git a/package.json b/package.json
index c60ef74552..a6c7f63804 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"@walletconnect/jsonrpc-provider": "1.0.3"
},
"devDependencies": {
+ "@apollo/client": "^3.6.8",
"@babel/preset-react": "^7.16.7",
"@binance-chain/bsc-connector": "^1.0.0",
"@builder.io/partytown": "^0.5.2",
@@ -115,6 +116,7 @@
"@types/js-cookie": "^3.0.1",
"@types/ms.macro": "^2.0.0",
"@types/node": "^16.11.0",
+ "@types/numeral": "^2.0.2",
"@types/qs": "^6.9.7",
"@types/react": "^17.0.2",
"@types/react-slider": "^1.3.1",
@@ -165,6 +167,8 @@
"graphql": "^15.5.3",
"graphql-request": "^3.5.0",
"graphql-tag": "^2.12.5",
+ "highcharts": "^10.1.0",
+ "highcharts-react-official": "^3.1.0",
"husky": "^7.0.0",
"identity-obj-proxy": "^3.0.0",
"ioredis": "^4.27.7",
@@ -178,6 +182,7 @@
"lottie-react": "2.1.0",
"madge": "^5.0.1",
"millify": "^4.0.0",
+ "moment": "^2.29.3",
"ms.macro": "^2.0.0",
"next": "^12.1.5",
"next-pwa": "5.4.7",
@@ -185,7 +190,7 @@
"next-unused": "^0.0.6",
"ngrok": "^4.3.1",
"node-vibrant": "3.1.6",
- "numeral": "^2.0.0",
+ "numeral": "^2.0.6",
"polished": "^4.1.0",
"postcss": "^8.4.0",
"postcss-flexbugs-fixes": "^5.0.2",
@@ -198,6 +203,7 @@
"react-dom": "^17.0.2",
"react-dropzone": "^12.0.4",
"react-feather": "^2.0.9",
+ "react-icons": "^4.4.0",
"react-hook-form": "7.29.0",
"react-infinite-scroll-component": "^6.1.0",
"react-popper": "^2.2.5",
diff --git a/public/images/tokens/icon-quiz.jpg b/public/images/tokens/icon-quiz.jpg
new file mode 100644
index 0000000000..f957b607d6
Binary files /dev/null and b/public/images/tokens/icon-quiz.jpg differ
diff --git a/src/components/Header/useMenu.tsx b/src/components/Header/useMenu.tsx
index 01d9e1de63..74aa3cd603 100644
--- a/src/components/Header/useMenu.tsx
+++ b/src/components/Header/useMenu.tsx
@@ -275,6 +275,16 @@ const useMenu: UseMenu = () => {
title: 'Pools',
link: `/analytics/pools`,
},
+ {
+ key: 'kashi',
+ title: 'Kashi Pairs',
+ link: `/analytics/kashi/pairs`,
+ },
+ {
+ key: 'kashi',
+ title: 'Kashi Tokens',
+ link: `/analytics/kashi/tokens`,
+ },
],
}
diff --git a/src/features/analytics/kashi/components/KashiPairTotalCardEx.tsx b/src/features/analytics/kashi/components/KashiPairTotalCardEx.tsx
new file mode 100644
index 0000000000..fa7564e74c
--- /dev/null
+++ b/src/features/analytics/kashi/components/KashiPairTotalCardEx.tsx
@@ -0,0 +1,125 @@
+import { useLingui } from '@lingui/react'
+import KashiMediumRiskLendingPair from 'app/features/kashi/KashiMediumRiskLendingPair'
+import { formatNumber } from 'app/functions'
+import classNames from 'classnames'
+
+import Progress from './Progress'
+
+type AttributesByBorrowType = {
+ progressColor: 'green' | 'pink' | 'blue'
+ title: string
+ users: string
+}
+
+type AttributesMapByBorrowType = {
+ borrow: AttributesByBorrowType
+ asset: AttributesByBorrowType
+ supply: AttributesByBorrowType
+}
+
+const KashiPairTotalCard = ({
+ containerClass = '',
+ data,
+ borrow = 'borrow',
+ loading = false,
+}: {
+ containerClass?: string
+ data: KashiMediumRiskLendingPair[]
+ borrow?: 'borrow' | 'asset' | 'supply'
+ loading?: boolean
+}) => {
+ const { i18n } = useLingui()
+ const AttributesMapByBorrow = {
+ borrow: {
+ progressColor: 'pink',
+ title: i18n._('Total Borrow'),
+ },
+ asset: {
+ progressColor: 'green',
+ title: i18n._('Total Available'),
+ },
+ supply: {
+ progressColor: 'blue',
+ title: i18n._('Total Supply'),
+ },
+ } as AttributesMapByBorrowType
+
+ const attributes = AttributesMapByBorrow[borrow]
+ const isLoading = loading || !data
+ const amount = data.reduce(
+ (
+ value: { totalAssetAmountUSD: number; currentAllAssetsUSD: number; currentBorrowAmountUSD: number },
+ market: KashiMediumRiskLendingPair
+ ) => {
+ value.currentAllAssetsUSD += Number(market.currentAllAssetsUSD?.toFixed(0)) || 0
+ value.currentBorrowAmountUSD += Number(market.currentBorrowAmountUSD?.toFixed(0)) || 0
+ value.totalAssetAmountUSD += Number(market.totalAssetAmountUSD?.toFixed(0)) || 0
+ return value
+ },
+ { totalAssetAmountUSD: 0, currentAllAssetsUSD: 0, currentBorrowAmountUSD: 0 }
+ )
+
+ const sortFunc = {
+ borrow: (a: KashiMediumRiskLendingPair, b: KashiMediumRiskLendingPair) =>
+ (Number(b.currentBorrowAmountUSD?.toFixed(0)) || 0) - (Number(a.currentBorrowAmountUSD?.toFixed(0)) || 0),
+ asset: (a: KashiMediumRiskLendingPair, b: KashiMediumRiskLendingPair) =>
+ (Number(b.totalAssetAmountUSD?.toFixed(0)) || 0) - (Number(a.totalAssetAmountUSD?.toFixed(0)) || 0),
+ supply: (a: KashiMediumRiskLendingPair, b: KashiMediumRiskLendingPair) =>
+ (Number(b.currentAllAssetsUSD?.toFixed(0)) || 0) - (Number(a.currentAllAssetsUSD?.toFixed(0)) || 0),
+ }
+ const top3 = data.length > 3 ? [...data].sort(sortFunc[borrow]).slice(0, 3) : [...data]
+
+ return (
+
+
{attributes.title}
+
+
+ {isLoading ? (
+
+ ) : (
+ formatNumber(
+ borrow === 'borrow'
+ ? amount.currentBorrowAmountUSD
+ : borrow === 'asset'
+ ? amount.totalAssetAmountUSD
+ : amount.currentAllAssetsUSD,
+ true
+ )
+ )}
+
+
Top 3 Markets
+ {isLoading ? (
+ <>
+
+
+
+ >
+ ) : (
+ top3.map((market) => (
+
+ ))
+ )}
+
+
+ )
+}
+
+export default KashiPairTotalCard
diff --git a/src/features/analytics/kashi/components/KashiPairTotalDayDatasChart.tsx b/src/features/analytics/kashi/components/KashiPairTotalDayDatasChart.tsx
new file mode 100644
index 0000000000..8d73c10e08
--- /dev/null
+++ b/src/features/analytics/kashi/components/KashiPairTotalDayDatasChart.tsx
@@ -0,0 +1,110 @@
+import { useLingui } from '@lingui/react'
+import TailwindConfig from 'app/features/analytics/kashi/config/tailwind'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const KashiPairTotalDayDatasChart = ({ loading, data }: { loading: boolean; data: KashiPairDayDataMap[] }) => {
+ const { i18n } = useLingui()
+ const getSeries = () => {
+ let supplyData: any[] = []
+ let borrowData: any[] = []
+ data.forEach((item) => {
+ supplyData.push({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalAsset).add(BigNumber.from(item.totalBorrow)).toNumber() / 100.0,
+ })
+ borrowData.push({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalBorrow).toNumber() / 100.0,
+ })
+ })
+ return [
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.green.DEFAULT,
+ name: i18n._('Supply'),
+ data: supplyData,
+ tooltip: {
+ pointFormat: i18n._('Supply') + ' ${point.y}',
+ },
+ },
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.pink.DEFAULT,
+ name: i18n._('Borrow'),
+ data: borrowData,
+ tooltip: {
+ pointFormat: i18n._('Borrow') + ' ${point.y}',
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ }
+
+ return (
+
+
{i18n._('Total Supply & Total Borrow')}
+ {loading || !data || data.length === 0 ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+export default KashiPairTotalDayDatasChart
diff --git a/src/features/analytics/kashi/components/PairCard.tsx b/src/features/analytics/kashi/components/PairCard.tsx
new file mode 100644
index 0000000000..b3f1fdd680
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairCard.tsx
@@ -0,0 +1,87 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import numeral from 'numeral'
+
+import { KashiPair } from '../types/KashiPair'
+
+const PairCard = ({ containerClass = '', data }: { containerClass?: string; data?: KashiPair }) => {
+ return (
+
+
{i18n._('Info')}
+
+
+
{i18n._('Supply')}
+ {!data ? (
+
+ ) : (
+
+ {numeral(
+ BigNumber.from(data?.totalAsset).add(BigNumber.from(data.totalBorrow)).toNumber() / 100.0
+ ).format('($0,0.00)')}
+
+ )}
+
+
+
{i18n._('Utilization')}
+ {!data ? (
+
+ ) : (
+
+ {numeral(
+ BigNumber.from(data?.utilization).div(BigNumber.from('100000000000000')).toNumber() / 10000.0
+ ).format('(0,0.00%)')}
+
+ )}
+
+
+
{i18n._('Available')}
+ {!data ? (
+
+ ) : (
+
{numeral(Number(data?.totalAsset) / 100.0).format('($0,0.00)')}
+ )}
+
+
+
{i18n._('Borrow')}
+ {!data ? (
+
+ ) : (
+
{numeral(Number(data?.totalBorrow) / 100.0).format('($0,0.00)')}
+ )}
+
+
+
{i18n._('Supply APY')}
+ {!data ? (
+
+ ) : (
+
+ {numeral(BigNumber.from(data?.supplyAPR).div(BigNumber.from('1000000000000')).toNumber() / 100000).format(
+ '%0.00'
+ )}
+
+ )}
+
+
+
{i18n._('Borrow APY')}
+ {!data ? (
+
+ ) : (
+
+ {numeral(BigNumber.from(data?.borrowAPR).div(BigNumber.from('1000000000000')).toNumber() / 100000).format(
+ '%0.00'
+ )}
+
+ )}
+
+
+
+ )
+}
+
+export default PairCard
diff --git a/src/features/analytics/kashi/components/PairCollateralPieChart.tsx b/src/features/analytics/kashi/components/PairCollateralPieChart.tsx
new file mode 100644
index 0000000000..a360671819
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairCollateralPieChart.tsx
@@ -0,0 +1,100 @@
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts'
+import HighchartsReact from 'highcharts-react-official'
+import { useRouter } from 'next/router'
+
+import TailwindConfig from '../config/tailwind'
+import { useAppContext } from '../context/AppContext'
+import { KashiPairNew } from '../types/KashiPair'
+
+const PairCollateralPieChart = ({
+ containerClass = '',
+ type = 'supply',
+ title = 'Supply',
+ data,
+}: {
+ type?: 'supply' | 'asset' | 'borrow'
+ containerClass?: string
+ title?: string
+ data?: KashiPairNew[]
+}) => {
+ const router = useRouter()
+ const { tokenUtilService } = useAppContext()
+ const valueFuncs = {
+ supply: (kashiPair: KashiPairNew) =>
+ BigNumber.from(kashiPair.totalAssetAmount).add(BigNumber.from(kashiPair.totalBorrowAmount)).toNumber() / 100.0,
+ asset: (kashiPair: KashiPairNew) => BigNumber.from(kashiPair.totalAssetAmount).toNumber() / 100.0,
+ borrow: (kashiPair: KashiPairNew) => BigNumber.from(kashiPair.totalBorrowAmount).toNumber() / 100.0,
+ }
+
+ const getSeries = () => {
+ let seriesData: any[] = []
+ data?.forEach((item) => {
+ seriesData.push({
+ id: item.id,
+ name: tokenUtilService.pairSymbol(item.asset?.symbol, item.collateral?.symbol),
+ y: valueFuncs[type](item),
+ })
+ })
+ return [
+ {
+ name: title,
+ data: seriesData,
+ innerSize: '50%',
+ tooltip: {
+ headerFormat: "{point.key}
",
+ pointFormat: "${point.y}",
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ text: '',
+ },
+ chart: {
+ type: 'pie',
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ colors: ['#10b981', '#2085ec', '#72b4eb', '#0a417a', '#8464a0', '#cea9bc', '#a855f7', '#323232'],
+ series: getSeries(),
+ plotOptions: {
+ pie: {
+ cursor: 'pointer',
+ dataLabels: {
+ enabled: false,
+ },
+ // showInLegend: true,
+ events: {
+ click: (event: Highcharts.SeriesClickEventObject) => {
+ router.push(`/pair/${(event.point as any).id}`)
+ },
+ },
+ },
+ },
+ }
+
+ return (
+
+
{title}
+ {!data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default PairCollateralPieChart
diff --git a/src/features/analytics/kashi/components/PairInteresetPerSecondDayDataChart.tsx b/src/features/analytics/kashi/components/PairInteresetPerSecondDayDataChart.tsx
new file mode 100644
index 0000000000..4a2fb25399
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairInteresetPerSecondDayDataChart.tsx
@@ -0,0 +1,142 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const PairInterestPerSecondDayDataChart = ({
+ containerClass = '',
+ title = 'Supply & Borrow APR',
+ data,
+}: {
+ containerClass?: string
+ title?: string
+ data?: KashiPairDayDataMap[]
+}) => {
+ const getSeries = () => {
+ let borrowData: any[] = []
+ let supplyData: any[] = []
+ data?.forEach((item) => {
+ borrowData.push({
+ x: moment(item.date).valueOf(),
+ y:
+ BigNumber.from(item.avgInterestPerSecond)
+ .mul(3600 * 24 * 365)
+ .div(1e14)
+ .toNumber() / 1e2,
+ })
+ supplyData.push({
+ x: moment(item.date).valueOf(),
+ y:
+ BigNumber.from(item.avgInterestPerSecond)
+ .mul(3600 * 24 * 365)
+ .mul(BigNumber.from(item.avgUtilization))
+ .div(1e14)
+ .div(1e14)
+ .div(1e4)
+ .toNumber() / 1e2,
+ })
+ })
+ return [
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.green.DEFAULT,
+ data: supplyData,
+ tooltip: {
+ pointFormat: 'Supply APR {point.y}%',
+ },
+ },
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.pink.DEFAULT,
+ data: borrowData,
+ tooltip: {
+ pointFormat: 'Borrow APR {point.y}%',
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ }
+
+ return (
+
+
{title}
+ {!data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default PairInterestPerSecondDayDataChart
diff --git a/src/features/analytics/kashi/components/PairMarketTable.tsx b/src/features/analytics/kashi/components/PairMarketTable.tsx
new file mode 100644
index 0000000000..bb88db1425
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairMarketTable.tsx
@@ -0,0 +1,433 @@
+/* eslint-disable @next/next/no-img-element */
+import { i18n } from '@lingui/core'
+import { useLingui } from '@lingui/react'
+import { ChainId, Token } from '@sushiswap/core-sdk'
+import { CurrencyLogoArray } from 'app/components/CurrencyLogo'
+import { useActiveWeb3React } from 'app/services/web3'
+import { BigNumber } from 'ethers'
+import { useRouter } from 'next/router'
+import numeral from 'numeral'
+import React, { useEffect, useState } from 'react'
+import { FaChevronDown, FaChevronUp } from 'react-icons/fa'
+
+import { useAppContext } from '../context/AppContext'
+import { KashiPairNew } from '../types/KashiPair'
+
+type OrderBy = 'asset' | 'collateral' | 'totalSupply' | 'totalAsset' | 'supplyAPR' | 'totalBorrow' | 'borrowAPR' | ''
+type OrderDirection = 'asc' | 'desc'
+
+const PairMarketTableHead = ({
+ onSort,
+ orderBy,
+ orderDirection,
+}: {
+ onSort: (orderBy: OrderBy) => void
+ orderBy: OrderBy
+ orderDirection: OrderDirection
+}) => {
+ const { i18n } = useLingui()
+ const iconByDirection = {
+ asc: ,
+ desc: ,
+ }
+
+ return (
+
+
+ {
+ onSort('asset')
+ }}
+ className="cursor-pointer"
+ >
+ Asset{orderBy === 'asset' && iconByDirection[orderDirection]}
+
+ /
+ {
+ onSort('collateral')
+ }}
+ className="cursor-pointer"
+ >
+ {i18n._('Collateral')}
+ {orderBy === 'collateral' && iconByDirection[orderDirection]}
+
+ |
+ {
+ onSort('totalSupply')
+ }}
+ >
+
+ {i18n._('TVL')}
+ {orderBy === 'totalSupply' && iconByDirection[orderDirection]}
+
+ |
+ {
+ onSort('totalBorrow')
+ }}
+ >
+
+ {i18n._('Borrowed')}
+ {orderBy === 'totalBorrow' && iconByDirection[orderDirection]}
+
+ |
+
+ {
+ onSort('supplyAPR')
+ }}
+ >
+
+ {i18n._('Supply APR')}
+ {orderBy === 'supplyAPR' && iconByDirection[orderDirection]}
+
+ |
+ {
+ onSort('totalAsset')
+ }}
+ >
+
+ {i18n._('Available')}
+ {orderBy === 'totalAsset' && iconByDirection[orderDirection]}
+
+ |
+ {
+ onSort('borrowAPR')
+ }}
+ >
+
+ {i18n._('Borrow APR')}
+ {orderBy === 'borrowAPR' && iconByDirection[orderDirection]}
+
+ |
+
+ )
+}
+
+const PairMarketTableRowLoading = () => (
+
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+)
+
+const PairMarketTableRow = ({ data, index }: { data: KashiPairNew; index: number }) => {
+ const { tokenUtilService, handleLogoError } = useAppContext()
+ const router = useRouter()
+ const goto = (route: string) => {
+ router.push(route)
+ }
+
+ const { chainId } = useActiveWeb3React()
+
+ const asset = new Token(
+ chainId ?? ChainId.ETHEREUM,
+ data.asset?.id ?? '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ Number(data.asset?.decimals ?? 0),
+ data.asset?.symbol,
+ data.asset?.name
+ )
+
+ const collateral = new Token(
+ Number(chainId) ?? ChainId.ETHEREUM,
+ data.collateral?.id ?? '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ Number(data.collateral?.decimals ?? 0),
+ data.collateral?.symbol,
+ data.collateral?.name
+ )
+
+ return (
+ <>
+ goto(`/analytics/kashi/pairs/${data.id}`)}
+ className="border-t border-l-2 border-transparent cursor-pointer border-t-gray-900 hover:bg-dark-900"
+ >
+
+
+
+
+
+
+ {tokenUtilService.pairSymbol(data.asset?.symbol, data.collateral?.symbol)}
+
+
+ |
+
+
+ {numeral(
+ BigNumber.from(data?.totalAssetAmount).add(BigNumber.from(data.totalBorrowAmount)).toNumber() / 100
+ ).format('$0,.00')}
+
+
+ {numeral(
+ BigNumber.from(data?.totalAsset?.elastic)
+ .add(BigNumber.from(data.totalBorrow?.elastic))
+ .div(
+ BigNumber.from('10').pow(
+ Number(data.asset?.decimals && Number(data.asset?.decimals) >= 2 ? data.asset?.decimals : 2) - 2
+ )
+ )
+ .toNumber() / 100
+ ).format('0,.00')}
+
+ {data.asset?.symbol}
+
+ |
+
+
+ {numeral(BigNumber.from(data?.totalBorrowAmount).toNumber() / 100).format('$0,.00')}
+
+
+ {numeral(
+ BigNumber.from(data?.totalBorrow?.elastic)
+ .div(
+ BigNumber.from('10').pow(
+ Number(data.asset?.decimals && Number(data.asset?.decimals) >= 2 ? data.asset?.decimals : 2) - 2
+ )
+ )
+ .toNumber() / 100
+ ).format('0,.00')}
+
+ {data.asset?.symbol}
+
+ |
+
+
+ {numeral(
+ BigNumber.from(data?.kpi?.supplyAPR).div(BigNumber.from('1000000000000')).toNumber() / 100000
+ ).format('%0.00')}
+
+ {i18n._(`annualized`)}
+ |
+
+
+ {numeral(BigNumber.from(data?.totalAssetAmount).toNumber() / 100).format('$0,.00')}
+
+
+ {numeral(
+ BigNumber.from(data?.totalAsset?.elastic)
+ .div(
+ BigNumber.from('10').pow(
+ Number(data.asset?.decimals && Number(data.asset?.decimals) >= 2 ? data.asset?.decimals : 2) - 2
+ )
+ )
+ .toNumber() / 100
+ ).format('0,.00')}
+
+ {data.asset?.symbol}
+
+ |
+
+
+
+ {numeral(
+ BigNumber.from(data?.kpi?.borrowAPR).div(BigNumber.from('1000000000000')).toNumber() / 100000
+ ).format('%0.00')}
+
+ {i18n._(`annualized`)}
+ |
+
+ >
+ )
+}
+
+const PairMarketTable = ({ loading = false, data = [] }: { loading?: boolean; data: KashiPairNew[] }) => {
+ const [orderBy, setOrderBy] = useState('totalSupply')
+ const [orderDirection, setOrderDirection] = useState('desc')
+
+ const [fullList, setFullList] = useState([])
+ const [sortedList, setSortedList] = useState([])
+ const [list, setList] = useState([])
+ const [isMore, setMore] = useState(false)
+ const [search, setSearch] = useState('')
+ const { tokenUtilService } = useAppContext()
+
+ useEffect(() => {
+ setFullList(data)
+ }, [data])
+
+ useEffect(() => {
+ let newSortedList = [...fullList]
+ const compareFuncs = {
+ asset: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ (a.asset?.symbol.toLowerCase() || '').localeCompare(b.asset?.symbol.toLowerCase() || ''),
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ (b.asset?.symbol.toLowerCase() || '').localeCompare(a.asset?.symbol.toLowerCase() || ''),
+ },
+ collateral: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ (a.collateral?.symbol.toLowerCase() || '').localeCompare(b.collateral?.symbol.toLowerCase() || ''),
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ (b.collateral?.symbol.toLowerCase() || '').localeCompare(a.collateral?.symbol.toLowerCase() || ''),
+ },
+ totalSupply: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalAssetAmount)
+ .add(BigNumber.from(a.totalBorrowAmount))
+ .lte(BigNumber.from(b.totalAssetAmount).add(BigNumber.from(b.totalBorrowAmount)))
+ ? -1
+ : 1,
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalAssetAmount)
+ .add(BigNumber.from(a.totalBorrowAmount))
+ .gte(BigNumber.from(b.totalAssetAmount).add(BigNumber.from(b.totalBorrowAmount)))
+ ? -1
+ : 1,
+ },
+ totalAsset: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalAssetAmount).lte(BigNumber.from(b.totalAssetAmount)) ? -1 : 1,
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalAssetAmount).gte(BigNumber.from(b.totalAssetAmount)) ? -1 : 1,
+ },
+ totalBorrow: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalBorrowAmount).lte(BigNumber.from(b.totalBorrowAmount)) ? 1 : -1,
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalBorrowAmount).gte(BigNumber.from(b.totalBorrowAmount)) ? -1 : 1,
+ },
+ supplyAPR: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.kpi?.supplyAPR).lte(BigNumber.from(b.kpi?.supplyAPR)) ? -1 : 1,
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.kpi?.supplyAPR).gte(BigNumber.from(b.kpi?.supplyAPR)) ? -1 : 1,
+ },
+ borrowAPR: {
+ asc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.kpi?.borrowAPR).lte(BigNumber.from(b.kpi?.borrowAPR)) ? -1 : 1,
+ desc: (a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.kpi?.borrowAPR).gte(BigNumber.from(b.kpi?.borrowAPR)) ? -1 : 1,
+ },
+ }
+
+ if (orderBy) {
+ newSortedList.sort(compareFuncs[orderBy][orderDirection])
+ }
+ setSortedList(newSortedList)
+ }, [fullList, orderBy, orderDirection])
+
+ useEffect(() => {
+ setList([])
+ }, [sortedList])
+
+ const handleLoadMore = () => {
+ if (isMore) return
+ setMore(true)
+ if (list.length < sortedList.length) {
+ const start = list.length
+ const end = Math.min(start + 20, sortedList.length)
+ const newList = [...list, ...sortedList.slice(start, end)]
+ setList(newList)
+ }
+ setMore(false)
+ }
+
+ const handleSort = (orderField: OrderBy) => {
+ if (orderBy === orderField) {
+ setOrderDirection(orderDirection === 'asc' ? 'desc' : 'asc')
+ return
+ }
+ setOrderBy(orderField)
+ setOrderDirection('desc')
+ }
+
+ const handleSearchChange = (event: React.SyntheticEvent) => {
+ const target = event.target as HTMLInputElement
+ setSearch(target.value)
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {loading ? (
+
+
+
+
+
+
+ ) : (
+
+ {sortedList
+ .filter((value) => {
+ const token = tokenUtilService.pairSymbol(value.asset?.symbol, value.collateral?.symbol)
+ if (token) {
+ return token.toLowerCase().indexOf(search.toLowerCase()) >= 0
+ }
+ return false
+ })
+ .map((data, index) => (
+
+ ))}
+
+ )}
+
+
+ )
+}
+export default PairMarketTable
diff --git a/src/features/analytics/kashi/components/PairSupplyAccruedInterestDayDataChart.tsx b/src/features/analytics/kashi/components/PairSupplyAccruedInterestDayDataChart.tsx
new file mode 100644
index 0000000000..507e07eea3
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairSupplyAccruedInterestDayDataChart.tsx
@@ -0,0 +1,124 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const PairSupplyAccruedInterestDayDataChart = ({
+ containerClass = '',
+ title = i18n._('Accrued Interest'),
+ data,
+}: {
+ containerClass?: string
+ title?: string
+ data?: KashiPairDayDataMap[]
+}) => {
+ const getSeries = () => {
+ let seriesData: any[] = []
+ const valueFunc = (item: KashiPairDayDataMap) => ({
+ x: moment(item.date).valueOf(),
+ y:
+ BigNumber.from(item.totalBorrow)
+ .mul(BigNumber.from(item.avgInterestPerSecond))
+ .mul(3600 * 24)
+ .div(BigNumber.from(10).pow(18))
+ .toNumber() / 100.0,
+ })
+ data?.forEach((item) => {
+ seriesData.push(valueFunc(item))
+ })
+ return [
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.blue.DEFAULT,
+ data: seriesData,
+ tooltip: {
+ pointFormat: i18n._('Accrued Interest') + ' ${point.y}',
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ }
+
+ return (
+
+
{title}
+ {!data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default PairSupplyAccruedInterestDayDataChart
diff --git a/src/features/analytics/kashi/components/PairSupplyBorrowDayDataChart.tsx b/src/features/analytics/kashi/components/PairSupplyBorrowDayDataChart.tsx
new file mode 100644
index 0000000000..0bab8baa1a
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairSupplyBorrowDayDataChart.tsx
@@ -0,0 +1,139 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const AttributesByType = {
+ supply: {
+ color: TailwindConfig.theme.colors.green.DEFAULT,
+ valueFunc: (item: KashiPairDayDataMap) => ({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalAsset).add(BigNumber.from(item.totalBorrow)).toNumber() / 100.0,
+ }),
+ tooltip: {
+ pointFormat: 'Supply ${point.y}',
+ },
+ },
+ borrow: {
+ color: TailwindConfig.theme.colors.pink.DEFAULT,
+ valueFunc: (item: KashiPairDayDataMap) => ({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalBorrow).toNumber() / 100.0,
+ }),
+ tooltip: {
+ pointFormat: 'Borrow ${point.y}',
+ },
+ },
+}
+
+const PairSupplyBorrowDayDataChart = ({
+ type = 'supply',
+ containerClass = '',
+ title = i18n._('Deposit'),
+ data,
+}: {
+ type?: 'supply' | 'borrow'
+ containerClass?: string
+ title?: string
+ data?: KashiPairDayDataMap[]
+}) => {
+ const getSeries = () => {
+ let seriesData: any[] = []
+ const attribute = AttributesByType[type]
+ data?.forEach((item) => {
+ seriesData.push(attribute.valueFunc(item))
+ })
+ return [
+ {
+ type: 'area',
+ color: attribute.color,
+ data: seriesData,
+ tooltip: attribute.tooltip,
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ }
+
+ return (
+
+
{title}
+ {!data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default PairSupplyBorrowDayDataChart
diff --git a/src/features/analytics/kashi/components/PairSupplyBorrowMonthDataChart.tsx b/src/features/analytics/kashi/components/PairSupplyBorrowMonthDataChart.tsx
new file mode 100644
index 0000000000..d1ceca7b08
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairSupplyBorrowMonthDataChart.tsx
@@ -0,0 +1,154 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const AttributesByType = {
+ supply: {
+ color: TailwindConfig.theme.colors.green.DEFAULT,
+ valueFunc: (item: KashiPairDayDataMap) => ({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalAsset).add(BigNumber.from(item.totalBorrow)).toNumber() / 100.0,
+ }),
+ tooltip: {
+ pointFormat: i18n._('Supply') + ' ${point.y}',
+ },
+ },
+ borrow: {
+ color: TailwindConfig.theme.colors.pink.DEFAULT,
+ valueFunc: (item: KashiPairDayDataMap) => ({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalBorrow).toNumber() / 100.0,
+ }),
+ tooltip: {
+ pointFormat: i18n._('Borrow') + ' ${point.y}',
+ },
+ },
+}
+
+const PairSupplyBorrowMonthDataChart = ({
+ containerClass = '',
+ title = i18n._('Monthly Net Supply & Borrow'),
+ data,
+}: {
+ // type?: "supply" | "borrow";
+ containerClass?: string
+ title?: string
+ data?: KashiPairDayDataMap[]
+}) => {
+ const getSeries = () => {
+ let borrowSeriesData: any[] = []
+ const borrowAttribute = AttributesByType['borrow']
+
+ let supplySeriesData: any[] = []
+ const supplyAttribute = AttributesByType['supply']
+ data?.forEach((item) => {
+ borrowSeriesData.push(borrowAttribute.valueFunc(item))
+ supplySeriesData.push(supplyAttribute.valueFunc(item))
+ })
+ return [
+ {
+ type: 'area',
+ color: supplyAttribute.color,
+ data: supplySeriesData,
+ pointIntervalUnit: 'month',
+ tooltip: supplyAttribute.tooltip,
+ },
+ {
+ type: 'area',
+ color: borrowAttribute.color,
+ data: borrowSeriesData,
+ pointIntervalUnit: 'month',
+ tooltip: borrowAttribute.tooltip,
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'month',
+ count: 5,
+ text: '5m',
+ title: i18n._('View 5 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ {
+ type: 'ytd',
+ text: 'YTD',
+ title: i18n._('View year to date'),
+ },
+ {
+ type: 'year',
+ count: 1,
+ text: '1y',
+ title: i18n._('View 1 year'),
+ },
+ {
+ type: 'all',
+ text: 'All',
+ title: i18n._('View all'),
+ },
+ ],
+ selected: 0,
+ },
+ }
+
+ return (
+
+
{title}
+ {!data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default PairSupplyBorrowMonthDataChart
diff --git a/src/features/analytics/kashi/components/PairUtilizationDayDataChart.tsx b/src/features/analytics/kashi/components/PairUtilizationDayDataChart.tsx
new file mode 100644
index 0000000000..593a23a066
--- /dev/null
+++ b/src/features/analytics/kashi/components/PairUtilizationDayDataChart.tsx
@@ -0,0 +1,119 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const PairUtilizationDayDataChart = ({
+ containerClass = '',
+ title = 'Utilization Rate',
+ data,
+}: {
+ containerClass?: string
+ title?: string
+ data?: KashiPairDayDataMap[]
+}) => {
+ const getSeries = () => {
+ let utilizationData: any[] = []
+ data?.forEach((item) => {
+ utilizationData.push({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.avgUtilization).div(BigInt(1e14)).toNumber() / 1e2,
+ })
+ })
+ return [
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.blue.DEFAULT,
+ name: i18n._('Utilization'),
+ data: utilizationData,
+ tooltip: {
+ pointFormat: i18n._('Utilization Rate') + ' {point.y}%',
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ }
+
+ return (
+
+
{title}
+ {!data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default PairUtilizationDayDataChart
diff --git a/src/features/analytics/kashi/components/Progress.tsx b/src/features/analytics/kashi/components/Progress.tsx
new file mode 100644
index 0000000000..a96bf9f368
--- /dev/null
+++ b/src/features/analytics/kashi/components/Progress.tsx
@@ -0,0 +1,88 @@
+import { formatPercent } from 'app/functions'
+import classNames from 'classnames'
+
+const classByColor = {
+ blue: {
+ title: 'text-gray-500',
+ percent: 'text-blue-500',
+ progressContainer: 'bg-blue-300',
+ progressActive: 'bg-blue-500',
+ },
+ pink: {
+ title: 'text-gray-500',
+ percent: 'text-pink-500',
+ progressContainer: 'bg-pink-300',
+ progressActive: 'bg-pink-500',
+ },
+ green: {
+ title: 'text-gray-500',
+ percent: 'text-green-500',
+ progressContainer: 'bg-green-300',
+ progressActive: 'bg-green-500',
+ },
+}
+
+const Progress = ({
+ loading = false,
+ color = 'green',
+ progress = 0,
+ title = '',
+ containerClass = '',
+}: {
+ loading?: boolean
+ color?: 'blue' | 'pink' | 'green'
+ progress?: number
+ title?: string
+ containerClass?: string
+}) => {
+ const byColor = classByColor[color]
+ return (
+
+
+
+
+ {loading ? (
+
+ ) : (
+ formatPercent(progress * 100)
+ )}
+
+
+
+ {loading ? (
+
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+ )
+}
+
+export default Progress
diff --git a/src/features/analytics/kashi/components/TokenCard.tsx b/src/features/analytics/kashi/components/TokenCard.tsx
new file mode 100644
index 0000000000..468df0a287
--- /dev/null
+++ b/src/features/analytics/kashi/components/TokenCard.tsx
@@ -0,0 +1,71 @@
+import { i18n } from '@lingui/core'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import numeral from 'numeral'
+
+import { Token } from '../types/Token'
+
+const TokenCard = ({
+ containerClass = '',
+ data,
+ totalAsset = BigInt(0),
+ totalBorrow = BigInt(0),
+}: {
+ containerClass?: string
+ data?: Token
+ totalAsset?: BigInt
+ totalBorrow?: BigInt
+}) => {
+ return (
+
+
{i18n._('Info')}
+
+
+
{i18n._('Supply')}
+ {!data ? (
+
+ ) : (
+
+ {numeral(BigNumber.from(totalAsset).add(BigNumber.from(totalBorrow)).toNumber() / 100.0).format(
+ '($0,0.00)'
+ )}
+
+ )}
+
+
+
{i18n._('Available')}
+ {!data ? (
+
+ ) : (
+
{numeral(Number(totalAsset) / 100.0).format('($0,0.00)')}
+ )}
+
+
+
{i18n._('Borrow')}
+ {!data ? (
+
+ ) : (
+
{numeral(Number(totalBorrow) / 100.0).format('($0,0.00)')}
+ )}
+
+
+
{i18n._('Oracle Price')}
+ {!data ? (
+
+ ) : (
+
+ {numeral(BigNumber.from(data.price).div(BigNumber.from(1e6)).toNumber() / 100).format('($0,0.00)')}
+
+ )}
+
+
+
+ )
+}
+
+export default TokenCard
diff --git a/src/features/analytics/kashi/components/TokenMarketTable.tsx b/src/features/analytics/kashi/components/TokenMarketTable.tsx
new file mode 100644
index 0000000000..37f415500e
--- /dev/null
+++ b/src/features/analytics/kashi/components/TokenMarketTable.tsx
@@ -0,0 +1,291 @@
+/* eslint-disable @next/next/no-img-element */
+import { i18n } from '@lingui/core'
+import { ChainId, Token } from '@sushiswap/core-sdk'
+import { CurrencyLogo } from 'app/components/CurrencyLogo'
+import { useActiveWeb3React } from 'app/services/web3'
+import { BigNumber } from 'ethers'
+import { useRouter } from 'next/router'
+import numeral from 'numeral'
+import React, { useEffect, useState } from 'react'
+import { FaChevronDown, FaChevronUp } from 'react-icons/fa'
+
+import { useAppContext } from '../context/AppContext'
+import { KashiPairsByTokenNew } from '../types/KashiPair'
+
+type OrderBy = 'symbol' | 'totalSupply' | 'totalAsset' | 'totalBorrow' | ''
+type OrderDirection = 'asc' | 'desc'
+
+const MarketTableHead = ({
+ onSort,
+ orderBy,
+ orderDirection,
+}: {
+ onSort: (orderBy: OrderBy) => void
+ orderBy: OrderBy
+ orderDirection: OrderDirection
+}) => {
+ const iconByDirection = {
+ asc: ,
+ desc: ,
+ }
+
+ return (
+
+ {
+ onSort('symbol')
+ }}
+ >
+ Token {orderBy === 'symbol' && iconByDirection[orderDirection]}
+ |
+ {
+ onSort('totalSupply')
+ }}
+ >
+
+ Total Supply
+ {orderBy === 'totalSupply' && iconByDirection[orderDirection]}
+
+ |
+ {
+ onSort('totalAsset')
+ }}
+ >
+
+ Total Available
+ {orderBy === 'totalAsset' && iconByDirection[orderDirection]}
+
+ |
+ {
+ onSort('totalBorrow')
+ }}
+ >
+
+ Total Borrow
+ {orderBy === 'totalBorrow' && iconByDirection[orderDirection]}
+
+ |
+
+ )
+}
+
+const MarketTableRowLoading = () => (
+
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+)
+
+const MarketTableRow = ({ data, index }: { data: KashiPairsByTokenNew; index: number }) => {
+ const { tokenUtilService, handleLogoError } = useAppContext()
+ const router = useRouter()
+ const goto = (route: string) => {
+ router.push(route)
+ }
+
+ const { chainId } = useActiveWeb3React()
+
+ const asset = new Token(
+ chainId ?? ChainId.ETHEREUM,
+ data.token?.id ?? '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ Number(data.token?.decimals ?? 0),
+ data.token?.symbol,
+ data.token?.name
+ )
+
+ return (
+ <>
+ goto(`/analytics/kashi/tokens/${data.token.id}`)}
+ className="border-t border-l-2 border-transparent cursor-pointer border-t-gray-900 hover:bg-dark-900"
+ >
+
+
+
+
+
+ {tokenUtilService.symbol(data.token.symbol)}
+
+
+ |
+
+ {numeral(BigNumber.from(data.totalAsset).add(BigNumber.from(data.totalBorrow)).toNumber() / 100).format(
+ '$0,.00'
+ )}
+ |
+
+ {numeral(BigNumber.from(data.totalAsset).toNumber() / 100).format('$0,.00')}
+ |
+
+ {numeral(BigNumber.from(data.totalBorrow).toNumber() / 100).format('$0,.00')}
+ |
+
+ >
+ )
+}
+
+const TokenMarketTable = ({ loading = false, data = [] }: { loading?: boolean; data: KashiPairsByTokenNew[] }) => {
+ const [orderBy, setOrderBy] = useState('')
+ const [orderDirection, setOrderDirection] = useState('desc')
+
+ const [fullList, setFullList] = useState([])
+ const [sortedList, setSortedList] = useState([])
+ const [list, setList] = useState([])
+ const [isMore, setMore] = useState(false)
+ const [search, setSearch] = useState('')
+
+ useEffect(() => {
+ setFullList(data)
+ }, [data])
+
+ useEffect(() => {
+ let newSortedList = [...fullList]
+ const compareFuncs = {
+ symbol: {
+ asc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ (a.token.symbol.toLowerCase() || '').localeCompare(b.token.symbol.toLowerCase() || ''),
+ desc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ (b.token.symbol.toLowerCase() || '').localeCompare(a.token.symbol.toLowerCase() || ''),
+ },
+ totalSupply: {
+ asc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ BigNumber.from(a.totalAsset)
+ .add(BigNumber.from(a.totalBorrow))
+ .lte(BigNumber.from(b.totalAsset).add(BigNumber.from(b.totalBorrow)))
+ ? -1
+ : 1,
+ desc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ BigNumber.from(a.totalAsset)
+ .add(BigNumber.from(a.totalBorrow))
+ .gte(BigNumber.from(b.totalAsset).add(BigNumber.from(b.totalBorrow)))
+ ? -1
+ : 1,
+ },
+ totalAsset: {
+ asc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ BigNumber.from(a.totalAsset).lte(BigNumber.from(b.totalAsset)) ? -1 : 1,
+ desc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ BigNumber.from(a.totalAsset).gte(BigNumber.from(b.totalAsset)) ? -1 : 1,
+ },
+ totalBorrow: {
+ asc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ BigNumber.from(a.totalBorrow).lte(BigNumber.from(b.totalBorrow)) ? -1 : 1,
+ desc: (a: KashiPairsByTokenNew, b: KashiPairsByTokenNew) =>
+ BigNumber.from(a.totalBorrow).gte(BigNumber.from(b.totalBorrow)) ? -1 : 1,
+ },
+ }
+
+ if (orderBy) {
+ newSortedList.sort(compareFuncs[orderBy][orderDirection])
+ }
+ setSortedList(newSortedList)
+ }, [fullList, orderBy, orderDirection])
+
+ useEffect(() => {
+ setList([])
+ }, [sortedList])
+
+ const handleLoadMore = () => {
+ if (isMore) return
+ setMore(true)
+ if (list.length < sortedList.length) {
+ const start = list.length
+ const end = Math.min(start + 20, sortedList.length)
+ const newList = [...list, ...sortedList.slice(start, end)]
+ setList(newList)
+ }
+ setMore(false)
+ }
+
+ const handleSort = (orderField: OrderBy) => {
+ if (orderBy === orderField) {
+ setOrderDirection(orderDirection === 'asc' ? 'desc' : 'asc')
+ return
+ }
+ setOrderBy(orderField)
+ setOrderDirection('desc')
+ }
+
+ const handleSearchChange = (event: React.SyntheticEvent) => {
+ const target = event.target as HTMLInputElement
+ setSearch(target.value)
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {loading ? (
+
+
+
+
+
+
+ ) : (
+
+ {sortedList
+ .filter((value) => value.token.symbol.toLowerCase().indexOf(search.toLowerCase()) >= 0)
+ .map((data, index) => (
+
+ ))}
+
+ )}
+
+
+ >
+ )
+}
+export default TokenMarketTable
diff --git a/src/features/analytics/kashi/components/TotalCard.tsx b/src/features/analytics/kashi/components/TotalCard.tsx
new file mode 100644
index 0000000000..27ac0e9665
--- /dev/null
+++ b/src/features/analytics/kashi/components/TotalCard.tsx
@@ -0,0 +1,114 @@
+import { useLingui } from '@lingui/react'
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import numeral from 'numeral'
+
+import { useAppContext } from '../context/AppContext'
+import { KashiPairNew } from '../types/KashiPair'
+import Progress from './Progress'
+
+export type TotalData = {
+ amount: BigInt
+ volumeIn24H: BigInt
+ topMarkets: KashiPairNew[]
+}
+
+type AttributesByBorrowType = {
+ progressColor: 'pink' | 'blue' | 'green'
+ title: string
+ users: string
+}
+
+type AttributesMapByBorrowType = {
+ borrow: AttributesByBorrowType
+ asset: AttributesByBorrowType
+ supply: AttributesByBorrowType
+}
+
+const TotalCard = ({
+ containerClass = '',
+ data,
+ borrow = 'borrow',
+ loading = false,
+}: {
+ containerClass?: string
+ data: TotalData
+ borrow?: 'borrow' | 'asset' | 'supply'
+ loading?: boolean
+}) => {
+ const { i18n } = useLingui()
+ const AttributesMapByBorrow = {
+ borrow: {
+ progressColor: 'pink',
+ title: i18n._('Total Borrow'),
+ users: i18n._('Borrowers'),
+ },
+ asset: {
+ progressColor: 'green',
+ title: i18n._('Total Available'),
+ users: i18n._('Suppliers'),
+ },
+ supply: {
+ progressColor: 'blue',
+ title: i18n._('Total Supply'),
+ users: i18n._('Suppliers'),
+ },
+ } as AttributesMapByBorrowType
+ const attributes = AttributesMapByBorrow[borrow]
+ const isLoading = data.amount === BigInt(0) || loading
+ const { tokenUtilService } = useAppContext()
+
+ return (
+
+
{attributes.title}
+
+
+ {isLoading ? (
+
+ ) : (
+ numeral(Number(data.amount) / 100.0).format('($0,0.00)')
+ )}
+
+
{i18n._('Top 3 Markets')}
+ {isLoading ? (
+ <>
+
+
+
+ >
+ ) : (
+ data.topMarkets.map((marketData) => (
+
+ ))
+ )}
+
+
+ )
+}
+
+export default TotalCard
diff --git a/src/features/analytics/kashi/components/TotalCompareChart.tsx b/src/features/analytics/kashi/components/TotalCompareChart.tsx
new file mode 100644
index 0000000000..63009f56c7
--- /dev/null
+++ b/src/features/analytics/kashi/components/TotalCompareChart.tsx
@@ -0,0 +1,113 @@
+import { i18n } from '@lingui/core'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const TotalCompareChart = ({ loading, data }: { loading: boolean; data: KashiPairDayDataMap[] }) => {
+ const getSeries = () => {
+ let seriesData: any[] = []
+ data.forEach((item) => {
+ if (BigNumber.from(item.totalAsset).add(BigNumber.from(item.totalBorrow)).gt(0)) {
+ const percent =
+ BigNumber.from(item.totalBorrow)
+ .mul(BigNumber.from('10000'))
+ .div(BigNumber.from(item.totalAsset).add(BigNumber.from(item.totalBorrow)))
+ .toNumber() / 100
+ seriesData.push({
+ x: moment(item.date).valueOf(),
+ y: percent,
+ })
+ }
+ })
+
+ return [
+ {
+ type: 'column',
+ color: TailwindConfig.theme.colors.pink.DEFAULT,
+ name: 'Ratio',
+ data: seriesData,
+ tooltip: {
+ pointFormat: 'Ratio {point.y}%',
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ series: getSeries(),
+ }
+
+ return (
+
+
{i18n._('Borrow vs Supply Ratio')}
+ {loading || !data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default TotalCompareChart
diff --git a/src/features/analytics/kashi/components/TotalDayDataChart.tsx b/src/features/analytics/kashi/components/TotalDayDataChart.tsx
new file mode 100644
index 0000000000..de7bda16c7
--- /dev/null
+++ b/src/features/analytics/kashi/components/TotalDayDataChart.tsx
@@ -0,0 +1,119 @@
+import { i18n } from '@lingui/core'
+import { BigNumber } from 'ethers'
+import Highcharts from 'highcharts/highstock'
+import HighchartsReact from 'highcharts-react-official'
+import moment from 'moment'
+
+import TailwindConfig from '../config/tailwind'
+import { KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+function TotalDayDataChart({ loading, data }: { loading: boolean; data: KashiPairDayDataMap[] }) {
+ const getSeries = () => {
+ let supplyData: any[] = []
+ let borrowData: any[] = []
+ data.forEach((item) => {
+ supplyData.push({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalAsset).add(BigNumber.from(item.totalBorrow)).toNumber() / 100.0,
+ })
+ borrowData.push({
+ x: moment(item.date).valueOf(),
+ y: BigNumber.from(item.totalBorrow).toNumber() / 100.0,
+ })
+ })
+ return [
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.green.DEFAULT,
+ name: i18n._('Supply'),
+ data: supplyData,
+ tooltip: {
+ pointFormat: 'Supply ${point.y}',
+ },
+ },
+ {
+ type: 'line',
+ color: TailwindConfig.theme.colors.pink.DEFAULT,
+ name: i18n._('Borrow'),
+ data: borrowData,
+ tooltip: {
+ pointFormat: 'Borrow ${point.y}',
+ },
+ },
+ ]
+ }
+
+ const options = {
+ title: {
+ style: {
+ height: '50px',
+ padding: '24px',
+ fontWeight: 'bold',
+ fontSize: '18px',
+ },
+ },
+ scrollbar: {
+ enabled: false,
+ },
+ chart: {
+ backgroundColor: TailwindConfig.theme.colors['dark-900'],
+ },
+ yAxis: [
+ {
+ gridLineColor: TailwindConfig.theme.colors['dark-600'],
+ },
+ ],
+ series: getSeries(),
+ rangeSelector: {
+ buttons: [
+ {
+ type: 'week',
+ count: 1,
+ text: '1w',
+ title: i18n._('View 1 week'),
+ },
+ {
+ type: 'month',
+ count: 1,
+ text: '1m',
+ title: i18n._('View 1 month'),
+ },
+ {
+ type: 'month',
+ count: 3,
+ text: '3m',
+ title: i18n._('View 3 months'),
+ },
+ {
+ type: 'month',
+ count: 6,
+ text: '6m',
+ title: i18n._('View 6 months'),
+ },
+ ],
+ selected: 1,
+ },
+ }
+
+ return (
+
+
{i18n._('Total Supply & Total Borrow')}
+ {loading || !data || data.length === 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )
+}
+
+export default TotalDayDataChart
diff --git a/src/features/analytics/kashi/components/TotalTokenCard.tsx b/src/features/analytics/kashi/components/TotalTokenCard.tsx
new file mode 100644
index 0000000000..71477901c7
--- /dev/null
+++ b/src/features/analytics/kashi/components/TotalTokenCard.tsx
@@ -0,0 +1,113 @@
+import classNames from 'classnames'
+import { BigNumber } from 'ethers'
+import numeral from 'numeral'
+
+import { useAppContext } from '../context/AppContext'
+import { KashiPairsByTokenNew } from '../types/KashiPair'
+import Progress from './Progress'
+
+export type TotalData = {
+ amount: BigInt
+ volumeIn24H: BigInt
+ totalUsers: BigInt
+ topMarkets: KashiPairsByTokenNew[]
+}
+
+type AttributesByBorrowType = {
+ progressColor: 'blue' | 'green' | 'pink'
+ title: string
+ users: string
+}
+
+type AttributesMapByBorrowType = {
+ borrow: AttributesByBorrowType
+ asset: AttributesByBorrowType
+ supply: AttributesByBorrowType
+}
+
+const AttributesMapByBorrow = {
+ borrow: {
+ progressColor: 'pink',
+ title: 'Total Borrow',
+ users: 'Borrowers',
+ },
+ asset: {
+ progressColor: 'green',
+ title: 'Total Available',
+ users: 'Suppliers',
+ },
+ supply: {
+ progressColor: 'blue',
+ title: 'Total Supply',
+ users: 'Suppliers',
+ },
+} as AttributesMapByBorrowType
+
+const TotakTokenCard = ({
+ containerClass = '',
+ data,
+ borrow = 'borrow',
+ loading = false,
+}: {
+ containerClass?: string
+ data: TotalData
+ borrow?: 'borrow' | 'asset' | 'supply'
+ loading?: boolean
+}) => {
+ const attributes = AttributesMapByBorrow[borrow]
+ const isLoading = data.amount === BigInt(0) || loading
+
+ const { tokenUtilService } = useAppContext()
+
+ return (
+
+
{attributes.title}
+
+
+ {isLoading ? (
+
+ ) : (
+ numeral(Number(data.amount) / 100.0).format('($0,0.00)')
+ )}
+
+
Top 3 Markets
+ {isLoading ? (
+ <>
+
+
+
+ >
+ ) : (
+ data.topMarkets.map((marketData) => (
+
+ ))
+ )}
+
+
+ )
+}
+
+export default TotakTokenCard
diff --git a/src/features/analytics/kashi/config/tailwind.ts b/src/features/analytics/kashi/config/tailwind.ts
new file mode 100644
index 0000000000..a0b201e96a
--- /dev/null
+++ b/src/features/analytics/kashi/config/tailwind.ts
@@ -0,0 +1,9 @@
+// @ts-ignore
+import resolveConfig from 'tailwindcss/resolveConfig'
+
+import config from '../../../../../tailwind.config.js'
+
+// @ts-ignore
+const TailwindConfig = resolveConfig(config)
+
+export default TailwindConfig
diff --git a/src/features/analytics/kashi/context/AppContext.tsx b/src/features/analytics/kashi/context/AppContext.tsx
new file mode 100644
index 0000000000..e7a9a4df34
--- /dev/null
+++ b/src/features/analytics/kashi/context/AppContext.tsx
@@ -0,0 +1,46 @@
+import React, { createContext, ReactNode, useContext } from 'react'
+
+import CalculateService from '../services/utils/CalculateService'
+import TokenUtilService from '../services/utils/TokenUtilService'
+
+export const handleLogoError = (event: React.SyntheticEvent) => {
+ const imgElement = event.target as HTMLImageElement
+ imgElement.src = '/images/tokens/icon-quiz.jpg'
+}
+interface AppContextProps {
+ handleLogoError: (event: React.SyntheticEvent) => void
+ calculateService: CalculateService
+ tokenUtilService: TokenUtilService
+}
+const AppContext = createContext({} as AppContextProps)
+
+export const AnalyticsKashiAppContextProvider = ({ children }: { children: ReactNode }) => {
+ const calculateService = CalculateService.getInstance()
+ const tokenUtilService = TokenUtilService.getInstance()
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useAppContext = () => {
+ return useContext(AppContext)
+}
+
+export const useCalculateService = () => {
+ const { calculateService } = useAppContext()
+ return calculateService
+}
+
+export const useTokenUtilService = () => {
+ const { tokenUtilService } = useAppContext()
+ return tokenUtilService
+}
diff --git a/src/features/analytics/kashi/services/utils/CalculateService.ts b/src/features/analytics/kashi/services/utils/CalculateService.ts
new file mode 100644
index 0000000000..33f5859b6e
--- /dev/null
+++ b/src/features/analytics/kashi/services/utils/CalculateService.ts
@@ -0,0 +1,496 @@
+import { BigNumber } from 'ethers'
+import moment from 'moment'
+
+import { KashiPair, KashiPairNew, KashiPairsByToken, KashiPairsByTokenNew } from '../../types/KashiPair'
+import { KashiPairDayData, KashiPairDayDataMap, KashiPairDayDataMapsCollateral } from '../../types/KashiPairDayData'
+import { Token, TokenNew } from '../../types/Token'
+
+class CalculateService {
+ protected static instance: CalculateService
+ constructor() {}
+
+ protected extractKashiPairTokenSymbols(kashiPairs: KashiPair[], tokenField: 'asset' | 'collateral' = 'asset') {
+ const symbols = [] as string[]
+
+ kashiPairs.forEach((kashiPair) => {
+ const symbol = kashiPair[tokenField]?.symbol || ''
+
+ const index = symbols.indexOf(symbol)
+ if (index === -1) {
+ symbols.push(symbol)
+ }
+ })
+ return symbols
+ }
+
+ extractKashiPairSymbols(kashiPairs: KashiPair[]) {
+ const symbols = [] as string[]
+
+ kashiPairs.forEach((kashiPair) => {
+ const symbolAsset = kashiPair.asset?.symbol || ''
+ const symbolCollateral = kashiPair.collateral?.symbol || ''
+
+ const indexAsset = symbols.indexOf(symbolAsset)
+ if (indexAsset === -1) {
+ symbols.push(symbolAsset)
+ }
+
+ const indexCollateral = symbols.indexOf(symbolCollateral)
+ if (indexCollateral === -1) {
+ symbols.push(symbolCollateral)
+ }
+ })
+ return symbols
+ }
+
+ extractKashiPairCollateralSymbols(kashiPairs: KashiPair[]) {
+ return this.extractKashiPairTokenSymbols(kashiPairs, 'collateral')
+ }
+
+ extractKashiPairAssetSymbols(kashiPairs: KashiPair[]) {
+ return this.extractKashiPairTokenSymbols(kashiPairs, 'asset')
+ }
+
+ extractTokenSymbols(tokens: Token[]) {
+ const symbols = [] as string[]
+
+ tokens.forEach((token) => {
+ const symbol = token.symbol || ''
+ const index = symbols.indexOf(symbol)
+ if (index === -1) {
+ symbols.push(symbol)
+ }
+ })
+ return symbols
+ }
+
+ calculateKashiPairPrices(kashiPairs: KashiPair[], pricesMap: { [key: string]: BigInt }) {
+ let sumTotalAsset = BigNumber.from('0'),
+ sumTotalBorrow = BigNumber.from('0')
+
+ const newKashiPairs = kashiPairs.map((kashiPair) => {
+ let totalAsset = BigNumber.from('0'),
+ totalBorrow = BigNumber.from('0')
+
+ if (kashiPair.asset) {
+ totalAsset = BigNumber.from(pricesMap?.[kashiPair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalAssetElastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.asset.decimals) + 6))
+ totalBorrow = BigNumber.from(pricesMap?.[kashiPair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalBorrowElastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.asset.decimals) + 6))
+ }
+ sumTotalAsset = sumTotalAsset.add(totalAsset)
+ sumTotalBorrow = sumTotalBorrow.add(totalBorrow)
+ const newKashiPair = {
+ ...kashiPair,
+ totalAsset: totalAsset.toBigInt(),
+ totalBorrow: totalBorrow.toBigInt(),
+ }
+ return newKashiPair
+ })
+ return {
+ totalAsset: sumTotalAsset,
+ totalBorrow: sumTotalBorrow,
+ kashiPairs: newKashiPairs,
+ }
+ }
+
+ calculateKashiPairPricesNew(kashiPairs: KashiPairNew[], pricesMap: { [key: string]: BigInt }) {
+ let sumTotalAsset = BigNumber.from('0'),
+ sumTotalBorrow = BigNumber.from('0')
+
+ const newKashiPairs = kashiPairs.map((kashiPair) => {
+ let totalAsset = BigNumber.from('0'),
+ totalBorrow = BigNumber.from('0')
+
+ if (kashiPair.asset) {
+ totalAsset = BigNumber.from(pricesMap?.[kashiPair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalAsset?.elastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.asset.decimals) + 6))
+ totalBorrow = BigNumber.from(pricesMap?.[kashiPair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalBorrow?.elastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.asset.decimals) + 6))
+ }
+ sumTotalAsset = sumTotalAsset.add(totalAsset)
+ sumTotalBorrow = sumTotalBorrow.add(totalBorrow)
+ const newKashiPair = {
+ ...kashiPair,
+ totalAssetAmount: totalAsset.toBigInt(),
+ totalBorrowAmount: totalBorrow.toBigInt(),
+ }
+ return newKashiPair
+ })
+ return {
+ totalAsset: sumTotalAsset,
+ totalBorrow: sumTotalBorrow,
+ kashiPairs: newKashiPairs,
+ }
+ }
+
+ calculateKashiPairPricesGroupByAsset(kashiPairs: KashiPair[], pricesMap: { [key: string]: BigInt }) {
+ const { totalAsset, totalBorrow, kashiPairs: newKashiPairs } = this.calculateKashiPairPrices(kashiPairs, pricesMap)
+
+ const kashiPairsByTokenMap: { [key: string]: KashiPairsByToken } = {}
+ newKashiPairs.forEach((kashiPair) => {
+ const { asset } = kashiPair
+ if (asset) {
+ if (kashiPairsByTokenMap[asset.id]) {
+ const kashiPairsByToken = kashiPairsByTokenMap[asset.id]
+ kashiPairsByToken.kashiPairs.push(kashiPair)
+ kashiPairsByToken.totalAsset = BigNumber.from(kashiPairsByToken.totalAsset)
+ .add(BigNumber.from(kashiPair.totalAsset))
+ .toBigInt()
+
+ kashiPairsByToken.totalBorrow = BigNumber.from(kashiPairsByToken.totalBorrow)
+ .add(BigNumber.from(kashiPair.totalBorrow))
+ .toBigInt()
+ } else {
+ kashiPairsByTokenMap[asset.id] = {
+ token: asset,
+ totalAsset: kashiPair.totalAsset,
+ totalBorrow: kashiPair.totalBorrow,
+ kashiPairs: [kashiPair],
+ }
+ }
+ }
+ })
+
+ const kashiPairsByTokens = Object.values(kashiPairsByTokenMap)
+ return {
+ totalAsset,
+ totalBorrow,
+ kashiPairsByTokens,
+ }
+ }
+
+ calculateKashiPairPricesGroupByAssetNew(kashiPairs: KashiPairNew[], pricesMap: { [key: string]: BigInt }) {
+ const {
+ totalAsset,
+ totalBorrow,
+ kashiPairs: newKashiPairs,
+ } = this.calculateKashiPairPricesNew(kashiPairs, pricesMap)
+
+ const kashiPairsByTokenMap: { [key: string]: KashiPairsByTokenNew } = {}
+ newKashiPairs.forEach((kashiPair) => {
+ const { asset } = kashiPair
+ if (asset) {
+ if (kashiPairsByTokenMap[asset.id]) {
+ const kashiPairsByToken = kashiPairsByTokenMap[asset.id]
+ kashiPairsByToken.kashiPairs.push(kashiPair)
+ kashiPairsByToken.totalAsset = BigNumber.from(kashiPairsByToken.totalAsset)
+ .add(BigNumber.from(kashiPair.totalAssetAmount))
+ .toBigInt()
+
+ kashiPairsByToken.totalBorrow = BigNumber.from(kashiPairsByToken.totalBorrow)
+ .add(BigNumber.from(kashiPair.totalBorrowAmount))
+ .toBigInt()
+ } else {
+ kashiPairsByTokenMap[asset.id] = {
+ token: asset,
+ totalAsset: kashiPair.totalAssetAmount,
+ totalBorrow: kashiPair.totalBorrowAmount,
+ kashiPairs: [kashiPair],
+ }
+ }
+ }
+ })
+
+ const kashiPairsByTokens = Object.values(kashiPairsByTokenMap)
+ return {
+ totalAsset,
+ totalBorrow,
+ kashiPairsByTokens,
+ }
+ }
+
+ calculateTokenPrices(tokens: Token[], pricesMap: { [key: string]: BigInt }) {
+ let sumTotalSupply = BigNumber.from('0')
+
+ const newTokens = tokens.map((token) => {
+ let totalSupply = BigNumber.from('0')
+ totalSupply = BigNumber.from(pricesMap[token.symbol] ?? 0)
+ .mul(BigNumber.from(token.totalSupplyElastic))
+ .div(BigNumber.from('10').pow(Number(token.decimals) + 6))
+ sumTotalSupply = sumTotalSupply.add(totalSupply)
+ const newToken = {
+ ...token,
+ price: (pricesMap[token.symbol] ?? 0) || 0,
+ totalSupply: totalSupply.toBigInt(),
+ }
+ return newToken
+ })
+ return {
+ totalSupply: sumTotalSupply,
+ tokens: newTokens,
+ }
+ }
+
+ calculateTokenPricesNew(tokens: TokenNew[], pricesMap: { [key: string]: BigInt }) {
+ let sumTotalSupply = BigNumber.from('0')
+
+ const newTokens = tokens.map((token) => {
+ let totalSupply = BigNumber.from('0')
+ totalSupply = BigNumber.from(pricesMap[token.symbol] ?? 0)
+ .mul(BigNumber.from(token.rebase?.elastic))
+ .div(BigNumber.from('10').pow(Number(token.decimals) + 6))
+ sumTotalSupply = sumTotalSupply.add(totalSupply)
+ const newToken = {
+ ...token,
+ price: (pricesMap[token.symbol] ?? 0) || 0,
+ totalSupply: totalSupply.toBigInt(),
+ }
+ return newToken
+ })
+ return {
+ totalSupply: sumTotalSupply,
+ tokens: newTokens,
+ }
+ }
+
+ calculateKashiPairDayDataPrices(kashiPairs: KashiPairDayData[], pricesMap: { [key: string]: BigInt }) {
+ const kashiPairsMaps: KashiPairDayDataMap[] = []
+
+ let sumTotalAsset = BigNumber.from('0'),
+ sumTotalBorrow = BigNumber.from('0'),
+ sumAvgExchangeRate = BigNumber.from('0'),
+ sumAvgUtilization = BigNumber.from('0'),
+ sumAvgInterestPerSecond = BigNumber.from('0')
+
+ const newKashiPairs = kashiPairs.map((kashiPair) => {
+ let totalAsset = BigNumber.from('0'),
+ totalBorrow = BigNumber.from('0')
+
+ if (kashiPair.pair.asset) {
+ totalAsset = BigNumber.from(pricesMap[kashiPair.pair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalAssetElastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.pair.asset.decimals) + 6))
+ totalBorrow = BigNumber.from(pricesMap[kashiPair.pair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalBorrowElastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.pair.asset.decimals) + 6))
+ }
+
+ sumTotalAsset = sumTotalAsset.add(totalAsset)
+ sumTotalBorrow = sumTotalBorrow.add(totalBorrow)
+ sumAvgExchangeRate = sumAvgExchangeRate.add(BigNumber.from(kashiPair.avgExchangeRate))
+ sumAvgUtilization = sumAvgUtilization.add(BigNumber.from(kashiPair.avgUtilization))
+ sumAvgInterestPerSecond = sumAvgInterestPerSecond.add(BigNumber.from(kashiPair.avgInterestPerSecond))
+
+ const newKashiPair = {
+ ...kashiPair,
+ totalAsset: totalAsset.toBigInt(),
+ totalBorrow: totalBorrow.toBigInt(),
+ }
+
+ const kashiPairDate = moment.unix(kashiPair.date).format('YYYY-MM-DD')
+ const itemKashiPairMap = kashiPairsMaps.find((kashiPairMap) => kashiPairMap.date === kashiPairDate)
+
+ if (itemKashiPairMap) {
+ itemKashiPairMap.totalAsset = BigNumber.from(itemKashiPairMap.totalAsset).add(totalAsset).toBigInt()
+ itemKashiPairMap.totalBorrow = BigNumber.from(itemKashiPairMap.totalBorrow).add(totalBorrow).toBigInt()
+ itemKashiPairMap.avgExchangeRate = BigNumber.from(itemKashiPairMap.avgExchangeRate)
+ .add(BigNumber.from(kashiPair.avgExchangeRate))
+ .toBigInt()
+ itemKashiPairMap.avgUtilization = BigNumber.from(itemKashiPairMap.avgUtilization)
+ .add(BigNumber.from(kashiPair.avgUtilization))
+ .toBigInt()
+ itemKashiPairMap.avgInterestPerSecond = BigNumber.from(itemKashiPairMap.avgInterestPerSecond)
+ .add(BigNumber.from(kashiPair.avgInterestPerSecond))
+ .toBigInt()
+ itemKashiPairMap.kashiPairs.push(newKashiPair)
+ } else {
+ kashiPairsMaps.push({
+ totalAsset: totalAsset.toBigInt(),
+ totalBorrow: totalBorrow.toBigInt(),
+ avgExchangeRate: kashiPair.avgExchangeRate || BigInt(0),
+ avgUtilization: kashiPair.avgUtilization || BigInt(0),
+ avgInterestPerSecond: kashiPair.avgInterestPerSecond || BigInt(0),
+ date: kashiPairDate,
+ kashiPairs: [newKashiPair],
+ })
+ }
+
+ let prevKashiPairsMap: KashiPairDayDataMap = {
+ kashiPairs: [],
+ totalAsset: BigInt(0),
+ totalBorrow: BigInt(0),
+ avgExchangeRate: BigInt(0),
+ avgUtilization: BigInt(0),
+ avgInterestPerSecond: BigInt(0),
+ date: '0000-00-00',
+ }
+ kashiPairsMaps.forEach((kashiPairMap) => {
+ const { kashiPairs: prevKashiPairs } = prevKashiPairsMap
+ const { kashiPairs } = kashiPairMap
+ prevKashiPairs.forEach((prevKashiPair) => {
+ const index = kashiPairs.findIndex((kashiPair) => prevKashiPair.pair.id === kashiPair.pair.id)
+ if (index === -1) {
+ kashiPairMap.totalAsset = BigNumber.from(kashiPairMap.totalAsset)
+ .add(BigNumber.from(prevKashiPair.totalAsset))
+ .toBigInt()
+ kashiPairMap.totalBorrow = BigNumber.from(kashiPairMap.totalBorrow)
+ .add(BigNumber.from(prevKashiPair.totalBorrow))
+ .toBigInt()
+ kashiPairMap.avgExchangeRate = BigNumber.from(kashiPairMap.avgExchangeRate)
+ .add(BigNumber.from(prevKashiPair.avgExchangeRate))
+ .toBigInt()
+ kashiPairMap.avgUtilization = BigNumber.from(kashiPairMap.avgUtilization)
+ .add(BigNumber.from(prevKashiPair.avgUtilization))
+ .toBigInt()
+ kashiPairMap.avgInterestPerSecond = BigNumber.from(kashiPairMap.avgInterestPerSecond)
+ .add(BigNumber.from(prevKashiPair.avgInterestPerSecond))
+ .toBigInt()
+ kashiPairs.push(prevKashiPair)
+ }
+ })
+
+ kashiPairMap.avgExchangeRate = BigNumber.from(kashiPairMap.avgExchangeRate)
+ .div(BigNumber.from(kashiPairMap.kashiPairs.length))
+ .toBigInt()
+ kashiPairMap.avgUtilization = BigNumber.from(kashiPairMap.avgUtilization)
+ .div(BigNumber.from(kashiPairMap.kashiPairs.length))
+ .toBigInt()
+ kashiPairMap.avgInterestPerSecond = BigNumber.from(kashiPairMap.avgInterestPerSecond)
+ .div(BigNumber.from(kashiPairMap.kashiPairs.length))
+ .toBigInt()
+ prevKashiPairsMap = kashiPairMap
+ })
+ kashiPairsMaps.sort((a, b) => a.date.localeCompare(b.date))
+ return newKashiPair
+ })
+
+ return {
+ totalAsset: sumTotalAsset.toBigInt(),
+ totalBorrow: sumTotalBorrow.toBigInt(),
+ kashiPairs: newKashiPairs,
+ kashiPairsMaps,
+ }
+ }
+
+ calculateKashiPairDayDataPricesMonthly(kashiPairs: KashiPairDayData[], pricesMap: { [key: string]: BigInt }) {
+ const kashiPairsMaps: KashiPairDayDataMap[] = []
+
+ let sumTotalAsset = BigNumber.from('0'),
+ sumTotalBorrow = BigNumber.from('0'),
+ sumAvgExchangeRate = BigNumber.from('0'),
+ sumAvgUtilization = BigNumber.from('0'),
+ sumAvgInterestPerSecond = BigNumber.from('0')
+
+ const newKashiPairs = kashiPairs.map((kashiPair) => {
+ let totalAsset = BigNumber.from('0'),
+ totalBorrow = BigNumber.from('0')
+
+ if (kashiPair.pair.asset) {
+ totalAsset = BigNumber.from(pricesMap[kashiPair.pair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalAssetElastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.pair.asset.decimals) + 6))
+ totalBorrow = BigNumber.from(pricesMap[kashiPair.pair.asset.symbol] ?? 0)
+ .mul(BigNumber.from(kashiPair.totalBorrowElastic))
+ .div(BigNumber.from('10').pow(Number(kashiPair.pair.asset.decimals) + 6))
+ }
+
+ sumTotalAsset = sumTotalAsset.add(totalAsset)
+ sumTotalBorrow = sumTotalBorrow.add(totalBorrow)
+ sumAvgExchangeRate = sumAvgExchangeRate.add(BigNumber.from(kashiPair.avgExchangeRate))
+ sumAvgUtilization = sumAvgUtilization.add(BigNumber.from(kashiPair.avgUtilization))
+ sumAvgInterestPerSecond = sumAvgInterestPerSecond.add(BigNumber.from(kashiPair.avgInterestPerSecond))
+
+ const newKashiPair = {
+ ...kashiPair,
+ totalAsset: totalAsset.toBigInt(),
+ totalBorrow: totalBorrow.toBigInt(),
+ }
+
+ const kashiPairDate = moment.unix(kashiPair.date).startOf('month').format('YYYY-MM-DD')
+ const itemKashiPairMap = kashiPairsMaps.find((kashiPairMap) => kashiPairMap.date === kashiPairDate)
+
+ if (itemKashiPairMap) {
+ itemKashiPairMap.totalAsset = BigNumber.from(itemKashiPairMap.totalAsset).add(totalAsset).toBigInt()
+ itemKashiPairMap.totalBorrow = BigNumber.from(itemKashiPairMap.totalBorrow).add(totalBorrow).toBigInt()
+ itemKashiPairMap.avgExchangeRate = BigNumber.from(itemKashiPairMap.avgExchangeRate)
+ .add(BigNumber.from(kashiPair.avgExchangeRate))
+ .toBigInt()
+ itemKashiPairMap.avgUtilization = BigNumber.from(itemKashiPairMap.avgUtilization)
+ .add(BigNumber.from(kashiPair.avgUtilization))
+ .toBigInt()
+ itemKashiPairMap.avgInterestPerSecond = BigNumber.from(itemKashiPairMap.avgInterestPerSecond)
+ .add(BigNumber.from(kashiPair.avgInterestPerSecond))
+ .toBigInt()
+ itemKashiPairMap.kashiPairs.push(newKashiPair)
+ } else {
+ kashiPairsMaps.push({
+ totalAsset: totalAsset.toBigInt(),
+ totalBorrow: totalBorrow.toBigInt(),
+ avgExchangeRate: kashiPair.avgExchangeRate || BigInt(0),
+ avgUtilization: kashiPair.avgUtilization || BigInt(0),
+ avgInterestPerSecond: kashiPair.avgInterestPerSecond || BigInt(0),
+ date: kashiPairDate,
+ kashiPairs: [newKashiPair],
+ })
+ }
+ kashiPairsMaps.forEach((value) => {
+ value.avgExchangeRate = BigNumber.from(value.avgExchangeRate)
+ .div(BigNumber.from(value.kashiPairs.length))
+ .toBigInt()
+ value.avgUtilization = BigNumber.from(value.avgUtilization)
+ .div(BigNumber.from(value.kashiPairs.length))
+ .toBigInt()
+ value.avgInterestPerSecond = BigNumber.from(value.avgInterestPerSecond)
+ .div(BigNumber.from(value.kashiPairs.length))
+ .toBigInt()
+ })
+ kashiPairsMaps.sort((a, b) => a.date.localeCompare(b.date))
+ return newKashiPair
+ })
+
+ return {
+ totalAsset: sumTotalAsset.toBigInt(),
+ totalBorrow: sumTotalBorrow.toBigInt(),
+ kashiPairs: newKashiPairs,
+ kashiPairsMaps,
+ }
+ }
+
+ calculateKashiPairDayDataPricesByCollateral(
+ kashiPairs: KashiPairDayData[],
+ pricesMap: { [key: string]: BigInt }
+ ): KashiPairDayDataMapsCollateral[] {
+ const kashiPairsMapGroupTemp: {
+ [key: string]: { kashiPairs: KashiPairDayData[]; collateral: Token }
+ } = {}
+ kashiPairs.forEach((kashiPair) => {
+ const { collateral } = kashiPair.pair
+ if (collateral && collateral.id) {
+ if (kashiPairsMapGroupTemp[collateral.id]) {
+ kashiPairsMapGroupTemp[collateral.id].kashiPairs.push(kashiPair)
+ } else {
+ kashiPairsMapGroupTemp[collateral.id] = {
+ collateral,
+ kashiPairs: [kashiPair],
+ }
+ }
+ }
+ })
+ const kashiPairsMapGroup = Object.values(kashiPairsMapGroupTemp)
+
+ const kashiPairsMapCollateralGroup = kashiPairsMapGroup.map((value) => {
+ const { kashiPairsMaps } = this.calculateKashiPairDayDataPrices(value.kashiPairs, pricesMap)
+ return {
+ collateral: value.collateral,
+ kashiPairsMaps,
+ }
+ })
+
+ return kashiPairsMapCollateralGroup.sort((a, b) => a.collateral.symbol.localeCompare(b.collateral.symbol))
+ }
+
+ static getInstance() {
+ if (CalculateService.instance) {
+ return CalculateService.instance
+ }
+ CalculateService.instance = new CalculateService()
+ return CalculateService.instance
+ }
+}
+
+export default CalculateService
diff --git a/src/features/analytics/kashi/services/utils/TokenUtilService.ts b/src/features/analytics/kashi/services/utils/TokenUtilService.ts
new file mode 100644
index 0000000000..c6f4a849ff
--- /dev/null
+++ b/src/features/analytics/kashi/services/utils/TokenUtilService.ts
@@ -0,0 +1,32 @@
+class TokenUtilService {
+ protected static instance: TokenUtilService
+ constructor() {}
+
+ static getInstance() {
+ if (TokenUtilService.instance) {
+ return TokenUtilService.instance
+ }
+ TokenUtilService.instance = new TokenUtilService()
+ return TokenUtilService.instance
+ }
+
+ symbol(symbol: string | undefined): string | undefined {
+ if (symbol && symbol.toLocaleLowerCase() === 'ftx token') {
+ return 'FTT'
+ }
+ return symbol
+ }
+
+ pairSymbol(symbol1: string | undefined, symbol2: string | undefined): string {
+ return `${this.symbol(symbol1)}/${this.symbol(symbol2)}`
+ }
+
+ name(name: string | undefined): string | undefined {
+ if (name && name.toLocaleLowerCase() === 'ftt') {
+ return 'FTX Token'
+ }
+ return name
+ }
+}
+
+export default TokenUtilService
diff --git a/src/features/analytics/kashi/types/Enums.ts b/src/features/analytics/kashi/types/Enums.ts
new file mode 100644
index 0000000000..629d947e65
--- /dev/null
+++ b/src/features/analytics/kashi/types/Enums.ts
@@ -0,0 +1,9 @@
+export enum ChainIdEnum {
+ Ethereum = '0x1',
+ Gnosis = '0x64',
+}
+
+export enum NativeCoinEnum {
+ USD = 'USD',
+ ETH = 'ETH',
+}
diff --git a/src/features/analytics/kashi/types/KashiPair.ts b/src/features/analytics/kashi/types/KashiPair.ts
new file mode 100644
index 0000000000..50cb62dd2a
--- /dev/null
+++ b/src/features/analytics/kashi/types/KashiPair.ts
@@ -0,0 +1,83 @@
+import { Rebase } from '@sushiswap/core-sdk'
+
+import { Token } from './Token'
+
+export type KashiPair = {
+ id: string
+ type?: string
+ owner?: string
+ feeTo?: string
+ name: string
+ symbol: string
+ oracle?: string
+ asset?: Token
+ collateral?: Token
+ exchangeRate?: BigInt
+ totalAssetElastic?: BigInt
+ totalAssetBase?: BigInt
+ totalAsset?: BigInt
+ totalCollateralShare?: BigInt
+ totalBorrowElastic?: BigInt
+ totalBorrowBase?: BigInt
+ totalBorrow?: BigInt
+ interestPerSecond?: BigInt
+ utilization?: BigInt
+ feesEarnedFraction?: BigInt
+ totalFeesEarnedFraction?: BigInt
+ lastAccrued?: BigInt
+ supplyAPR?: BigInt
+ borrowAPR?: BigInt
+ block?: BigInt
+ timestamp?: BigInt
+}
+
+export type KashiPairKpi = {
+ id: string
+ supplyAPR?: BigInt
+ borrowAPR?: BigInt
+ utilization?: BigInt
+ totalFeesEarnedFraction?: BigInt
+}
+
+export type KashiPairAccrueInfo = {
+ id: string
+ interestPerSecond?: BigInt
+ lastAccrued?: BigInt
+ feesEarnedFraction?: BigInt
+}
+
+export type KashiPairNew = {
+ id: string
+ type?: string
+ owner?: string
+ feeTo?: string
+ name: string
+ symbol: string
+ oracle?: string
+ asset?: Token
+ collateral?: Token
+ exchangeRate?: BigInt
+ totalAsset?: Rebase
+ totalCollateralShare?: BigInt
+ totalBorrow?: Rebase
+ accrueInfo?: KashiPairAccrueInfo
+ kpi?: KashiPairKpi
+ totalAssetAmount?: BigInt
+ totalBorrowAmount?: BigInt
+ block?: BigInt
+ timestamp?: BigInt
+}
+
+export type KashiPairsByToken = {
+ token: Token
+ totalAsset: BigInt
+ totalBorrow: BigInt
+ kashiPairs: KashiPair[]
+}
+
+export type KashiPairsByTokenNew = {
+ token: Token
+ totalAsset: BigInt
+ totalBorrow: BigInt
+ kashiPairs: KashiPairNew[]
+}
diff --git a/src/features/analytics/kashi/types/KashiPairDayData.ts b/src/features/analytics/kashi/types/KashiPairDayData.ts
new file mode 100644
index 0000000000..d0c15221df
--- /dev/null
+++ b/src/features/analytics/kashi/types/KashiPairDayData.ts
@@ -0,0 +1,33 @@
+import { KashiPair } from './KashiPair'
+import { Token } from './Token'
+
+export type KashiPairDayData = {
+ id: string
+ date: number
+ pair: KashiPair
+ totalAsset?: BigInt
+ totalAssetElastic?: BigInt
+ totalAssetBase?: BigInt
+ totalCollateralShare?: BigInt
+ totalBorrow?: BigInt
+ totalBorrowElastic?: BigInt
+ totalBorrowBase?: BigInt
+ avgExchangeRate?: BigInt
+ avgUtilization?: BigInt
+ avgInterestPerSecond?: BigInt
+}
+
+export type KashiPairDayDataMap = {
+ totalAsset: BigInt
+ totalBorrow: BigInt
+ avgExchangeRate: BigInt
+ avgUtilization: BigInt
+ avgInterestPerSecond: BigInt
+ date: string
+ kashiPairs: KashiPairDayData[]
+}
+
+export type KashiPairDayDataMapsCollateral = {
+ collateral: Token
+ kashiPairsMaps: KashiPairDayDataMap[]
+}
diff --git a/src/features/analytics/kashi/types/Token.ts b/src/features/analytics/kashi/types/Token.ts
new file mode 100644
index 0000000000..457c3b47d4
--- /dev/null
+++ b/src/features/analytics/kashi/types/Token.ts
@@ -0,0 +1,26 @@
+import { Rebase } from '@sushiswap/core-sdk'
+
+export type Token = {
+ id: string
+ name: string
+ symbol: string
+ decimals: BigInt
+ totalSupply?: BigInt
+ totalSupplyElastic?: BigInt
+ totalSupplyBase?: BigInt
+ price?: BigInt
+ block?: BigInt
+ timestamp?: BigInt
+}
+
+export type TokenNew = {
+ id: string
+ name: string
+ symbol: string
+ decimals: BigInt
+ totalSupply?: BigInt
+ rebase?: Rebase
+ price?: BigInt
+ block?: BigInt
+ timestamp?: BigInt
+}
diff --git a/src/features/analytics/kashi/types/User.ts b/src/features/analytics/kashi/types/User.ts
new file mode 100644
index 0000000000..957a1c1c72
--- /dev/null
+++ b/src/features/analytics/kashi/types/User.ts
@@ -0,0 +1,5 @@
+export type User = {
+ id: string
+ block?: BigInt
+ timestamp?: BigInt
+}
diff --git a/src/features/analytics/kashi/views/KashiPairView.tsx b/src/features/analytics/kashi/views/KashiPairView.tsx
new file mode 100644
index 0000000000..37b3e7aeb7
--- /dev/null
+++ b/src/features/analytics/kashi/views/KashiPairView.tsx
@@ -0,0 +1,137 @@
+/* eslint-disable @next/next/no-img-element */
+import { i18n } from '@lingui/core'
+import { ChainId, Token } from '@sushiswap/core-sdk'
+import { CurrencyLogoArray } from 'app/components/CurrencyLogo'
+import { useKashiTokens } from 'app/features/kashi/hooks'
+import { useKashiPricesSubgraphWithLoadingIndicator } from 'app/hooks/usePricesSubgraph'
+import { useDataKashiPairWithLoadingIndicator } from 'app/services/graph/hooks/kashipairs'
+import { useActiveWeb3React } from 'app/services/web3'
+import { useRouter } from 'next/router'
+import { useEffect, useState } from 'react'
+
+import PairCard from '../components/PairCard'
+import PairInterestPerSecondDayDataChart from '../components/PairInteresetPerSecondDayDataChart'
+import PairSupplyAccruedInterestDayDataChart from '../components/PairSupplyAccruedInterestDayDataChart'
+import PairSupplyBorrowDayDataChart from '../components/PairSupplyBorrowDayDataChart'
+import PairSupplyBorrowMonthDataChart from '../components/PairSupplyBorrowMonthDataChart'
+import PairUtilizationDayDataChart from '../components/PairUtilizationDayDataChart'
+import { useAppContext } from '../context/AppContext'
+import { KashiPair } from '../types/KashiPair'
+import { KashiPairDayData, KashiPairDayDataMap } from '../types/KashiPairDayData'
+const KashiPairView = () => {
+ const { tokenUtilService, calculateService, handleLogoError } = useAppContext()
+ const [kashiPair, setKashiPair] = useState()
+ const [kashiPairDayData, setKashiPairDayData] = useState([])
+
+ const [kashiPairDayDataMonthly, setKashiPairDayDataMonthly] = useState([])
+
+ const router = useRouter()
+ const { id } = router.query
+
+ const { chainId } = useActiveWeb3React()
+ const { loading: loadingKashiPairs, data: dataKashiPairs } = useDataKashiPairWithLoadingIndicator({
+ chainId,
+ variables: { id },
+ })
+
+ const tokens = useKashiTokens()
+ const { loading: loadingPrice, data: pricesMap } = useKashiPricesSubgraphWithLoadingIndicator(Object.values(tokens))
+
+ useEffect(() => {
+ if (dataKashiPairs) {
+ setKashiPairData()
+ }
+ }, [dataKashiPairs, loadingPrice])
+
+ const setKashiPairData = async () => {
+ if (!loadingPrice) {
+ const { kashiPairs, kashiPairDayDatas }: { kashiPairs: KashiPair[]; kashiPairDayDatas: KashiPairDayData[] } =
+ dataKashiPairs
+
+ const { kashiPairs: newKashiPairs } = calculateService.calculateKashiPairPrices(kashiPairs, pricesMap)
+
+ const kashiPair = newKashiPairs[0]
+ setKashiPair(kashiPair)
+
+ const { kashiPairsMaps } = calculateService.calculateKashiPairDayDataPrices(kashiPairDayDatas, pricesMap)
+ setKashiPairDayData(kashiPairsMaps)
+
+ const { kashiPairsMaps: kashiPairsMapMonthly } = calculateService.calculateKashiPairDayDataPricesMonthly(
+ kashiPairDayDatas,
+ pricesMap
+ )
+ setKashiPairDayDataMonthly(kashiPairsMapMonthly)
+ }
+ }
+
+ // let asset = undefined
+ // let collateral = undefined
+ const kashiAsset = kashiPair?.asset
+ const kashiCollateral = kashiPair?.collateral
+ // if (kashiAsset !== undefined && kashiCollateral !== undefined && chainId !== undefined) {
+ const asset = new Token(
+ chainId ?? ChainId.ETHEREUM,
+ kashiAsset?.id ?? '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ Number(kashiAsset?.decimals ?? 0),
+ kashiAsset?.symbol,
+ kashiAsset?.name
+ )
+ const collateral = new Token(
+ chainId ?? ChainId.ETHEREUM,
+ kashiCollateral?.id ?? '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ Number(kashiCollateral?.decimals ?? 0),
+ kashiCollateral?.symbol,
+ kashiCollateral?.name
+ )
+ // }
+ return (
+ <>
+
+
+ {!kashiPair && asset !== undefined && collateral !== undefined ? (
+
+ ) : (
+
+
+
+
+
+
+ {tokenUtilService.symbol(kashiPair?.asset?.symbol)}/
+ {tokenUtilService.symbol(kashiPair?.collateral?.symbol)}
+
+
+
+ )}
+
+
+
+ >
+ )
+}
+
+export default KashiPairView
diff --git a/src/features/analytics/kashi/views/KashiPairsView.tsx b/src/features/analytics/kashi/views/KashiPairsView.tsx
new file mode 100644
index 0000000000..e2db5196b2
--- /dev/null
+++ b/src/features/analytics/kashi/views/KashiPairsView.tsx
@@ -0,0 +1,177 @@
+import { useKashiTokens } from 'app/features/kashi/hooks'
+import { useKashiPricesSubgraphWithLoadingIndicator } from 'app/hooks/usePricesSubgraph'
+import {
+ useDataKashiPairsDayDataWithLoadingIndicator,
+ useDataKashiPairsWithLoadingIndicator,
+} from 'app/services/graph/hooks/kashipairs'
+import { useActiveWeb3React } from 'app/services/web3'
+import { BigNumber } from 'ethers'
+import { useEffect, useState } from 'react'
+
+import PairMarketTable from '../components/PairMarketTable'
+import TotalCard from '../components/TotalCard'
+import TotalCompareChart from '../components/TotalCompareChart'
+import TotalDayDataChart from '../components/TotalDayDataChart'
+import { useAppContext } from '../context/AppContext'
+import { KashiPairNew } from '../types/KashiPair'
+import { KashiPairDayData, KashiPairDayDataMap } from '../types/KashiPairDayData'
+
+const KashiPairsView = () => {
+ const { chainId } = useActiveWeb3React()
+ const { loading: loadingKashiPairs, data: dataKashiPairs } = useDataKashiPairsWithLoadingIndicator({ chainId })
+
+ const { loading: loadingKashiPairsDayData0, data: dataKashiPairsDayData0 } =
+ useDataKashiPairsDayDataWithLoadingIndicator({
+ chainId,
+ variables: { skip: 0 },
+ })
+
+ const { loading: loadingKashiPairsDayData1, data: dataKashiPairsDayData1 } =
+ useDataKashiPairsDayDataWithLoadingIndicator({
+ chainId,
+ variables: { skip: 1000 },
+ })
+
+ const [kashiPairsDayData, setKashiPairsDayData] = useState([])
+
+ const [calculating, setCalculating] = useState(true)
+ //const [pricesMap, setPricesMap] = useState<{ [key: string]: BigInt }>({})
+ const [kashiPairs, setKashiPairs] = useState([] as KashiPairNew[])
+ const { calculateService } = useAppContext()
+
+ const [totalAssetsAmount, setTotalAssetsAmount] = useState(BigInt(0))
+ const [totalBorrowsAmount, setTotalBorrowsAmount] = useState(BigInt(0))
+
+ const [top3MarketsBySupply, setTop3MarketsBySupply] = useState([])
+ const [top3MarketsByAsset, setTop3MarketsByAsset] = useState([])
+ const [top3MarketsByBorrow, setTop3MarketsByBorrow] = useState([])
+
+ const loading = calculating || loadingKashiPairs
+ const loadingDayData = loading || loadingKashiPairsDayData0 || loadingKashiPairsDayData1
+
+ const tokens = useKashiTokens()
+ const { loading: loadingPrice, data: pricesMap } = useKashiPricesSubgraphWithLoadingIndicator(Object.values(tokens))
+
+ useEffect(() => {
+ if (dataKashiPairs) {
+ if (dataKashiPairs.kashiPairs) {
+ setKashiPairsData(dataKashiPairs.kashiPairs)
+ }
+ }
+ }, [dataKashiPairs, loadingPrice])
+
+ const setKashiPairsData = async (kashiPairsData: KashiPairNew[]) => {
+ if (!loadingPrice) {
+ const {
+ totalAsset: totalAssetsValue,
+ totalBorrow: totalBorrowsValue,
+ kashiPairs: newKashiPairs,
+ } = calculateService.calculateKashiPairPricesNew(kashiPairsData, pricesMap)
+
+ const sortedKashiPairsBySupply = [...newKashiPairs].sort((a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalAssetAmount)
+ .add(BigNumber.from(a.totalBorrowAmount))
+ .lte(BigNumber.from(b.totalAssetAmount).add(BigNumber.from(b.totalBorrowAmount)))
+ ? 1
+ : -1
+ )
+
+ const sortedKashiPairsByAsset = [...newKashiPairs].sort((a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalAssetAmount).lte(BigNumber.from(b.totalAssetAmount)) ? 1 : -1
+ )
+
+ const sortedKashiPairsByBorrow = [...newKashiPairs].sort((a: KashiPairNew, b: KashiPairNew) =>
+ BigNumber.from(a.totalBorrowAmount).lte(BigNumber.from(b.totalBorrowAmount)) ? 1 : -1
+ )
+
+ setCalculating(false)
+
+ setTotalAssetsAmount(totalAssetsValue.toBigInt())
+ setTotalBorrowsAmount(totalBorrowsValue.toBigInt())
+
+ setTop3MarketsBySupply(
+ sortedKashiPairsBySupply.slice(0, sortedKashiPairsBySupply.length < 3 ? sortedKashiPairsBySupply.length : 3)
+ )
+ setTop3MarketsByAsset(
+ sortedKashiPairsByAsset.slice(0, sortedKashiPairsByAsset.length < 3 ? sortedKashiPairsByAsset.length : 3)
+ )
+ setTop3MarketsByBorrow(
+ sortedKashiPairsByBorrow.slice(0, sortedKashiPairsByBorrow.length < 3 ? sortedKashiPairsByBorrow.length : 3)
+ )
+
+ setKashiPairs(newKashiPairs)
+ }
+ }
+
+ useEffect(() => {}, [kashiPairs])
+
+ useEffect(() => {
+ if (!loadingKashiPairs && !calculating && !loadingKashiPairsDayData0 && !loadingKashiPairsDayData1) {
+ const dataKashiPairsDayDataMap = [dataKashiPairsDayData0, dataKashiPairsDayData1]
+
+ const dataKashiPairsDayData = dataKashiPairsDayDataMap.reduce(
+ (prev: KashiPairDayData[], current?: { kashiPairDayDatas?: KashiPairDayData[] }) => [
+ ...prev,
+ ...(current && current.kashiPairDayDatas ? current.kashiPairDayDatas : []),
+ ],
+ []
+ )
+ const { kashiPairsMaps: kashiPairsMap } = calculateService.calculateKashiPairDayDataPrices(
+ dataKashiPairsDayData,
+ pricesMap
+ )
+ setKashiPairsDayData(kashiPairsMap)
+ }
+ }, [loadingKashiPairs, calculating, loadingKashiPairsDayData0, loadingKashiPairsDayData1])
+ return (
+ <>
+ {/* */}
+
+
+
+
+
+
+
+ >
+ )
+}
+export default KashiPairsView
diff --git a/src/features/analytics/kashi/views/KashiTokenView.tsx b/src/features/analytics/kashi/views/KashiTokenView.tsx
new file mode 100644
index 0000000000..5c92337464
--- /dev/null
+++ b/src/features/analytics/kashi/views/KashiTokenView.tsx
@@ -0,0 +1,181 @@
+/* eslint-disable @next/next/no-img-element */
+import { ChainId, Token as TokenSDK } from '@sushiswap/core-sdk'
+import { CurrencyLogo, CurrencyLogoArray } from 'app/components/CurrencyLogo'
+import { useKashiTokens } from 'app/features/kashi/hooks'
+import { useKashiPricesSubgraphWithLoadingIndicator } from 'app/hooks/usePricesSubgraph'
+import {
+ useDataKashiTokensDayDataWithLoadingIndicator,
+ useDataKashiTokenWithLoadingIndicator,
+} from 'app/services/graph/hooks/kashipairs'
+import { useActiveWeb3React } from 'app/services/web3'
+import { useRouter } from 'next/router'
+import { useEffect, useState } from 'react'
+
+import PairCollateralPieChart from '../components/PairCollateralPieChart'
+import PairSupplyBorrowDayDataChart from '../components/PairSupplyBorrowDayDataChart'
+import TokenCard from '../components/TokenCard'
+import { useAppContext } from '../context/AppContext'
+import { KashiPairNew } from '../types/KashiPair'
+import { KashiPairDayDataMap, KashiPairDayDataMapsCollateral } from '../types/KashiPairDayData'
+import { Token, TokenNew } from '../types/Token'
+
+const KashiTokenView = () => {
+ const [token, setToken] = useState()
+ const [totalAsset, setTotalAsset] = useState(BigInt(0))
+ const [totalBorrow, setTotalBorrow] = useState(BigInt(0))
+
+ const [kashiPairs, setKashiPairs] = useState([])
+ const [kashiPairDayDataMaps, setKashiPairDayDataMaps] = useState([])
+
+ const [kashiPairDayDataMapsCollaterals, setKashiPairDayDataMapsCollaterals] = useState<
+ KashiPairDayDataMapsCollateral[]
+ >([])
+
+ const { calculateService, tokenUtilService } = useAppContext()
+
+ const router = useRouter()
+ const { id } = router.query
+ const { chainId } = useActiveWeb3React()
+ const { loading: loadingDataToken, data: dataToken } = useDataKashiTokenWithLoadingIndicator({
+ chainId,
+ variables: { id },
+ })
+
+ const pairIds = kashiPairs.map((kashiPair) => kashiPair.id)
+
+ const { loading: loadingKashiPairDayData, data: dataKashiPairDayData } =
+ useDataKashiTokensDayDataWithLoadingIndicator({
+ chainId,
+ variables: { pairIds },
+ })
+
+ const tokens = useKashiTokens()
+ const { loading: loadingPrice, data: pricesMap } = useKashiPricesSubgraphWithLoadingIndicator(Object.values(tokens))
+
+ useEffect(() => {
+ if (dataToken) {
+ setTokenData()
+ }
+ }, [dataToken, loadingPrice])
+
+ useEffect(() => {
+ if (!loadingPrice) {
+ const pricesMapKeys = Object.keys(pricesMap)
+ if (pricesMapKeys.length > 0 && dataKashiPairDayData && dataKashiPairDayData.kashiPairDayDatas.length > 0) {
+ const kashiPairDayDataMapsCollaterals = calculateService.calculateKashiPairDayDataPricesByCollateral(
+ dataKashiPairDayData.kashiPairDayDatas,
+ pricesMap
+ )
+
+ const { kashiPairsMaps } = calculateService.calculateKashiPairDayDataPrices(
+ dataKashiPairDayData.kashiPairDayDatas,
+ pricesMap
+ )
+ setKashiPairDayDataMapsCollaterals(kashiPairDayDataMapsCollaterals)
+ setKashiPairDayDataMaps(kashiPairsMaps)
+ }
+ }
+ }, [dataKashiPairDayData])
+
+ const setTokenData = async () => {
+ const { tokens, kashiPairs }: { tokens: TokenNew[]; kashiPairs: KashiPairNew[] } = dataToken
+
+ const { tokens: newTokens } = calculateService.calculateTokenPricesNew(tokens, pricesMap)
+
+ const token = newTokens[0]
+ setToken(token)
+
+ const {
+ kashiPairs: newKashiPairs,
+ totalAsset,
+ totalBorrow,
+ } = calculateService.calculateKashiPairPricesNew(kashiPairs, pricesMap)
+
+ setTotalAsset(totalAsset.toBigInt())
+ setTotalBorrow(totalBorrow.toBigInt())
+ setKashiPairs(newKashiPairs)
+ }
+
+ const token0 = new TokenSDK(
+ chainId ?? ChainId.ETHEREUM,
+ token?.id ?? '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ Number(token?.decimals ?? 0),
+ token?.symbol,
+ token?.name
+ )
+ return (
+ <>
+
+
+ {!token ? (
+
+ ) : (
+
+
+
+
+
+
{tokenUtilService.symbol(token?.symbol)}
+
+
+ )}
+
+
+
+
+
+
+
+ {kashiPairDayDataMapsCollaterals
+ ?.filter((value) => value.kashiPairsMaps.length > 0)
+ .map((value) => {
+ const { collateral, kashiPairsMaps } = value
+ const token1 = new TokenSDK(
+ ChainId.ETHEREUM,
+ collateral?.id ?? '',
+ Number(collateral?.decimals ?? 0),
+ collateral?.symbol,
+ collateral?.name
+ )
+ return (
+
+
+
+
+
+
+
+ {tokenUtilService.symbol(token?.symbol)}/{tokenUtilService.symbol(collateral?.symbol)}
+
+
+
+
+
+ )
+ })}
+
+
+ >
+ )
+}
+
+export default KashiTokenView
diff --git a/src/features/analytics/kashi/views/KashiTokensView.tsx b/src/features/analytics/kashi/views/KashiTokensView.tsx
new file mode 100644
index 0000000000..da5b7eba6c
--- /dev/null
+++ b/src/features/analytics/kashi/views/KashiTokensView.tsx
@@ -0,0 +1,128 @@
+import { useKashiTokens } from 'app/features/kashi/hooks'
+import { useKashiPricesSubgraphWithLoadingIndicator } from 'app/hooks/usePricesSubgraph'
+import { useDataKashiTokensWithLoadingIndicator } from 'app/services/graph/hooks/kashipairs'
+import { useActiveWeb3React } from 'app/services/web3'
+import { BigNumber } from 'ethers'
+import { useEffect, useState } from 'react'
+
+import TokenMarketTable from '../components/TokenMarketTable'
+import TotakTokenCard from '../components/TotalTokenCard'
+import { useAppContext } from '../context/AppContext'
+import { KashiPairsByTokenNew } from '../types/KashiPair'
+
+const KashiTokensView = () => {
+ const { chainId } = useActiveWeb3React()
+ const { loading: loadingToken, data: dataKashiPairs } = useDataKashiTokensWithLoadingIndicator({ chainId })
+
+ const [calculating, setCalculating] = useState(true)
+ const [totalAsset, setTotalAsset] = useState(BigInt(0))
+ const [totalBorrow, setTotalBorrow] = useState(BigInt(0))
+ const [top3MarketsBySupply, setTop3MarketsBySupply] = useState([])
+ const [top3MarketsByAsset, setTop3MarketsByAsset] = useState([])
+ const [top3MarketsByBorrow, setTop3MarketsByBorrow] = useState([])
+
+ const [kashiPairsByTokens, setKashiPairsByTokens] = useState([])
+ const { calculateService } = useAppContext()
+ const loading = loadingToken || calculating
+
+ const tokens = useKashiTokens()
+ const { loading: loadingPrice, data: pricesMap } = useKashiPricesSubgraphWithLoadingIndicator(Object.values(tokens))
+
+ useEffect(() => {
+ if (dataKashiPairs) {
+ if (dataKashiPairs.kashiPairs) {
+ setDataKashiPairs()
+ }
+ }
+ }, [dataKashiPairs, loadingPrice])
+
+ const setDataKashiPairs = async () => {
+ if (!loadingPrice) {
+ const { kashiPairs } = dataKashiPairs
+
+ const { kashiPairsByTokens, totalAsset, totalBorrow } = calculateService.calculateKashiPairPricesGroupByAssetNew(
+ kashiPairs,
+ pricesMap
+ )
+ setCalculating(false)
+ kashiPairsByTokens.sort((a, b) =>
+ BigNumber.from(a.totalAsset)
+ .add(BigNumber.from(a.totalBorrow))
+ .gte(BigNumber.from(b.totalAsset).add(BigNumber.from(b.totalBorrow)))
+ ? -1
+ : 1
+ )
+
+ const kashiPairsByTokensSortedByAsset = [...kashiPairsByTokens].sort((a, b) =>
+ a.totalAsset > b.totalAsset ? -1 : 1
+ )
+
+ const kashiPairsByTokensSortedByBorrow = [...kashiPairsByTokens].sort((a, b) =>
+ a.totalBorrow > b.totalBorrow ? -1 : 1
+ )
+
+ setTop3MarketsBySupply(kashiPairsByTokens.slice(0, kashiPairsByTokens.length < 3 ? kashiPairsByTokens.length : 3))
+ setTop3MarketsByAsset(
+ kashiPairsByTokensSortedByAsset.slice(
+ 0,
+ kashiPairsByTokensSortedByAsset.length < 3 ? kashiPairsByTokensSortedByAsset.length : 3
+ )
+ )
+ setTop3MarketsByBorrow(
+ kashiPairsByTokensSortedByBorrow.slice(
+ 0,
+ kashiPairsByTokensSortedByBorrow.length < 3 ? kashiPairsByTokensSortedByBorrow.length : 3
+ )
+ )
+
+ setKashiPairsByTokens(kashiPairsByTokens)
+ setTotalAsset(totalAsset.toBigInt())
+ setTotalBorrow(totalBorrow.toBigInt())
+ }
+ }
+
+ return (
+ <>
+ {/* */}
+
+
+
+
+
+
+
+
+ >
+ )
+}
+export default KashiTokensView
diff --git a/src/features/analytics/tokens/TokenList.tsx b/src/features/analytics/tokens/TokenList.tsx
deleted file mode 100644
index c25565a7a1..0000000000
--- a/src/features/analytics/tokens/TokenList.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import { getAddress } from '@ethersproject/address'
-import { Token as CoreToken } from '@sushiswap/core-sdk'
-import { CurrencyLogo } from 'app/components/CurrencyLogo'
-import LineGraph from 'app/components/LineGraph'
-import Table, { Column } from 'app/components/Table'
-import { formatNumber, formatPercent } from 'app/functions'
-import { useRouter } from 'next/router'
-import React from 'react'
-
-import ColoredNumber from '../ColoredNumber'
-
-type TokenListColumnType = 'name' | 'price' | 'liquidity' | 'priceChange' | 'volumeChange' | 'lastWeekGraph'
-
-interface Token {
- token: {
- id: string
- symbol: string
- }
- liquidity: number
- volume1d: number
- volume1w: number
- price: number
- change1d: number
- change1w: number
-}
-
-interface TokenListProps {
- tokens: Token[]
- enabledColumns?: TokenListColumnType[]
-}
-
-interface TokenListNameProps {
- token: {
- id: string
- symbol: string
- decimals: number
- name: string
- }
-}
-
-function TokenListName({ token }: TokenListNameProps): JSX.Element {
- const router = useRouter()
- const chainId = Number(router.query.chainId)
- const currency = new CoreToken(chainId, getAddress(token?.id), token?.decimals || 18, token?.symbol, token?.name)
- return (
- <>
-
- {/*@ts-ignore TYPE NEEDS FIXING*/}
-
-
{token.symbol}
-
- >
- )
-}
-
-export default function TokenList({
- tokens,
- enabledColumns = Object.keys(TokenListColumns) as TokenListColumnType[],
-}: TokenListProps): JSX.Element {
- const router = useRouter()
- const chainId = Number(router.query.chainId)
- const columns = React.useMemo(() => enabledColumns.map((col) => TokenListColumns[col]), [enabledColumns])
- return (
- <>
- {tokens && (
-
- columns={columns}
- data={tokens}
- defaultSortBy={{ id: 'liquidity', desc: true }}
- link={{ href: `/analytics/${chainId}/tokens/`, id: 'token.id' }}
- />
- )}
- >
- )
-}
-
-const TokenListColumns: Record = {
- name: {
- Header: 'Name',
- accessor: 'token',
- Cell: (props) => ,
- disableSortBy: true,
- align: 'left',
- },
- price: {
- Header: 'Price',
- accessor: 'price',
- Cell: (props) => formatNumber(props.value, true, undefined, 2),
- align: 'right',
- },
- liquidity: {
- Header: 'Liquidity',
- accessor: 'liquidity',
- Cell: (props) => formatNumber(props.value, true, false),
- align: 'right',
- },
- priceChange: {
- Header: 'Daily / Weekly % Change',
- accessor: (row) => (
-
-
-
- {row.change1w > 0 && '+'}
- {formatPercent(row.change1w)}
-
-
- ),
- align: 'right',
- sortType: (a, b) => a.original.change1d - b.original.change1d,
- },
- volumeChange: {
- Header: 'Daily / Weekly Volume',
- accessor: (row) => (
-
-
{formatNumber(row.volume1d, true, false, 2)}
-
{formatNumber(row.volume1w, true, false, 2)}
-
- ),
- align: 'right',
- },
- lastWeekGraph: {
- Header: 'Last Week',
- accessor: 'graph',
- Cell: (props) => (
-
-
- = 0 ? '#00ff4f' : '#ff3838' }} />
-
-
- ),
- disableSortBy: true,
- align: 'right',
- },
-}
diff --git a/src/hooks/usePricesSubgraph.ts b/src/hooks/usePricesSubgraph.ts
new file mode 100644
index 0000000000..73707f16fc
--- /dev/null
+++ b/src/hooks/usePricesSubgraph.ts
@@ -0,0 +1,88 @@
+import { Currency, CurrencyAmount, USD } from '@sushiswap/core-sdk'
+import { useNativePrice, useTokens, useTridentTokens } from 'app/services/graph'
+import { useActiveWeb3React } from 'app/services/web3'
+import { useMemo } from 'react'
+
+export function useKashiPricesSubgraph(
+ currencies?: Currency[],
+ useTrident = false
+): { [symbol: string]: BigInt } | undefined {
+ const { chainId } = useActiveWeb3React()
+
+ const stablecoin = chainId ? CurrencyAmount.fromRawAmount(USD[chainId], 0).currency : undefined
+
+ const { data: ethPrice } = useNativePrice({ chainId })
+ const tokensLegacy = useTokens({
+ chainId,
+ variables: { where: { id_in: currencies?.map((currency) => currency.wrapped.address.toLowerCase()) } },
+ shouldFetch: currencies && currencies?.length > 0,
+ }) as any[] | undefined
+ const { data: tokensTrident } = useTridentTokens({
+ chainId,
+ variables: {
+ where: { id_in: currencies?.map((currency) => currency.wrapped.address.toLowerCase()) },
+ },
+ shouldFetch: currencies && currencies?.length > 0,
+ })
+
+ return useMemo(() => {
+ if (!currencies || currencies.length === 0 || !stablecoin || !(tokensLegacy || tokensTrident)) {
+ return undefined
+ }
+
+ const prices: { [symbol: string]: BigInt } = {}
+
+ currencies.map((currency) => {
+ let price: number | undefined = undefined
+
+ const tokenLegacy = tokensLegacy?.find((token) => token.id === currency.wrapped.address.toLowerCase())
+ const tokenTrident = tokensTrident?.find((token) => token.id === currency.wrapped.address.toLowerCase())
+
+ // handle usdc
+ if (currency?.wrapped.equals(stablecoin)) {
+ if (currency.wrapped.symbol) {
+ prices[currency.wrapped.symbol] = BigInt('100000000')
+ }
+ return
+ }
+
+ if (tokenLegacy && tokenTrident) {
+ if (tokenLegacy.liquidity > tokenTrident.kpi.liquidity) {
+ price = ethPrice * tokenLegacy.derivedETH
+ } else {
+ price = tokenTrident.price.derivedUSD
+ }
+ } else if (ethPrice && tokenLegacy) {
+ price = ethPrice * tokenLegacy.derivedETH
+ } else if (tokenTrident) {
+ price = tokenTrident.price.derivedUSD
+ }
+
+ if (price !== undefined && price !== 0) {
+ const base = 10 ** (String(price).split('.')?.[1]?.length ?? 0)
+ const quote = Math.floor(price * 10 ** 8)
+ try {
+ if (currency.wrapped.symbol) {
+ prices[currency.wrapped.symbol] = BigInt(quote)
+ //prices[currency.wrapped.symbol] = (quote * (base * 10 ** currency.decimals)) / 10 ** stablecoin.decimals
+ }
+ } catch {}
+ }
+ })
+
+ return prices
+ }, [currencies, stablecoin, tokensLegacy, tokensTrident, ethPrice])
+}
+
+export function useKashiPricesSubgraphWithLoadingIndicator(currencies?: Currency[], useTrident = false) {
+ // Bandaid solution for now, might become permanent
+ const data = useKashiPricesSubgraph(currencies, useTrident)
+ return useMemo(() => {
+ if (!data) return { data: {}, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: {}, loading: true }
+ }
+ }, [data])
+}
diff --git a/src/pages/analytics/kashi/pairs/[id].tsx b/src/pages/analytics/kashi/pairs/[id].tsx
new file mode 100644
index 0000000000..f83345217f
--- /dev/null
+++ b/src/pages/analytics/kashi/pairs/[id].tsx
@@ -0,0 +1,32 @@
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import Typography from 'app/components/Typography'
+import { AnalyticsKashiAppContextProvider } from 'app/features/analytics/kashi/context/AppContext'
+import KashiPairView from 'app/features/analytics/kashi/views/KashiPairView'
+import { TridentBody, TridentHeader } from 'app/layouts/Trident'
+import React, { FC } from 'react'
+
+const AnalyticsKashiPairPage: FC = () => {
+ const { i18n } = useLingui()
+ return (
+ <>
+
+
+
+ {i18n._(t`Kashi Analytics.`)}
+
+
+ {i18n._(t`Lend and borrow assets in Kashi isolated risk markets`)}
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default AnalyticsKashiPairPage
diff --git a/src/pages/analytics/kashi/pairs/index.tsx b/src/pages/analytics/kashi/pairs/index.tsx
new file mode 100644
index 0000000000..daad8694c8
--- /dev/null
+++ b/src/pages/analytics/kashi/pairs/index.tsx
@@ -0,0 +1,32 @@
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import Typography from 'app/components/Typography'
+import { AnalyticsKashiAppContextProvider } from 'app/features/analytics/kashi/context/AppContext'
+import KashiPairsView from 'app/features/analytics/kashi/views/KashiPairsView'
+import { TridentBody, TridentHeader } from 'app/layouts/Trident'
+import React, { FC } from 'react'
+
+const AnalyticsKashiPairsPage: FC = () => {
+ const { i18n } = useLingui()
+ return (
+ <>
+
+
+
+ {i18n._(t`Kashi Analytics.`)}
+
+
+ {i18n._(t`Lend and borrow assets in Kashi isolated risk markets`)}
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default AnalyticsKashiPairsPage
diff --git a/src/pages/analytics/kashi/tokens/[id].tsx b/src/pages/analytics/kashi/tokens/[id].tsx
new file mode 100644
index 0000000000..e89ec7ce05
--- /dev/null
+++ b/src/pages/analytics/kashi/tokens/[id].tsx
@@ -0,0 +1,32 @@
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import Typography from 'app/components/Typography'
+import { AnalyticsKashiAppContextProvider } from 'app/features/analytics/kashi/context/AppContext'
+import KashiTokenView from 'app/features/analytics/kashi/views/KashiTokenView'
+import { TridentBody, TridentHeader } from 'app/layouts/Trident'
+import React, { FC } from 'react'
+
+const AnalyticsKashiPairPage: FC = () => {
+ const { i18n } = useLingui()
+ return (
+ <>
+
+
+
+ {i18n._(t`Kashi Analytics.`)}
+
+
+ {i18n._(t`Lend and borrow assets in Kashi isolated risk markets`)}
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default AnalyticsKashiPairPage
diff --git a/src/pages/analytics/kashi/tokens/index.tsx b/src/pages/analytics/kashi/tokens/index.tsx
new file mode 100644
index 0000000000..3c41fa3677
--- /dev/null
+++ b/src/pages/analytics/kashi/tokens/index.tsx
@@ -0,0 +1,32 @@
+import { t } from '@lingui/macro'
+import { useLingui } from '@lingui/react'
+import Typography from 'app/components/Typography'
+import { AnalyticsKashiAppContextProvider } from 'app/features/analytics/kashi/context/AppContext'
+import KashiTokensView from 'app/features/analytics/kashi/views/KashiTokensView'
+import { TridentBody, TridentHeader } from 'app/layouts/Trident'
+import React, { FC } from 'react'
+
+const AnalyticsKashiTokensPage: FC = () => {
+ const { i18n } = useLingui()
+ return (
+ <>
+
+
+
+ {i18n._(t`Kashi Token Analytics.`)}
+
+
+ {i18n._(t`Lend and borrow assets in Kashi isolated risk markets`)}
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default AnalyticsKashiTokensPage
diff --git a/src/services/graph/fetchers/index.ts b/src/services/graph/fetchers/index.ts
index 478f29ec9b..c3632a470c 100644
--- a/src/services/graph/fetchers/index.ts
+++ b/src/services/graph/fetchers/index.ts
@@ -3,6 +3,7 @@ export * from './bentobox'
export * from './blocks'
export * from './exchange'
export * from './kashi'
+export * from './kashipairs'
export * from './masterchef'
export * from './pager'
export * from './pools'
diff --git a/src/services/graph/fetchers/kashipairs.ts b/src/services/graph/fetchers/kashipairs.ts
new file mode 100644
index 0000000000..84563d8b4d
--- /dev/null
+++ b/src/services/graph/fetchers/kashipairs.ts
@@ -0,0 +1,70 @@
+import { ChainId } from '@sushiswap/core-sdk'
+import { GRAPH_HOST } from 'app/services/graph/constants'
+
+import {
+ dataKashiPairsQuery,
+ kashiPairQuery,
+ kashiPairsDayDataQuery,
+ kashiTokenDayDataQuery,
+ kashiTokenQuery,
+ kashiTokensQuery,
+} from '../queries'
+import { pager, pagerRequestOnce } from './pager'
+
+const KASHI = {
+ [ChainId.ETHEREUM]: 'sushiswap/bentobox',
+ [ChainId.ARBITRUM]: 'sushiswap/arbitrum-bentobox',
+ [ChainId.FANTOM]: 'sushiswap/fantom-bentobox',
+ [ChainId.BSC]: 'sushiswap/bsc-bentobox',
+ [ChainId.MATIC]: 'sushiswap/matic-bentobox',
+}
+
+const KASHINew = {
+ [ChainId.ARBITRUM]: 'sushiswap/kashi-arbitrum',
+ [ChainId.MATIC]: 'sushiswap/kashi-polygon',
+}
+
+// @ts-ignore TYPE NEEDS FIXING
+const fetcher = async (chainId = ChainId.ETHEREUM, query, variables = undefined) =>
+ // @ts-ignore TYPE NEEDS FIXING
+ pager(`${GRAPH_HOST[chainId]}/subgraphs/name/${KASHI[chainId]}`, query, variables)
+
+// @ts-ignore TYPE NEEDS FIXING
+const fetcherNew = async (chainId = ChainId.ETHEREUM, query, variables = undefined) =>
+ // @ts-ignore TYPE NEEDS FIXING
+ pager(`${GRAPH_HOST[chainId]}/subgraphs/name/${KASHINew[chainId]}`, query, variables)
+
+// @ts-ignore TYPE NEEDS FIXING
+const fetcherWithLimit = async (chainId = ChainId.ETHEREUM, query, variables = undefined) =>
+ // @ts-ignore TYPE NEEDS FIXING
+ pagerRequestOnce(`${GRAPH_HOST[chainId]}/subgraphs/name/${KASHI[chainId]}`, query, variables)
+
+export const getDataKashiPair = async (chainId = ChainId.ETHEREUM, variables = undefined) => {
+ const data = await fetcher(chainId, kashiPairQuery, variables)
+ return data
+}
+
+export const getDataKashiPairs = async (chainId = ChainId.ETHEREUM, variables = undefined) => {
+ const data = await fetcherNew(chainId, dataKashiPairsQuery)
+ return data
+}
+
+export const getDataKashiPairsDayData = async (chainId = ChainId.ETHEREUM, variables = undefined) => {
+ const data = await fetcherWithLimit(chainId, kashiPairsDayDataQuery, variables)
+ return data
+}
+
+export const getDataKashiToken = async (chainId = ChainId.ETHEREUM, variables = undefined) => {
+ const data = await fetcherNew(chainId, kashiTokenQuery, variables)
+ return data
+}
+
+export const getDataKashiTokens = async (chainId = ChainId.ETHEREUM, variables = undefined) => {
+ const data = await fetcherNew(chainId, kashiTokensQuery)
+ return data
+}
+
+export const getDataTokenPairsDayData = async (chainId = ChainId.ETHEREUM, variables = undefined) => {
+ const data = await fetcherWithLimit(chainId, kashiTokenDayDataQuery, variables)
+ return data
+}
diff --git a/src/services/graph/fetchers/pager.ts b/src/services/graph/fetchers/pager.ts
index 4560482ec0..610386ad43 100644
--- a/src/services/graph/fetchers/pager.ts
+++ b/src/services/graph/fetchers/pager.ts
@@ -28,3 +28,16 @@ export async function pager(endpoint, query, variables = {}) {
}
return data
}
+
+// @ts-ignore TYPE NEEDS FIXING
+export async function pagerRequestOnce(endpoint, query, variables = {}) {
+ if (endpoint.includes('undefined')) return {}
+
+ let data: any = {}
+ const req = await request(endpoint, query, variables)
+
+ Object.keys(req).forEach((key) => {
+ data[key] = data[key] ? [...data[key], ...req[key]] : req[key]
+ })
+ return data
+}
diff --git a/src/services/graph/hooks/kashipairs.ts b/src/services/graph/hooks/kashipairs.ts
new file mode 100644
index 0000000000..e3c72c7dfb
--- /dev/null
+++ b/src/services/graph/hooks/kashipairs.ts
@@ -0,0 +1,195 @@
+import { ChainId } from '@sushiswap/core-sdk'
+import stringify from 'fast-json-stable-stringify'
+import { useMemo } from 'react'
+import useSWR from 'swr'
+
+import {
+ getDataKashiPair,
+ getDataKashiPairs,
+ getDataKashiPairsDayData,
+ getDataKashiToken,
+ getDataKashiTokens,
+ getDataTokenPairsDayData,
+} from '../fetchers'
+import { GraphProps } from '../interfaces'
+
+export function useDataKashiPair({
+ chainId = ChainId.ETHEREUM,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const data = useSWR(
+ shouldFetch ? () => ['dataKashiPair', chainId, stringify(variables)] : null,
+ // @ts-ignore
+ (_, chainId) => getDataKashiPair(chainId, variables),
+ swrConfig
+ )
+
+ return data
+}
+
+export function useDataKashiPairWithLoadingIndicator({
+ chainId = ChainId.ETHEREUM,
+ variables = undefined,
+}: GraphProps) {
+ const { data } = useDataKashiPair({ chainId, variables })
+ return useMemo(() => {
+ if (!data) return { data: undefined, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: undefined, loading: true }
+ }
+ }, [data])
+}
+
+export function useDataKashiPairs({
+ chainId = ChainId.ETHEREUM,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const data = useSWR(
+ shouldFetch ? () => ['dataKashiPairs', chainId, stringify(variables)] : null,
+ // @ts-ignore
+ (_, chainId) => getDataKashiPairs(chainId, variables),
+ swrConfig
+ )
+
+ return data
+}
+
+export function useDataKashiPairsWithLoadingIndicator({ chainId = ChainId.ETHEREUM }) {
+ const { data } = useDataKashiPairs({ chainId })
+ return useMemo(() => {
+ if (!data) return { data: undefined, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: undefined, loading: true }
+ }
+ }, [data])
+}
+
+export function useDataKashiPairsDayData({
+ chainId = ChainId.ETHEREUM,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const data = useSWR(
+ shouldFetch ? () => ['dataKashiPairsDayData', chainId, stringify(variables)] : null,
+ // @ts-ignore
+ (_, chainId) => getDataKashiPairsDayData(chainId, variables),
+ swrConfig
+ )
+
+ return data
+}
+
+export function useDataKashiPairsDayDataWithLoadingIndicator({
+ chainId = ChainId.ETHEREUM,
+ variables = undefined,
+}: GraphProps) {
+ // Bandaid solution for now, might become permanent
+ const { data } = useDataKashiPairsDayData({ chainId, variables })
+ return useMemo(() => {
+ if (!data) return { data: undefined, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: undefined, loading: true }
+ }
+ }, [data])
+}
+
+export function useDataKashiToken({
+ chainId = ChainId.ETHEREUM,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const data = useSWR(
+ shouldFetch ? () => ['dataKashiPairs', chainId, stringify(variables)] : null,
+ // @ts-ignore
+ (_, chainId) => getDataKashiToken(chainId, variables),
+ swrConfig
+ )
+
+ return data
+}
+
+export function useDataKashiTokenWithLoadingIndicator({
+ chainId = ChainId.ETHEREUM,
+ variables = undefined,
+}: GraphProps) {
+ const { data } = useDataKashiToken({ chainId, variables })
+ return useMemo(() => {
+ if (!data) return { data: undefined, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: undefined, loading: true }
+ }
+ }, [data])
+}
+
+export function useDataKashiTokens({
+ chainId = ChainId.ETHEREUM,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const data = useSWR(
+ shouldFetch ? () => ['dataKashiPairs', chainId, stringify(variables)] : null,
+ // @ts-ignore
+ (_, chainId) => getDataKashiTokens(chainId, variables),
+ swrConfig
+ )
+
+ return data
+}
+
+export function useDataKashiTokensWithLoadingIndicator({ chainId = ChainId.ETHEREUM }) {
+ const { data } = useDataKashiTokens({ chainId })
+ return useMemo(() => {
+ if (!data) return { data: undefined, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: undefined, loading: true }
+ }
+ }, [data])
+}
+
+export function useDataKashiTokenDayData({
+ chainId = ChainId.ETHEREUM,
+ variables,
+ shouldFetch = true,
+ swrConfig = undefined,
+}: GraphProps) {
+ const data = useSWR(
+ shouldFetch ? () => ['dataKashiTokenDayData', chainId, stringify(variables)] : null,
+ // @ts-ignore
+ (_, chainId) => getDataTokenPairsDayData(chainId, variables),
+ swrConfig
+ )
+
+ return data
+}
+
+export function useDataKashiTokensDayDataWithLoadingIndicator({
+ chainId = ChainId.ETHEREUM,
+ variables = undefined,
+}: GraphProps) {
+ const { data } = useDataKashiTokenDayData({ chainId, variables })
+ return useMemo(() => {
+ if (!data) return { data: undefined, loading: true }
+ try {
+ return { data: data, loading: false }
+ } catch (error) {
+ return { data: undefined, loading: true }
+ }
+ }, [data])
+}
diff --git a/src/services/graph/queries/index.ts b/src/services/graph/queries/index.ts
index 26aae1c25b..a87b2ea58c 100644
--- a/src/services/graph/queries/index.ts
+++ b/src/services/graph/queries/index.ts
@@ -2,6 +2,7 @@ export * from './bentobox'
export * from './blocks'
export * from './exchange'
export * from './kashi'
+export * from './kashipairs'
export * from './masterchef'
export * from './masterchef-v2'
export * from './minichef'
diff --git a/src/services/graph/queries/kashipairs.ts b/src/services/graph/queries/kashipairs.ts
new file mode 100644
index 0000000000..c07831a314
--- /dev/null
+++ b/src/services/graph/queries/kashipairs.ts
@@ -0,0 +1,266 @@
+import gql from 'graphql-tag'
+
+export const kashiPairQuery = gql`
+ query GetPair($id: String) {
+ kashiPairs(first: 1, where: { id: $id }) {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ }
+ exchangeRate
+ totalAssetElastic
+ totalAssetBase
+ supplyAPR
+ totalBorrowElastic
+ totalBorrowBase
+ borrowAPR
+ utilization
+ }
+ kashiPairDayDatas(first: 1000, where: { pair: $id }, orderBy: date, orderDirection: desc) {
+ id
+ date
+ pair {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ }
+ }
+ totalAssetElastic
+ totalAssetBase
+ totalCollateralShare
+ totalBorrowElastic
+ totalBorrowBase
+ avgExchangeRate
+ avgUtilization
+ avgInterestPerSecond
+ }
+ }
+`
+
+export const dataKashiPairsQuery = gql`
+ query GetPairs {
+ kashiPairs(first: 1000) {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ }
+ totalAsset {
+ base
+ elastic
+ }
+ totalBorrow {
+ base
+ elastic
+ }
+ kpi {
+ supplyAPR
+ borrowAPR
+ utilization
+ totalFeesEarnedFraction
+ }
+ exchangeRate
+ accrueInfo {
+ interestPerSecond
+ }
+ }
+ }
+`
+
+export const kashiPairsDayDataQuery = gql`
+ query GetDataKashiPairsDayData($skip: Int) {
+ kashiPairDayDatas(first: 1000, skip: $skip, orderBy: date, orderDirection: desc) {
+ id
+ date
+ pair {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ }
+ }
+ totalAssetElastic
+ totalAssetBase
+ totalCollateralShare
+ totalBorrowElastic
+ totalBorrowBase
+ avgExchangeRate
+ avgUtilization
+ avgInterestPerSecond
+ }
+ }
+`
+
+export const kashiTokensQuery = gql`
+ query GetPairs {
+ kashiPairs(first: 1000) {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ }
+ totalAsset {
+ base
+ elastic
+ }
+ totalBorrow {
+ base
+ elastic
+ }
+ kpi {
+ supplyAPR
+ borrowAPR
+ utilization
+ }
+ accrueInfo {
+ interestPerSecond
+ }
+ exchangeRate
+ }
+ }
+`
+
+export const kashiTokenQuery = gql`
+ query GetToken($id: String) {
+ tokens(first: 1, where: { id: $id }) {
+ id
+ name
+ symbol
+ decimals
+ rebase {
+ base
+ elastic
+ }
+ # block
+ # timestamp
+ }
+ kashiPairs(first: 1000, where: { asset: $id }) {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ rebase {
+ base
+ elastic
+ }
+ # block
+ # timestamp
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ rebase {
+ base
+ elastic
+ }
+ # block
+ # timestamp
+ }
+ totalAsset {
+ base
+ elastic
+ }
+ totalBorrow {
+ base
+ elastic
+ }
+ totalCollateralShare
+ kpi {
+ supplyAPR
+ borrowAPR
+ }
+ }
+ }
+`
+
+export const kashiTokenDayDataQuery = gql`
+ query GetDataKashiPairsDayData($pairIds: [String]) {
+ kashiPairDayDatas(first: 1000, where: { pair_in: $pairIds }, orderBy: date, orderDirection: desc) {
+ id
+ date
+ pair {
+ id
+ name
+ symbol
+ asset {
+ id
+ name
+ symbol
+ decimals
+ }
+ collateral {
+ id
+ name
+ symbol
+ decimals
+ }
+ }
+ totalAssetElastic
+ totalAssetBase
+ totalCollateralShare
+ totalBorrowElastic
+ totalBorrowBase
+ avgExchangeRate
+ avgUtilization
+ avgInterestPerSecond
+ }
+ }
+`
diff --git a/src/styles/index.css b/src/styles/index.css
index 23620e5785..7913870ae9 100644
--- a/src/styles/index.css
+++ b/src/styles/index.css
@@ -103,3 +103,7 @@ input[type=datetime-local]::-webkit-calendar-picker-indicator {
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
}
+
+.highcharts-credits {
+ display: none;
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 8ab6b8e30e..13c5baa7dc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -19,6 +19,24 @@
jsonpointer "^5.0.0"
leven "^3.1.0"
+"@apollo/client@^3.6.8":
+ version "3.6.8"
+ resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.6.8.tgz#97e876fdbc4c287652a61895fd2b6f0b157a7667"
+ integrity sha512-p/J6KRHZZPGX0bZtMLvRFAIcReYsRYGg+Jz9MkgabWPy0L8rwgyolq9fvKsNqkH888Tj9Yvwrxz9V84KfcORJA==
+ dependencies:
+ "@graphql-typed-document-node/core" "^3.1.1"
+ "@wry/context" "^0.6.0"
+ "@wry/equality" "^0.5.0"
+ "@wry/trie" "^0.3.0"
+ graphql-tag "^2.12.6"
+ hoist-non-react-statics "^3.3.2"
+ optimism "^0.16.1"
+ prop-types "^15.7.2"
+ symbol-observable "^4.0.0"
+ ts-invariant "^0.10.3"
+ tslib "^2.3.0"
+ zen-observable-ts "^1.2.5"
+
"@babel/code-frame@7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
@@ -1773,6 +1791,11 @@
dependencies:
cross-fetch "^3.1.5"
+"@graphql-typed-document-node/core@^3.1.1":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052"
+ integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==
+
"@hapi/hoek@^9.0.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
@@ -3117,6 +3140,11 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
+"@types/numeral@^2.0.2":
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-2.0.2.tgz#8ea2c4f4e64c0cc948ad7da375f6f827778a7912"
+ integrity sha512-A8F30k2gYJ/6e07spSCPpkuZu79LCnkPTvqmIWQzNGcrzwFKpVOydG41lNt5wZXjSI149qjyzC2L1+F2PD/NUA==
+
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -3834,6 +3862,27 @@
dependencies:
lodash "^4"
+"@wry/context@^0.6.0":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2"
+ integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/equality@^0.5.0":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.2.tgz#72c8a7a7d884dff30b612f4f8464eba26c080e73"
+ integrity sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/trie@^0.3.0":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.1.tgz#2279b790f15032f8bcea7fc944d27988e5b3b139"
+ integrity sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw==
+ dependencies:
+ tslib "^2.3.0"
+
"@zeit/schemas@2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
@@ -7402,7 +7451,7 @@ graphql-request@^3.5.0:
extract-files "^9.0.0"
form-data "^3.0.0"
-graphql-tag@^2.12.5:
+graphql-tag@^2.12.5, graphql-tag@^2.12.6:
version "2.12.6"
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1"
integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==
@@ -7514,6 +7563,16 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
+highcharts-react-official@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/highcharts-react-official/-/highcharts-react-official-3.1.0.tgz#921e5614f9e8580bc99a1e3b531d51a70f396500"
+ integrity sha512-CkWJHrVMOc6CT8KFu1dR+a0w5OxCVKKgZUNWtEi5TmR0xqBDIDe+RyM652MAN/jBYppxMo6TCUVlRObCyWAn0Q==
+
+highcharts@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/highcharts/-/highcharts-10.1.0.tgz#561872ae8ea819131d78949136d05bcaaa99108b"
+ integrity sha512-gW/pzy/Qy/hiYeCflYHpwgsP2nqQavwuRUswRf+papmbutZ3I7K7AIZJ/yZ9NL5yzpF7X7r2dX7/nzZGN4A1rg==
+
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -9599,6 +9658,11 @@ module-lookup-amd@^7.0.1:
requirejs "^2.3.5"
requirejs-config-file "^4.0.0"
+moment@^2.29.3:
+ version "2.29.3"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3"
+ integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==
+
mrmime@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
@@ -9895,7 +9959,7 @@ number-to-bn@1.7.0:
bn.js "4.11.6"
strip-hex-prefix "1.0.0"
-numeral@^2.0.0:
+numeral@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506"
integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==
@@ -10009,6 +10073,14 @@ opener@^1.5.2:
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
+optimism@^0.16.1:
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.1.tgz#7c8efc1f3179f18307b887e18c15c5b7133f6e7d"
+ integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg==
+ dependencies:
+ "@wry/context" "^0.6.0"
+ "@wry/trie" "^0.3.0"
+
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
@@ -11086,6 +11158,11 @@ react-hook-form@7.29.0:
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.29.0.tgz#5e7e41a483b70731720966ed8be52163ea1fecf1"
integrity sha512-NcJqWRF6el5HMW30fqZRt27s+lorvlCCDbTpAyHoodQeYWXgQCvZJJQLC1kRMKdrJknVH0NIg3At6TUzlZJFOQ==
+react-icons@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703"
+ integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==
+
react-infinite-scroll-component@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz#7e511e7aa0f728ac3e51f64a38a6079ac522407f"
@@ -12356,6 +12433,11 @@ swr@^1.2.0:
resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8"
integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==
+symbol-observable@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
+ integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==
+
symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
@@ -12651,6 +12733,13 @@ trim-newlines@^3.0.0:
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
+ts-invariant@^0.10.3:
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c"
+ integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==
+ dependencies:
+ tslib "^2.1.0"
+
ts-loader@^7.0.0:
version "7.0.5"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5"
@@ -12713,6 +12802,11 @@ tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+tslib@^2.3.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+ integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
@@ -13725,6 +13819,18 @@ yup@^0.32.11:
property-expr "^2.0.4"
toposort "^2.0.2"
+zen-observable-ts@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
+ integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==
+ dependencies:
+ zen-observable "0.8.15"
+
+zen-observable@0.8.15:
+ version "0.8.15"
+ resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
+ integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
+
zustand@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d"