add platform stats and use next font
This commit is contained in:
parent
bd36d90f2c
commit
9d70eac464
|
@ -0,0 +1,27 @@
|
|||
import Tooltip from '../shared/Tooltip'
|
||||
|
||||
const HeroStat = ({
|
||||
title,
|
||||
tooltipContent,
|
||||
value,
|
||||
}: {
|
||||
title: string
|
||||
tooltipContent?: string
|
||||
value: string
|
||||
}) => {
|
||||
return (
|
||||
<div className="col-span-4 sm:col-span-2 lg:col-span-1">
|
||||
<Tooltip
|
||||
content={tooltipContent ? tooltipContent : ''}
|
||||
placement="top-start"
|
||||
>
|
||||
<p className="lg:text-lg mb-1">{title}</p>
|
||||
</Tooltip>
|
||||
<p className="text-4xl md:text-5xl font-display tracking-tight text-th-fgd-1">
|
||||
{value}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeroStat
|
|
@ -16,9 +16,11 @@ import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|||
import { MotionPathPlugin } from 'gsap/dist/MotionPathPlugin'
|
||||
import ColorBlur from '../shared/ColorBlur'
|
||||
import Ottersec from '../icons/Ottersec'
|
||||
import { MarketData } from '../../types'
|
||||
import { AppStatsData, MarketData } from '../../types'
|
||||
import TabsText from '../shared/TabsText'
|
||||
import MarketCard from './MarketCard'
|
||||
import { formatNumericValue, numberCompacter } from '../../utils'
|
||||
import HeroStat from './HeroStat'
|
||||
|
||||
gsap.registerPlugin(MotionPathPlugin)
|
||||
gsap.registerPlugin(ScrollTrigger)
|
||||
|
@ -44,9 +46,11 @@ const MOBILE_IMAGE_CLASSES =
|
|||
const HomePage = ({
|
||||
perpData,
|
||||
spotData,
|
||||
appData,
|
||||
}: {
|
||||
perpData: MarketData
|
||||
spotData: MarketData
|
||||
appData: AppStatsData
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'home'])
|
||||
const [activeMarketTab, setActiveMarketTab] = useState('spot')
|
||||
|
@ -67,6 +71,47 @@ const HomePage = ({
|
|||
return tabs
|
||||
}, [perpData, spotData])
|
||||
|
||||
const formattedSpotData = useMemo(() => {
|
||||
if (!spotData || !Object.keys(spotData)?.length) return []
|
||||
const data = Object.entries(spotData)
|
||||
.sort((a, b) => b[1][0].quote_volume_24h - a[1][0].quote_volume_24h)
|
||||
.map(([key, value]) => {
|
||||
const data = value[0]
|
||||
return { name: key, data }
|
||||
})
|
||||
return data
|
||||
}, [spotData])
|
||||
|
||||
const formattedPerpData = useMemo(() => {
|
||||
if (!perpData || !Object.keys(perpData)?.length) return []
|
||||
const data = Object.entries(perpData)
|
||||
.sort((a, b) => b[1][0].quote_volume_24h - a[1][0].quote_volume_24h)
|
||||
.map(([key, value]) => {
|
||||
const data = value[0]
|
||||
return { name: key, data }
|
||||
})
|
||||
return data
|
||||
}, [perpData])
|
||||
|
||||
const formattedAppStatsData = useMemo(() => {
|
||||
if (!appData || !Object.keys(appData).length) return
|
||||
// volume
|
||||
const spotVol24h = appData?.openbook_volume_usd_24h || 0
|
||||
const perpVol24h = appData?.perp_volume_usd_24h || 0
|
||||
const swapVol24h = appData?.swap_volume_usd_24h || 0
|
||||
const totalVol24h = spotVol24h + perpVol24h + swapVol24h
|
||||
|
||||
// number of trades
|
||||
const spotTrades24h = appData?.num_openbook_fills_24h || 0
|
||||
const perpTrades24h = appData?.num_perp_fills_24h || 0
|
||||
const swapTrades24h = appData?.num_swaps_24h || 0
|
||||
const totalTrades24h = spotTrades24h + perpTrades24h + swapTrades24h
|
||||
|
||||
const weeklyActiveTraders = appData?.weekly_active_mango_accounts || 0
|
||||
|
||||
return { totalVol24h, totalTrades24h, weeklyActiveTraders }
|
||||
}, [appData])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const ctx = gsap.context((self) => {
|
||||
const boxes = self.selector('.highlight-features')
|
||||
|
@ -216,7 +261,38 @@ const HomePage = ({
|
|||
</div>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
<SectionWrapper>
|
||||
<div className="bg-[url('/images/new/cube-bg.png')] bg-repeat py-12 lg:py-16">
|
||||
<SectionWrapper noPaddingY>
|
||||
<div className="grid grid-cols-4 gap-6">
|
||||
<HeroStat
|
||||
title={t('markets')}
|
||||
value={(
|
||||
formattedSpotData.length + formattedPerpData.length
|
||||
).toString()}
|
||||
/>
|
||||
<HeroStat
|
||||
title={t('home:active-traders')}
|
||||
tooltipContent={t('home:tooltip-active-traders')}
|
||||
value={formatNumericValue(
|
||||
formattedAppStatsData.weeklyActiveTraders
|
||||
)}
|
||||
/>
|
||||
<HeroStat
|
||||
title={t('home:daily-volume')}
|
||||
tooltipContent={t('home:tooltip-daily-volume')}
|
||||
value={`$${numberCompacter.format(
|
||||
formattedAppStatsData.totalVol24h
|
||||
)}`}
|
||||
/>
|
||||
<HeroStat
|
||||
title={t('home:daily-trades')}
|
||||
tooltipContent={t('home:tooltip-daily-trades')}
|
||||
value={formatNumericValue(formattedAppStatsData.totalTrades24h)}
|
||||
/>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
</div>
|
||||
<SectionWrapper className="mt-10 md:mt-0">
|
||||
<div
|
||||
className="grid grid-cols-6 gap-4 md:gap-6 xl:gap-8"
|
||||
ref={callouts}
|
||||
|
@ -269,10 +345,7 @@ const HomePage = ({
|
|||
/>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
{spotData &&
|
||||
Object.keys(spotData).length &&
|
||||
perpData &&
|
||||
Object.keys(perpData).length ? (
|
||||
{formattedSpotData.length && formattedPerpData.length ? (
|
||||
<SectionWrapper className="border-t border-th-bkg-3">
|
||||
<div className="w-full h-full">
|
||||
<h2 className="mb-4 text-center">{t('markets')}</h2>
|
||||
|
@ -289,24 +362,12 @@ const HomePage = ({
|
|||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 max-h-[580px] overflow-auto thin-scroll">
|
||||
{activeMarketTab === 'spot'
|
||||
? Object.entries(spotData)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
b[1][0].quote_volume_24h - a[1][0].quote_volume_24h
|
||||
)
|
||||
.map(([key, value]) => {
|
||||
const data = value[0]
|
||||
return <MarketCard name={key} data={data} key={key} />
|
||||
})
|
||||
: Object.entries(perpData)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
b[1][0].quote_volume_24h - a[1][0].quote_volume_24h
|
||||
)
|
||||
.map(([key, value]) => {
|
||||
const data = value[0]
|
||||
return <MarketCard name={key} data={data} key={key} />
|
||||
})}
|
||||
? formattedSpotData.map((data) => (
|
||||
<MarketCard marketData={data} key={data.name} />
|
||||
))
|
||||
: formattedPerpData.map((data) => (
|
||||
<MarketCard marketData={data} key={data.name} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
|
|
@ -6,20 +6,21 @@ import SimpleAreaChart from '../shared/SimpleAreaChart'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
const MarketCard = ({
|
||||
name,
|
||||
data,
|
||||
}: {
|
||||
type MarketCardData = {
|
||||
name: string
|
||||
data: MarketsDataItem
|
||||
}) => {
|
||||
}
|
||||
|
||||
const MarketCard = ({ marketData }: { marketData: MarketCardData }) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { last_price, price_24h, price_history, quote_volume_24h } = data
|
||||
const { name, data } = marketData
|
||||
const isSpot = name.includes('/')
|
||||
const baseSymbol = isSpot ? name.split('/')[0] : name.split('-')[0]
|
||||
const quoteSymbol = isSpot ? name.split('/')[1] : 'USDC'
|
||||
const change =
|
||||
last_price && price_24h ? ((last_price - price_24h) / last_price) * 100 : 0
|
||||
data?.last_price && data?.price_24h
|
||||
? ((data.last_price - data.price_24h) / data.last_price) * 100
|
||||
: 0
|
||||
return (
|
||||
<div className="p-4 rounded-lg border border-th-bkg-3">
|
||||
<div className="flex items-start justify-between">
|
||||
|
@ -36,10 +37,10 @@ const MarketCard = ({
|
|||
<div>
|
||||
<p className="text-th-fgd-4">{name}</p>
|
||||
<p className="text-th-fgd-1 font-bold">
|
||||
{last_price ? (
|
||||
{data?.last_price ? (
|
||||
<span>
|
||||
{quoteSymbol === 'USDC' ? '$' : ''}
|
||||
{formatNumericValue(last_price)}{' '}
|
||||
{formatNumericValue(data.last_price)}{' '}
|
||||
{quoteSymbol !== 'USDC' ? (
|
||||
<span className="text-th-fgd-4 font-normal">
|
||||
{quoteSymbol}
|
||||
|
@ -54,12 +55,12 @@ const MarketCard = ({
|
|||
<Change change={change} suffix="%" />
|
||||
<span className="text-th-fgd-4">|</span>
|
||||
<p className="whitespace-nowrap">
|
||||
{quote_volume_24h ? (
|
||||
{data?.quote_volume_24h ? (
|
||||
<span>
|
||||
{quoteSymbol === 'USDC' ? '$' : ''}
|
||||
{isNaN(quote_volume_24h)
|
||||
{isNaN(data.quote_volume_24h)
|
||||
? '0.00'
|
||||
: numberCompacter.format(quote_volume_24h)}{' '}
|
||||
: numberCompacter.format(data.quote_volume_24h)}{' '}
|
||||
{quoteSymbol !== 'USDC' ? (
|
||||
<span className="text-th-fgd-4 font-normal">
|
||||
{quoteSymbol}
|
||||
|
@ -73,16 +74,18 @@ const MarketCard = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{price_history && price_history.length ? (
|
||||
{data?.price_history && data?.price_history.length ? (
|
||||
<div className="h-12 w-20">
|
||||
<SimpleAreaChart
|
||||
color={
|
||||
price_history[0].price <=
|
||||
price_history[price_history.length - 1]?.price
|
||||
data.price_history[0].price <=
|
||||
data.price_history[data.price_history.length - 1]?.price
|
||||
? 'var(--up)'
|
||||
: 'var(--down)'
|
||||
}
|
||||
data={price_history.sort((a, b) => a.time.localeCompare(b.time))}
|
||||
data={data.price_history.sort((a, b) =>
|
||||
a.time.localeCompare(b.time)
|
||||
)}
|
||||
name={name}
|
||||
xKey="time"
|
||||
yKey="price"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ReactNode, useEffect, useState } from 'react'
|
||||
import Footer from '../footer/Footer'
|
||||
import TopNavigation from '../navigation/TopNavigation'
|
||||
// import ColorBlur from '../shared/ColorBlur'
|
||||
import { ttCommons, ttCommonsExpanded, ttCommonsMono } from '../../utils/fonts'
|
||||
|
||||
const LayoutWrapper = ({ children }: { children: ReactNode }) => {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
|
@ -15,16 +15,13 @@ const LayoutWrapper = ({ children }: { children: ReactNode }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-th-bkg-1">
|
||||
<main
|
||||
className={`bg-th-bkg-1 ${ttCommons.variable} ${ttCommonsExpanded.variable} ${ttCommonsMono.variable} font-sans`}
|
||||
>
|
||||
<TopNavigation />
|
||||
{/* <ColorBlur
|
||||
className="left-0 top-0 animate-blob"
|
||||
height="25%"
|
||||
width="50%"
|
||||
/> */}
|
||||
<div>{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import React, { HTMLAttributes, ReactNode } from 'react'
|
||||
import Tippy, { TippyProps } from '@tippyjs/react'
|
||||
import 'tippy.js/animations/scale.css'
|
||||
import { ttCommons } from '../../utils/fonts'
|
||||
|
||||
type TooltipProps = {
|
||||
content: ReactNode
|
||||
placement?: TippyProps['placement']
|
||||
className?: string
|
||||
children?: ReactNode
|
||||
delay?: number
|
||||
show?: boolean
|
||||
maxWidth?: string
|
||||
}
|
||||
|
||||
const Tooltip = ({
|
||||
children,
|
||||
content,
|
||||
className,
|
||||
placement = 'top',
|
||||
delay = 0,
|
||||
show = true,
|
||||
maxWidth = '20rem',
|
||||
}: TooltipProps) => {
|
||||
if (show) {
|
||||
return (
|
||||
<Tippy
|
||||
animation="scale"
|
||||
placement={placement}
|
||||
appendTo={() => document.body}
|
||||
maxWidth={maxWidth}
|
||||
interactive
|
||||
delay={[delay, 0]}
|
||||
content={
|
||||
content ? (
|
||||
<div
|
||||
className={`${ttCommons.variable} font-sans rounded-md bg-th-bkg-2 p-3 font-body text-sm tracking-wide text-th-fgd-3 outline-none focus:outline-none ${className}`}
|
||||
style={{ boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.25)' }}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<div className="outline-none focus:outline-none">{children}</div>
|
||||
</Tippy>
|
||||
)
|
||||
} else {
|
||||
return <>{children}</>
|
||||
}
|
||||
}
|
||||
|
||||
const Content = ({
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
className?: string
|
||||
} & HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
className={`inline-block cursor-help border-b border-dashed border-th-fgd-3 border-opacity-20 hover:border-th-bkg-2 ${className}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Tooltip.Content = Content
|
||||
|
||||
export default Tooltip
|
Binary file not shown.
Binary file not shown.
|
@ -23,6 +23,7 @@
|
|||
"dependencies": {
|
||||
"@headlessui/react": "^1.0.0",
|
||||
"@heroicons/react": "^2.0.16",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"decimal.js": "^10.4.3",
|
||||
"gsap": "^3.11.5",
|
||||
"i18next": "^22.4.10",
|
||||
|
|
|
@ -8,17 +8,19 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
const promises = [
|
||||
fetch(`${MANGO_DATA_API_URL}/stats/perp-market-summary`),
|
||||
fetch(`${MANGO_DATA_API_URL}/stats/spot-market-summary`),
|
||||
fetch(`${MANGO_DATA_API_URL}/stats/mango-protocol-summary`),
|
||||
]
|
||||
|
||||
try {
|
||||
const data = await Promise.all(promises)
|
||||
const perpData = await data[0].json()
|
||||
const spotData = await data[1].json()
|
||||
const appData = await data[2].json()
|
||||
|
||||
return {
|
||||
props: {
|
||||
perpData,
|
||||
spotData,
|
||||
appData,
|
||||
...(await serverSideTranslations(locale, [
|
||||
'common',
|
||||
'home',
|
||||
|
@ -31,8 +33,9 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
console.error('Failed to fetch market data', e)
|
||||
return {
|
||||
props: {
|
||||
perpData: null, // or an empty array []
|
||||
spotData: null, // or an empty array []
|
||||
perpData: null,
|
||||
spotData: null,
|
||||
appData: null,
|
||||
...(await serverSideTranslations(locale, [
|
||||
'common',
|
||||
'home',
|
||||
|
@ -47,8 +50,9 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
const Index: NextPage<InferGetStaticPropsType<typeof getStaticProps>> = ({
|
||||
perpData,
|
||||
spotData,
|
||||
appData,
|
||||
}) => {
|
||||
return <HomePage perpData={perpData} spotData={spotData} />
|
||||
return <HomePage perpData={perpData} spotData={spotData} appData={appData} />
|
||||
}
|
||||
|
||||
export default Index
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"active-traders": "Active Traders",
|
||||
"borrow-desc": "All tokens on Mango can be borrowed for use in other DeFi activities. Plus, all deposits earn interest without unlock periods.",
|
||||
"borrow-heading": "Borrow and earn interest",
|
||||
"build-desc": "Mango is 100% open source and highly composable. Build trading bots, new product integrations, community tools or whatever you desire. Explore the code and get building.",
|
||||
|
@ -9,6 +10,8 @@
|
|||
"competitive-fees-desc": "Low fees for taker trades and rebates for maker trades. Plus, Solana's extremely low transaction costs.",
|
||||
"cross-margin": "Cross-margin accounts",
|
||||
"cross-margin-desc": "Leverage across all your positions. Open multiple accounts to isolate your risk.",
|
||||
"daily-trades": "24h Trades",
|
||||
"daily-volume": "24h Volume",
|
||||
"deeply-liquid": "Deeply liquid markets",
|
||||
"deeply-liquid-desc": "Get the best price with access to all of the liquidity on Solana.",
|
||||
"explore-the-code": "Explore the code",
|
||||
|
@ -28,6 +31,9 @@
|
|||
"swap-now": "Swap Now",
|
||||
"token-listings-desc": "Anyone can easily list any token on Mango. A governance proposal is created upon submission and if successful the token will list automatically.",
|
||||
"token-listings-heading": "Permissionless token listings",
|
||||
"tooltip-active-traders": "Weekly active Mango Accounts",
|
||||
"tooltip-daily-trades": "Number of trades across spot, swap and perp",
|
||||
"tooltip-daily-volume": "Volume across spot, swap and perp",
|
||||
"trade-your-way": "Trade your way",
|
||||
"trade-your-way-desc": "Leverage swap or orderbook maxi? Mango is optimized for all devices so you can trade how you want, when you want."
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"active-traders": "Active Traders",
|
||||
"borrow-desc": "All tokens on Mango can be borrowed for use in other DeFi activities. Plus, all deposits earn interest without unlock periods.",
|
||||
"borrow-heading": "Borrow and earn interest",
|
||||
"build-desc": "Mango is 100% open source and highly composable. Build trading bots, new product integrations, community tools or whatever you desire. Explore the code and get building.",
|
||||
|
@ -9,6 +10,8 @@
|
|||
"competitive-fees-desc": "Low fees for taker trades and rebates for maker trades. Plus, Solana's extremely low transaction costs.",
|
||||
"cross-margin": "Cross-margin accounts",
|
||||
"cross-margin-desc": "Leverage across all your positions. Open multiple accounts to isolate your risk.",
|
||||
"daily-trades": "24h Trades",
|
||||
"daily-volume": "24h Volume",
|
||||
"deeply-liquid": "Deeply liquid markets",
|
||||
"deeply-liquid-desc": "Get the best price with access to all of the liquidity on Solana.",
|
||||
"explore-the-code": "Explore the code",
|
||||
|
@ -28,6 +31,9 @@
|
|||
"swap-now": "Swap Now",
|
||||
"token-listings-desc": "Anyone can easily list any token on Mango. A governance proposal is created upon submission and if successful the token will list automatically.",
|
||||
"token-listings-heading": "Permissionless token listings",
|
||||
"tooltip-active-traders": "Weekly active Mango Accounts",
|
||||
"tooltip-daily-trades": "Number of trades across spot, swap and perp",
|
||||
"tooltip-daily-volume": "Volume across spot, swap and perp",
|
||||
"trade-your-way": "Trade your way",
|
||||
"trade-your-way-desc": "Leverage swap or orderbook maxi? Mango is optimized for all devices so you can trade how you want, when you want."
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"active-traders": "Active Traders",
|
||||
"borrow-desc": "All tokens on Mango can be borrowed for use in other DeFi activities. Plus, all deposits earn interest without unlock periods.",
|
||||
"borrow-heading": "Borrow and earn interest",
|
||||
"build-desc": "Mango is 100% open source and highly composable. Build trading bots, new product integrations, community tools or whatever you desire. Explore the code and get building.",
|
||||
|
@ -9,6 +10,8 @@
|
|||
"competitive-fees-desc": "Low fees for taker trades and rebates for maker trades. Plus, Solana's extremely low transaction costs.",
|
||||
"cross-margin": "Cross-margin accounts",
|
||||
"cross-margin-desc": "Leverage across all your positions. Open multiple accounts to isolate your risk.",
|
||||
"daily-trades": "24h Trades",
|
||||
"daily-volume": "24h Volume",
|
||||
"deeply-liquid": "Deeply liquid markets",
|
||||
"deeply-liquid-desc": "Get the best price with access to all of the liquidity on Solana.",
|
||||
"explore-the-code": "Explore the code",
|
||||
|
@ -28,6 +31,9 @@
|
|||
"swap-now": "Swap Now",
|
||||
"token-listings-desc": "Anyone can easily list any token on Mango. A governance proposal is created upon submission and if successful the token will list automatically.",
|
||||
"token-listings-heading": "Permissionless token listings",
|
||||
"tooltip-active-traders": "Weekly active Mango Accounts",
|
||||
"tooltip-daily-trades": "Number of trades across spot, swap and perp",
|
||||
"tooltip-daily-volume": "Volume across spot, swap and perp",
|
||||
"trade-your-way": "Trade your way",
|
||||
"trade-your-way-desc": "Leverage swap or orderbook maxi? Mango is optimized for all devices so you can trade how you want, when you want."
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"active-traders": "Active Traders",
|
||||
"borrow-desc": "All tokens on Mango can be borrowed for use in other DeFi activities. Plus, all deposits earn interest without unlock periods.",
|
||||
"borrow-heading": "Borrow and earn interest",
|
||||
"build-desc": "Mango is 100% open source and highly composable. Build trading bots, new product integrations, community tools or whatever you desire. Explore the code and get building.",
|
||||
|
@ -9,6 +10,8 @@
|
|||
"competitive-fees-desc": "Low fees for taker trades and rebates for maker trades. Plus, Solana's extremely low transaction costs.",
|
||||
"cross-margin": "Cross-margin accounts",
|
||||
"cross-margin-desc": "Leverage across all your positions. Open multiple accounts to isolate your risk.",
|
||||
"daily-trades": "24h Trades",
|
||||
"daily-volume": "24h Volume",
|
||||
"deeply-liquid": "Deeply liquid markets",
|
||||
"deeply-liquid-desc": "Get the best price with access to all of the liquidity on Solana.",
|
||||
"explore-the-code": "Explore the code",
|
||||
|
@ -28,6 +31,9 @@
|
|||
"swap-now": "Swap Now",
|
||||
"token-listings-desc": "Anyone can easily list any token on Mango. A governance proposal is created upon submission and if successful the token will list automatically.",
|
||||
"token-listings-heading": "Permissionless token listings",
|
||||
"tooltip-active-traders": "Weekly active Mango Accounts",
|
||||
"tooltip-daily-trades": "Number of trades across spot, swap and perp",
|
||||
"tooltip-daily-volume": "Volume across spot, swap and perp",
|
||||
"trade-your-way": "Trade your way",
|
||||
"trade-your-way-desc": "Leverage swap or orderbook maxi? Mango is optimized for all devices so you can trade how you want, when you want."
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"active-traders": "Active Traders",
|
||||
"borrow-desc": "All tokens on Mango can be borrowed for use in other DeFi activities. Plus, all deposits earn interest without unlock periods.",
|
||||
"borrow-heading": "Borrow and earn interest",
|
||||
"build-desc": "Mango is 100% open source and highly composable. Build trading bots, new product integrations, community tools or whatever you desire. Explore the code and get building.",
|
||||
|
@ -9,6 +10,8 @@
|
|||
"competitive-fees-desc": "Low fees for taker trades and rebates for maker trades. Plus, Solana's extremely low transaction costs.",
|
||||
"cross-margin": "Cross-margin accounts",
|
||||
"cross-margin-desc": "Leverage across all your positions. Open multiple accounts to isolate your risk.",
|
||||
"daily-trades": "24h Trades",
|
||||
"daily-volume": "24h Volume",
|
||||
"deeply-liquid": "Deeply liquid markets",
|
||||
"deeply-liquid-desc": "Get the best price with access to all of the liquidity on Solana.",
|
||||
"explore-the-code": "Explore the code",
|
||||
|
@ -28,6 +31,9 @@
|
|||
"swap-now": "Swap Now",
|
||||
"token-listings-desc": "Anyone can easily list any token on Mango. A governance proposal is created upon submission and if successful the token will list automatically.",
|
||||
"token-listings-heading": "Permissionless token listings",
|
||||
"tooltip-active-traders": "Weekly active Mango Accounts",
|
||||
"tooltip-daily-trades": "Number of trades across spot, swap and perp",
|
||||
"tooltip-daily-volume": "Volume across spot, swap and perp",
|
||||
"trade-your-way": "Trade your way",
|
||||
"trade-your-way-desc": "Leverage swap or orderbook maxi? Mango is optimized for all devices so you can trade how you want, when you want."
|
||||
}
|
|
@ -2,34 +2,6 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Mono';
|
||||
src: url('/fonts/TT_Commons_Pro_Mono_Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons Expanded';
|
||||
src: url('/fonts/TT_Commons_Pro_Expanded_DemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: normal;
|
||||
src: url('/fonts/TT_Commons_Pro_Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: medium;
|
||||
src: url('/fonts/TT_Commons_Pro_Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: bold;
|
||||
src: url('/fonts/TT_Commons_Pro_DemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
|
||||
html,
|
||||
|
@ -164,6 +136,12 @@ th {
|
|||
|
||||
body {
|
||||
@apply font-body text-sm;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
main {
|
||||
@apply font-body;
|
||||
}
|
||||
|
||||
button {
|
||||
|
|
|
@ -5,9 +5,9 @@ module.exports = {
|
|||
],
|
||||
theme: {
|
||||
fontFamily: {
|
||||
display: ['TT Commons Expanded, sans-serif'],
|
||||
body: 'TT Commons, sans-serif',
|
||||
mono: ['TT Mono, mono'],
|
||||
display: ['var(--font-display)'],
|
||||
body: ['var(--font-body)'],
|
||||
mono: ['var(--font-mono)'],
|
||||
},
|
||||
extend: {
|
||||
height: {
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
export type MarketData = { [key: string]: MarketsDataItem[] }
|
||||
|
||||
export type MarketsDataItem = {
|
||||
base_volume_1h: number
|
||||
base_volume_24h: number
|
||||
change_1h: number
|
||||
change_7d: number
|
||||
change_24h: number
|
||||
change_30d: number
|
||||
last_price: number
|
||||
price_1h: number
|
||||
price_24h: number
|
||||
price_history: { price: number; time: string }[]
|
||||
quote_volume_1h: number
|
||||
quote_volume_24h: number
|
||||
base_volume_1h: number | undefined
|
||||
base_volume_24h: number | undefined
|
||||
change_1h: number | undefined
|
||||
change_7d: number | undefined
|
||||
change_24h: number | undefined
|
||||
change_30d: number | undefined
|
||||
last_price: number | undefined
|
||||
price_1h: number | undefined
|
||||
price_24h: number | undefined
|
||||
price_history: { price: number; time: string }[] | undefined
|
||||
quote_volume_1h: number | undefined
|
||||
quote_volume_24h: number | undefined
|
||||
}
|
||||
|
||||
export type AppStatsData = {
|
||||
weekly_active_mango_accounts: number
|
||||
perp_volume_usd_24h: number
|
||||
num_perp_fills_24h: number
|
||||
swap_volume_usd_24h: number
|
||||
num_swaps_24h: number
|
||||
openbook_volume_usd_24h: number
|
||||
num_openbook_fills_24h: number
|
||||
open_interest: number
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import localFont from 'next/font/local'
|
||||
|
||||
export const ttCommonsMono = localFont({
|
||||
src: '../fonts/TT_Commons_Pro_Mono_Medium.woff2',
|
||||
variable: '--font-mono',
|
||||
})
|
||||
|
||||
export const ttCommons = localFont({
|
||||
src: [
|
||||
{
|
||||
path: '../fonts/TT_Commons_Pro_Normal.woff2',
|
||||
weight: '500',
|
||||
style: 'normal',
|
||||
},
|
||||
{
|
||||
path: '../fonts/TT_Commons_Pro_Medium.woff2',
|
||||
weight: '600',
|
||||
style: 'medium',
|
||||
},
|
||||
{
|
||||
path: '../fonts/TT_Commons_Pro_DemiBold.woff2',
|
||||
weight: '700',
|
||||
style: 'bold',
|
||||
},
|
||||
],
|
||||
variable: '--font-body',
|
||||
})
|
||||
|
||||
export const ttCommonsExpanded = localFont({
|
||||
src: '../fonts/TT_Commons_Pro_Expanded_DemiBold.woff2',
|
||||
variable: '--font-display',
|
||||
})
|
19
yarn.lock
19
yarn.lock
|
@ -701,6 +701,11 @@
|
|||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@popperjs/core@^2.9.0":
|
||||
version "2.11.8"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
||||
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9"
|
||||
|
@ -744,6 +749,13 @@
|
|||
"@babel/runtime" "^7.12.5"
|
||||
"@testing-library/dom" "^7.28.1"
|
||||
|
||||
"@tippyjs/react@^4.2.6":
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.6.tgz#971677a599bf663f20bb1c60a62b9555b749cc71"
|
||||
integrity sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==
|
||||
dependencies:
|
||||
tippy.js "^6.3.1"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||
|
@ -6044,6 +6056,13 @@ tiny-invariant@^1.3.1:
|
|||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
|
||||
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
|
||||
|
||||
tippy.js@^6.3.1:
|
||||
version "6.3.7"
|
||||
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
|
||||
integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
|
||||
dependencies:
|
||||
"@popperjs/core" "^2.9.0"
|
||||
|
||||
tmp@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
|
||||
|
|
Loading…
Reference in New Issue