add equity leaderboard
This commit is contained in:
parent
8cc4053fc0
commit
761dbeccc1
|
@ -1,12 +1,11 @@
|
||||||
|
import TabsText from '@components/shared/TabsText'
|
||||||
import SwapOrders from '@components/swap/SwapTriggerOrders'
|
import SwapOrders from '@components/swap/SwapTriggerOrders'
|
||||||
import OpenOrders from '@components/trade/OpenOrders'
|
import OpenOrders from '@components/trade/OpenOrders'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
const AccountOrders = () => {
|
const AccountOrders = () => {
|
||||||
const { t } = useTranslation('trade')
|
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
|
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
|
||||||
const [activeTab, setActiveTab] = useState('trade:limit')
|
const [activeTab, setActiveTab] = useState('trade:limit')
|
||||||
|
@ -24,21 +23,12 @@ const AccountOrders = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex space-x-6 px-4 py-4 md:px-6">
|
<div className="px-4 py-4 md:px-6">
|
||||||
{tabsWithCount.map((tab) => (
|
<TabsText
|
||||||
<button
|
activeTab={activeTab}
|
||||||
className={`flex items-center space-x-2 text-base font-bold focus:outline-none ${
|
onChange={setActiveTab}
|
||||||
activeTab === tab[0] ? 'text-th-active' : ''
|
tabs={tabsWithCount}
|
||||||
}`}
|
/>
|
||||||
onClick={() => setActiveTab(tab[0])}
|
|
||||||
key={tab[0]}
|
|
||||||
>
|
|
||||||
<span>{t(tab[0])}</span>
|
|
||||||
<div className="rounded-md bg-th-bkg-3 px-1.5 py-0.5 font-body text-xs font-medium text-th-fgd-2">
|
|
||||||
<span>{tab[1]}</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-1 flex-col border-t border-th-bkg-3">
|
<div className="flex flex-1 flex-col border-t border-th-bkg-3">
|
||||||
{activeTab === 'trade:limit' ? <OpenOrders /> : <SwapOrders />}
|
{activeTab === 'trade:limit' ? <OpenOrders /> : <SwapOrders />}
|
||||||
|
|
|
@ -9,40 +9,92 @@ import { useMemo, useState } from 'react'
|
||||||
import { EmptyObject } from 'types'
|
import { EmptyObject } from 'types'
|
||||||
import { MANGO_DATA_API_URL } from 'utils/constants'
|
import { MANGO_DATA_API_URL } from 'utils/constants'
|
||||||
import LeaderboardTable from './LeaderboardTable'
|
import LeaderboardTable from './LeaderboardTable'
|
||||||
|
import TabsText from '@components/shared/TabsText'
|
||||||
|
|
||||||
export interface LeaderboardRes {
|
export interface LeaderboardRes {
|
||||||
date_hour: string
|
date_hour: string
|
||||||
mango_account: string
|
mango_account: string
|
||||||
pnl: number
|
|
||||||
profile_image_url: string | null
|
profile_image_url: string | null
|
||||||
profile_name: string
|
profile_name: string
|
||||||
start_date_hour: string
|
|
||||||
trader_category: string
|
trader_category: string
|
||||||
wallet_pk: string
|
wallet_pk: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DaysToShow = '1DAY' | '1WEEK' | 'ALLTIME'
|
export interface PnlLeaderboardRes extends LeaderboardRes {
|
||||||
|
pnl: number
|
||||||
|
start_date_hour: string
|
||||||
|
}
|
||||||
|
|
||||||
const isLeaderboard = (
|
export interface EquityLeaderboardRes extends LeaderboardRes {
|
||||||
response: null | EmptyObject | LeaderboardRes[],
|
account_equity: number
|
||||||
): response is LeaderboardRes[] => {
|
}
|
||||||
|
|
||||||
|
const isPnlLeaderboardRes = (
|
||||||
|
response: null | EmptyObject | PnlLeaderboardRes[],
|
||||||
|
): response is PnlLeaderboardRes[] => {
|
||||||
if (response && Array.isArray(response)) {
|
if (response && Array.isArray(response)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchLeaderboard = async (
|
const isEquityLeaderboardRes = (
|
||||||
|
response: null | EmptyObject | EquityLeaderboardRes[],
|
||||||
|
): response is EquityLeaderboardRes[] => {
|
||||||
|
if (response && Array.isArray(response)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isPnlLeaderboard = (
|
||||||
|
item: PnlLeaderboardRes | EquityLeaderboardRes,
|
||||||
|
): item is PnlLeaderboardRes => {
|
||||||
|
if ('pnl' in item) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isEquityLeaderboard = (
|
||||||
|
item: PnlLeaderboardRes | EquityLeaderboardRes,
|
||||||
|
): item is EquityLeaderboardRes => {
|
||||||
|
if ('account_equity' in item) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type DaysToShow = '1DAY' | '1WEEK' | 'ALLTIME'
|
||||||
|
|
||||||
|
const fetchPnlLeaderboard = async (
|
||||||
daysToShow: DaysToShow,
|
daysToShow: DaysToShow,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
): Promise<Array<LeaderboardRes>> => {
|
): Promise<Array<PnlLeaderboardRes>> => {
|
||||||
const data = await fetch(
|
const data = await fetch(
|
||||||
`${MANGO_DATA_API_URL}/leaderboard-pnl?over_period=${daysToShow}&offset=${offset}`,
|
`${MANGO_DATA_API_URL}/leaderboard-pnl?over_period=${daysToShow}&offset=${offset}`,
|
||||||
)
|
)
|
||||||
const parsedData: null | EmptyObject | LeaderboardRes[] = await data.json()
|
const parsedData: null | EmptyObject | PnlLeaderboardRes[] = await data.json()
|
||||||
|
|
||||||
let leaderboardData
|
let leaderboardData
|
||||||
if (isLeaderboard(parsedData)) {
|
if (isPnlLeaderboardRes(parsedData)) {
|
||||||
|
leaderboardData = parsedData
|
||||||
|
}
|
||||||
|
|
||||||
|
return leaderboardData ?? []
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchEquityLeaderboard = async (
|
||||||
|
offset = 0,
|
||||||
|
): Promise<Array<EquityLeaderboardRes>> => {
|
||||||
|
const data = await fetch(
|
||||||
|
`${MANGO_DATA_API_URL}/leaderboard-account-equity?offset=${offset}`,
|
||||||
|
)
|
||||||
|
const parsedData: null | EmptyObject | EquityLeaderboardRes[] =
|
||||||
|
await data.json()
|
||||||
|
|
||||||
|
let leaderboardData
|
||||||
|
if (isEquityLeaderboardRes(parsedData)) {
|
||||||
leaderboardData = parsedData
|
leaderboardData = parsedData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,12 +104,18 @@ const fetchLeaderboard = async (
|
||||||
const LeaderboardPage = () => {
|
const LeaderboardPage = () => {
|
||||||
const { t } = useTranslation(['common', 'leaderboard'])
|
const { t } = useTranslation(['common', 'leaderboard'])
|
||||||
const [daysToShow, setDaysToShow] = useState<DaysToShow>('ALLTIME')
|
const [daysToShow, setDaysToShow] = useState<DaysToShow>('ALLTIME')
|
||||||
|
const [leaderboardToShow, setLeaderboardToShow] = useState('pnl')
|
||||||
const { hiddenAccounts } = useHiddenMangoAccounts()
|
const { hiddenAccounts } = useHiddenMangoAccounts()
|
||||||
|
|
||||||
const { data, isLoading, isFetching, isFetchingNextPage, fetchNextPage } =
|
const {
|
||||||
useInfiniteQuery(
|
data: pnlData,
|
||||||
['leaderboard', daysToShow],
|
isLoading: loadingPnl,
|
||||||
({ pageParam }) => fetchLeaderboard(daysToShow, pageParam),
|
isFetching: fetchingPnl,
|
||||||
|
isFetchingNextPage: fetchingNextPnlPage,
|
||||||
|
fetchNextPage: fetchNextPnlPage,
|
||||||
|
} = useInfiniteQuery(
|
||||||
|
['pnl-leaderboard', daysToShow, leaderboardToShow],
|
||||||
|
({ pageParam }) => fetchPnlLeaderboard(daysToShow, pageParam),
|
||||||
{
|
{
|
||||||
cacheTime: 1000 * 60 * 10,
|
cacheTime: 1000 * 60 * 10,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
|
@ -68,56 +126,113 @@ const LeaderboardPage = () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const leaderboardData = useMemo(() => {
|
const {
|
||||||
if (data?.pages.length) {
|
data: equityData,
|
||||||
|
isLoading: loadingEquity,
|
||||||
|
isFetching: fetchingEquity,
|
||||||
|
isFetchingNextPage: fetchingNextEquityPage,
|
||||||
|
fetchNextPage: fetchNextEquityPage,
|
||||||
|
} = useInfiniteQuery(
|
||||||
|
['equity-leaderboard', leaderboardToShow],
|
||||||
|
({ pageParam }) => fetchEquityLeaderboard(pageParam),
|
||||||
|
{
|
||||||
|
cacheTime: 1000 * 60 * 10,
|
||||||
|
staleTime: 1000 * 60 * 5,
|
||||||
|
retry: 3,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
keepPreviousData: true,
|
||||||
|
getNextPageParam: (_lastPage, pages) => pages.length * 20,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const pnlLeaderboardData = useMemo(() => {
|
||||||
|
if (pnlData?.pages.length) {
|
||||||
if (hiddenAccounts) {
|
if (hiddenAccounts) {
|
||||||
return data.pages
|
return pnlData.pages
|
||||||
.flat()
|
.flat()
|
||||||
.filter((d) => !hiddenAccounts.includes(d.mango_account))
|
.filter((d) => !hiddenAccounts.includes(d.mango_account))
|
||||||
} else {
|
} else {
|
||||||
return data.pages.flat()
|
return pnlData.pages.flat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}, [data, daysToShow])
|
}, [pnlData, daysToShow])
|
||||||
|
|
||||||
|
const equityLeaderboardData = useMemo(() => {
|
||||||
|
if (equityData?.pages.length) {
|
||||||
|
if (hiddenAccounts) {
|
||||||
|
return equityData.pages
|
||||||
|
.flat()
|
||||||
|
.filter((d) => !hiddenAccounts.includes(d.mango_account))
|
||||||
|
} else {
|
||||||
|
return equityData.pages.flat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}, [equityData])
|
||||||
|
|
||||||
|
const leaderboardData = useMemo(() => {
|
||||||
|
return leaderboardToShow === 'pnl'
|
||||||
|
? pnlLeaderboardData
|
||||||
|
: equityLeaderboardData
|
||||||
|
}, [leaderboardToShow, pnlLeaderboardData, equityLeaderboardData])
|
||||||
|
|
||||||
const handleDaysToShow = (days: DaysToShow) => {
|
const handleDaysToShow = (days: DaysToShow) => {
|
||||||
setDaysToShow(days)
|
setDaysToShow(days)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isPnl = leaderboardToShow === 'pnl'
|
||||||
|
const loading = isPnl ? loadingPnl : loadingEquity
|
||||||
|
const fetching = isPnl ? fetchingPnl : fetchingEquity
|
||||||
|
const fetchingNextPage = isPnl ? fetchingNextPnlPage : fetchingNextEquityPage
|
||||||
|
const fetchNextPage = isPnl ? fetchNextPnlPage : fetchNextEquityPage
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 md:p-10 lg:px-0">
|
<div className="p-4 md:p-10 lg:px-0">
|
||||||
<div className="grid grid-cols-12">
|
<div className="grid grid-cols-12">
|
||||||
<div className="col-span-12 lg:col-span-8 lg:col-start-3">
|
<div className="col-span-12 lg:col-span-8 lg:col-start-3">
|
||||||
<div className="mb-6 flex flex-col md:flex-row md:items-center md:justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 className="mb-2">{t('leaderboard')}</h1>
|
<h1 className="mb-2">{t('leaderboard')}</h1>
|
||||||
<p className="mb-4 md:mb-0">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
{t('leaderboard:leaderboard-desc')}
|
<div>
|
||||||
|
<TabsText
|
||||||
|
activeTab={leaderboardToShow}
|
||||||
|
onChange={(v: string) => setLeaderboardToShow(v)}
|
||||||
|
tabs={[
|
||||||
|
['pnl', 0],
|
||||||
|
['equity', 0],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<p className="mt-1">
|
||||||
|
{isPnl
|
||||||
|
? t('leaderboard:leaderboard-desc-pnl')
|
||||||
|
: t('leaderboard:leaderboard-desc-equity')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{isPnl ? (
|
||||||
<div className="w-full md:w-48">
|
<div className="w-full md:w-48">
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
activeValue={daysToShow}
|
activeValue={daysToShow}
|
||||||
disabled={isLoading}
|
disabled={loadingPnl}
|
||||||
onChange={(v) => handleDaysToShow(v)}
|
onChange={(v) => handleDaysToShow(v)}
|
||||||
names={['24h', '7d', '30d', t('all')]}
|
names={['24h', '7d', '30d', t('all')]}
|
||||||
values={['1DAY', '1WEEK', 'ALLTIME']}
|
values={['1DAY', '1WEEK', 'ALLTIME']}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{leaderboardData.length ? (
|
{leaderboardData.length ? (
|
||||||
<LeaderboardTable
|
<LeaderboardTable
|
||||||
data={leaderboardData}
|
data={leaderboardData}
|
||||||
loading={isFetching && !isFetchingNextPage}
|
loading={fetching && !fetchingNextPage}
|
||||||
|
type={leaderboardToShow}
|
||||||
/>
|
/>
|
||||||
) : !isFetching && !isLoading ? (
|
) : !fetching && !loading ? (
|
||||||
<div className="flex flex-col items-center rounded-md border border-th-bkg-3 p-4">
|
<div className="flex flex-col items-center rounded-md border border-th-bkg-3 p-4">
|
||||||
<NoSymbolIcon className="mb-1 h-7 w-7 text-th-fgd-4" />
|
<NoSymbolIcon className="mb-1 h-7 w-7 text-th-fgd-4" />
|
||||||
<p>{t('leaderboard:leaderboard-unavailable')}</p>
|
<p>{t('leaderboard:leaderboard-unavailable')}</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{isLoading || isFetchingNextPage ? (
|
{loading || fetchingNextPage ? (
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
{[...Array(20)].map((x, i) => (
|
{[...Array(20)].map((x, i) => (
|
||||||
<SheenLoader className="flex flex-1" key={i}>
|
<SheenLoader className="flex flex-1" key={i}>
|
||||||
|
|
|
@ -4,14 +4,21 @@ import SheenLoader from '@components/shared/SheenLoader'
|
||||||
import { ChevronRightIcon } from '@heroicons/react/20/solid'
|
import { ChevronRightIcon } from '@heroicons/react/20/solid'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
import { formatCurrencyValue } from 'utils/numbers'
|
import { formatCurrencyValue } from 'utils/numbers'
|
||||||
import { LeaderboardRes } from './LeaderboardPage'
|
import {
|
||||||
|
EquityLeaderboardRes,
|
||||||
|
PnlLeaderboardRes,
|
||||||
|
isEquityLeaderboard,
|
||||||
|
isPnlLeaderboard,
|
||||||
|
} from './LeaderboardPage'
|
||||||
|
|
||||||
const LeaderboardTable = ({
|
const LeaderboardTable = ({
|
||||||
data,
|
data,
|
||||||
loading,
|
loading,
|
||||||
|
type,
|
||||||
}: {
|
}: {
|
||||||
data: LeaderboardRes[]
|
data: PnlLeaderboardRes[] | EquityLeaderboardRes[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
|
type: string
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -21,7 +28,8 @@ const LeaderboardTable = ({
|
||||||
item={d}
|
item={d}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
rank={i + 1}
|
rank={i + 1}
|
||||||
key={d.mango_account + d.pnl + i}
|
key={d.mango_account + i}
|
||||||
|
type={type}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,13 +43,20 @@ const LeaderboardRow = ({
|
||||||
item,
|
item,
|
||||||
loading,
|
loading,
|
||||||
rank,
|
rank,
|
||||||
|
type,
|
||||||
}: {
|
}: {
|
||||||
item: LeaderboardRes
|
item: PnlLeaderboardRes | EquityLeaderboardRes
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
rank: number
|
rank: number
|
||||||
|
type: string
|
||||||
}) => {
|
}) => {
|
||||||
const { profile_name, profile_image_url, mango_account, pnl, wallet_pk } =
|
const { profile_name, profile_image_url, mango_account, wallet_pk } = item
|
||||||
item
|
const value =
|
||||||
|
type === 'pnl' && isPnlLeaderboard(item)
|
||||||
|
? item.pnl
|
||||||
|
: isEquityLeaderboard(item)
|
||||||
|
? item.account_equity
|
||||||
|
: 0
|
||||||
const { isTablet } = useViewport()
|
const { isTablet } = useViewport()
|
||||||
|
|
||||||
return !loading ? (
|
return !loading ? (
|
||||||
|
@ -83,7 +98,7 @@ const LeaderboardRow = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="mr-3 text-right font-mono md:text-base">
|
<span className="mr-3 text-right font-mono md:text-base">
|
||||||
{formatCurrencyValue(pnl, 2)}
|
{formatCurrencyValue(value, 2)}
|
||||||
</span>
|
</span>
|
||||||
<ChevronRightIcon className="h-5 w-5 text-th-fgd-3" />
|
<ChevronRightIcon className="h-5 w-5 text-th-fgd-3" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
const TabsText = ({
|
||||||
|
tabs,
|
||||||
|
activeTab,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
tabs: [string, number][]
|
||||||
|
activeTab: string
|
||||||
|
onChange: (tab: string) => void
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
|
return (
|
||||||
|
<div className="flex space-x-6">
|
||||||
|
{tabs.map((tab) => (
|
||||||
|
<button
|
||||||
|
className={`flex items-center space-x-2 text-base font-bold focus:outline-none ${
|
||||||
|
activeTab === tab[0] ? 'text-th-active' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => onChange(tab[0])}
|
||||||
|
key={tab[0]}
|
||||||
|
>
|
||||||
|
<span>{t(tab[0])}</span>
|
||||||
|
{tab[1] ? (
|
||||||
|
<div className="rounded-md bg-th-bkg-3 px-1.5 py-0.5 font-body text-xs font-medium text-th-fgd-2">
|
||||||
|
<span>{tab[1]}</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabsText
|
|
@ -79,6 +79,7 @@
|
||||||
"edit-account": "Edit Account Name",
|
"edit-account": "Edit Account Name",
|
||||||
"edit-profile-image": "Edit Profile Image",
|
"edit-profile-image": "Edit Profile Image",
|
||||||
"enable-notifications": "Enable Notifications",
|
"enable-notifications": "Enable Notifications",
|
||||||
|
"equity": "Equity",
|
||||||
"error-borrow-exceeds-limit": "Maximum borrow for the current period is {{remaining}}. New period starts {{resetTime}}",
|
"error-borrow-exceeds-limit": "Maximum borrow for the current period is {{remaining}}. New period starts {{resetTime}}",
|
||||||
"error-repay-insufficient-funds": "Not enough {{token}} in your wallet to repay this amount",
|
"error-repay-insufficient-funds": "Not enough {{token}} in your wallet to repay this amount",
|
||||||
"error-token-positions-full": "Not enough token positions available in your account",
|
"error-token-positions-full": "Not enough token positions available in your account",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"leaderboard-desc": "Top 100 Mango traders by total PnL (spot and perp)",
|
"leaderboard-desc-equity": "Top 100 Mango traders by account equity",
|
||||||
|
"leaderboard-desc-pnl": "Top 100 Mango traders by total PnL (spot and perp)",
|
||||||
"leaderboard-unavailable": "Leaderboard unavailable"
|
"leaderboard-unavailable": "Leaderboard unavailable"
|
||||||
}
|
}
|
|
@ -79,6 +79,7 @@
|
||||||
"edit-account": "Edit Account Name",
|
"edit-account": "Edit Account Name",
|
||||||
"edit-profile-image": "Edit Profile Image",
|
"edit-profile-image": "Edit Profile Image",
|
||||||
"enable-notifications": "Enable Notifications",
|
"enable-notifications": "Enable Notifications",
|
||||||
|
"equity": "Equity",
|
||||||
"error-borrow-exceeds-limit": "Maximum borrow for the current period is {{remaining}}. New period starts {{resetTime}}",
|
"error-borrow-exceeds-limit": "Maximum borrow for the current period is {{remaining}}. New period starts {{resetTime}}",
|
||||||
"error-repay-insufficient-funds": "Not enough {{token}} in your wallet to repay this amount",
|
"error-repay-insufficient-funds": "Not enough {{token}} in your wallet to repay this amount",
|
||||||
"error-token-positions-full": "Not enough token positions available in your account",
|
"error-token-positions-full": "Not enough token positions available in your account",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"leaderboard-desc": "Top 100 Mango traders by total PnL (spot and perp)",
|
"leaderboard-desc-equity": "Top 100 Mango traders by account equity",
|
||||||
|
"leaderboard-desc-pnl": "Top 100 Mango traders by total PnL (spot and perp)",
|
||||||
"leaderboard-unavailable": "Leaderboard unavailable"
|
"leaderboard-unavailable": "Leaderboard unavailable"
|
||||||
}
|
}
|
|
@ -79,6 +79,7 @@
|
||||||
"edit-account": "Edit Account Name",
|
"edit-account": "Edit Account Name",
|
||||||
"edit-profile-image": "Edit Profile Image",
|
"edit-profile-image": "Edit Profile Image",
|
||||||
"enable-notifications": "Enable Notifications",
|
"enable-notifications": "Enable Notifications",
|
||||||
|
"equity": "Equity",
|
||||||
"error-borrow-exceeds-limit": "Maximum borrow for the current period is {{remaining}}. New period starts {{resetTime}}",
|
"error-borrow-exceeds-limit": "Maximum borrow for the current period is {{remaining}}. New period starts {{resetTime}}",
|
||||||
"error-repay-insufficient-funds": "Not enough {{token}} in your wallet to repay this amount",
|
"error-repay-insufficient-funds": "Not enough {{token}} in your wallet to repay this amount",
|
||||||
"error-token-positions-full": "Not enough token positions available in your account",
|
"error-token-positions-full": "Not enough token positions available in your account",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"leaderboard-desc": "Top 100 Mango traders by total PnL (spot and perp)",
|
"leaderboard-desc-equity": "Top 100 Mango traders by account equity",
|
||||||
|
"leaderboard-desc-pnl": "Top 100 Mango traders by total PnL (spot and perp)",
|
||||||
"leaderboard-unavailable": "Leaderboard unavailable"
|
"leaderboard-unavailable": "Leaderboard unavailable"
|
||||||
}
|
}
|
|
@ -79,6 +79,7 @@
|
||||||
"edit-account": "编辑帐户标签",
|
"edit-account": "编辑帐户标签",
|
||||||
"edit-profile-image": "切换头像",
|
"edit-profile-image": "切换头像",
|
||||||
"enable-notifications": "开启通知",
|
"enable-notifications": "开启通知",
|
||||||
|
"equity": "Equity",
|
||||||
"error-borrow-exceeds-limit": "目前期间的最大借贷为{{remaining}}.新期间从{{resetTime}}开始",
|
"error-borrow-exceeds-limit": "目前期间的最大借贷为{{remaining}}.新期间从{{resetTime}}开始",
|
||||||
"error-repay-insufficient-funds": "钱包里的{{token}}不足归还",
|
"error-repay-insufficient-funds": "钱包里的{{token}}不足归还",
|
||||||
"error-token-positions-full": "你帐户的币位已占满",
|
"error-token-positions-full": "你帐户的币位已占满",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"leaderboard-desc": "按总盈亏(现货和永续合约)计算的前100名Mango交易商",
|
"leaderboard-desc-equity": "Top 100 Mango traders by account equity",
|
||||||
|
"leaderboard-desc-pnl": "按总盈亏(现货和永续合约)计算的前100名Mango交易商",
|
||||||
"leaderboard-unavailable": "无法显示排行榜"
|
"leaderboard-unavailable": "无法显示排行榜"
|
||||||
}
|
}
|
|
@ -79,6 +79,7 @@
|
||||||
"edit-account": "編輯帳戶標籤",
|
"edit-account": "編輯帳戶標籤",
|
||||||
"edit-profile-image": "切換頭像",
|
"edit-profile-image": "切換頭像",
|
||||||
"enable-notifications": "開啟通知",
|
"enable-notifications": "開啟通知",
|
||||||
|
"equity": "Equity",
|
||||||
"error-borrow-exceeds-limit": "目前期間的最大借貸為{{remaining}}.新期間從{{resetTime}}開始",
|
"error-borrow-exceeds-limit": "目前期間的最大借貸為{{remaining}}.新期間從{{resetTime}}開始",
|
||||||
"error-repay-insufficient-funds": "錢包裡的{{token}}不足歸還",
|
"error-repay-insufficient-funds": "錢包裡的{{token}}不足歸還",
|
||||||
"error-token-positions-full": "你帳戶的幣位已占滿",
|
"error-token-positions-full": "你帳戶的幣位已占滿",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"leaderboard-desc": "按總盈虧(現貨和永續合約)計算的前100名Mango交易商",
|
"leaderboard-desc-equity": "Top 100 Mango traders by account equity",
|
||||||
|
"leaderboard-desc-pnl": "按總盈虧(現貨和永續合約)計算的前100名Mango交易商",
|
||||||
"leaderboard-unavailable": "無法顯示排行榜"
|
"leaderboard-unavailable": "無法顯示排行榜"
|
||||||
}
|
}
|
Loading…
Reference in New Issue