add customizable slots for new accounts
This commit is contained in:
parent
0566345311
commit
30427a2c66
|
@ -0,0 +1,151 @@
|
|||
import Slider from '@components/forms/Slider'
|
||||
import Button, { IconButton, LinkButton } from '@components/shared/Button'
|
||||
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { MAX_ACCOUNTS } from 'utils/constants'
|
||||
|
||||
export type ACCOUNT_SLOTS = {
|
||||
tokenSlots: number
|
||||
serumSlots: number
|
||||
perpSlots: number
|
||||
}
|
||||
|
||||
export const DEFAULT_SLOTS: ACCOUNT_SLOTS = {
|
||||
tokenSlots: parseInt(MAX_ACCOUNTS.tokenAccounts),
|
||||
serumSlots: parseInt(MAX_ACCOUNTS.spotOpenOrders),
|
||||
perpSlots: parseInt(MAX_ACCOUNTS.perpAccounts),
|
||||
}
|
||||
|
||||
const CreateAccountAdvancedOptions = ({
|
||||
slots,
|
||||
setSlots,
|
||||
onClose,
|
||||
}: {
|
||||
slots: ACCOUNT_SLOTS
|
||||
setSlots: (slots: React.SetStateAction<ACCOUNT_SLOTS> | ACCOUNT_SLOTS) => void
|
||||
onClose?: () => void
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'settings'])
|
||||
|
||||
const handleResetDefaults = () => {
|
||||
setSlots(DEFAULT_SLOTS)
|
||||
}
|
||||
|
||||
const calculateMaxValues = () => {
|
||||
const maxTotalSlots = 28
|
||||
|
||||
const maxTokenSlots = Math.floor(
|
||||
(maxTotalSlots - slots.serumSlots - slots.perpSlots * 2) / 2,
|
||||
)
|
||||
const maxSerumSlots =
|
||||
maxTotalSlots - slots.tokenSlots * 2 - slots.perpSlots * 2
|
||||
const maxPerpSlots = Math.floor(
|
||||
(maxTotalSlots - slots.tokenSlots * 2 - slots.serumSlots) / 2,
|
||||
)
|
||||
|
||||
return {
|
||||
maxTokenSlots,
|
||||
maxSerumSlots,
|
||||
maxPerpSlots,
|
||||
}
|
||||
}
|
||||
|
||||
const handleSliderChange = (property: keyof ACCOUNT_SLOTS, value: string) => {
|
||||
setSlots((prevSlots: ACCOUNT_SLOTS) => ({
|
||||
...prevSlots,
|
||||
[property]: parseInt(value),
|
||||
}))
|
||||
|
||||
const { maxTokenSlots, maxSerumSlots, maxPerpSlots } = calculateMaxValues()
|
||||
|
||||
// Ensure each property stays within its maximum value
|
||||
if (property === 'tokenSlots') {
|
||||
setSlots((prevSlots: ACCOUNT_SLOTS) => ({
|
||||
...prevSlots,
|
||||
serumSlots: Math.min(prevSlots.serumSlots, maxSerumSlots),
|
||||
perpSlots: Math.min(prevSlots.perpSlots, maxPerpSlots),
|
||||
}))
|
||||
} else if (property === 'serumSlots') {
|
||||
setSlots((prevSlots: ACCOUNT_SLOTS) => ({
|
||||
...prevSlots,
|
||||
tokenSlots: Math.min(prevSlots.tokenSlots, maxTokenSlots),
|
||||
perpSlots: Math.min(prevSlots.perpSlots, maxPerpSlots),
|
||||
}))
|
||||
} else if (property === 'perpSlots') {
|
||||
setSlots((prevSlots: ACCOUNT_SLOTS) => ({
|
||||
...prevSlots,
|
||||
tokenSlots: Math.min(prevSlots.tokenSlots, maxTokenSlots),
|
||||
serumSlots: Math.min(prevSlots.serumSlots, maxSerumSlots),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const { tokenSlots, serumSlots, perpSlots } = slots
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-2 flex items-center">
|
||||
{onClose ? (
|
||||
<IconButton className="mr-3" onClick={onClose} size="small">
|
||||
<ArrowLeftIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
<h2 className="w-full text-center">{t('account:advanced-options')}</h2>
|
||||
{onClose ? <div className="h-5 w-5" /> : null}
|
||||
</div>
|
||||
<p>{t('account:advanced-options-desc')}</p>
|
||||
<div className="mt-4 rounded-md border border-th-bkg-3 p-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h3 className="text-sm">{t('settings:account-slots')}</h3>
|
||||
<LinkButton onClick={handleResetDefaults}>
|
||||
{t('account:reset-defaults')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<p>{t('tokens')}</p>
|
||||
<p className="font-mono text-th-fgd-1">{tokenSlots}</p>
|
||||
</div>
|
||||
<Slider
|
||||
amount={tokenSlots}
|
||||
max={calculateMaxValues().maxTokenSlots.toString()}
|
||||
min="0"
|
||||
onChange={(value) => handleSliderChange('tokenSlots', value)}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<p>{t('spot-markets')}</p>
|
||||
<p className="font-mono text-th-fgd-1">{serumSlots}</p>
|
||||
</div>
|
||||
<Slider
|
||||
amount={serumSlots}
|
||||
max={calculateMaxValues().maxSerumSlots.toString()}
|
||||
min="0"
|
||||
onChange={(value) => handleSliderChange('serumSlots', value)}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<p>{t('perp-markets')}</p>
|
||||
<p className="font-mono text-th-fgd-1">{perpSlots}</p>
|
||||
</div>
|
||||
<Slider
|
||||
amount={perpSlots}
|
||||
max={calculateMaxValues().maxPerpSlots.toString()}
|
||||
min="0"
|
||||
onChange={(value) => handleSliderChange('perpSlots', value)}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button className="mt-6 w-full" size="large" onClick={onClose}>
|
||||
{t('save')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateAccountAdvancedOptions
|
|
@ -1,8 +1,8 @@
|
|||
import { ChangeEvent, useState } from 'react'
|
||||
import { ChangeEvent, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { createSolanaMessage, notify } from '../../utils/notifications'
|
||||
import Button, { IconButton } from '../shared/Button'
|
||||
import Button, { IconButton, LinkButton } from '../shared/Button'
|
||||
import BounceLoader from '../shared/BounceLoader'
|
||||
import Input from '../forms/Input'
|
||||
import Label from '../forms/Label'
|
||||
|
@ -18,6 +18,11 @@ import NotificationCookieStore from '@store/notificationCookieStore'
|
|||
import { usePlausible } from 'next-plausible'
|
||||
import { TelemetryEvents } from 'utils/telemetry'
|
||||
import { waitForSlot } from 'utils/network'
|
||||
import CreateAccountAdvancedOptions, {
|
||||
ACCOUNT_SLOTS,
|
||||
DEFAULT_SLOTS,
|
||||
} from './CreateAccountAdvancedOptions'
|
||||
import { EnterBottomExitBottom } from '@components/shared/Transitions'
|
||||
|
||||
const getNextAccountNumber = (accounts: MangoAccount[]): number => {
|
||||
if (accounts.length > 1) {
|
||||
|
@ -43,13 +48,22 @@ const CreateAccountForm = ({
|
|||
const [loading, setLoading] = useState(false)
|
||||
const [name, setName] = useState('')
|
||||
const [signToNotifications, setSignToNotifications] = useState(true)
|
||||
const [showAdvancedOptionsAccountForm, setShowAdvancedOptionsAccountForm] =
|
||||
useState(false)
|
||||
const [slots, setSlots] = useState<ACCOUNT_SLOTS>(DEFAULT_SLOTS)
|
||||
//whole context needed to sign msgs
|
||||
const walletContext = useWallet()
|
||||
const { maxSolDeposit } = useSolBalance()
|
||||
const telemetry = usePlausible<TelemetryEvents>()
|
||||
const setCookie = NotificationCookieStore((s) => s.setCookie)
|
||||
|
||||
const handleNewAccount = async () => {
|
||||
const hasSetCustomOptions = useMemo(() => {
|
||||
return !!Object.entries(slots).find(
|
||||
(slot) => slot[1] !== DEFAULT_SLOTS[slot[0] as keyof ACCOUNT_SLOTS],
|
||||
)
|
||||
}, [slots])
|
||||
|
||||
const handleNewAccount = useCallback(async () => {
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const existingMangoAccts = mangoStore.getState().mangoAccounts
|
||||
|
@ -58,16 +72,19 @@ const CreateAccountForm = ({
|
|||
|
||||
if (!group || !walletContext.wallet) return
|
||||
setLoading(true)
|
||||
const perpOpenOrdersSlots = slots.perpSlots
|
||||
? parseInt(MAX_ACCOUNTS.perpOpenOrders)
|
||||
: 0
|
||||
try {
|
||||
const newAccountNum = getNextAccountNumber(existingMangoAccts)
|
||||
const { signature: tx, slot } = await client.createMangoAccount(
|
||||
group,
|
||||
newAccountNum,
|
||||
name || `Account ${newAccountNum + 1}`,
|
||||
parseInt(MAX_ACCOUNTS.tokenAccounts), // tokens
|
||||
parseInt(MAX_ACCOUNTS.spotOpenOrders), // serum3
|
||||
parseInt(MAX_ACCOUNTS.perpAccounts), // perps
|
||||
parseInt(MAX_ACCOUNTS.perpOpenOrders), // perp Oo
|
||||
slots.tokenSlots, // tokens
|
||||
slots.serumSlots, // serum3
|
||||
slots.perpSlots, // perps
|
||||
perpOpenOrdersSlots, // perp Oo
|
||||
)
|
||||
if (tx) {
|
||||
if (signToNotifications) {
|
||||
|
@ -83,11 +100,16 @@ const CreateAccountForm = ({
|
|||
const newAccount = mangoAccounts.find(
|
||||
(acc) => acc.accountNum === newAccountNum,
|
||||
)
|
||||
const filteredMangoAccounts = reloadedMangoAccounts?.length
|
||||
? reloadedMangoAccounts.filter(
|
||||
(acc) => !acc.name.includes('Leverage Stake'),
|
||||
)
|
||||
: []
|
||||
if (newAccount) {
|
||||
set((s) => {
|
||||
s.mangoAccount.current = newAccount
|
||||
s.mangoAccount.lastSlot = slot
|
||||
s.mangoAccounts = reloadedMangoAccounts
|
||||
s.mangoAccounts = filteredMangoAccounts
|
||||
})
|
||||
}
|
||||
telemetry('accountCreate', {
|
||||
|
@ -116,14 +138,14 @@ const CreateAccountForm = ({
|
|||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [signToNotifications, slots])
|
||||
|
||||
return loading ? (
|
||||
<div className="flex h-full flex-1 flex-col items-center justify-center">
|
||||
<BounceLoader loadingMessage={t('creating-account')} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<div className="relative flex h-full min-h-[462px] flex-col justify-between overflow-hidden">
|
||||
<div className="pb-3">
|
||||
<div className="flex items-center">
|
||||
{handleBack ? (
|
||||
|
@ -163,6 +185,17 @@ const CreateAccountForm = ({
|
|||
{maxSolDeposit <= 0 ? (
|
||||
<InlineNotification type="error" desc={t('deposit-more-sol')} />
|
||||
) : null}
|
||||
<LinkButton
|
||||
className="mx-auto mt-4"
|
||||
onClick={() => setShowAdvancedOptionsAccountForm(true)}
|
||||
>
|
||||
{t('account:advanced-options')}
|
||||
</LinkButton>
|
||||
{hasSetCustomOptions ? (
|
||||
<p className="mt-1 text-center text-th-success">
|
||||
{t('account:custom-account-options-saved')}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<Button
|
||||
className="mt-6 w-full"
|
||||
|
@ -172,6 +205,17 @@ const CreateAccountForm = ({
|
|||
>
|
||||
{t('create-account')}
|
||||
</Button>
|
||||
|
||||
<EnterBottomExitBottom
|
||||
className="absolute bottom-0 left-0 z-20 h-full w-full bg-th-bkg-1"
|
||||
show={showAdvancedOptionsAccountForm}
|
||||
>
|
||||
<CreateAccountAdvancedOptions
|
||||
slots={slots}
|
||||
setSlots={setSlots}
|
||||
onClose={() => setShowAdvancedOptionsAccountForm(false)}
|
||||
/>
|
||||
</EnterBottomExitBottom>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ const Slider = ({
|
|||
? '0% 100%'
|
||||
: ((value - min) * 100) / (max - min) + '% 100%'
|
||||
}
|
||||
}, [value])
|
||||
}, [value, max])
|
||||
|
||||
useEffect(() => {
|
||||
if (amount) {
|
||||
|
|
|
@ -103,13 +103,13 @@ const MangoAccountsListModal = ({
|
|||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<div className="inline-block w-full transform overflow-x-hidden">
|
||||
<div className="flex min-h-[400px] flex-col justify-between">
|
||||
<div className="flex min-h-[462px] flex-col justify-between">
|
||||
<div>
|
||||
<h2 className="text-center">{t('accounts')}</h2>
|
||||
{loading ? (
|
||||
<Loading />
|
||||
) : mangoAccounts.length ? (
|
||||
<div className="thin-scroll mt-4 max-h-[320px] space-y-2 overflow-y-auto">
|
||||
<div className="thin-scroll mt-4 max-h-[374px] space-y-2 overflow-y-auto">
|
||||
{sortedMangoAccounts.map((acc) => {
|
||||
if (
|
||||
mangoAccount &&
|
||||
|
|
|
@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'notifications',
|
||||
|
|
|
@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'borrow',
|
||||
'close-account',
|
||||
'common',
|
||||
|
|
|
@ -40,6 +40,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'notifications',
|
||||
|
|
|
@ -20,6 +20,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'notifications',
|
||||
|
|
|
@ -18,6 +18,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'notifications',
|
||||
|
|
|
@ -14,6 +14,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'notifications',
|
||||
|
|
|
@ -10,6 +10,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'governance',
|
||||
|
|
|
@ -8,6 +8,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'governance',
|
||||
|
|
|
@ -20,6 +20,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'nft-market',
|
||||
|
|
|
@ -8,6 +8,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'governance',
|
||||
|
|
|
@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'close-account',
|
||||
'common',
|
||||
'notifications',
|
||||
|
|
|
@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'activity',
|
||||
'close-account',
|
||||
'common',
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"account-is-private": "Account enabled private mode",
|
||||
"account-stats": "Account Stats",
|
||||
"advanced-options": "Advanced Options",
|
||||
"advanced-options-desc": "Mango Accounts have limits on the number of tokens and markets an account can hold at one time. This is due to how accounts on Solana work. Use the sliders to customize the slots for your new account.",
|
||||
"assets": "Assets",
|
||||
"assets-liabilities": "Assets & Liabilities",
|
||||
"collateral-value": "Collateral Value",
|
||||
"custom-account-options-saved": "Advanced options set",
|
||||
"cumulative-interest-chart": "Cumulative Interest Chart",
|
||||
"daily-volume": "24h Volume",
|
||||
"export": "Export {{dataType}}",
|
||||
|
@ -28,6 +31,7 @@
|
|||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"refresh-balance": "Refresh Balance",
|
||||
"reset-defaults": "Reset Defaults",
|
||||
"slots-open-account": "Open a new account to increase the number of tokens you can hold on Mango.",
|
||||
"slots-settings-path": "Account Settings > Account Slots",
|
||||
"token-slots-full": "You're out of token slots",
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"account-is-private": "Account enabled private mode",
|
||||
"account-stats": "Account Stats",
|
||||
"advanced-options": "Advanced Options",
|
||||
"advanced-options-desc": "Mango Accounts have limits on the number of tokens and markets an account can hold at one time. This is due to how accounts on Solana work. Use the sliders to customize the slots for your new account.",
|
||||
"assets": "Assets",
|
||||
"assets-liabilities": "Assets & Liabilities",
|
||||
"collateral-value": "Collateral Value",
|
||||
"custom-account-options-saved": "Advanced options set",
|
||||
"cumulative-interest-chart": "Cumulative Interest Chart",
|
||||
"daily-volume": "24h Volume",
|
||||
"export": "Export {{dataType}}",
|
||||
|
@ -28,6 +31,7 @@
|
|||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"refresh-balance": "Refresh Balance",
|
||||
"reset-defaults": "Reset Defaults",
|
||||
"slots-open-account": "Open a new account to increase the number of tokens you can hold on Mango.",
|
||||
"slots-settings-path": "Account Settings > Account Slots",
|
||||
"token-slots-full": "You're out of token slots",
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"account-is-private": "Account enabled private mode",
|
||||
"account-stats": "Account Stats",
|
||||
"advanced-options": "Advanced Options",
|
||||
"advanced-options-desc": "Mango Accounts have limits on the number of tokens and markets an account can hold at one time. This is due to how accounts on Solana work. Use the sliders to customize the slots for your new account.",
|
||||
"assets": "Assets",
|
||||
"assets-liabilities": "Assets & Liabilities",
|
||||
"collateral-value": "Collateral Value",
|
||||
"custom-account-options-saved": "Advanced options set",
|
||||
"cumulative-interest-chart": "Cumulative Interest Chart",
|
||||
"daily-volume": "24h Volume",
|
||||
"export": "Export {{dataType}}",
|
||||
|
@ -28,6 +31,7 @@
|
|||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"refresh-balance": "Refresh Balance",
|
||||
"reset-defaults": "Reset Defaults",
|
||||
"slots-open-account": "Open a new account to increase the number of tokens you can hold on Mango.",
|
||||
"slots-settings-path": "Account Settings > Account Slots",
|
||||
"token-slots-full": "You're out of token slots",
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"account-is-private": "此帐户已开启隐私模式",
|
||||
"account-stats": "帐户统计",
|
||||
"advanced-options": "Advanced Options",
|
||||
"advanced-options-desc": "Mango Accounts have limits on the number of tokens and markets an account can hold at one time. This is due to how accounts on Solana work. Use the sliders to customize the slots for your new account.",
|
||||
"assets": "资产",
|
||||
"assets-liabilities": "资产和债务",
|
||||
"collateral-value": "质押品价值",
|
||||
"custom-account-options-saved": "Advanced options set",
|
||||
"cumulative-interest-chart": "累积利息图表",
|
||||
"daily-volume": "24小时交易量",
|
||||
"export": "导出{{dataType}}",
|
||||
|
@ -28,6 +31,7 @@
|
|||
"pnl-chart": "盈亏图表",
|
||||
"pnl-history": "盈亏历史",
|
||||
"refresh-balance": "更新余额",
|
||||
"reset-defaults": "Reset Defaults",
|
||||
"slots-open-account": "Open a new account to increase the number of tokens you can hold on Mango.",
|
||||
"slots-settings-path": "Account Settings > Account Slots",
|
||||
"token-slots-full": "You're out of token slots",
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"account-is-private": "此帳戶已開啟隱私模式",
|
||||
"account-stats": "帳戶統計",
|
||||
"advanced-options": "Advanced Options",
|
||||
"advanced-options-desc": "Mango Accounts have limits on the number of tokens and markets an account can hold at one time. This is due to how accounts on Solana work. Use the sliders to customize the slots for your new account.",
|
||||
"assets": "資產",
|
||||
"assets-liabilities": "資產和債務",
|
||||
"collateral-value": "質押品價值",
|
||||
"custom-account-options-saved": "Advanced options set",
|
||||
"cumulative-interest-chart": "累積利息圖表",
|
||||
"daily-volume": "24小時交易量",
|
||||
"export": "導出{{dataType}}",
|
||||
|
@ -28,6 +31,7 @@
|
|||
"pnl-chart": "盈虧圖表",
|
||||
"pnl-history": "盈虧歷史",
|
||||
"refresh-balance": "更新餘額",
|
||||
"reset-defaults": "Reset Defaults",
|
||||
"slots-open-account": "Open a new account to increase the number of tokens you can hold on Mango.",
|
||||
"slots-settings-path": "Account Settings > Account Slots",
|
||||
"token-slots-full": "You're out of token slots",
|
||||
|
|
Loading…
Reference in New Issue