Merge branch 'main' into onboarding-tours
This commit is contained in:
commit
c0bb9ddcda
|
@ -14,6 +14,7 @@ import { LAST_ACCOUNT_KEY } from '../utils/constants'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { retryFn } from '../utils'
|
||||
import Loading from './shared/Loading'
|
||||
import ActionTokenList from './account/ActionTokenList'
|
||||
|
||||
const MangoAccountsList = ({
|
||||
mangoAccount,
|
||||
|
@ -22,6 +23,7 @@ const MangoAccountsList = ({
|
|||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const mangoAccounts = mangoStore((s) => s.mangoAccounts.accounts)
|
||||
const actions = mangoStore((s) => s.actions)
|
||||
const loading = mangoStore((s) => s.mangoAccount.initialLoad)
|
||||
const [showNewAccountModal, setShowNewAccountModal] = useState(false)
|
||||
const [, setLastAccountViewed] = useLocalStorageStringState(LAST_ACCOUNT_KEY)
|
||||
|
@ -41,6 +43,7 @@ const MangoAccountsList = ({
|
|||
s.mangoAccount.lastUpdatedAt = new Date().toISOString()
|
||||
})
|
||||
setLastAccountViewed(acc.publicKey.toString())
|
||||
actions.fetchSerumOpenOrders(acc)
|
||||
} catch (e) {
|
||||
console.warn('Error selecting account', e)
|
||||
}
|
||||
|
@ -76,7 +79,7 @@ const MangoAccountsList = ({
|
|||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Panel className="absolute top-[13.5px] -right-5 z-10 mr-4 w-56 rounded-md rounded-t-none border border-th-bkg-2 bg-th-bkg-3 p-4 text-th-fgd-3">
|
||||
<Popover.Panel className="absolute top-[13.5px] -right-5 z-20 mr-4 w-56 rounded-md rounded-t-none border border-th-bkg-2 bg-th-bkg-3 p-4 text-th-fgd-3">
|
||||
{loading ? (
|
||||
<Loading />
|
||||
) : mangoAccounts.length ? (
|
||||
|
@ -84,7 +87,7 @@ const MangoAccountsList = ({
|
|||
<div key={acc.publicKey.toString()}>
|
||||
<button
|
||||
onClick={() => handleSelectMangoAccount(acc)}
|
||||
className="mb-3 flex w-full items-center justify-between border-b border-th-bkg-4 pb-3 hover:text-th-fgd-1"
|
||||
className="default-transition mb-3 flex w-full items-center justify-between border-b border-th-bkg-4 pb-3 hover:text-th-fgd-1"
|
||||
>
|
||||
{acc.name}
|
||||
{acc.publicKey.toString() ===
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
} from '@blockworks-foundation/mango-v4'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import AccountActions from './AccountActions'
|
||||
import DepositModal from '../modals/DepositModal'
|
||||
import WithdrawModal from '../modals/WithdrawModal'
|
||||
|
@ -138,6 +138,19 @@ const AccountPage = () => {
|
|||
return mangoAccount ? mangoAccount.getHealthRatioUi(HealthType.maint) : 0
|
||||
}, [mangoAccount])
|
||||
|
||||
const handleChartToShow = (chartName: string) => {
|
||||
if (chartName === 'cumulative-interest-value') {
|
||||
if (interestTotalValue > 1 || interestTotalValue < -1) {
|
||||
setChartToShow(chartName)
|
||||
}
|
||||
}
|
||||
if (chartName === 'pnl') {
|
||||
if (performanceData.length > 4) {
|
||||
setChartToShow(chartName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !chartToShow ? (
|
||||
<>
|
||||
<div className="flex flex-wrap items-center justify-between border-b-0 border-th-bkg-3 px-6 pt-3 pb-0 md:border-b md:pb-3">
|
||||
|
@ -233,7 +246,7 @@ const AccountPage = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 border-b border-th-bkg-3">
|
||||
<div className="col-span-4 border-t border-th-bkg-3 py-3 pl-6 md:col-span-1 md:col-span-2 md:border-l md:border-t-0 lg:col-span-1">
|
||||
<div className="col-span-4 flex border-t border-th-bkg-3 py-3 pl-6 md:col-span-1 md:col-span-2 md:border-l md:border-t-0 lg:col-span-1">
|
||||
<div id="account-step-four">
|
||||
<Tooltip
|
||||
maxWidth="20rem"
|
||||
|
@ -276,7 +289,7 @@ const AccountPage = () => {
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-4 border-t border-th-bkg-3 py-3 pl-6 md:col-span-1 md:col-span-2 md:border-l md:border-t-0 lg:col-span-1">
|
||||
<div className="col-span-4 flex border-t border-th-bkg-3 py-3 pl-6 md:col-span-1 md:col-span-2 md:border-l md:border-t-0 lg:col-span-1">
|
||||
<div id="account-step-five">
|
||||
<Tooltip
|
||||
content="The amount of capital you have to trade or borrow against. When your free collateral reaches $0 you won't be able to make withdrawals."
|
||||
|
@ -299,51 +312,59 @@ const AccountPage = () => {
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-4 flex items-center justify-between border-t border-th-bkg-3 py-3 px-6 md:col-span-2 md:col-span-2 md:border-l lg:col-span-1 lg:border-t-0">
|
||||
<div id="account-step-six">
|
||||
<Tooltip
|
||||
content="The amount your account has made or lost."
|
||||
placement="bottom-start"
|
||||
>
|
||||
<p className="tooltip-underline text-th-fgd-3">{t('pnl')}</p>
|
||||
</Tooltip>
|
||||
<p className="mt-1 text-2xl font-bold text-th-fgd-1">
|
||||
{formatFixedDecimals(accountPnl, true)}
|
||||
</p>
|
||||
</div>
|
||||
{performanceData.length > 4 ? (
|
||||
<IconButton
|
||||
onClick={() => setChartToShow('pnl')}
|
||||
size={!isMobile ? 'small' : 'medium'}
|
||||
>
|
||||
<ChevronRightIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="col-span-4 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 md:col-span-1 md:col-span-2 md:border-l lg:col-span-1 lg:border-t-0">
|
||||
<div id="account-step-seven">
|
||||
<Tooltip
|
||||
content="The value of interest earned (deposits) minus interest paid (borrows)."
|
||||
maxWidth="20rem"
|
||||
placement="bottom-end"
|
||||
>
|
||||
<p className="tooltip-underline text-th-fgd-3">
|
||||
{t('total-interest-value')}
|
||||
<button
|
||||
className={`col-span-4 border-t border-th-bkg-3 py-3 px-6 md:col-span-2 md:col-span-2 md:border-l lg:col-span-1 lg:border-t-0 ${
|
||||
performanceData.length > 4
|
||||
? 'default-transition cursor-pointer md:hover:bg-th-bkg-2'
|
||||
: 'cursor-default'
|
||||
}`}
|
||||
onClick={() => handleChartToShow('pnl')}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div id="account-step-six">
|
||||
<Tooltip
|
||||
content="The amount your account has made or lost."
|
||||
placement="bottom-start"
|
||||
>
|
||||
<p className="tooltip-underline text-th-fgd-3">{t('pnl')}</p>
|
||||
</Tooltip>
|
||||
<p className="mt-1 text-2xl font-bold text-th-fgd-1">
|
||||
{formatFixedDecimals(accountPnl, true)}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="mt-1 text-2xl font-bold text-th-fgd-1">
|
||||
{formatFixedDecimals(interestTotalValue, true)}
|
||||
</p>
|
||||
</div>
|
||||
{performanceData.length > 4 ? (
|
||||
<ChevronRightIcon className="h-6 w-6" />
|
||||
) : null}
|
||||
</div>
|
||||
{interestTotalValue > 1 || interestTotalValue < -1 ? (
|
||||
<IconButton
|
||||
onClick={() => setChartToShow('cumulative-interest-value')}
|
||||
size={!isMobile ? 'small' : 'medium'}
|
||||
>
|
||||
<ChevronRightIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
className={`col-span-4 border-t border-th-bkg-3 py-3 pl-6 text-left md:col-span-1 md:col-span-2 md:border-l lg:col-span-1 lg:border-t-0 ${
|
||||
interestTotalValue > 1 || interestTotalValue < -1
|
||||
? 'default-transition cursor-pointer md:hover:bg-th-bkg-2'
|
||||
: 'cursor-default'
|
||||
}`}
|
||||
onClick={() => handleChartToShow('cumulative-interest-value')}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div id="account-step-seven">
|
||||
<Tooltip
|
||||
content="The value of interest earned (deposits) minus interest paid (borrows)."
|
||||
maxWidth="20rem"
|
||||
placement="bottom-end"
|
||||
>
|
||||
<p className="tooltip-underline text-th-fgd-3">
|
||||
{t('total-interest-value')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="mt-1 text-2xl font-bold text-th-fgd-1">
|
||||
{formatFixedDecimals(interestTotalValue, true)}
|
||||
</p>
|
||||
</div>
|
||||
{interestTotalValue > 1 || interestTotalValue < -1 ? (
|
||||
<ChevronRightIcon className="h-6 w-6" />
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<AccountTabs />
|
||||
{showDepositModal ? (
|
||||
|
|
|
@ -18,7 +18,7 @@ const TabButtons: FunctionComponent<TabButtonsProps> = ({
|
|||
rounded = false,
|
||||
fillWidth = false,
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'swap'])
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -29,7 +29,7 @@ const TabButtons: FunctionComponent<TabButtonsProps> = ({
|
|||
{values.map(([label, count], i) => (
|
||||
<div className={fillWidth ? 'flex-1' : ''} key={label + i}>
|
||||
<button
|
||||
className={`default-transition flex h-12 w-full items-center justify-center px-6 font-bold ${
|
||||
className={`default-transition flex h-12 w-full items-center justify-center px-4 font-bold md:px-6 ${
|
||||
rounded ? 'rounded-md' : 'rounded-none'
|
||||
} ${showBorders ? 'border-r border-th-bkg-3' : ''} ${
|
||||
label === activeValue
|
||||
|
@ -44,7 +44,7 @@ const TabButtons: FunctionComponent<TabButtonsProps> = ({
|
|||
<div
|
||||
className={`ml-1.5 rounded ${
|
||||
label === activeValue ? 'bg-th-bkg-4' : 'bg-th-bkg-3'
|
||||
} px-1.5 font-mono text-xxs`}
|
||||
} px-1.5 font-mono text-xxs text-th-fgd-2`}
|
||||
>
|
||||
{count}
|
||||
</div>
|
||||
|
|
|
@ -135,7 +135,7 @@ const OraclePrice = () => {
|
|||
}
|
||||
|
||||
const AdvancedMarketHeader = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||
const coingeckoPrices = mangoStore((s) => s.coingeckoPrices.data)
|
||||
|
||||
|
@ -164,7 +164,7 @@ const AdvancedMarketHeader = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div id="trade-step-two" className="ml-6 flex-col">
|
||||
<div className="text-xs text-th-fgd-4">{t('oracle-price')}</div>
|
||||
<div className="text-xs text-th-fgd-4">{t('trade:oracle-price')}</div>
|
||||
<OraclePrice />
|
||||
</div>
|
||||
<div className="ml-6 flex-col">
|
||||
|
|
|
@ -18,6 +18,9 @@ import NumberFormat, {
|
|||
import { notify } from 'utils/notifications'
|
||||
import SpotSlider from './SpotSlider'
|
||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
||||
import Image from 'next/image'
|
||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import Loading from '@components/shared/Loading'
|
||||
|
||||
const TABS: [string, number][] = [
|
||||
['Limit', 0],
|
||||
|
@ -25,20 +28,40 @@ const TABS: [string, number][] = [
|
|||
]
|
||||
|
||||
const AdvancedTradeForm = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const set = mangoStore.getState().set
|
||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||
const [useMargin, setUseMargin] = useState(true)
|
||||
const [placingOrder, setPlacingOrder] = useState(false)
|
||||
|
||||
const baseSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split('/')[0]
|
||||
}, [selectedMarket])
|
||||
|
||||
const baseLogoURI = useMemo(() => {
|
||||
if (!baseSymbol || !jupiterTokens.length) return ''
|
||||
const token = jupiterTokens.find((t) => t.symbol === baseSymbol)
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
return ''
|
||||
}, [baseSymbol, jupiterTokens])
|
||||
|
||||
const quoteSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split('/')[1]
|
||||
}, [selectedMarket])
|
||||
|
||||
const quoteLogoURI = useMemo(() => {
|
||||
if (!quoteSymbol || !jupiterTokens.length) return ''
|
||||
const token = jupiterTokens.find((t) => t.symbol === quoteSymbol)
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
return ''
|
||||
}, [quoteSymbol, jupiterTokens])
|
||||
|
||||
const setTradeType = useCallback(
|
||||
(tradeType: 'Limit' | 'Market') => {
|
||||
set((s) => {
|
||||
|
@ -66,7 +89,6 @@ const AdvancedTradeForm = () => {
|
|||
const handleBaseSizeChange = useCallback(
|
||||
(e: NumberFormatValues, info: SourceInfo) => {
|
||||
if (info.source !== 'event') return
|
||||
console.log('ho')
|
||||
|
||||
set((s) => {
|
||||
s.tradeForm.baseSize = e.value
|
||||
|
@ -84,7 +106,6 @@ const AdvancedTradeForm = () => {
|
|||
const handleQuoteSizeChange = useCallback(
|
||||
(e: NumberFormatValues, info: SourceInfo) => {
|
||||
if (info.source !== 'event') return
|
||||
console.log('hi')
|
||||
|
||||
set((s) => {
|
||||
s.tradeForm.quoteSize = e.value
|
||||
|
@ -155,7 +176,7 @@ const AdvancedTradeForm = () => {
|
|||
const selectedMarket = mangoStore.getState().selectedMarket.current
|
||||
|
||||
if (!group || !mangoAccount) return
|
||||
|
||||
setPlacingOrder(true)
|
||||
try {
|
||||
const orderType = tradeForm.ioc
|
||||
? Serum3OrderType.immediateOrCancel
|
||||
|
@ -197,6 +218,8 @@ const AdvancedTradeForm = () => {
|
|||
type: 'error',
|
||||
})
|
||||
console.error('Place trade error:', e)
|
||||
} finally {
|
||||
setPlacingOrder(false)
|
||||
}
|
||||
}, [t])
|
||||
|
||||
|
@ -255,7 +278,7 @@ const AdvancedTradeForm = () => {
|
|||
{tradeForm.tradeType === 'Limit' ? (
|
||||
<>
|
||||
<div className="mb-2 mt-4 flex items-center justify-between">
|
||||
<p className="text-xs text-th-fgd-3">Limit Price</p>
|
||||
<p className="text-xs text-th-fgd-3">{t('trade:limit-price')}</p>
|
||||
</div>
|
||||
<div className="default-transition flex items-center rounded-md border border-th-bkg-4 bg-th-bkg-1 p-2 text-xs font-bold text-th-fgd-1 md:hover:border-th-fgd-4 md:hover:bg-th-bkg-2 lg:text-base">
|
||||
<NumberFormat
|
||||
|
@ -278,10 +301,21 @@ const AdvancedTradeForm = () => {
|
|||
</>
|
||||
) : null}
|
||||
<div className="my-2 flex items-center justify-between">
|
||||
<p className="text-xs text-th-fgd-3">{t('amount')}</p>
|
||||
<p className="text-xs text-th-fgd-3">{t('trade:amount')}</p>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="default-transition flex items-center rounded-md border border-th-bkg-4 bg-th-bkg-1 p-2 text-xs font-bold text-th-fgd-1 md:hover:border-th-fgd-4 md:hover:bg-th-bkg-2 lg:text-base">
|
||||
<div className="default-transition flex items-center rounded-md rounded-b-none border border-th-bkg-4 bg-th-bkg-1 p-2 text-xs font-bold text-th-fgd-1 md:hover:z-10 md:hover:border-th-fgd-4 md:hover:bg-th-bkg-2 lg:text-base">
|
||||
{baseLogoURI ? (
|
||||
<Image
|
||||
className="rounded-full"
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={baseLogoURI}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
)}
|
||||
<NumberFormat
|
||||
inputMode="decimal"
|
||||
thousandSeparator=","
|
||||
|
@ -290,7 +324,7 @@ const AdvancedTradeForm = () => {
|
|||
decimalScale={6}
|
||||
name="amountIn"
|
||||
id="amountIn"
|
||||
className="w-full bg-transparent font-mono focus:outline-none"
|
||||
className="ml-2 w-full bg-transparent font-mono focus:outline-none"
|
||||
placeholder="0.00"
|
||||
value={tradeForm.baseSize}
|
||||
onValueChange={handleBaseSizeChange}
|
||||
|
@ -299,7 +333,18 @@ const AdvancedTradeForm = () => {
|
|||
{baseSymbol}
|
||||
</div>
|
||||
</div>
|
||||
<div className="default-transition mt-1 flex items-center rounded-md border border-th-bkg-4 bg-th-bkg-1 p-2 text-xs font-bold text-th-fgd-1 md:hover:border-th-fgd-4 md:hover:bg-th-bkg-2 lg:text-base">
|
||||
<div className="default-transition -mt-[1px] flex items-center rounded-md rounded-t-none border border-th-bkg-4 bg-th-bkg-1 p-2 text-xs font-bold text-th-fgd-1 md:hover:border-th-fgd-4 md:hover:bg-th-bkg-2 lg:text-base">
|
||||
{quoteLogoURI ? (
|
||||
<Image
|
||||
className="rounded-full"
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={quoteLogoURI}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
)}
|
||||
<NumberFormat
|
||||
inputMode="decimal"
|
||||
thousandSeparator=","
|
||||
|
@ -308,7 +353,7 @@ const AdvancedTradeForm = () => {
|
|||
decimalScale={6}
|
||||
name="amountIn"
|
||||
id="amountIn"
|
||||
className="w-full bg-transparent font-mono focus:outline-none"
|
||||
className="ml-2 w-full bg-transparent font-mono focus:outline-none"
|
||||
placeholder="0.00"
|
||||
value={tradeForm.quoteSize}
|
||||
onValueChange={handleQuoteSizeChange}
|
||||
|
@ -330,13 +375,13 @@ const AdvancedTradeForm = () => {
|
|||
className="hidden md:block"
|
||||
delay={250}
|
||||
placement="left"
|
||||
content={t('tooltip-post')}
|
||||
content={t('trade:tooltip-post')}
|
||||
>
|
||||
<Checkbox
|
||||
checked={tradeForm.postOnly}
|
||||
onChange={(e) => handlePostOnlyChange(e.target.checked)}
|
||||
>
|
||||
Post
|
||||
{t('trade:post')}
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
@ -344,8 +389,8 @@ const AdvancedTradeForm = () => {
|
|||
<Tooltip
|
||||
className="hidden md:block"
|
||||
delay={250}
|
||||
placement="top"
|
||||
content={t('tooltip-ioc')}
|
||||
placement="left"
|
||||
content={t('trade:tooltip-ioc')}
|
||||
>
|
||||
<div className="flex items-center text-xs text-th-fgd-3">
|
||||
<Checkbox
|
||||
|
@ -363,13 +408,13 @@ const AdvancedTradeForm = () => {
|
|||
<Tooltip
|
||||
delay={250}
|
||||
placement="left"
|
||||
content={t('tooltip-enable-margin')}
|
||||
content={t('trade:tooltip-enable-margin')}
|
||||
>
|
||||
<Checkbox
|
||||
checked={useMargin}
|
||||
onChange={(e) => setUseMargin(e.target.checked)}
|
||||
>
|
||||
{t('margin')}
|
||||
{t('trade:margin')}
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
@ -385,7 +430,16 @@ const AdvancedTradeForm = () => {
|
|||
disabled={false}
|
||||
size="large"
|
||||
>
|
||||
<span className="capitalize">Place {tradeForm.side} Order</span>
|
||||
{!placingOrder ? (
|
||||
<span className="capitalize">
|
||||
{t('trade:place-order', { side: tradeForm.side })}
|
||||
</span>
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Loading />
|
||||
<span>{t('trade:placing-order')}</span>
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Serum3Side } from '@blockworks-foundation/mango-v4'
|
||||
import { IconButton } from '@components/shared/Button'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import SideBadge from '@components/shared/SideBadge'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import { LinkIcon, TrashIcon } from '@heroicons/react/20/solid'
|
||||
|
@ -7,17 +8,22 @@ import { Order } from '@project-serum/serum/lib/market'
|
|||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { formatFixedDecimals } from 'utils/numbers'
|
||||
import { formatFixedDecimals, getDecimalCount } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import MarketLogos from './MarketLogos'
|
||||
|
||||
const OpenOrders = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { connected } = useWallet()
|
||||
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
|
||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
||||
const [cancelId, setCancelId] = useState<string>('')
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const handleCancelOrder = useCallback(
|
||||
async (o: Order) => {
|
||||
|
@ -28,7 +34,7 @@ const OpenOrders = () => {
|
|||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (!group || !mangoAccount) return
|
||||
|
||||
setCancelId(o.orderId.toString())
|
||||
try {
|
||||
const tx = await client.serum3CancelOrder(
|
||||
group,
|
||||
|
@ -46,11 +52,13 @@ const OpenOrders = () => {
|
|||
} catch (e: any) {
|
||||
console.error('Error canceling', e)
|
||||
notify({
|
||||
title: t('order-error'),
|
||||
title: t('trade:cancel-order-error'),
|
||||
description: e.message,
|
||||
txid: e.txid,
|
||||
type: 'error',
|
||||
})
|
||||
} finally {
|
||||
setCancelId('')
|
||||
}
|
||||
},
|
||||
[t]
|
||||
|
@ -58,93 +66,184 @@ const OpenOrders = () => {
|
|||
|
||||
return connected ? (
|
||||
Object.values(openOrders).flat().length ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-left">{t('market')}</th>
|
||||
<th className="text-right">{t('side')}</th>
|
||||
<th className="text-right">{t('size')}</th>
|
||||
<th className="text-right">{t('price')}</th>
|
||||
<th className="text-right">{t('value')}</th>
|
||||
<th className="text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(openOrders)
|
||||
.map(([marketPk, orders]) => {
|
||||
return orders.map((o) => {
|
||||
const group = mangoStore.getState().group
|
||||
const market = group?.getSerum3MarketByPk(
|
||||
new PublicKey(marketPk)
|
||||
)
|
||||
let baseLogoURI = ''
|
||||
let quoteLogoURI = ''
|
||||
const baseSymbol = group?.getFirstBankByTokenIndex(
|
||||
market!.baseTokenIndex
|
||||
).name
|
||||
const quoteSymbol = group?.getFirstBankByTokenIndex(
|
||||
market!.quoteTokenIndex
|
||||
).name
|
||||
if (jupiterTokens.length) {
|
||||
baseLogoURI = jupiterTokens.find(
|
||||
(t) => t.symbol === baseSymbol
|
||||
)!.logoURI
|
||||
quoteLogoURI = jupiterTokens.find(
|
||||
(t) => t.symbol === quoteSymbol
|
||||
)!.logoURI
|
||||
}
|
||||
return (
|
||||
<tr key={`${o.side}${o.size}${o.price}`} className="my-1 p-2">
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos
|
||||
baseURI={baseLogoURI}
|
||||
quoteURI={quoteLogoURI}
|
||||
/>
|
||||
{market?.name}
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<SideBadge side={o.side} />
|
||||
</td>
|
||||
<td className="text-right">{o.size}</td>
|
||||
<td className="text-right">
|
||||
<span>
|
||||
{o.price}{' '}
|
||||
<span className="text-th-fgd-4">{quoteSymbol}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
{formatFixedDecimals(o.size * o.price, true)}
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('cancel')}>
|
||||
<IconButton
|
||||
onClick={() => handleCancelOrder(o)}
|
||||
size="small"
|
||||
>
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
showTableView ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-left">{t('market')}</th>
|
||||
<th className="text-right">{t('trade:side')}</th>
|
||||
<th className="text-right">{t('trade:size')}</th>
|
||||
<th className="text-right">{t('price')}</th>
|
||||
<th className="text-right">{t('value')}</th>
|
||||
<th className="text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(openOrders)
|
||||
.map(([marketPk, orders]) => {
|
||||
return orders.map((o) => {
|
||||
const group = mangoStore.getState().group
|
||||
const market = group?.getSerum3MarketByPk(
|
||||
new PublicKey(marketPk)
|
||||
)
|
||||
let baseLogoURI = ''
|
||||
let quoteLogoURI = ''
|
||||
const baseSymbol = group?.getFirstBankByTokenIndex(
|
||||
market!.baseTokenIndex
|
||||
).name
|
||||
const quoteSymbol = group?.getFirstBankByTokenIndex(
|
||||
market!.quoteTokenIndex
|
||||
).name
|
||||
if (jupiterTokens.length) {
|
||||
baseLogoURI = jupiterTokens.find(
|
||||
(t) => t.symbol === baseSymbol
|
||||
)!.logoURI
|
||||
quoteLogoURI = jupiterTokens.find(
|
||||
(t) => t.symbol === quoteSymbol
|
||||
)!.logoURI
|
||||
}
|
||||
return (
|
||||
<tr
|
||||
key={`${o.side}${o.size}${o.price}`}
|
||||
className="my-1 p-2"
|
||||
>
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos
|
||||
baseURI={baseLogoURI}
|
||||
quoteURI={quoteLogoURI}
|
||||
/>
|
||||
{market?.name}
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<SideBadge side={o.side} />
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{o.size.toLocaleString(undefined, {
|
||||
maximumFractionDigits: getDecimalCount(o.size),
|
||||
})}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="font-mono">
|
||||
{o.price.toLocaleString(undefined, {
|
||||
maximumFractionDigits: getDecimalCount(o.price),
|
||||
})}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quoteSymbol}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{formatFixedDecimals(o.size * o.price, true)}
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('cancel')}>
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() => handleCancelOrder(o)}
|
||||
size="small"
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
})
|
||||
.flat()}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="pb-20">
|
||||
{Object.entries(openOrders).map(([marketPk, orders]) => {
|
||||
return orders.map((o) => {
|
||||
const group = mangoStore.getState().group
|
||||
const market = group?.getSerum3MarketByPk(new PublicKey(marketPk))
|
||||
let baseLogoURI = ''
|
||||
let quoteLogoURI = ''
|
||||
const baseSymbol = group?.getFirstBankByTokenIndex(
|
||||
market!.baseTokenIndex
|
||||
).name
|
||||
const quoteSymbol = group?.getFirstBankByTokenIndex(
|
||||
market!.quoteTokenIndex
|
||||
).name
|
||||
if (jupiterTokens.length) {
|
||||
baseLogoURI = jupiterTokens.find(
|
||||
(t) => t.symbol === baseSymbol
|
||||
)!.logoURI
|
||||
quoteLogoURI = jupiterTokens.find(
|
||||
(t) => t.symbol === quoteSymbol
|
||||
)!.logoURI
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={`${o.side}${o.size}${o.price}`}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos
|
||||
baseURI={baseLogoURI}
|
||||
quoteURI={quoteLogoURI}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm text-th-fgd-1">{market?.name}</p>
|
||||
<span
|
||||
className={`capitalize ${
|
||||
o.side === 'buy' ? 'text-th-green' : 'text-th-red'
|
||||
}`}
|
||||
>
|
||||
{o.side}
|
||||
</span>{' '}
|
||||
<span className="font-mono">
|
||||
{o.size.toLocaleString(undefined, {
|
||||
maximumFractionDigits: getDecimalCount(o.size),
|
||||
})}
|
||||
</span>{' '}
|
||||
<span className="text-th-fgd-4">at</span>{' '}
|
||||
<span className="font-mono">
|
||||
{o.price.toLocaleString(undefined, {
|
||||
maximumFractionDigits: getDecimalCount(o.price),
|
||||
})}
|
||||
</span>{' '}
|
||||
<span className="text-th-fgd-4">{quoteSymbol}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 pl-4">
|
||||
<span>{formatFixedDecimals(o.size * o.price, true)}</span>
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() => handleCancelOrder(o)}
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
.flat()}
|
||||
</tbody>
|
||||
</table>
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<p>No open orders...</p>
|
||||
<p>{t('trade:no-orders')}</p>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<LinkIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p>Connect to view your open orders</p>
|
||||
<p>{t('trade:connect-orders')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ const groupBy = (
|
|||
const depth = 40
|
||||
|
||||
const Orderbook = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||
|
||||
// const [openOrderPrices, setOpenOrderPrices] = useState<any[]>([])
|
||||
|
@ -382,7 +382,7 @@ const Orderbook = () => {
|
|||
<div className="flex items-center justify-between border-b border-th-bkg-3 px-4 py-2">
|
||||
<div id="trade-step-three" className="flex items-center space-x-2">
|
||||
<Tooltip
|
||||
content={showBuys ? 'Hide Buys' : 'Show Buys'}
|
||||
content={showBuys ? t('trade:hide-bids') : t('trade:show-bids')}
|
||||
placement="top"
|
||||
>
|
||||
<button
|
||||
|
@ -396,7 +396,7 @@ const Orderbook = () => {
|
|||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content={showSells ? 'Hide Sells' : 'Show Sells'}
|
||||
content={showSells ? t('trade:hide-asks') : t('trade:show-asks')}
|
||||
placement="top"
|
||||
>
|
||||
<button
|
||||
|
@ -412,7 +412,7 @@ const Orderbook = () => {
|
|||
</div>
|
||||
{serum3MarketExternal ? (
|
||||
<div id="trade-step-four">
|
||||
<Tooltip content="Grouping" placement="top">
|
||||
<Tooltip content={t('trade:grouping')} placement="top">
|
||||
<GroupSize
|
||||
tickSize={serum3MarketExternal.tickSize}
|
||||
onChange={onGroupSizeChange}
|
||||
|
@ -423,7 +423,7 @@ const Orderbook = () => {
|
|||
) : null}
|
||||
</div>
|
||||
<div className="grid grid-cols-2 px-4 pt-2 pb-1 text-xxs text-th-fgd-4">
|
||||
<div className="col-span-1 text-right">{t('size')}</div>
|
||||
<div className="col-span-1 text-right">{t('trade:size')}</div>
|
||||
<div className="col-span-1 text-right">{t('price')}</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -465,7 +465,7 @@ const Orderbook = () => {
|
|||
id="trade-step-nine"
|
||||
>
|
||||
<div className="col-span-1 flex justify-between">
|
||||
<div className="text-xxs">{t('spread')}</div>
|
||||
<div className="text-xxs">{t('trade:spread')}</div>
|
||||
<div className="font-mono">
|
||||
{orderbookData?.spreadPercentage.toFixed(2)}%
|
||||
</div>
|
||||
|
|
|
@ -4,12 +4,12 @@ import Orderbook from './Orderbook'
|
|||
import RecentTrades from './RecentTrades'
|
||||
|
||||
const TABS: [string, number][] = [
|
||||
['book', 0],
|
||||
['trades', 0],
|
||||
['trade:book', 0],
|
||||
['trade:trades', 0],
|
||||
]
|
||||
|
||||
const OrderbookAndTrades = () => {
|
||||
const [activeTab, setActiveTab] = useState('book')
|
||||
const [activeTab, setActiveTab] = useState('trade:book')
|
||||
return (
|
||||
<div className="hide-scroll h-full">
|
||||
<div className="border-b border-r border-th-bkg-3">
|
||||
|
@ -20,11 +20,17 @@ const OrderbookAndTrades = () => {
|
|||
fillWidth
|
||||
/>
|
||||
</div>
|
||||
<div className={`h-full ${activeTab === 'book' ? 'visible' : 'hidden'}`}>
|
||||
<div
|
||||
className={`h-full ${
|
||||
activeTab === 'trade:book' ? 'visible' : 'hidden'
|
||||
}`}
|
||||
>
|
||||
<Orderbook />
|
||||
</div>
|
||||
<div
|
||||
className={`h-full ${activeTab === 'trades' ? 'visible' : 'hidden'}`}
|
||||
className={`h-full ${
|
||||
activeTab === 'trade:trades' ? 'visible' : 'hidden'
|
||||
}`}
|
||||
>
|
||||
<RecentTrades />
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@ import { ChartTradeType } from 'types'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const RecentTrades = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const [trades, setTrades] = useState<any[]>([])
|
||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||
const selectedMarketPk = selectedMarket?.serumMarketExternal.toBase58()
|
||||
|
@ -60,11 +60,11 @@ const RecentTrades = () => {
|
|||
}
|
||||
}, 5000)
|
||||
return (
|
||||
<div className="h-full overflow-y-scroll ">
|
||||
<div className="thin-scroll h-full overflow-y-scroll">
|
||||
<div className={`mb-1 grid grid-cols-3 px-4 pt-2 text-xxs text-th-fgd-4`}>
|
||||
<div className="text-right">{`${t('price')} (${quoteSymbol})`} </div>
|
||||
<div className={`text-right`}>
|
||||
{t('size')} ({baseSymbol})
|
||||
{t('trade:size')} ({baseSymbol})
|
||||
</div>
|
||||
<div className={`text-right`}>{t('time')}</div>
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Image from 'next/image'
|
||||
import { useMemo } from 'react'
|
||||
import { formatDecimal } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
|
||||
const Balances = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||
const group = mangoStore((s) => s.group)
|
||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const banks = useMemo(() => {
|
||||
if (group) {
|
||||
|
@ -41,14 +45,14 @@ const Balances = () => {
|
|||
return []
|
||||
}, [group, mangoAccount])
|
||||
|
||||
return (
|
||||
return showTableView ? (
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bg-th-bkg-1 text-left">{t('token')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('balance')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('in-orders')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('unsettled')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('trade:in-orders')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('trade:unsettled')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -97,6 +101,61 @@ const Balances = () => {
|
|||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<>
|
||||
{banks.map(({ key, value }) => {
|
||||
const bank = value[0]
|
||||
|
||||
let logoURI
|
||||
if (jupiterTokens.length) {
|
||||
logoURI = jupiterTokens.find(
|
||||
(t) => t.address === bank.mint.toString()
|
||||
)!.logoURI
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={key}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
{logoURI ? (
|
||||
<Image alt="" width="20" height="20" src={logoURI} />
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<span>{bank.name}</span>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="mb-0.5 font-mono text-sm text-th-fgd-1">
|
||||
{mangoAccount
|
||||
? formatDecimal(
|
||||
mangoAccount.getTokenBalanceUi(bank),
|
||||
bank.mintDecimals
|
||||
)
|
||||
: 0}
|
||||
</p>
|
||||
<div className="flex space-x-3">
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
{t('trade:in-orders')}:{' '}
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
{spotBalances[bank.mint.toString()]?.inOrders || 0.0}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
{t('trade:unsettled')}:{' '}
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
{spotBalances[bank.mint.toString()]?.unsettled || 0.0}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,18 +4,59 @@ import OpenOrders from './OpenOrders'
|
|||
import Balances from './TradeBalances'
|
||||
import UnsettledTrades from './UnsettledTrades'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { toUiDecimals } from '@blockworks-foundation/mango-v4'
|
||||
|
||||
const TradeInfoTabs = () => {
|
||||
const [selectedTab, setSelectedTab] = useState('Balances')
|
||||
const [selectedTab, setSelectedTab] = useState('balances')
|
||||
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const openOrdersAccounts =
|
||||
mangoStore.getState().mangoAccount.openOrderAccounts
|
||||
const group = mangoStore((s) => s.group)
|
||||
|
||||
const unsettledSpotBalances = useMemo(() => {
|
||||
if (!group || !mangoAccount || !openOrdersAccounts) return {}
|
||||
const unsettledBalances: Record<string, { base: number; quote: number }> =
|
||||
{}
|
||||
mangoAccount.serum3Active().forEach((serumMarket) => {
|
||||
const market = group.getSerum3MarketByIndex(serumMarket.marketIndex)!
|
||||
const openOrdersAccForMkt = openOrdersAccounts.find((oo) =>
|
||||
oo.market.equals(market.serumMarketExternal)
|
||||
)
|
||||
if (openOrdersAccForMkt) {
|
||||
const baseTokenUnsettled = toUiDecimals(
|
||||
openOrdersAccForMkt!.baseTokenFree.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex)
|
||||
.mintDecimals
|
||||
)
|
||||
const quoteTokenUnsettled = toUiDecimals(
|
||||
openOrdersAccForMkt!.quoteTokenFree
|
||||
// @ts-ignore
|
||||
.add(openOrdersAccForMkt['referrerRebatesAccrued'])
|
||||
.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex)
|
||||
.mintDecimals
|
||||
)
|
||||
unsettledBalances[market.serumMarketExternal.toString()] = {
|
||||
base: baseTokenUnsettled,
|
||||
quote: quoteTokenUnsettled,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const filtered = Object.entries(unsettledBalances).filter(
|
||||
([_mkt, balance]) => balance.base > 0 || balance.quote > 0
|
||||
)
|
||||
return Object.fromEntries(filtered)!
|
||||
}, [mangoAccount, group, openOrdersAccounts])
|
||||
|
||||
const tabsWithCount: [string, number][] = useMemo(() => {
|
||||
return [
|
||||
['Balances', 0],
|
||||
['Orders', Object.values(openOrders).flat().length],
|
||||
['Unsettled', 0],
|
||||
['balances', 0],
|
||||
['trade:orders', Object.values(openOrders).flat().length],
|
||||
['trade:unsettled', Object.values(unsettledSpotBalances).flat().length],
|
||||
]
|
||||
}, [openOrders])
|
||||
}, [openOrders, mangoAccount])
|
||||
|
||||
return (
|
||||
<div className="hide-scroll h-full overflow-y-scroll">
|
||||
|
@ -27,9 +68,11 @@ const TradeInfoTabs = () => {
|
|||
showBorders
|
||||
/>
|
||||
</div>
|
||||
{selectedTab === 'Balances' ? <Balances /> : null}
|
||||
{selectedTab === 'Orders' ? <OpenOrders /> : null}
|
||||
{selectedTab === 'Unsettled' ? <UnsettledTrades /> : null}
|
||||
{selectedTab === 'balances' ? <Balances /> : null}
|
||||
{selectedTab === 'trade:orders' ? <OpenOrders /> : null}
|
||||
{selectedTab === 'trade:unsettled' ? (
|
||||
<UnsettledTrades unsettledSpotBalances={unsettledSpotBalances} />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,50 +1,28 @@
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import { toUiDecimals } from '@blockworks-foundation/mango-v4'
|
||||
import Button from '@components/shared/Button'
|
||||
import { IconButton } from '@components/shared/Button'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { CheckIcon, LinkIcon } from '@heroicons/react/20/solid'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
|
||||
const UnsettledTrades = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const openOrdersAccounts =
|
||||
mangoStore.getState().mangoAccount.openOrderAccounts
|
||||
const UnsettledTrades = ({
|
||||
unsettledSpotBalances,
|
||||
}: {
|
||||
unsettledSpotBalances: any
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { connected } = useWallet()
|
||||
const group = mangoStore((s) => s.group)
|
||||
// const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
||||
|
||||
const unsettledSpotBalances = useMemo(() => {
|
||||
if (!group || !mangoAccount) return {}
|
||||
const unsettledBalances: Record<string, { base: number; quote: number }> =
|
||||
{}
|
||||
mangoAccount.serum3Active().forEach((serumMarket) => {
|
||||
const market = group.getSerum3MarketByIndex(serumMarket.marketIndex)!
|
||||
const openOrdersAccForMkt = openOrdersAccounts.find((oo) =>
|
||||
oo.market.equals(market.serumMarketExternal)
|
||||
)
|
||||
const baseTokenUnsettled = toUiDecimals(
|
||||
openOrdersAccForMkt!.baseTokenFree.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex).mintDecimals
|
||||
)
|
||||
const quoteTokenUnsettled = toUiDecimals(
|
||||
openOrdersAccForMkt!.quoteTokenFree
|
||||
// @ts-ignore
|
||||
.add(openOrdersAccForMkt['referrerRebatesAccrued'])
|
||||
.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex).mintDecimals
|
||||
)
|
||||
unsettledBalances[market.serumMarketExternal.toString()] = {
|
||||
base: baseTokenUnsettled,
|
||||
quote: quoteTokenUnsettled,
|
||||
}
|
||||
})
|
||||
|
||||
const filtered = Object.entries(unsettledBalances).filter(
|
||||
([_mkt, balance]) => balance.base > 0 || balance.quote > 0
|
||||
)
|
||||
return Object.fromEntries(filtered)!
|
||||
}, [mangoAccount, group, openOrdersAccounts])
|
||||
const [settleMktAddress, setSettleMktAddress] = useState<string>('')
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const handleSettleFunds = useCallback(async (mktAddress: string) => {
|
||||
const client = mangoStore.getState().client
|
||||
|
@ -53,7 +31,7 @@ const UnsettledTrades = () => {
|
|||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (!group || !mangoAccount) return
|
||||
|
||||
setSettleMktAddress(mktAddress)
|
||||
try {
|
||||
const txid = await client.serum3SettleFunds(
|
||||
group,
|
||||
|
@ -70,63 +48,139 @@ const UnsettledTrades = () => {
|
|||
} catch (e: any) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Settle transaction failed',
|
||||
title: t('trade:settle-funds-error'),
|
||||
description: e?.message,
|
||||
txid: e?.txid,
|
||||
})
|
||||
console.error('Settle funds error:', e)
|
||||
} finally {
|
||||
setSettleMktAddress('')
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!group) return null
|
||||
|
||||
// console.log('unsettledSpotBalances', unsettledSpotBalances)
|
||||
|
||||
return (
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bg-th-bkg-1 text-left">Market</th>
|
||||
<th className="bg-th-bkg-1 text-right">Base</th>
|
||||
<th className="bg-th-bkg-1 text-right">Quote</th>
|
||||
<th className="bg-th-bkg-1 text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(unsettledSpotBalances).map(([mktAddress, balance]) => {
|
||||
const market = group.getSerum3MarketByPk(new PublicKey(mktAddress))
|
||||
console.log('market', mktAddress)
|
||||
const base = market?.name.split('/')[0]
|
||||
const quote = market?.name.split('/')[1]
|
||||
|
||||
return (
|
||||
<tr key={mktAddress} className="text-sm">
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<span>{market ? market.name : ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{unsettledSpotBalances[mktAddress].base || 0.0} {base}
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{unsettledSpotBalances[mktAddress].quote || 0.0} {quote}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<Button
|
||||
onClick={() => handleSettleFunds(mktAddress)}
|
||||
className={`text-white`}
|
||||
disabled={false}
|
||||
size="small"
|
||||
>
|
||||
<span>Settle</span>
|
||||
</Button>
|
||||
</td>
|
||||
return connected ? (
|
||||
Object.values(unsettledSpotBalances).flat().length ? (
|
||||
showTableView ? (
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bg-th-bkg-1 text-left">{t('market')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('trade:base')}</th>
|
||||
<th className="bg-th-bkg-1 text-right">{t('trade:quote')}</th>
|
||||
<th className="bg-th-bkg-1 text-right" />
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.entries(unsettledSpotBalances).map(
|
||||
([mktAddress, balance]) => {
|
||||
const market = group.getSerum3MarketByPk(
|
||||
new PublicKey(mktAddress)
|
||||
)
|
||||
const base = market?.name.split('/')[0]
|
||||
const quote = market?.name.split('/')[1]
|
||||
|
||||
return (
|
||||
<tr key={mktAddress} className="text-sm">
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<span>{market ? market.name : ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{unsettledSpotBalances[mktAddress].base || 0.0}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{base}
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{unsettledSpotBalances[mktAddress].quote || 0.0}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quote}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('trade:settle-funds')}>
|
||||
<IconButton
|
||||
onClick={() => handleSettleFunds(mktAddress)}
|
||||
size="small"
|
||||
>
|
||||
{settleMktAddress === mktAddress ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="pb-20">
|
||||
{Object.entries(unsettledSpotBalances).map(
|
||||
([mktAddress, balance]) => {
|
||||
const market = group.getSerum3MarketByPk(
|
||||
new PublicKey(mktAddress)
|
||||
)
|
||||
const base = market?.name.split('/')[0]
|
||||
const quote = market?.name.split('/')[1]
|
||||
|
||||
return (
|
||||
<div
|
||||
key={mktAddress}
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span>{market ? market.name : ''}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
{unsettledSpotBalances[mktAddress].base ? (
|
||||
<span className="font-mono text-sm">
|
||||
{unsettledSpotBalances[mktAddress].base}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{base}
|
||||
</span>
|
||||
</span>
|
||||
) : null}
|
||||
{unsettledSpotBalances[mktAddress].quote ? (
|
||||
<span className="font-mono text-sm">
|
||||
{unsettledSpotBalances[mktAddress].quote}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quote}
|
||||
</span>
|
||||
</span>
|
||||
) : null}
|
||||
<IconButton onClick={() => handleSettleFunds(mktAddress)}>
|
||||
{settleMktAddress === mktAddress ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<p>{t('trade:no-unsettled')}</p>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<LinkIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p>{t('trade:connect-unsettled')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import EditProfileModal from '@components/modals/EditProfileModal'
|
|||
|
||||
const ConnectedMenu = () => {
|
||||
const { t } = useTranslation('common')
|
||||
// const [showProfileImageModal, setShowProfileImageModal] = useState(false)
|
||||
const [showEditProfileModal, setShowEditProfileModal] = useState(false)
|
||||
const set = mangoStore((s) => s.set)
|
||||
const { publicKey, disconnect, wallet } = useWallet()
|
||||
|
@ -31,6 +30,7 @@ const ConnectedMenu = () => {
|
|||
set((state) => {
|
||||
state.mangoAccount.current = undefined
|
||||
state.connected = false
|
||||
state.mangoAccount.openOrders = {}
|
||||
})
|
||||
disconnect()
|
||||
wallet?.adapter.disconnect()
|
||||
|
|
|
@ -8,14 +8,12 @@
|
|||
"account-update-success": "Account updated successfully",
|
||||
"account-value": "Account Value",
|
||||
"accounts": "Accounts",
|
||||
"amount": "Amount",
|
||||
"asset-weight": "Asset Weight",
|
||||
"asset-weight-desc": "The asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral.",
|
||||
"available": "Available",
|
||||
"available-balance": "Available Balance",
|
||||
"balance": "Balance",
|
||||
"balances": "Balances",
|
||||
"book": "Book",
|
||||
"borrow": "Borrow",
|
||||
"borrow-amount": "Borrow Amount",
|
||||
"borrow-fee": "Borrow Fee",
|
||||
|
@ -46,7 +44,6 @@
|
|||
"governance": "Governance",
|
||||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"in-orders": "In Orders",
|
||||
"insufficient-sol": "Solana requires 0.00757 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
|
@ -54,7 +51,6 @@
|
|||
"leverage": "Leverage",
|
||||
"liability-weight": "Liability Weight",
|
||||
"liquidity": "Liquidity",
|
||||
"margin": "Margin",
|
||||
"market": "Market",
|
||||
"max": "Max",
|
||||
"max-borrow": "Max Borrow",
|
||||
|
@ -62,7 +58,6 @@
|
|||
"new-account": "New Account",
|
||||
"new-account-failed": "Failed to create account",
|
||||
"new-account-success": "Your new account is ready 😎",
|
||||
"oracle-price": "Oracle Price",
|
||||
"pnl": "PnL",
|
||||
"price": "Price",
|
||||
"quantity": "Quantity",
|
||||
|
@ -75,9 +70,6 @@
|
|||
"sell": "Sell",
|
||||
"settings": "Settings",
|
||||
"show-zero-balances": "Show Zero Balances",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"stats": "Stats",
|
||||
"swap": "Swap",
|
||||
"time": "Time",
|
||||
|
@ -90,11 +82,9 @@
|
|||
"total-deposit-value": "Total Deposit Value",
|
||||
"total-interest-value": "Total Interest Value",
|
||||
"trade": "Trade",
|
||||
"trades": "Trades",
|
||||
"trade-history": "Trade History",
|
||||
"transaction": "Transaction",
|
||||
"unavailable": "Unavailable",
|
||||
"unsettled": "Unsettled",
|
||||
"update": "Update",
|
||||
"utilization": "Utilization",
|
||||
"value": "Value",
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"amount": "Amount",
|
||||
"base": "Base Token",
|
||||
"book": "Book",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
"connect-unsettled": "Connect to view your unsettled trades",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
"in-orders": "In Orders",
|
||||
"limit-price": "Limit Price",
|
||||
"margin": "Margin",
|
||||
"no-orders": "No open orders...",
|
||||
"no-unsettled": "No unsettled funds...",
|
||||
"oracle-price": "Oracle Price",
|
||||
"orders": "Orders",
|
||||
"order-error": "Failed to place order",
|
||||
"post": "Post",
|
||||
"place-order": "Place {{side}} Order",
|
||||
"placing-order": "Placing Order",
|
||||
"quote": "Quote Token",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
"show-asks": "Show Asks",
|
||||
"show-bids": "Show Bids",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate or cancel orders are guaranteed to be the taker or they will be canceled.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker order or else they will be canceled.",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -8,14 +8,12 @@
|
|||
"account-update-success": "Account updated successfully",
|
||||
"account-value": "Account Value",
|
||||
"accounts": "Accounts",
|
||||
"amount": "Amount",
|
||||
"asset-weight": "Asset Weight",
|
||||
"asset-weight-desc": "The asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral.",
|
||||
"available": "Available",
|
||||
"available-balance": "Available Balance",
|
||||
"balance": "Balance",
|
||||
"balances": "Balances",
|
||||
"book": "Book",
|
||||
"borrow": "Borrow",
|
||||
"borrow-amount": "Borrow Amount",
|
||||
"borrow-fee": "Borrow Fee",
|
||||
|
@ -46,7 +44,6 @@
|
|||
"governance": "Governance",
|
||||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"in-orders": "In Orders",
|
||||
"insufficient-sol": "Solana requires 0.00757 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
|
@ -54,7 +51,6 @@
|
|||
"leverage": "Leverage",
|
||||
"liability-weight": "Liability Weight",
|
||||
"liquidity": "Liquidity",
|
||||
"margin": "Margin",
|
||||
"market": "Market",
|
||||
"max": "Max",
|
||||
"max-borrow": "Max Borrow",
|
||||
|
@ -62,7 +58,6 @@
|
|||
"new-account": "New Account",
|
||||
"new-account-failed": "Failed to create account",
|
||||
"new-account-success": "Your new account is ready 😎",
|
||||
"oracle-price": "Oracle Price",
|
||||
"pnl": "PnL",
|
||||
"price": "Price",
|
||||
"quantity": "Quantity",
|
||||
|
@ -75,9 +70,6 @@
|
|||
"sell": "Sell",
|
||||
"settings": "Settings",
|
||||
"show-zero-balances": "Show Zero Balances",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"stats": "Stats",
|
||||
"swap": "Swap",
|
||||
"time": "Time",
|
||||
|
@ -90,11 +82,9 @@
|
|||
"total-deposit-value": "Total Deposit Value",
|
||||
"total-interest-value": "Total Interest Value",
|
||||
"trade": "Trade",
|
||||
"trades": "Trades",
|
||||
"trade-history": "Trade History",
|
||||
"transaction": "Transaction",
|
||||
"unavailable": "Unavailable",
|
||||
"unsettled": "Unsettled",
|
||||
"update": "Update",
|
||||
"utilization": "Utilization",
|
||||
"value": "Value",
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"amount": "Amount",
|
||||
"base": "Base Token",
|
||||
"book": "Book",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
"connect-unsettled": "Connect to view your unsettled trades",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
"in-orders": "In Orders",
|
||||
"limit-price": "Limit Price",
|
||||
"margin": "Margin",
|
||||
"no-orders": "No open orders...",
|
||||
"no-unsettled": "No unsettled funds...",
|
||||
"oracle-price": "Oracle Price",
|
||||
"orders": "Orders",
|
||||
"order-error": "Failed to place order",
|
||||
"post": "Post",
|
||||
"place-order": "Place {{side}} Order",
|
||||
"placing-order": "Placing Order",
|
||||
"quote": "Quote Token",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
"show-asks": "Show Asks",
|
||||
"show-bids": "Show Bids",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate or cancel orders are guaranteed to be the taker or they will be canceled.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker order or else they will be canceled.",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -8,14 +8,12 @@
|
|||
"account-update-success": "Account updated successfully",
|
||||
"account-value": "Account Value",
|
||||
"accounts": "Accounts",
|
||||
"amount": "Amount",
|
||||
"asset-weight": "Asset Weight",
|
||||
"asset-weight-desc": "The asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral.",
|
||||
"available": "Available",
|
||||
"available-balance": "Available Balance",
|
||||
"balance": "Balance",
|
||||
"balances": "Balances",
|
||||
"book": "Book",
|
||||
"borrow": "Borrow",
|
||||
"borrow-amount": "Borrow Amount",
|
||||
"borrow-fee": "Borrow Fee",
|
||||
|
@ -46,7 +44,6 @@
|
|||
"governance": "Governance",
|
||||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"in-orders": "In Orders",
|
||||
"insufficient-sol": "Solana requires 0.00757 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
|
@ -54,7 +51,6 @@
|
|||
"leverage": "Leverage",
|
||||
"liability-weight": "Liability Weight",
|
||||
"liquidity": "Liquidity",
|
||||
"margin": "Margin",
|
||||
"market": "Market",
|
||||
"max": "Max",
|
||||
"max-borrow": "Max Borrow",
|
||||
|
@ -62,7 +58,6 @@
|
|||
"new-account": "New Account",
|
||||
"new-account-failed": "Failed to create account",
|
||||
"new-account-success": "Your new account is ready 😎",
|
||||
"oracle-price": "Oracle Price",
|
||||
"pnl": "PnL",
|
||||
"price": "Price",
|
||||
"quantity": "Quantity",
|
||||
|
@ -75,9 +70,6 @@
|
|||
"sell": "Sell",
|
||||
"settings": "Settings",
|
||||
"show-zero-balances": "Show Zero Balances",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"stats": "Stats",
|
||||
"swap": "Swap",
|
||||
"time": "Time",
|
||||
|
@ -90,11 +82,9 @@
|
|||
"total-deposit-value": "Total Deposit Value",
|
||||
"total-interest-value": "Total Interest Value",
|
||||
"trade": "Trade",
|
||||
"trades": "Trades",
|
||||
"trade-history": "Trade History",
|
||||
"transaction": "Transaction",
|
||||
"unavailable": "Unavailable",
|
||||
"unsettled": "Unsettled",
|
||||
"update": "Update",
|
||||
"utilization": "Utilization",
|
||||
"value": "Value",
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"amount": "Amount",
|
||||
"base": "Base Token",
|
||||
"book": "Book",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
"connect-unsettled": "Connect to view your unsettled trades",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
"in-orders": "In Orders",
|
||||
"limit-price": "Limit Price",
|
||||
"margin": "Margin",
|
||||
"no-orders": "No open orders...",
|
||||
"no-unsettled": "No unsettled funds...",
|
||||
"oracle-price": "Oracle Price",
|
||||
"orders": "Orders",
|
||||
"order-error": "Failed to place order",
|
||||
"post": "Post",
|
||||
"place-order": "Place {{side}} Order",
|
||||
"placing-order": "Placing Order",
|
||||
"quote": "Quote Token",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
"show-asks": "Show Asks",
|
||||
"show-bids": "Show Bids",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate or cancel orders are guaranteed to be the taker or they will be canceled.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker order or else they will be canceled.",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
|
@ -8,14 +8,12 @@
|
|||
"account-update-success": "Account updated successfully",
|
||||
"account-value": "Account Value",
|
||||
"accounts": "Accounts",
|
||||
"amount": "Amount",
|
||||
"asset-weight": "Asset Weight",
|
||||
"asset-weight-desc": "The asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral.",
|
||||
"available": "Available",
|
||||
"available-balance": "Available Balance",
|
||||
"balance": "Balance",
|
||||
"balances": "Balances",
|
||||
"book": "Book",
|
||||
"borrow": "Borrow",
|
||||
"borrow-amount": "Borrow Amount",
|
||||
"borrow-fee": "Borrow Fee",
|
||||
|
@ -46,7 +44,6 @@
|
|||
"governance": "Governance",
|
||||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"in-orders": "In Orders",
|
||||
"insufficient-sol": "Solana requires 0.00757 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
|
@ -54,7 +51,6 @@
|
|||
"leverage": "Leverage",
|
||||
"liability-weight": "Liability Weight",
|
||||
"liquidity": "Liquidity",
|
||||
"margin": "Margin",
|
||||
"market": "Market",
|
||||
"max": "Max",
|
||||
"max-borrow": "Max Borrow",
|
||||
|
@ -62,7 +58,6 @@
|
|||
"new-account": "New Account",
|
||||
"new-account-failed": "Failed to create account",
|
||||
"new-account-success": "Your new account is ready 😎",
|
||||
"oracle-price": "Oracle Price",
|
||||
"pnl": "PnL",
|
||||
"price": "Price",
|
||||
"quantity": "Quantity",
|
||||
|
@ -75,9 +70,6 @@
|
|||
"sell": "Sell",
|
||||
"settings": "Settings",
|
||||
"show-zero-balances": "Show Zero Balances",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"stats": "Stats",
|
||||
"swap": "Swap",
|
||||
"time": "Time",
|
||||
|
@ -90,11 +82,9 @@
|
|||
"total-deposit-value": "Total Deposit Value",
|
||||
"total-interest-value": "Total Interest Value",
|
||||
"trade": "Trade",
|
||||
"trades": "Trades",
|
||||
"trade-history": "Trade History",
|
||||
"transaction": "Transaction",
|
||||
"unavailable": "Unavailable",
|
||||
"unsettled": "Unsettled",
|
||||
"update": "Update",
|
||||
"utilization": "Utilization",
|
||||
"value": "Value",
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"amount": "Amount",
|
||||
"base": "Base Token",
|
||||
"book": "Book",
|
||||
"cancel-order-error": "Failed to cancel order",
|
||||
"connect-orders": "Connect to view your open orders",
|
||||
"connect-unsettled": "Connect to view your unsettled trades",
|
||||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
"in-orders": "In Orders",
|
||||
"limit-price": "Limit Price",
|
||||
"margin": "Margin",
|
||||
"no-orders": "No open orders...",
|
||||
"no-unsettled": "No unsettled funds...",
|
||||
"oracle-price": "Oracle Price",
|
||||
"orders": "Orders",
|
||||
"order-error": "Failed to place order",
|
||||
"post": "Post",
|
||||
"place-order": "Place {{side}} Order",
|
||||
"placing-order": "Placing Order",
|
||||
"quote": "Quote Token",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
"show-asks": "Show Asks",
|
||||
"show-bids": "Show Bids",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"spread": "Spread",
|
||||
"tooltip-enable-margin": "Enable spot margin for this trade",
|
||||
"tooltip-ioc": "Immediate or cancel orders are guaranteed to be the taker or they will be canceled.",
|
||||
"tooltip-post": "Post orders are guaranteed to be the maker order or else they will be canceled.",
|
||||
"trades": "Trades",
|
||||
"unsettled": "Unsettled"
|
||||
}
|
Loading…
Reference in New Issue