Merge branch 'main' into chinese-localization

This commit is contained in:
rjpeterson 2023-12-02 19:03:49 -08:00
commit adb73e1cd0
57 changed files with 514 additions and 203 deletions

View File

@ -1,4 +1,4 @@
import { Group } from '@blockworks-foundation/mango-v4'
import { Group, I64_MAX_BN } from '@blockworks-foundation/mango-v4'
import { MangoTokenStatsItem, TokenStatsItem } from 'types'
import { MANGO_DATA_API_URL } from 'utils/constants'
@ -39,10 +39,40 @@ export const processTokenStatsData = (
mangoStatsMap[date].feesCollected += c.collected_fees * uiPrice
})
// add most recent value, using most recent datapoint to patch difficult to compute stats
for (const banks of group.banksMapByTokenIndex.values()) {
const bank = banks[0]
const now = new Date().toISOString()
const filtered = data.filter(
(x: TokenStatsItem) => bank.tokenIndex === x?.token_index,
)
if (!filtered || filtered.length === 0) {
continue
}
const previous = filtered.reduce((max, cur) =>
max.date_hour > cur.date_hour ? max : cur,
)
let tokenStatsItem: TokenStatsItem = {
borrow_apr: previous.borrow_apr,
borrow_rate: bank.getBorrowRateUi() / 100,
collected_fees: previous.collected_fees,
date_hour: now,
deposit_apr: previous.deposit_apr,
deposit_rate: bank.getDepositRateUi() / 100,
mango_group: bank.group.toBase58(),
price: bank.uiPrice,
symbol: bank.name,
token_index: bank.tokenIndex,
total_borrows: bank.uiBorrows(),
total_deposits: bank.uiDeposits(),
}
data.push(tokenStatsItem)
}
const mangoStats: MangoTokenStatsItem[] = Object.values(mangoStatsMap)
mangoStats.sort(
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
)
return mangoStats
return [data, mangoStats]
}

View File

