diff --git a/src/components/ButtonStyled/CsvButton.tsx b/src/components/ButtonStyled/CsvButton.tsx index 1fe3dbb0a..72c132075 100644 --- a/src/components/ButtonStyled/CsvButton.tsx +++ b/src/components/ButtonStyled/CsvButton.tsx @@ -29,20 +29,26 @@ const Badge: React.FC = ({ text, color, isLight }) => { ) } +const GrayButton = styled(ButtonDark)` + background: ${({ theme }) => (theme.mode === 'dark' ? '#22242a' : '#eaeaea')}; +` + const CSVDownloadButton = ({ onClick, style = {}, isLight = false, - customText = '' + customText = '', + isGray = false }: { onClick: () => void style?: CSSProperties isLight?: boolean customText?: string + isGray?: boolean }) => { const { isVerified } = useVerified() const router = useRouter() - const Button = isLight ? ButtonLight : ButtonDark + const Button = isGray ? GrayButton : isLight ? ButtonLight : ButtonDark const text = customText || 'Download .csv' if (!isVerified && IS_PRO_API_ENABLED) { return ( diff --git a/src/components/Filters/yields/Dropdowns.tsx b/src/components/Filters/yields/Dropdowns.tsx index b0e9c6eb4..807306dea 100644 --- a/src/components/Filters/yields/Dropdowns.tsx +++ b/src/components/Filters/yields/Dropdowns.tsx @@ -10,6 +10,7 @@ import type { IDropdownMenusProps } from './types' import { YIELDS_SETTINGS } from '~/contexts/LocalStorage' import { ColumnFilters } from '../common/ColumnFilters' import { NotifyButton } from './NotifyButton' +import CSVDownloadButton from '~/components/ButtonStyled/CsvButton' const BAD_DEBT_KEY = YIELDS_SETTINGS.NO_BAD_DEBT.toLowerCase() @@ -48,7 +49,8 @@ export function YieldFilterDropdowns({ showLTV, showTotalSupplied, showTotalBorrowed, - showAvailable + showAvailable, + onCSVDownload }: IDropdownMenusProps) { const router = useRouter() @@ -241,8 +243,15 @@ export function YieldFilterDropdowns({ )} {!isMobile && ( -
+
+ {onCSVDownload ? ( + + ) : null}
)} diff --git a/src/components/Filters/yields/types.ts b/src/components/Filters/yields/types.ts index a9882fb97..4401b63a5 100644 --- a/src/components/Filters/yields/types.ts +++ b/src/components/Filters/yields/types.ts @@ -35,6 +35,7 @@ export interface IDropdownMenusProps { showTotalSupplied?: boolean showTotalBorrowed?: boolean showAvailable?: boolean + onCSVDownload?: () => void } export interface IYieldFiltersProps extends IDropdownMenusProps { @@ -46,4 +47,5 @@ export interface IYieldFiltersProps extends IDropdownMenusProps { strategyInputsData?: Array<{ name: string; symbol: string; image?: string | null; image2?: string | null }> noOfStrategies?: number showSearchOnMobile?: boolean + onCSVDownload?: () => void } diff --git a/src/components/RecentProtocols/index.tsx b/src/components/RecentProtocols/index.tsx index b3c8e2219..2c75949fb 100644 --- a/src/components/RecentProtocols/index.tsx +++ b/src/components/RecentProtocols/index.tsx @@ -7,7 +7,9 @@ import { ProtocolsChainsSearch } from '~/components/Search' import { Dropdowns, TableFilters, TableHeader } from '~/components/Table/shared' import { FiltersByChain, HideForkedProtocols, TVLRange } from '~/components/Filters' import { useCalcStakePool2Tvl } from '~/hooks/data' -import { getPercentChange } from '~/utils' + +import { download, getPercentChange } from '~/utils' +import { IFormattedProtocol } from '~/api/types' import { FlexRow } from '~/layout/ProtocolAndPool' import { ButtonLight } from '../ButtonStyled' import { ArrowUpRight, Plus, X } from 'react-feather' @@ -16,6 +18,7 @@ import { useDialogState, Dialog } from 'ariakit/dialog' import { DialogForm } from '../Filters/common/Base' import { useMutation } from 'react-query' import { airdropsEligibilityCheck } from './airdrops' +import CSVDownloadButton from '../ButtonStyled/CsvButton' function getSelectedChainFilters(chainQueryParam, allChains) { if (chainQueryParam) { @@ -149,6 +152,24 @@ export function RecentProtocols({ }, [protocols, chain, chainList, forkedList, toHideForkedProtocols, minTvl, maxTvl]) const protocolsData = useCalcStakePool2Tvl(data) + const downloadCSV = () => { + const headers = ['Name', 'Chain', 'TVL', 'Change 1d', 'Change 7d', 'Change 1m', 'Listed At'] + const csvData = protocolsData.map((row) => { + return { + Name: row.name, + Chain: row.chains.join(', '), + TVL: row.tvl, + 'Change 1d': row.change_1d, + 'Change 7d': row.change_7d, + 'Change 1m': row.change_1m, + 'Listed At': new Date(row.listedAt * 1000).toLocaleDateString() + } + }) + download( + 'protocols.csv', + [headers, ...csvData.map((row) => headers.map((header) => row[header]).join(','))].join('\n') + ) + } const { pathname } = useRouter() @@ -304,6 +325,7 @@ export function RecentProtocols({ + {forkedList && } diff --git a/src/components/Treasuries/index.tsx b/src/components/Treasuries/index.tsx index c0aeeb651..c7371255c 100644 --- a/src/components/Treasuries/index.tsx +++ b/src/components/Treasuries/index.tsx @@ -13,6 +13,8 @@ import { import VirtualTable from '~/components/Table/Table' import { fetchWithErrorLogging } from '~/utils/async' +import CSVDownloadButton from '../ButtonStyled/CsvButton' +import { download } from '~/utils' const fetch = fetchWithErrorLogging @@ -60,6 +62,37 @@ export function TreasuriesPage({ treasuries, treasuriesColumns }) { const [projectName, setProjectName] = React.useState('') + const downloadCSV = () => { + const headers = [ + 'Name', + 'Category', + 'Own Tokens', + 'Stablecoins', + 'Major Tokens', + 'Other Tokens', + 'TVL', + 'Change 1d', + 'Change 7d', + 'Change 1m' + ] + const data = treasuries.map((row) => { + return { + Name: row.name, + Category: row.category, + 'Own Tokens': row.ownTokens, + Stablecoins: row.stablecoins, + 'Major Tokens': row.majors, + 'Other Tokens': row.others, + TVL: row.tvl, + 'Change 1d': row.change_1d, + 'Change 7d': row.change_7d, + 'Change 1m': row.change_1m + } + }) + const csv = [headers.join(',')].concat(data.map((row) => headers.map((header) => row[header]).join(','))).join('\n') + download('treasuries.csv', csv) + } + React.useEffect(() => { const projectsColumns = instance.getColumn('name') const id = setTimeout(() => { @@ -71,7 +104,9 @@ export function TreasuriesPage({ treasuries, treasuriesColumns }) { return ( <> -
Protocol Treasuries
+
+ Protocol Treasuries +
diff --git a/src/components/YieldsPage/index.tsx b/src/components/YieldsPage/index.tsx index a711693f7..a71114007 100644 --- a/src/components/YieldsPage/index.tsx +++ b/src/components/YieldsPage/index.tsx @@ -7,6 +7,8 @@ import { AnnouncementWrapper } from '~/components/Announcement' import LocalLoader from '../LocalLoader' import { useFormatYieldQueryParams } from './hooks' import { toFilterPool } from './utils' +import CSVDownloadButton from '../ButtonStyled/CsvButton' +import { download } from '~/utils' const YieldPage = ({ pools, projectList, chainList, categoryList, tokens, tokenSymbolsList }) => { const { query, pathname, push } = useRouter() @@ -115,6 +117,70 @@ const YieldPage = ({ pools, projectList, chainList, categoryList, tokens, tokenS exactTokens, pathname ]) + const downloadCSV = React.useCallback(() => { + const headers = [ + 'Pool', + 'Project', + 'Chain', + 'TVL', + 'APY', + 'APY Base', + 'APY Reward', + 'Change 1d', + 'Change 7d', + 'Outlook', + 'Confidence', + 'Category', + 'IL 7d', + 'APY Base 7d', + 'APY Net 7d', + 'APY Mean 30d', + 'Volume 1d', + 'Volume 7d', + 'APY Base Inception', + 'APY Including LSD APY', + 'APY Base Including LSD APY', + 'APY Base Borrow', + 'APY Reward Borrow', + 'APY Borrow', + 'Total Supply USD', + 'Total Borrow USD', + 'Total Available USD' + ] + const csvData = poolsData.map((row) => { + return { + Pool: row.pool, + Project: row.project, + Chain: row.chains, + TVL: row.tvl, + APY: row.apy, + 'APY Base': row.apyBase, + 'APY Reward': row.apyReward, + 'Change 1d': row.change1d, + 'Change 7d': row.change7d, + Outlook: row.outlook, + Confidence: row.confidence, + Category: row.category, + 'IL 7d': row.il7d, + 'APY Base 7d': row.apyBase7d, + 'APY Net 7d': row.apyNet7d, + 'APY Mean 30d': row.apyMean30d, + 'Volume 1d': row.volumeUsd1d, + 'Volume 7d': row.volumeUsd7d, + 'APY Base Inception': row.apyBaseInception, + 'APY Including LSD APY': row.apyIncludingLsdApy, + 'APY Base Including LSD APY': row.apyBaseIncludingLsdApy, + 'APY Base Borrow': row.apyBaseBorrow, + 'APY Reward Borrow': row.apyRewardBorrow, + 'APY Borrow': row.apyBorrow, + 'Total Supply USD': row.totalSupplyUsd, + 'Total Borrow USD': row.totalBorrowUsd, + 'Total Available USD': row.totalAvailableUsd + } + }) + const csv = [headers].concat(csvData.map((row) => headers.map((header) => row[header]))).join('\n') + download('yields.csv', csv) + }, [poolsData]) return ( <> @@ -142,7 +208,6 @@ const YieldPage = ({ pools, projectList, chainList, categoryList, tokens, tokenS )} - {loading ? ( diff --git a/src/containers/BridgedContainer/index.tsx b/src/containers/BridgedContainer/index.tsx index 3d24d219a..14b86d98e 100644 --- a/src/containers/BridgedContainer/index.tsx +++ b/src/containers/BridgedContainer/index.tsx @@ -1,11 +1,15 @@ import { SortingState, getCoreRowModel, getSortedRowModel, useReactTable } from '@tanstack/react-table' import * as React from 'react' import { Header } from '~/Theme' +import CSVDownloadButton from '~/components/ButtonStyled/CsvButton' import { ProtocolsChainsSearch } from '~/components/Search' import { bridgedColumns } from '~/components/Table/Defi/columns' import VirtualTable from '~/components/Table/Table' +import { download } from '~/utils' +import { sluggify } from '~/utils/cache-client' + export default function ChainsContainer({ assets, chains, flows1d }) { const [sorting, setSorting] = React.useState([]) @@ -33,6 +37,26 @@ export default function ChainsContainer({ assets, chains, flows1d }) { getSortedRowModel: getSortedRowModel() }) + const onCSVDownload = () => { + const csvData = data.map((row) => { + return { + Chain: row.name, + Total: row.total?.total, + Change_24h: row?.change_24h, + Canonical: row?.canonical?.total, + OwnTokens: row?.ownTokens?.total, + ThirdParty: row?.thirdParty?.total, + Native: row?.native?.total + } + }) + const headers = Object.keys(csvData[0]) + const csv = [headers.join(',')] + .concat(csvData.map((row) => headers.map((header) => row[header]).join(','))) + .join('\n') + + download('bridged-chains.csv', csv) + } + return ( <> -
Bridged TVL for All chains
+
+ Bridged TVL for All chains +
) diff --git a/src/containers/DexsAndFees/index.tsx b/src/containers/DexsAndFees/index.tsx index c49027b9c..495985b30 100644 --- a/src/containers/DexsAndFees/index.tsx +++ b/src/containers/DexsAndFees/index.tsx @@ -16,6 +16,7 @@ import { volumeTypes } from '~/utils/adaptorsPages/utils' import { AnnouncementWrapper } from '~/components/Announcement' import { useFeesManager } from '~/contexts/LocalStorage' import { ButtonDark } from '~/components/ButtonStyled' +import CSVDownloadButton from '~/components/ButtonStyled/CsvButton' const HeaderWrapper = styled(Header)` display: flex; @@ -228,21 +229,38 @@ export default function OverviewContainer(props: IOverviewContainerProps) { ]) const downloadCsv = React.useCallback(() => { - const columnsToPick = ['date', ...chartData[1]] - - const data = chartData[0].map((p) => { - const row = [] - columnsToPick.forEach((r) => { - row.push(p[r]) - }) - return row + const header = [ + 'Protocol', + 'Category', + 'Change 1d', + 'Change 7d', + 'Change 1m', + 'Total 1d', + 'Total 7d', + 'Total 1m', + 'Revenue 24h', + 'Revenue 7d', + 'Revenue 30d' + ] + const data = finalProtocolsList.map((protocol) => { + return [ + protocol.displayName, + protocol.category, + protocol.change_1d, + protocol.change_7d, + protocol.change_1m, + protocol.total24h, + protocol.total7d, + protocol.total30d, + protocol.revenue24h, + protocol.revenue7d, + protocol.revenue30d + ] }) + const csv = [header, ...data].map((row) => row.join(',')).join('\n') - const header = columnsToPick.join(',') - const rowsData = data.map((d) => d.join(',')).join('\n') - - download('protocols.csv', header + '\n' + rowsData) - }, [chartData]) + download(`${props.type}-protocols.csv`, csv) + }, [finalProtocolsList, props.type]) return ( <> @@ -279,11 +297,8 @@ export default function OverviewContainer(props: IOverviewContainerProps) {
- {props.chain === 'all' && props.type === 'dexs' ? ( - - Download CSV - - ) : null} + +

Updated daily at 00:00UTC

diff --git a/src/pages/forks.tsx b/src/pages/forks.tsx index bffaa3214..2a6cbb460 100644 --- a/src/pages/forks.tsx +++ b/src/pages/forks.tsx @@ -14,6 +14,8 @@ import { withPerformanceLogging } from '~/utils/perf' import type { IChartProps, IPieChartProps } from '~/components/ECharts/types' import { TableWithSearch } from '~/components/Table/TableWithSearch' import { forksColumn } from '~/components/Table/Defi/columns' +import CSVDownloadButton from '~/components/ButtonStyled/CsvButton' +import { download } from '~/utils' const PieChart = dynamic(() => import('~/components/ECharts/PieChart'), { ssr: false @@ -79,11 +81,27 @@ const PageView = ({ chartData, tokensProtocols, tokens, tokenLinks, parentTokens return { tokenTvls, tokensList } }, [chainsWithExtraTvlsByDay, tokensProtocols, forkedTokensData]) + const downloadCSV = () => { + const headers = ['Name', 'Forked Protocols', 'TVL', 'Forked TVL / Original TVL %'] + const csvData = tokensList.map((row) => { + return { + Name: row.name, + 'Forked Protocols': row.forkedProtocols, + TVL: row.tvl, + 'Forked TVL / Original TVL %': row.ftot + } + }) + const csv = [headers].concat(csvData.map((row) => headers.map((header) => row[header]))).join('\n') + download('forks.csv', csv) + } + return ( <> -
Total Value Locked All Forks
+
+ Total Value Locked All Forks +
diff --git a/src/pages/tokenUsage.tsx b/src/pages/tokenUsage.tsx index 388c18c02..48dc41cda 100644 --- a/src/pages/tokenUsage.tsx +++ b/src/pages/tokenUsage.tsx @@ -11,6 +11,8 @@ import { getAllCGTokensList, maxAgeForNext } from '~/api' import { fetcher } from '~/utils/useSWR' import Announcement from '~/components/Announcement' import { withPerformanceLogging } from '~/utils/perf' +import CSVDownloadButton from '~/components/ButtonStyled/CsvButton' +import { download } from '~/utils' export default function Tokens({ searchData }) { const router = useRouter() @@ -49,6 +51,19 @@ export default function Tokens({ searchData }) { ) }, [protocols, includeCentraliseExchanges]) + const downloadCSV = () => { + const data = filteredProtocols.map((p) => { + return { + Protocol: p.name, + Amount: p.amountUsd, + 'Amount (USD)': p.amountUsd + } + }) + const headers = ['Protocol', 'Amount', 'Amount (USD)'] + const csv = [headers.join(',')].concat(data.map((row) => headers.map((header) => row[header]).join(','))).join('\n') + download(`protocols-by-token-${tokenSybmol}.csv`, csv) + } + return ( This is not an exhaustive list @@ -62,6 +77,8 @@ export default function Tokens({ searchData }) { <> {`${tokenSybmol.toUpperCase()} usage in protocols`} + + {/* { const { protocols, chains } = await getSimpleProtocolsPageData(['name', 'extraTvl', 'chainTvls', 'category']) @@ -94,6 +95,20 @@ export default function Chains({ data, columns }) { ], [columns] ) + const downloadCSV = () => { + const headers = ['Chain', ...columns.map((column) => column.header)] + const csvData = data.map((row) => { + return { + Chain: row.chain, + ...Object.fromEntries(columns.map((column) => [column.header, row[column.header]])) + } + }) + + const csv = [headers, ...csvData.map((row) => headers.map((header) => row[header]))] + .map((row) => row.join(',')) + .join('\n') + download('top-protocols.csv', csv) + } const instance = useReactTable({ data, @@ -103,7 +118,10 @@ export default function Chains({ data, columns }) { return ( - Top Protocols + + Top Protocols + + )