@ -119,7 +119,7 @@ const CreateAccountForm = ({
}
return loading ? (
<div className="flex flex-1 flex-col items-center justify-center">
<div className="flex h-full flex-1 flex-col items-center justify-center">
<BounceLoader loadingMessage={t('creating-account')} />
</div>
) : (

View File

@ -9,6 +9,7 @@ import { useState } from 'react'
import Loading from '@components/shared/Loading'
import Tooltip from '@components/shared/Tooltip'
import { useTranslation } from 'react-i18next'
import { SETTINGS_BUTTON_TITLE_CLASSES } from '@components/settings/AccountSettings'
const HideMangoAccount = () => {
const { t } = useTranslation('settings')
@ -39,7 +40,9 @@ const HideMangoAccount = () => {
<>
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
<Tooltip content={t('settings:tooltip-private-account')}>
<p className="tooltip-underline">{t('settings:private-account')}</p>
<p className={`tooltip-underline ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:private-account')}
</p>
</Tooltip>
{signingForHide ? (
<Loading />

View File

@ -28,10 +28,13 @@ const Explore = () => {
const tabsWithCount: [string, number][] = useMemo(() => {
const perpMarkets = mangoStore.getState().perpMarkets
const followedAccountsNumber = followedAccounts
? followedAccounts.length
: 0
const tabs: [string, number][] = [
['tokens', banks.length],
['perp', perpMarkets.length],
['account:followed-accounts', followedAccounts?.length],
['account:followed-accounts', followedAccountsNumber],
]
return tabs
}, [banks, followedAccounts])
@ -69,7 +72,7 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
switch (activeTab) {
case 'tokens':
return <Spot />
case 'perp-markets':
case 'perp':
return (
<div className="mt-6 border-t border-th-bkg-3">
<PerpMarketsTable />

View File

@ -43,8 +43,8 @@ import { formatTokenSymbol } from 'utils/tokens'
export type FollowedAccountApi = {
mango_account: string
profile_image_url: string
profile_name: string
profile_image_url: string | undefined
profile_name: string | undefined
wallet_pk: string
}
@ -52,7 +52,7 @@ export interface FollowedAccount extends FollowedAccountApi {
mangoAccount: MangoAccount
}
const getFollowedMangoAccounts = async (accounts: FollowedAccount[]) => {
const getFollowedMangoAccounts = async (accounts: FollowedAccountApi[]) => {
const client = mangoStore.getState().client
const mangoAccounts = []
for (const account of accounts) {
@ -76,22 +76,25 @@ const FollowedAccounts = () => {
const [followedMangoAccounts, setFollowedMangoAccounts] = useState<
FollowedAccount[]
>([])
const [loading, setLoading] = useState(false)
const [loadingMangoAccounts, setLoadingMangoAccounts] = useState(true)
useEffect(() => {
if (!followedAccounts || !followedAccounts.length) return
if (!followedAccounts || !followedAccounts.length) {
setLoadingMangoAccounts(false)
return
}
const getAccounts = async () => {
setLoading(true)
setLoadingMangoAccounts(true)
const accounts = await getFollowedMangoAccounts(followedAccounts)
setFollowedMangoAccounts(accounts)
setLoading(false)
setLoadingMangoAccounts(false)
}
getAccounts()
}, [followedAccounts])
return (
<div className="px-4 pt-4 md:px-6 md:pb-10">
{loadingFollowedAccounts || loading ? (
{loadingFollowedAccounts || loadingMangoAccounts ? (
[...Array(3)].map((x, i) => (
<SheenLoader className="mt-2 flex flex-1" key={i}>
<div className="h-[94px] w-full bg-th-bkg-2" />

View File

@ -12,6 +12,7 @@ import useMangoAccount from 'hooks/useMangoAccount'
import { abbreviateAddress } from 'utils/formatting'
import InlineNotification from '@components/shared/InlineNotification'
import { isMangoError } from 'types'
import Tooltip from '@components/shared/Tooltip'
export const DEFAULT_DELEGATE = '11111111111111111111111111111111'
@ -75,7 +76,14 @@ const DelegateModal = ({ isOpen, onClose }: ModalProps) => {
<div className="flex h-full flex-col justify-between">
<div className="pb-4">
<h2 className="mb-1">{t('delegate-account')}</h2>
<p className="mb-4">{t('delegate-desc')}</p>
<p className="mb-4">
{t('delegate-account-desc')}{' '}
<div className="inline-block">
<Tooltip content={t('delegate-account-tooltip')}>
<span className="tooltip-underline">{t('more-info')}</span>
</Tooltip>
</div>
</p>
{mangoAccount &&
mangoAccount.delegate.toString() !== DEFAULT_DELEGATE ? (
<div className="mb-4">

View File

@ -42,6 +42,8 @@ import CreateAccountForm from '@components/account/CreateAccountForm'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import { Serum3Market } from '@blockworks-foundation/mango-v4'
export const SETTINGS_BUTTON_TITLE_CLASSES = 'text-th-fgd-1'
const CLOSE_WRAPPER_CLASSNAMES =
'mb-4 flex flex-col md:flex-row md:items-center md:justify-between rounded-md bg-th-bkg-2 px-4 py-3'
@ -215,7 +217,7 @@ const AccountSettings = () => {
return mangoAccount && group && !isDelegatedAccount && !isUnownedAccount ? (
<div className="border-b border-th-bkg-3">
<div className="pb-6">
<h3 className="mb-1 text-sm text-th-fgd-1">
<h3 className="mb-1 text-base text-th-fgd-1">
{t('settings:account-address')}
</h3>
<div className="flex items-center space-x-2">
@ -240,7 +242,7 @@ const AccountSettings = () => {
className={ROW_BUTTON_CLASSNAMES}
onClick={() => setShowEditAccountModal(true)}
>
<p>{t('edit-account')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>{t('edit-account')}</p>
<div className="flex items-center space-x-2">
<p className="text-th-fgd-2">{mangoAccount.name}</p>
<PencilIcon className="h-5 w-5 text-th-fgd-2" />
@ -250,9 +252,20 @@ const AccountSettings = () => {
className={ROW_BUTTON_CLASSNAMES}
onClick={() => setShowDelegateModal(true)}
>
<p>{t('delegate-account')}</p>
<div className="flex items-center space-x-2">
<p className="text-th-fgd-2">
<div>
<div className="w-max">
<Tooltip content={t('delegate-account-tooltip')}>
<p
className={`tooltip-underline mb-1 text-left ${SETTINGS_BUTTON_TITLE_CLASSES}`}
>
{t('delegate-account')}
</p>
</Tooltip>
</div>
<p className="text-left">{t('delegate-account-desc')}</p>
</div>
<div className="flex items-center space-x-2 pl-4">
<p className="whitespace-nowrap text-th-fgd-2">
{mangoAccount.delegate.toString() !== DEFAULT_DELEGATE
? abbreviateAddress(mangoAccount.delegate)
: ''}
@ -264,13 +277,13 @@ const AccountSettings = () => {
className={ROW_BUTTON_CLASSNAMES}
onClick={() => setShowCloseAccountModal(true)}
>
<p>{t('close-account')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>{t('close-account')}</p>
<TrashIcon className="h-5 w-5 text-th-fgd-2" />
</button>
</div>
<div className="mb-4">
<div>
<h3 className="mb-1 text-sm text-th-fgd-2">
<h3 className="mb-1 text-base text-th-fgd-1">
{t('settings:account-slots')}
</h3>
<p>{t('settings:account-slots-desc')}</p>
@ -294,9 +307,7 @@ const AccountSettings = () => {
<Disclosure.Button className="w-full border-t border-th-bkg-3 py-4 md:px-4 md:hover:bg-th-bkg-2">
<div className="flex items-center justify-between">
<div>
<p className="text-left font-bold text-th-fgd-2">
{t('tokens')}
</p>
<p className="text-left text-th-fgd-1">{t('tokens')}</p>
<p
className={`mt-1 ${getSlotsUsedColor(
usedTokens.length,
@ -403,7 +414,7 @@ const AccountSettings = () => {
<Disclosure.Button className="w-full border-t border-th-bkg-3 py-4 md:px-4 md:hover:bg-th-bkg-2">
<div className="flex items-center justify-between">
<div>
<p className="text-left font-bold text-th-fgd-2">
<p className="text-left text-th-fgd-1">
{t('settings:spot-markets')}
</p>
<p
@ -477,7 +488,7 @@ const AccountSettings = () => {
<Disclosure.Button className="w-full border-t border-th-bkg-3 py-4 md:px-4 md:hover:bg-th-bkg-2">
<div className="flex items-center justify-between">
<div>
<p className="text-left font-bold text-th-fgd-2">
<p className="text-left text-th-fgd-1">
{t('settings:perp-markets')}
</p>
<p
@ -553,7 +564,7 @@ const AccountSettings = () => {
<Disclosure.Button className="w-full border-t border-th-bkg-3 py-4 md:px-4 md:hover:bg-th-bkg-2">
<div className="flex items-center justify-between">
<div>
<p className="text-left font-bold text-th-fgd-2">
<p className="text-left text-th-fgd-1">
{t('settings:perp-open-orders')}
</p>
<p
@ -607,7 +618,9 @@ const AccountSettings = () => {
<>
<Disclosure.Button className="w-full border-t border-th-bkg-3 py-4 md:px-4 md:hover:bg-th-bkg-2">
<div className="flex items-center justify-between">
<p>{t('trade:trigger-orders')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('trade:trigger-orders')}
</p>
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
@ -679,10 +692,14 @@ const AccountSettings = () => {
</Disclosure>
</div>
<div className="pt-6">
<h3 className="mb-4 text-sm text-th-fgd-2">{t('settings:privacy')}</h3>
<h3 className="mb-4 text-base text-th-fgd-1">
{t('settings:privacy')}
</h3>
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
<Tooltip content={t('settings:tooltip-privacy-mode')}>
<p className="tooltip-underline">{t('settings:privacy-mode')}</p>
<p className={`tooltip-underline ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:privacy-mode')}
</p>
</Tooltip>
<Switch
checked={privacyMode}

View File

@ -2,6 +2,7 @@ import Switch from '@components/forms/Switch'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
export const INITIAL_ANIMATION_SETTINGS = {
'number-scroll': false,
@ -34,28 +35,34 @@ const AnimationSettings = () => {
return (
<div className="border-b border-th-bkg-3 pt-6">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-sm text-th-fgd-1">{t('settings:animations')}</h3>
<h3 className="text-base text-th-fgd-1">{t('settings:animations')}</h3>
<Switch
checked={!Object.values(animationSettings).includes(false)}
onChange={() => handleToggleAnimationSetting('all')}
/>
</div>
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:number-scroll')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:number-scroll')}
</p>
<Switch
checked={animationSettings['number-scroll']}
onChange={() => handleToggleAnimationSetting('number-scroll')}
/>
</div>
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:orderbook-flash')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:orderbook-flash')}
</p>
<Switch
checked={animationSettings['orderbook-flash']}
onChange={() => handleToggleAnimationSetting('orderbook-flash')}
/>
</div>
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:swap-success')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:swap-success')}
</p>
<Switch
checked={animationSettings['swap-success']}
onChange={() => handleToggleAnimationSetting('swap-success')}

View File

@ -2,6 +2,7 @@ import useLocalStorageState from 'hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import { AUTO_CONNECT_WALLET } from 'utils/constants'
import Switch from '@components/forms/Switch'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
const AutoConnectSettings = () => {
const { t } = useTranslation(['common', 'settings'])
@ -12,10 +13,12 @@ const AutoConnectSettings = () => {
return (
<>
<h3 className="mb-4 text-sm text-th-fgd-1">{t('wallet')}</h3>
<h3 className="mb-4 text-base text-th-fgd-1">{t('wallet')}</h3>
<div className="border-b border-th-bkg-3">
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:auto-connect-wallet')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:auto-connect-wallet')}
</p>
<Switch
checked={autoConnect}
onChange={() => setAutoConnect(!autoConnect)}

View File

@ -24,6 +24,7 @@ import {
} from 'utils/constants'
import mangoStore from '@store/mangoStore'
import { CUSTOM_SKINS } from 'utils/theme'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
const NOTIFICATION_POSITIONS = [
'bottom-left',
@ -123,7 +124,9 @@ const DisplaySettings = () => {
return (
<div className="border-b border-th-bkg-3">
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 md:mb-0">{t('settings:theme')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:theme')}
</p>
<div className="w-full min-w-[140px] md:w-auto">
<Select
value={theme || DEFAULT_THEMES[0]}
@ -141,7 +144,9 @@ const DisplaySettings = () => {
</div>
</div>
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 md:mb-0">{t('settings:language')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:language')}
</p>
<div className="w-full min-w-[220px] md:w-auto md:pl-4">
<ButtonGroup
activeValue={savedLanguage}
@ -152,7 +157,9 @@ const DisplaySettings = () => {
</div>
</div>
<div className="hidden border-t border-th-bkg-3 py-4 md:flex md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 md:mb-0">{t('settings:notification-position')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:notification-position')}
</p>
<div className="w-full min-w-[140px] md:w-auto">
<Select
value={t(`settings:${notificationPosition}`)}
@ -168,7 +175,9 @@ const DisplaySettings = () => {
</div>
</div>
<div className="hidden border-t border-th-bkg-3 py-4 md:px-4 lg:flex lg:items-center lg:justify-between">
<p className="mb-2 md:mb-0">{t('settings:trade-layout')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:trade-layout')}
</p>
<div className="flex space-x-3">
<Tooltip content={t('settings:chart-left')}>
<ChartLayoutButton
@ -201,7 +210,9 @@ const DisplaySettings = () => {
</div>
</div>
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 md:mb-0">{t('settings:swap-trade-size-selector')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:swap-trade-size-selector')}
</p>
<div className="w-full min-w-[160px] md:w-auto">
<ButtonGroup
activeValue={tradeFormUi}
@ -212,7 +223,9 @@ const DisplaySettings = () => {
</div>
</div>
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 lg:mb-0">{t('settings:trade-chart')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:trade-chart')}
</p>
<div className="w-full min-w-[220px] md:w-auto">
<ButtonGroup
activeValue={tradeChartUi}

View File

@ -11,6 +11,7 @@ import { NOTIFICATION_API } from 'utils/constants'
import NotificationCookieStore from '@store/notificationCookieStore'
import mangoStore from '@store/mangoStore'
import { createLedgerMessage, createSolanaMessage } from 'utils/notifications'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
const NotificationSettings = () => {
const { t } = useTranslation(['common', 'notifications', 'settings'])
@ -42,7 +43,9 @@ const NotificationSettings = () => {
<>
{isAuth ? (
<div className="flex items-center justify-between border-y border-th-bkg-3 p-4">
<p>{t('settings:limit-order-filled')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:limit-order-filled')}
</p>
<Switch
checked={!!data?.fillsNotifications}
onChange={() =>

View File

@ -19,7 +19,7 @@ const PreferredExplorerSettings = () => {
)
return (
<div className="pt-6">
<h3 className="mb-4 text-sm text-th-fgd-1">
<h3 className="mb-4 text-base text-th-fgd-1">
{t('settings:preferred-explorer')}
</h3>
<div className="space-y-2">

View File

@ -13,6 +13,7 @@ import {
USE_ORDERBOOK_FEED_KEY,
} from 'utils/constants'
import Tooltip from '@components/shared/Tooltip'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
export const TRITON_DEDICATED_URL = process.env.NEXT_PUBLIC_TRITON_TOKEN
? `https://mango.rpcpool.com/${process.env.NEXT_PUBLIC_TRITON_TOKEN}`
@ -115,7 +116,9 @@ const RpcSettings = () => {
return (
<div className="border-b border-th-bkg-3">
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 md:mb-0">{t('rpc-provider')}</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('rpc-provider')}
</p>
<div className="w-full min-w-[400px] md:w-auto">
<ButtonGroup
activeValue={rpcEndpoint.label}
@ -148,7 +151,9 @@ const RpcSettings = () => {
</div>
</div>
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
<p className="mb-2 md:mb-0">Priority Fee</p>
<p className={`mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
Priority Fee
</p>
<div className="w-full min-w-[220px] md:w-auto md:pl-4">
<ButtonGroup
activeValue={priorityFee.label}
@ -188,7 +193,9 @@ const RpcSettings = () => {
placement="top-start"
delay={100}
>
<p className="tooltip-underline">
<p
className={`tooltip-underline mb-2 md:mb-0 ${SETTINGS_BUTTON_TITLE_CLASSES}`}
>
{t('settings:orderbook-bandwidth-saving')}
</p>
</Tooltip>

View File

@ -2,6 +2,7 @@ import Switch from '@components/forms/Switch'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import { SOUND_SETTINGS_KEY } from 'utils/constants'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
export const INITIAL_SOUND_SETTINGS = {
'recent-trades': false,
@ -35,7 +36,7 @@ const SoundSettings = () => {
return (
<div className="border-b border-th-bkg-3 pt-6">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-sm text-th-fgd-1">{t('settings:sounds')}</h3>
<h3 className="text-base text-th-fgd-1">{t('settings:sounds')}</h3>
<Switch
checked={!Object.values(soundSettings).includes(false)}
onChange={() => handleToggleSoundSetting('all')}
@ -49,21 +50,27 @@ const SoundSettings = () => {
/>
</div> */}
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:swap-success')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:swap-success')}
</p>
<Switch
checked={soundSettings['swap-success']}
onChange={() => handleToggleSoundSetting('swap-success')}
/>
</div>
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:transaction-success')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:transaction-success')}
</p>
<Switch
checked={soundSettings['transaction-success']}
onChange={() => handleToggleSoundSetting('transaction-success')}
/>
</div>
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
<p>{t('settings:transaction-fail')}</p>
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>
{t('settings:transaction-fail')}
</p>
<Switch
checked={soundSettings['transaction-fail']}
onChange={() => handleToggleSoundSetting('transaction-fail')}

View File

@ -3,6 +3,7 @@ import useLocalStorageState from 'hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import { SEND_TELEMETRY_KEY } from 'utils/constants'
import Tooltip from '@components/shared/Tooltip'
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
const TelemetrySettings = () => {
const { t } = useTranslation('settings')
@ -15,7 +16,7 @@ const TelemetrySettings = () => {
return (
<div className="border-b border-th-bkg-3 pt-6">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-sm font-normal text-th-fgd-1">
<h3 className="text-base font-normal text-th-fgd-1">
{t('settings:telemetry')}
</h3>
</div>
@ -26,7 +27,9 @@ const TelemetrySettings = () => {
placement="top-start"
delay={100}
>
<p className="tooltip-underline">{t('settings:send-telemetry')}</p>
<p className={`tooltip-underline ${SETTINGS_BUTTON_TITLE_CLASSES}`}>
{t('settings:send-telemetry')}
</p>
</Tooltip>
<Switch
checked={storedSendTelemetry}

View File

@ -56,7 +56,7 @@ const InlineNotification: FunctionComponent<InlineNotificationProps> = ({
<ExclamationTriangleIcon className={`${iconClasses} text-th-warning`} />
) : null}
{type === 'info' ? (
<InformationCircleIcon className={`${iconClasses} text-th-fgd-4`} />
<InformationCircleIcon className={`${iconClasses} text-th-fgd-1`} />
) : null}
<div>
<div className="text-th-fgd-2">{title}</div>

View File

@ -9,8 +9,6 @@ import { LinkIcon } from '@heroicons/react/20/solid'
import mangoStore from '@store/mangoStore'
import { useCallback } from 'react'
const set = mangoStore.getState().set
const SecondaryConnectButton = ({
className,
isLarge,
@ -27,6 +25,7 @@ const SecondaryConnectButton = ({
const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
const handleConnect = useCallback(() => {
const set = mangoStore.getState().set
if (!isOnboarded) {
set((s) => {
s.showUserSetup = true

View File

@ -95,14 +95,14 @@ const ToggleFollowButton = ({
loading ||
!publicKey ||
!signMessage ||
(!isFollowed && followedAccounts?.length >= 10)
(!isFollowed && followedAccounts && followedAccounts?.length >= 10)
return (
<Tooltip
content={
!publicKey
? t('account:tooltip-connect-to-follow')
: !isFollowed && followedAccounts?.length >= 10
: !isFollowed && followedAccounts && followedAccounts?.length >= 10
? t('account:tooltip-follow-max-reached')
: showText
? ''

View File

@ -44,8 +44,6 @@ export const withValueLimit = (values: NumberFormatValues): boolean => {
export const NUMBER_FORMAT_CLASSNAMES =
'w-full rounded-r-lg h-[56px] box-border pb-4 border-l border-th-bkg-2 bg-th-input-bkg px-3 text-right font-mono text-xl text-th-fgd-1 focus:outline-none md:hover:bg-th-bkg-1'
const set = mangoStore.getState().set
const MarketSwapForm = ({
setShowTokenSelect,
onSuccess,
@ -65,6 +63,7 @@ const MarketSwapForm = ({
amountOut: amountOutFormValue,
swapMode,
} = mangoStore((s) => s.swap)
const set = mangoStore((s) => s.set)
const [isDraggingSlider, setIsDraggingSlider] = useState(false)
const { connected, publicKey } = useWallet()
const { mangoAccount } = useMangoAccount()
@ -115,7 +114,7 @@ const MarketSwapForm = ({
}
})
},
[],
[set],
)
const setAmountOutFormValue = useCallback(
@ -130,7 +129,7 @@ const MarketSwapForm = ({
}
})
},
[],
[set],
)
const handleAmountInChange = useCallback(
@ -146,7 +145,7 @@ const MarketSwapForm = ({
})
}
},
[outputBank, setAmountInFormValue, swapMode],
[outputBank, set, setAmountInFormValue, swapMode],
)
const handleAmountOutChange = useCallback(
@ -162,7 +161,7 @@ const MarketSwapForm = ({
})
}
},
[swapMode, setAmountOutFormValue],
[set, setAmountOutFormValue, swapMode],
)
const handleSliderDrag = useCallback(() => {
@ -184,7 +183,7 @@ const MarketSwapForm = ({
s.swap.amountOut = ''
})
},
[setAmountInFormValue],
[Set, setAmountInFormValue],
)
const handleMax = useCallback(
@ -194,7 +193,7 @@ const MarketSwapForm = ({
s.swap.amountOut = ''
})
},
[setAmountInFormValue],
[set, setAmountInFormValue],
)
const handleRepay = useCallback(
@ -204,7 +203,7 @@ const MarketSwapForm = ({
s.swap.amountIn = ''
})
},
[setAmountInFormValue],
[set, setAmountInFormValue],
)
/*
@ -244,7 +243,7 @@ const MarketSwapForm = ({
setAnimateSwitchArrow(
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1,
)
}, [setAmountInFormValue, amountOutAsDecimal, amountInAsDecimal])
}, [amountInAsDecimal, amountOutAsDecimal, set, setAmountInFormValue])
const loadingExactIn: boolean = useMemo(() => {
return (

View File

@ -63,8 +63,6 @@ import {
RefetchQueryFilters,
} from '@tanstack/react-query'
const set = mangoStore.getState().set
type JupiterRouteInfoProps = {
amountIn: Decimal
loadingRoute: boolean
@ -413,6 +411,7 @@ const SwapReviewRouteInfo = ({
const onWalletSwap = useCallback(async () => {
if (!selectedRoute || !inputBank || !outputBank || !wallet.publicKey) return
const actions = mangoStore.getState().actions
const set = mangoStore.getState().set
const connection = mangoStore.getState().connection
setSubmitting(true)
try {
@ -465,6 +464,7 @@ const SwapReviewRouteInfo = ({
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const actions = mangoStore.getState().actions
const set = mangoStore.getState().set
const mangoAccount = mangoStore.getState().mangoAccount.current
const inputBank = mangoStore.getState().swap.inputBank
const outputBank = mangoStore.getState().swap.outputBank

View File

@ -8,7 +8,6 @@ import {
Serum3Side,
} from '@blockworks-foundation/mango-v4'
import Checkbox from '@components/forms/Checkbox'
import Button from '@components/shared/Button'
import Tooltip from '@components/shared/Tooltip'
import mangoStore from '@store/mangoStore'
import Decimal from 'decimal.js'
@ -37,7 +36,6 @@ import {
} from 'utils/tradeForm'
import Image from 'next/legacy/image'
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
import Loading from '@components/shared/Loading'
import TabUnderline from '@components/shared/TabUnderline'
import PerpSlider from './PerpSlider'
import useLocalStorageState from 'hooks/useLocalStorageState'
@ -56,18 +54,15 @@ import {
getDecimalCount,
} from 'utils/numbers'
import LogoWithFallback from '@components/shared/LogoWithFallback'
import useIpAddress from 'hooks/useIpAddress'
import ButtonGroup from '@components/forms/ButtonGroup'
import TradeSummary from './TradeSummary'
import useMangoAccount from 'hooks/useMangoAccount'
import MaxSizeButton from './MaxSizeButton'
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
import { Howl } from 'howler'
import { useWallet } from '@solana/wallet-adapter-react'
import { isMangoError } from 'types'
import InlineNotification from '@components/shared/InlineNotification'
import SpotMarketOrderSwapForm from './SpotMarketOrderSwapForm'
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
import useRemainingBorrowsInPeriod from 'hooks/useRemainingBorrowsInPeriod'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
@ -78,6 +73,9 @@ import { getTokenBalance } from '@components/swap/TriggerSwapForm'
import useMangoAccountAccounts from 'hooks/useMangoAccountAccounts'
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
import AccountSlotsFullNotification from '@components/shared/AccountSlotsFullNotification'
import DepositWithdrawModal from '@components/modals/DepositWithdrawModal'
import CreateAccountModal from '@components/modals/CreateAccountModal'
import TradeformSubmitButton from './TradeformSubmitButton'
dayjs.extend(relativeTime)
@ -111,21 +109,20 @@ type FormErrors = Partial<Record<keyof TradeForm, string>>
const AdvancedTradeForm = () => {
const { t } = useTranslation(['common', 'settings', 'swap', 'trade'])
const { mangoAccount } = useMangoAccount()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const { usedSerum3, totalSerum3 } = useMangoAccountAccounts()
const tradeForm = mangoStore((s) => s.tradeForm)
const themeData = mangoStore((s) => s.themeData)
const [placingOrder, setPlacingOrder] = useState(false)
const [formErrors, setFormErrors] = useState<FormErrors>({})
const [showDepositModal, setShowDepositModal] = useState(false)
const [showCreateAccountModal, setShowCreateAccountModal] = useState(false)
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
const [savedCheckboxSettings, setSavedCheckboxSettings] =
useLocalStorageState(TRADE_CHECKBOXES_KEY, DEFAULT_CHECKBOX_SETTINGS)
const { ipAllowed, ipCountry } = useIpAddress()
const [soundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS,
)
const { connected } = useWallet()
const {
selectedMarket,
price: oraclePrice,
@ -723,7 +720,8 @@ const AdvancedTradeForm = () => {
[baseBank, isTriggerOrder, minOrderSize, oraclePrice, setFormErrors],
)
const disabled = !serumOrPerpMarket || !isMarketEnabled
const disabled =
!serumOrPerpMarket || !isMarketEnabled || !mangoAccountAddress
return (
<div>
@ -1026,53 +1024,14 @@ const AdvancedTradeForm = () => {
)}
</div>
<div className="mb-4 mt-6 flex px-3 md:px-4">
{ipAllowed ? (
connected ? (
<Button
className={`flex w-full items-center justify-center ${
tradeForm.side === 'buy'
? themeData.buttonStyle === 'raised'
? 'raised-buy-button'
: 'bg-th-up-dark text-white md:hover:bg-th-up-dark md:hover:brightness-90'
: themeData.buttonStyle === 'raised'
? 'raised-sell-button'
: 'bg-th-down-dark text-white md:hover:bg-th-down-dark md:hover:brightness-90'
}`}
disabled={disabled}
size="large"
type="submit"
>
{!placingOrder ? (
<span>
{t('trade:place-order', {
side:
tradeForm.side === 'buy'
? sideNames[0]
: sideNames[1],
})}
</span>
) : (
<div className="flex items-center space-x-2">
<Loading />
<span className="hidden sm:block">
{t('trade:placing-order')}
</span>
</div>
)}
</Button>
) : (
<SecondaryConnectButton
className="flex w-full items-center justify-center"
isLarge
/>
)
) : (
<Button disabled className="w-full leading-tight" size="large">
{t('country-not-allowed', {
country: ipCountry ? `(${ipCountry})` : '',
})}
</Button>
)}
<TradeformSubmitButton
disabled={disabled}
placingOrder={placingOrder}
setShowCreateAccountModal={setShowCreateAccountModal}
setShowDepositModal={setShowDepositModal}
sideNames={sideNames}
useMargin={savedCheckboxSettings.margin}
/>
</div>
</form>
{tradeForm.tradeType === 'Market' &&
@ -1084,14 +1043,18 @@ const AdvancedTradeForm = () => {
/>
</div>
) : null}
{serumSlotsFull && selectedMarket instanceof Serum3Market ? (
{serumSlotsFull &&
selectedMarket instanceof Serum3Market &&
mangoAccountAddress ? (
<div className="mb-4 px-4">
<AccountSlotsFullNotification
message={t('trade:error-serum-positions-full')}
/>
</div>
) : null}
{tokenPositionsFull && selectedMarket instanceof Serum3Market ? (
{tokenPositionsFull &&
selectedMarket instanceof Serum3Market &&
mangoAccountAddress ? (
<div className="mb-4 px-4">
<AccountSlotsFullNotification
message={t('error-token-positions-full')}
@ -1154,6 +1117,26 @@ const AdvancedTradeForm = () => {
<TradeSummary balanceBank={balanceBank} mangoAccount={mangoAccount} />
</>
)}
{showDepositModal ? (
<DepositWithdrawModal
action="deposit"
isOpen={showDepositModal}
onClose={() => setShowDepositModal(false)}
token={
selectedMarket instanceof Serum3Market
? tradeForm.side === 'buy'
? quoteBank?.name
: baseBank?.name
: 'USDC'
}
/>
) : null}
{showCreateAccountModal ? (
<CreateAccountModal
isOpen={showCreateAccountModal}
onClose={() => setShowCreateAccountModal(false)}
/>
) : null}
</div>
)
}

View File

@ -1,10 +1,10 @@
import { PerpMarket } from '@blockworks-foundation/mango-v4'
import ButtonGroup from '@components/forms/ButtonGroup'
import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useCallback, useMemo, useState } from 'react'
import { useCallback, useState } from 'react'
import { floorToDecimal } from 'utils/numbers'
import { usePerpMarketMax } from './PerpSlider'
const PerpButtonGroup = ({
minOrderDecimals,
@ -17,36 +17,13 @@ const PerpButtonGroup = ({
const { selectedMarket } = useSelectedMarket()
const { mangoAccount } = useMangoAccount()
const [sizePercentage, setSizePercentage] = useState('')
const tradeFormPrice = mangoStore((s) => s.tradeForm.price)
const leverageMax = useMemo(() => {
const group = mangoStore.getState().group
if (!mangoAccount || !group || !selectedMarket) return 100
if (!(selectedMarket instanceof PerpMarket)) return 100
try {
if (side === 'buy') {
return mangoAccount.getMaxQuoteForPerpBidUi(
group,
selectedMarket.perpMarketIndex,
)
} else {
return mangoAccount.getMaxBaseForPerpAskUi(
group,
selectedMarket.perpMarketIndex,
)
}
} catch (e) {
console.error('Error calculating max leverage perp btn grp: ', e)
return 0
}
}, [side, selectedMarket, mangoAccount, tradeFormPrice])
const perpMax = usePerpMarketMax(mangoAccount, selectedMarket, side)
const handleSizePercentage = useCallback(
(percentage: string) => {
const set = mangoStore.getState().set
setSizePercentage(percentage)
const size = leverageMax * (Number(percentage) / 100)
const size = perpMax * (Number(percentage) / 100)
set((s) => {
if (s.tradeForm.side === 'buy') {
@ -75,7 +52,7 @@ const PerpButtonGroup = ({
}
})
},
[leverageMax, minOrderDecimals, tickDecimals],
[perpMax, minOrderDecimals, tickDecimals],
)
return (

View File

@ -1,12 +1,40 @@
import { PerpMarket } from '@blockworks-foundation/mango-v4'
import { MangoAccount, PerpMarket } from '@blockworks-foundation/mango-v4'
import LeverageSlider from '@components/shared/LeverageSlider'
import mangoStore from '@store/mangoStore'
import BN from 'bn.js'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useCallback, useMemo } from 'react'
import { GenericMarket } from 'types'
import { floorToDecimal } from 'utils/numbers'
export const usePerpMarketMax = (
mangoAccount: MangoAccount | undefined,
selectedMarket: GenericMarket | undefined,
side: string,
) => {
const group = mangoStore.getState().group
if (!mangoAccount || !group || !selectedMarket) return 0
if (!(selectedMarket instanceof PerpMarket)) return 0
try {
if (side === 'buy') {
return mangoAccount.getMaxQuoteForPerpBidUi(
group,
selectedMarket.perpMarketIndex,
)
} else {
return mangoAccount.getMaxBaseForPerpAskUi(
group,
selectedMarket.perpMarketIndex,
)
}
} catch (e) {
console.error('Error calculating max leverage: ', e)
return 0
}
}
const PerpSlider = ({
minOrderDecimals,
tickDecimals,
@ -18,6 +46,7 @@ const PerpSlider = ({
const { selectedMarket, price: marketPrice } = useSelectedMarket()
const { mangoAccount } = useMangoAccount()
const tradeForm = mangoStore((s) => s.tradeForm)
const perpMax = usePerpMarketMax(mangoAccount, selectedMarket, side)
const step = useMemo(() => {
if (selectedMarket instanceof PerpMarket) {
@ -26,29 +55,6 @@ const PerpSlider = ({
return 0.01
}, [selectedMarket])
const leverageMax = useMemo(() => {
const group = mangoStore.getState().group
if (!mangoAccount || !group || !selectedMarket) return 100
if (!(selectedMarket instanceof PerpMarket)) return 100
try {
if (side === 'buy') {
return mangoAccount.getMaxQuoteForPerpBidUi(
group,
selectedMarket.perpMarketIndex,
)
} else {
return mangoAccount.getMaxBaseForPerpAskUi(
group,
selectedMarket.perpMarketIndex,
)
}
} catch (e) {
console.error('Error calculating max leverage for PerpSlider: ', e)
return 0
}
}, [side, selectedMarket, mangoAccount])
const handleSlide = useCallback(
(val: string) => {
const set = mangoStore.getState().set
@ -94,7 +100,7 @@ const PerpSlider = ({
? parseFloat(tradeForm.quoteSize)
: parseFloat(tradeForm.baseSize)
}
leverageMax={leverageMax}
leverageMax={perpMax}
onChange={handleSlide}
step={step}
/>

View File

@ -20,8 +20,8 @@ export const useSpotMarketMax = (
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
const max = useMemo(() => {
const group = mangoStore.getState().group
if (!mangoAccount || !group || !selectedMarket) return 100
if (!(selectedMarket instanceof Serum3Market)) return 100
if (!mangoAccount || !group || !selectedMarket) return 0
if (!(selectedMarket instanceof Serum3Market)) return 0
let leverageMax = 0
let spotMax = 0

View File

@ -0,0 +1,125 @@
import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
import Button from '@components/shared/Button'
import InlineNotification from '@components/shared/InlineNotification'
import Loading from '@components/shared/Loading'
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
import { useWallet } from '@solana/wallet-adapter-react'
import useIpAddress from 'hooks/useIpAddress'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useSpotMarketMax } from './SpotSlider'
import { usePerpMarketMax } from './PerpSlider'
import mangoStore from '@store/mangoStore'
import { useTranslation } from 'react-i18next'
const TradeformSubmitButton = ({
disabled,
placingOrder,
setShowCreateAccountModal,
setShowDepositModal,
sideNames,
useMargin,
}: {
disabled: boolean
placingOrder: boolean
setShowCreateAccountModal: (show: boolean) => void
setShowDepositModal: (show: boolean) => void
sideNames: string[]
useMargin: boolean
}) => {
const { t } = useTranslation(['common', 'trade'])
const side = mangoStore((s) => s.tradeForm.side)
const themeData = mangoStore((s) => s.themeData)
const { connected } = useWallet()
const {
initialLoad: mangoAccountLoading,
mangoAccount,
mangoAccountAddress,
} = useMangoAccount()
const { selectedMarket } = useSelectedMarket()
const { ipAllowed, ipCountry } = useIpAddress()
const spotMax = useSpotMarketMax(
mangoAccount,
selectedMarket,
side,
useMargin,
)
const perpMax = usePerpMarketMax(mangoAccount, selectedMarket, side)
return ipAllowed ? (
connected ? (
mangoAccountLoading ||
(selectedMarket instanceof Serum3Market && spotMax) ||
(selectedMarket instanceof PerpMarket && perpMax) ? (
<Button
className={`flex w-full items-center justify-center ${
side === 'buy'
? themeData.buttonStyle === 'raised'
? 'raised-buy-button'
: 'bg-th-up-dark text-white md:hover:bg-th-up-dark md:hover:brightness-90'
: themeData.buttonStyle === 'raised'
? 'raised-sell-button'
: 'bg-th-down-dark text-white md:hover:bg-th-down-dark md:hover:brightness-90'
}`}
disabled={disabled}
size="large"
type="submit"
>
{!placingOrder ? (
<span>
{t('trade:place-order', {
side: side === 'buy' ? sideNames[0] : sideNames[1],
})}
</span>
) : (
<div className="flex items-center space-x-2">
<Loading />
<span className="hidden sm:block">
{t('trade:placing-order')}
</span>
</div>
)}
</Button>
) : mangoAccountAddress ? (
<div className="w-full">
<Button
className="flex w-full items-center justify-center"
onClick={() => setShowDepositModal(true)}
size="large"
>
{t('deposit')}
</Button>
<div className="mt-4">
<InlineNotification
type="info"
desc={t('trade:no-balance-to-trade')}
/>
</div>
</div>
) : (
<div className="w-full">
<Button
className="flex w-full items-center justify-center"
onClick={() => setShowCreateAccountModal(true)}
size="large"
>
{t('create-account')}
</Button>
</div>
)
) : (
<SecondaryConnectButton
className="flex w-full items-center justify-center"
isLarge
/>
)
) : (
<Button disabled className="w-full leading-tight" size="large">
{t('country-not-allowed', {
country: ipCountry ? `(${ipCountry})` : '',
})}
</Button>
)
}
export default TradeformSubmitButton

View File

@ -1,3 +1,4 @@
import { FollowedAccountApi } from '@components/explore/FollowedAccounts'
import { useWallet } from '@solana/wallet-adapter-react'
import { useQuery } from '@tanstack/react-query'
import { isEmpty } from 'lodash'
@ -8,8 +9,8 @@ const fetchFollowedAccounts = async (walletPk: string | undefined) => {
const response = await fetch(
`${MANGO_DATA_API_URL}/user-data/following?wallet-pk=${walletPk}`,
)
const data = await response.json()
if (isEmpty(data)) {
const data: FollowedAccountApi[] = await response.json()
if (!data || isEmpty(data)) {
return []
} else return data
} catch (e) {

View File

@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -7,6 +7,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
props: {
...(await serverSideTranslations(locale, [
'borrow',
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -40,6 +40,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -20,6 +20,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'notifications',
'onboarding',
@ -86,9 +87,12 @@ const MangoAccountDashboard: NextPage = () => {
/>
<Button
className="flex items-center"
onClick={() =>
router.push(`/dashboard/mangoaccount?address=${searchString}`)
}
onClick={() => {
const encodedSearchString = encodeURIComponent(searchString)
router.push(
`/dashboard/mangoaccount?address=${encodedSearchString}`,
)
}}
disabled={!searchString}
size="large"
>

View File

@ -18,6 +18,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -14,6 +14,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -10,6 +10,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'governance',
'notifications',

View File

@ -8,6 +8,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'governance',
'notifications',

View File

@ -8,6 +8,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
...(await serverSideTranslations(locale, [
'account',
'activity',
'close-account',
'common',
'explore',
'governance',
@ -21,7 +22,6 @@ export async function getStaticProps({ locale }: { locale: string }) {
'stats',
'token',
'trade',
'close-account',
])),
},
}

View File

@ -7,6 +7,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
props: {
...(await serverSideTranslations(locale, [
'account',
'close-account',
'common',
'leaderboard',
'notifications',

View File

@ -20,7 +20,9 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'nft-market',
'notifications',
'onboarding',
'profile',
@ -28,7 +30,6 @@ export async function getStaticProps({ locale }: { locale: string }) {
'settings',
'token',
'trade',
'nft-market',
])),
},
}

View File

@ -8,6 +8,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'governance',
'notifications',

View File

@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -7,6 +7,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
props: {
...(await serverSideTranslations(locale, [
'activity',
'close-account',
'common',
'notifications',
'onboarding',

View File

@ -7,6 +7,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
props: {
...(await serverSideTranslations(locale, [
'account',
'close-account',
'common',
'notifications',
'onboarding',
@ -16,7 +17,6 @@ export async function getStaticProps({ locale }: { locale: string }) {
'settings',
'swap',
'trade',
'close-account',
])),
},
}

View File

@ -22,6 +22,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'onboarding',
'onboarding-tours',
'profile',
'search',
'settings',
'swap',
'trade',

52
public/icons/jlp.svg Normal file
View File

@ -0,0 +1,52 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_159_25)">
<path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="black"/>
<g filter="url(#filter0_d_159_25)">
<path d="M7.91492 21.7291C8.75241 22.9044 9.82668 23.8878 11.0673 24.6149C12.3079 25.3419 13.6868 25.7961 15.1137 25.9478C14.3796 24.8333 13.3125 23.808 11.9804 23.0274C10.6483 22.2467 9.23762 21.8203 7.91492 21.7291Z" fill="url(#paint0_linear_159_25)"/>
<path d="M13.77 19.9191C11.2036 18.4148 8.4258 18.0314 6.43823 18.7086C6.63011 19.3484 6.88316 19.9679 7.19382 20.5584C8.92068 20.518 10.8061 20.9912 12.5605 22.0193C14.3149 23.0473 15.6568 24.4662 16.4779 26C17.1405 25.9795 17.7996 25.8943 18.446 25.7458C18.0441 23.6686 16.3358 21.4237 13.77 19.9191Z" fill="url(#paint1_linear_159_25)"/>
<path d="M25.9998 13.8131C25.6736 12.474 25.0831 11.215 24.2639 10.1113C23.4446 9.00753 22.4133 8.08167 21.2315 7.38897C20.0498 6.69627 18.7417 6.25094 17.3856 6.07957C16.0295 5.9082 14.653 6.0143 13.3384 6.39154C15.5346 6.66271 17.9723 7.49451 20.363 8.89578C22.7538 10.297 24.6792 12.0213 25.9998 13.8131Z" fill="url(#paint2_linear_159_25)"/>
<path d="M22.805 18.7239C21.6808 16.8404 19.7551 15.0368 17.3828 13.6464C15.0106 12.2559 12.5043 11.4618 10.3276 11.4092C8.41256 11.3635 6.9753 11.9251 6.38535 12.9498C6.38199 12.9559 6.37728 12.9616 6.37359 12.9678C6.32051 13.1599 6.27481 13.3524 6.23315 13.5457C7.05694 13.2176 8.01142 13.0349 9.07273 13.0145C11.4329 12.9698 14.0742 13.7314 16.5117 15.1601C18.9491 16.5889 20.9148 18.5274 22.045 20.6174C22.5517 21.559 22.8678 22.4861 22.9931 23.3718C23.1389 23.2403 23.282 23.1043 23.4208 22.963C23.4245 22.9566 23.4269 22.9498 23.4305 22.943C24.0205 21.9173 23.7924 20.3797 22.805 18.7239Z" fill="url(#paint3_linear_159_25)"/>
<path d="M15.5777 16.7803C11.9449 14.651 7.93046 14.3174 6 15.8034C6.00379 16.2686 6.03904 16.7329 6.10549 17.1932C6.67337 17.0195 7.25864 16.9101 7.85049 16.8671C10.0077 16.7034 12.386 17.3098 14.5443 18.5754C16.7025 19.8411 18.4038 21.6261 19.3317 23.5964C19.5883 24.1363 19.7839 24.7034 19.915 25.2872C20.3443 25.1174 20.7618 24.9184 21.1645 24.6916C21.4877 22.2633 19.2115 18.91 15.5777 16.7803Z" fill="url(#paint4_linear_159_25)"/>
<path d="M24.6735 15.5713C23.5366 13.6898 21.594 11.8811 19.205 10.4815C16.8159 9.08194 14.2999 8.27521 12.1161 8.21047C10.4514 8.162 9.16198 8.56909 8.49341 9.33819C11.2695 8.86364 14.9312 9.66122 18.4803 11.7414C22.0295 13.8216 24.5311 16.6363 25.4966 19.3043C25.8276 18.3379 25.5396 17.0061 24.6735 15.5713Z" fill="url(#paint5_linear_159_25)"/>
</g>
</g>
<defs>
<filter id="filter0_d_159_25" x="-16.7448" y="-14.4703" width="65.4895" height="65.4896" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2.27449"/>
<feGaussianBlur stdDeviation="11.3724"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_159_25"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_159_25" result="shape"/>
</filter>
<linearGradient id="paint0_linear_159_25" x1="19.3191" y1="10.0625" x2="10.0078" y2="25.9282" gradientUnits="userSpaceOnUse">
<stop offset="0.0001" stop-color="#C7F284"/>
<stop offset="1" stop-color="#00BEF0"/>
</linearGradient>
<linearGradient id="paint1_linear_159_25" x1="19.3186" y1="10.0625" x2="10.0073" y2="25.9282" gradientUnits="userSpaceOnUse">
<stop offset="0.0001" stop-color="#C7F284"/>
<stop offset="1" stop-color="#00BEF0"/>
</linearGradient>
<linearGradient id="paint2_linear_159_25" x1="19.3186" y1="10.0625" x2="10.0073" y2="25.9282" gradientUnits="userSpaceOnUse">
<stop offset="0.0001" stop-color="#C7F284"/>
<stop offset="1" stop-color="#00BEF0"/>
</linearGradient>
<linearGradient id="paint3_linear_159_25" x1="19.3191" y1="10.0625" x2="10.0078" y2="25.9282" gradientUnits="userSpaceOnUse">
<stop offset="0.0001" stop-color="#C7F284"/>
<stop offset="1" stop-color="#00BEF0"/>
</linearGradient>
<linearGradient id="paint4_linear_159_25" x1="19.3188" y1="10.0625" x2="10.0075" y2="25.9282" gradientUnits="userSpaceOnUse">
<stop offset="0.0001" stop-color="#C7F284"/>
<stop offset="1" stop-color="#00BEF0"/>
</linearGradient>
<linearGradient id="paint5_linear_159_25" x1="19.3191" y1="10.0625" x2="10.0078" y2="25.9282" gradientUnits="userSpaceOnUse">
<stop offset="0.0001" stop-color="#C7F284"/>
<stop offset="1" stop-color="#00BEF0"/>
</linearGradient>
<clipPath id="clip0_159_25">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

15
public/icons/jto.svg Normal file
View File

@ -0,0 +1,15 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_161_9)">
<path d="M32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32C24.8366 32 32 24.8366 32 16Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9973 3.61826C9.24926 3.61826 3.77886 9.08866 3.77886 15.8367C3.77886 22.5848 9.24926 28.0552 15.9973 28.0552C22.7454 28.0552 28.2158 22.5848 28.2158 15.8367C28.2158 9.08866 22.7454 3.61826 15.9973 3.61826ZM2.66809 15.8367C2.66809 8.4752 8.6358 2.50749 15.9973 2.50749C23.3589 2.50749 29.3266 8.4752 29.3266 15.8367C29.3266 23.1983 23.3589 29.166 15.9973 29.166C8.6358 29.166 2.66809 23.1983 2.66809 15.8367Z" fill="white"/>
<path d="M14.3191 13.3703C15.3714 13.5705 16.3565 13.3538 17.2726 12.7215L20.9714 10.1687C20.4564 9.42234 19.4331 9.23632 18.6882 9.7536L16.3449 11.3811C15.8049 11.7538 15.2256 11.8847 14.6106 11.7714C13.9938 11.6593 13.5021 11.3357 13.1337 10.802L12.2077 9.46029L10.8794 10.3771L11.8054 11.7187C12.4277 12.6204 13.265 13.1713 14.3191 13.3703Z" fill="white"/>
<path d="M18.4124 14.2229C18.2142 15.2755 18.4325 16.2617 19.066 17.1796L21.6237 20.8854C22.3686 20.3712 22.5543 19.3499 22.038 18.6065L20.4039 16.2535C20.0305 15.7125 19.8986 15.1326 20.0108 14.5174C20.1217 13.9004 20.4442 13.409 20.977 13.0412L22.3161 12.117L21.3976 10.7862L20.0585 11.7104C19.1585 12.3316 18.6094 13.1684 18.4124 14.2229Z" fill="white"/>
<path d="M14.5912 18.9816C15.5074 18.3493 16.4925 18.1326 17.5448 18.3329C18.5989 18.5318 19.4362 19.0828 20.0585 19.9844L20.9845 21.326L19.6562 22.2428L18.7302 20.9012C18.3618 20.3674 17.8701 20.0439 17.2533 19.9318C16.6383 19.8184 16.059 19.9493 15.519 20.322L13.1757 21.9495C12.4309 22.4668 11.4076 22.2808 10.8925 21.5345L14.5912 18.9816Z" fill="white"/>
<path d="M13.5316 17.5971C13.7298 16.5445 13.5116 15.5583 12.8781 14.6404L10.3204 10.9346C9.57547 11.4488 9.38981 12.4701 9.9061 13.2135L11.5402 15.5665C11.9136 16.1075 12.0454 16.6874 11.9333 17.3026C11.8223 17.9196 11.4999 18.411 10.9671 18.7788L9.62798 19.703L10.5465 21.0338L11.8856 20.1096C12.7856 19.4884 13.3346 18.6516 13.5316 17.5971Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_161_9">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

11
public/icons/slcl.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_159_9)">
<path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="#0B0C10"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.2233 19.1268H14.2142V19.1305C10.8043 17.3513 9.94516 13.7758 11.4893 10.7076C13.0334 7.63925 16.3853 6.25685 19.7941 8.04053H19.8031L18.7695 10.0958L18.7603 10.0911C16.8119 9.07456 14.6812 9.81867 13.6657 11.8379C12.6502 13.8571 13.3013 16.0548 15.2486 17.0714H15.2578L14.2233 19.1268ZM17.9819 12.8702H17.9908L17.988 12.8663C21.3988 14.6455 22.2545 18.2257 20.7132 21.2902C19.1716 24.3548 15.8199 25.7363 12.4119 23.9565H12.4028L13.4366 21.901H13.4455C15.3933 22.9186 17.5249 22.1737 18.5404 20.1545C19.5558 18.135 18.9047 15.9375 16.9572 14.9207H16.948L17.9819 12.8702Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_159_9">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1022 B

View File

@ -62,8 +62,9 @@
"degraded": "Degraded",
"delegate": "Delegate",
"delegate-account": "Delegate Account",
"delegate-account-desc": "Give permissions to another wallet to perform certain actions on your account.",
"delegate-account-info": "Account delegated to: {{delegate}}",
"delegate-desc": "Delegate your Mango account to another wallet address",
"delegate-account-tooltip": "Common use cases are to create an account with a hardware wallet and delegate the account to make trades via a hot wallet or to delegate managing the account to another user. Delegates are not allowed to withdraw funds and are only allowed restricted usage of swap. Delegates can perform unfavourable trades that result in lost equity.",
"delegate-placeholder": "Enter a wallet address to delegate to",
"delete": "Delete",
"deposit": "Deposit",
@ -120,6 +121,7 @@
"max": "Max",
"max-borrow": "Max Borrow",
"more": "More",
"more-info": "More Info",
"new": "New",
"new-account": "New Account",
"new-account-failed": "Failed to create account",

View File

@ -58,6 +58,7 @@
"min-order-size": "Min Order Size",
"min-order-size-error": "Min order size is {{minSize}} {{symbol}}",
"more-details": "More Details",
"no-balance-to-trade": "No balance to trade. Deposit before placing an order.",
"no-balances": "No balances",
"no-borrows": "Borrow Disabled",
"no-markets-found": "No markets found...",

View File

@ -62,8 +62,9 @@
"degraded": "Degraded",
"delegate": "Delegate",
"delegate-account": "Delegate Account",
"delegate-account-desc": "Give permissions to another wallet to perform certain actions on your account.",
"delegate-account-info": "Account delegated to: {{delegate}}",
"delegate-desc": "Delegate your Mango account to another wallet address",
"delegate-account-tooltip": "Common use cases are to create an account with a hardware wallet and delegate the account to make trades via a hot wallet or to delegate managing the account to another user. Delegates are not allowed to withdraw funds and are only allowed restricted usage of swap. Delegates can perform unfavourable trades that result in lost equity.",
"delegate-placeholder": "Enter a wallet address to delegate to",
"delete": "Delete",
"deposit": "Deposit",
@ -120,6 +121,7 @@
"max": "Max",
"max-borrow": "Max Borrow",
"more": "More",
"more-info": "More Info",
"new": "New",
"new-account": "New Account",
"new-account-failed": "Failed to create account",

View File

@ -58,6 +58,7 @@
"min-order-size": "Min Order Size",
"min-order-size-error": "Min order size is {{minSize}} {{symbol}}",
"more-details": "More Details",
"no-balance-to-trade": "No balance to trade. Deposit before placing an order.",
"no-balances": "No balances",
"no-borrows": "Borrow Disabled",
"no-markets-found": "No markets found...",

View File

@ -62,8 +62,9 @@
"degraded": "Degraded",
"delegate": "Delegate",
"delegate-account": "Delegate Account",
"delegate-account-desc": "Give permissions to another wallet to perform certain actions on your account.",
"delegate-account-info": "Account delegated to: {{delegate}}",
"delegate-desc": "Delegate your Mango account to another wallet address",
"delegate-account-tooltip": "Common use cases are to create an account with a hardware wallet and delegate the account to make trades via a hot wallet or to delegate managing the account to another user. Delegates are not allowed to withdraw funds and are only allowed restricted usage of swap. Delegates can perform unfavourable trades that result in lost equity.",
"delegate-placeholder": "Enter a wallet address to delegate to",
"delete": "Delete",
"deposit": "Deposit",
@ -120,6 +121,7 @@
"max": "Max",
"max-borrow": "Max Borrow",
"more": "More",
"more-info": "More Info",
"new": "New",
"new-account": "New Account",
"new-account-failed": "Failed to create account",

View File

@ -58,6 +58,7 @@
"min-order-size": "Min Order Size",
"min-order-size-error": "Min order size is {{minSize}} {{symbol}}",
"more-details": "More Details",
"no-balance-to-trade": "No balance to trade. Deposit before placing an order.",
"no-balances": "No balances",
"no-borrows": "Borrow Disabled",
"no-markets-found": "No markets found...",

View File

@ -62,8 +62,9 @@
"degraded": "退化的",
"delegate": "委托",
"delegate-account": "委托帐户",
"delegate-account-desc": "Give permissions to another wallet to perform certain actions on your account.",
"delegate-account-info": "帐户委托给: {{delegate}}",
"delegate-desc": "以Mango帐户委托给别的钱包地址",
"delegate-account-tooltip": "Common use cases are to create an account with a hardware wallet and delegate the account to make trades via a hot wallet or to delegate managing the account to another user. Delegates are not allowed to withdraw funds and are only allowed restricted usage of swap. Delegates can perform unfavourable trades that result in lost equity.",
"delegate-placeholder": "输入受委钱包地执",
"delete": "删除",
"deposit": "存款",
@ -119,6 +120,7 @@
"max": "最多",
"max-borrow": "最多借贷",
"more": "更多",
"more-info": "More Info",
"new": "新",
"new-account": "开户",
"new-account-failed": "开户出错",

View File

@ -58,6 +58,7 @@
"min-order-size": "最小订单量",
"min-order-size-error": "订单的最小数量是 {{minSize}} {{symbol}}",
"more-details": "看细节",
"no-balance-to-trade": "No balance to trade. Deposit before placing an order.",
"no-balances": "无余额",
"no-borrows": "借贷已停用",
"no-markets-found": "无市场",

View File

@ -62,8 +62,9 @@
"degraded": "退化的",
"delegate": "委託",
"delegate-account": "委託帳戶",
"delegate-account-desc": "Give permissions to another wallet to perform certain actions on your account.",
"delegate-account-info": "帳戶委託給: {{delegate}}",
"delegate-desc": "以Mango帳戶委託給別的錢包地址",
"delegate-account-tooltip": "Common use cases are to create an account with a hardware wallet and delegate the account to make trades via a hot wallet or to delegate managing the account to another user. Delegates are not allowed to withdraw funds and are only allowed restricted usage of swap. Delegates can perform unfavourable trades that result in lost equity.",
"delegate-placeholder": "輸入受委錢包地執",
"delete": "刪除",
"deposit": "存款",
@ -119,6 +120,7 @@
"max": "最多",
"max-borrow": "最多借貸",
"more": "更多",
"more-info": "More Info",
"new": "新",
"new-account": "開戶",
"new-account-failed": "開戶出錯",

View File

@ -58,6 +58,7 @@
"min-order-size": "最小訂單量",
"min-order-size-error": "訂單的最小數量是 {{minSize}} {{symbol}}",
"more-details": "看細節",
"no-balance-to-trade": "No balance to trade. Deposit before placing an order.",
"no-balances": "無餘額",
"no-borrows": "借貸已停用",
"no-markets-found": "無市場",

View File

@ -788,12 +788,13 @@ const mangoStore = create<MangoStore>()(
state.perpStats.data = data
state.perpStats.loading = false
})
} catch {
} catch (error) {
set((state) => {
state.perpStats.loading = false
})
console.log('Failed to fetch perp stats data', error)
notify({
title: 'Failed to fetch token stats data',
title: 'Failed to fetch perp stats data',
type: 'error',
})
}
@ -913,8 +914,8 @@ const mangoStore = create<MangoStore>()(
})
try {
const data = await fetchTokenStatsData(group)
const mangoStats = processTokenStatsData(data, group)
const rawData = await fetchTokenStatsData(group)
const [data, mangoStats] = processTokenStatsData(rawData, group)
set((state) => {
state.tokenStats.data = data
@ -926,7 +927,7 @@ const mangoStore = create<MangoStore>()(
set((state) => {
state.tokenStats.loading = false
})
console.log('Failed to fetch token stats data', error)
notify({
title: 'Failed to fetch token stats data',
type: 'error',

View File

@ -147,6 +147,8 @@ export const CUSTOM_TOKEN_ICONS: { [key: string]: boolean } = {
guac: true,
hnt: true,
jitosol: true,
jlp: true,
jto: true,
kin: true,
ldo: true,
mngo: true,
@ -157,6 +159,7 @@ export const CUSTOM_TOKEN_ICONS: { [key: string]: boolean } = {
ray: true,
render: true,
rlb: true,
slcl: true,
sol: true,
stsol: true,
tbtc: true,