merge main
This commit is contained in:
commit
686c654c48
File diff suppressed because one or more lines are too long
|
@ -37,7 +37,7 @@ const NotificationsButton = () => {
|
|||
<BellIcon className="h-5 w-5" />
|
||||
<span className="sr-only">Notifications</span>
|
||||
{notificationCount !== 0 ? (
|
||||
<div className="absolute left-8 top-4">
|
||||
<div className="absolute left-5 top-4 sm:left-8">
|
||||
<span className="relative flex h-3.5 w-max items-center justify-center rounded-full bg-th-down px-1 text-xxs font-bold text-white">
|
||||
{formatNumericValue(notificationCount)}
|
||||
</span>
|
||||
|
|
|
@ -3,7 +3,7 @@ import Button from './Button'
|
|||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { WalletName, WalletReadyState } from '@solana/wallet-adapter-base'
|
||||
import { AUTO_CONNECT_WALLET, LAST_WALLET_NAME } from 'utils/constants'
|
||||
import { IS_ONBOARDED_KEY, LAST_WALLET_NAME } from 'utils/constants'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { LinkIcon } from '@heroicons/react/20/solid'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
|
@ -24,10 +24,10 @@ const SecondaryConnectButton = ({
|
|||
LAST_WALLET_NAME,
|
||||
'',
|
||||
)
|
||||
const [autoConnect] = useLocalStorageState(AUTO_CONNECT_WALLET, true)
|
||||
const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||
|
||||
const handleConnect = useCallback(() => {
|
||||
if (!autoConnect) {
|
||||
if (!isOnboarded) {
|
||||
set((s) => {
|
||||
s.showUserSetup = true
|
||||
})
|
||||
|
@ -48,7 +48,7 @@ const SecondaryConnectButton = ({
|
|||
})
|
||||
}
|
||||
}
|
||||
}, [autoConnect, lastWalletName, select, wallets])
|
||||
}, [isOnboarded, lastWalletName, select, wallets])
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
|
|
@ -3,6 +3,7 @@ import { PerpStatsItem } from 'types'
|
|||
import { formatNumericValue } from 'utils/numbers'
|
||||
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Switch from '@components/forms/Switch'
|
||||
|
||||
interface GroupedDataItem extends PerpStatsItem {
|
||||
intervalStartMillis: number
|
||||
|
@ -37,6 +38,8 @@ const groupByHourlyInterval = (
|
|||
return groupedData
|
||||
}
|
||||
|
||||
const YEARLY_HOURS = 8760
|
||||
|
||||
const AverageFundingChart = ({
|
||||
loading,
|
||||
marketStats,
|
||||
|
@ -46,6 +49,7 @@ const AverageFundingChart = ({
|
|||
}) => {
|
||||
const { t } = useTranslation(['common', 'stats', 'trade'])
|
||||
const [daysToShow, setDaysToShow] = useState('30')
|
||||
const [showAsApr, setShowAsApr] = useState(false)
|
||||
|
||||
const [interval, intervalString] = useMemo(() => {
|
||||
if (daysToShow === '30') {
|
||||
|
@ -60,26 +64,49 @@ const AverageFundingChart = ({
|
|||
const chartData = useMemo(() => {
|
||||
if (!marketStats) return []
|
||||
const groupedData = groupByHourlyInterval(marketStats, interval)
|
||||
if (showAsApr) {
|
||||
const multiplier = YEARLY_HOURS / interval
|
||||
for (const data of groupedData) {
|
||||
data.funding_rate_hourly = data.funding_rate_hourly * multiplier
|
||||
}
|
||||
}
|
||||
return groupedData
|
||||
}, [daysToShow, interval, marketStats])
|
||||
}, [daysToShow, interval, marketStats, showAsApr])
|
||||
|
||||
return (
|
||||
<DetailedAreaOrBarChart
|
||||
data={chartData}
|
||||
daysToShow={daysToShow}
|
||||
setDaysToShow={setDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loading}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={t('trade:average-funding', { interval: t(intervalString) })}
|
||||
xKey="date_hour"
|
||||
yKey="funding_rate_hourly"
|
||||
yDecimals={5}
|
||||
chartType="bar"
|
||||
tooltipDateFormat={daysToShow === '30' ? 'DD MMM YY' : ''}
|
||||
/>
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<div className="px-4 md:px-6">
|
||||
<DetailedAreaOrBarChart
|
||||
data={chartData}
|
||||
daysToShow={daysToShow}
|
||||
setDaysToShow={setDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loading}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={
|
||||
showAsApr
|
||||
? `${t('trade:average-funding', { interval: '' })} APR`
|
||||
: t('trade:average-funding', { interval: t(intervalString) })
|
||||
}
|
||||
xKey="date_hour"
|
||||
yKey="funding_rate_hourly"
|
||||
yDecimals={showAsApr ? 2 : 5}
|
||||
chartType="bar"
|
||||
tooltipDateFormat={daysToShow === '30' ? 'DD MMM YY' : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-end border-t border-th-bkg-3 px-4 py-2 md:px-6">
|
||||
<Switch
|
||||
checked={showAsApr}
|
||||
onChange={() => setShowAsApr(!showAsApr)}
|
||||
small
|
||||
>
|
||||
{t('stats:show-as-apr')}
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
|||
import AverageFundingChart from './AverageFundingChart'
|
||||
|
||||
const CHART_WRAPPER_CLASSES =
|
||||
'col-span-2 lg:col-span-1 border-b border-th-bkg-3 py-4 px-6'
|
||||
'col-span-2 lg:col-span-1 border-b border-th-bkg-3 py-4 px-4 md:px-6'
|
||||
import PerpMarketParams from './PerpMarketParams'
|
||||
import PerpVolumeChart from './PerpVolumeChart'
|
||||
|
||||
|
@ -74,7 +74,9 @@ const PerpMarketDetails = ({
|
|||
yKey={'open_interest'}
|
||||
/>
|
||||
</div>
|
||||
<div className={`${CHART_WRAPPER_CLASSES} lg:border-r`}>
|
||||
<div
|
||||
className={`col-span-2 border-b border-th-bkg-3 pt-4 lg:col-span-1 lg:border-r`}
|
||||
>
|
||||
<AverageFundingChart
|
||||
loading={loadingPerpStats}
|
||||
marketStats={marketStats}
|
||||
|
@ -92,9 +94,9 @@ const PerpMarketDetails = ({
|
|||
])}
|
||||
daysToShow={priceDaysToShow}
|
||||
setDaysToShow={setPriceDaysToShow}
|
||||
heightClass="h-64"
|
||||
heightClass="h-72"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
loaderHeightClass="h-[400px]"
|
||||
prefix="$"
|
||||
tickFormat={(x) => formatYAxis(x)}
|
||||
title={t('price')}
|
||||
|
|
|
@ -18,24 +18,14 @@ import SwapSlider from './SwapSlider'
|
|||
import PercentageSelectButtons from './PercentageSelectButtons'
|
||||
import BuyTokenInput from './BuyTokenInput'
|
||||
import SellTokenInput from './SellTokenInput'
|
||||
import Button from '@components/shared/Button'
|
||||
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
import { useTokenMax } from './useTokenMax'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import Link from 'next/link'
|
||||
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
|
||||
import useRemainingBorrowsInPeriod from 'hooks/useRemainingBorrowsInPeriod'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { formatCurrencyValue } from 'utils/numbers'
|
||||
import { SwapFormTokenListType } from './SwapFormTokenList'
|
||||
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
|
||||
import TopBarStore from '@store/topBarStore'
|
||||
import SwapFormSubmitButton from './SwapFormSubmitButton'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -60,7 +50,6 @@ const MarketSwapForm = ({
|
|||
setShowTokenSelect,
|
||||
onSuccess,
|
||||
}: MarketSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
//initial state is undefined null is returned on error
|
||||
const [selectedRoute, setSelectedRoute] =
|
||||
useState<JupiterV6RouteInfo | null>()
|
||||
|
@ -101,7 +90,6 @@ const MarketSwapForm = ({
|
|||
!isDraggingSlider
|
||||
),
|
||||
})
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
||||
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(amountInFormValue)
|
||||
|
@ -358,131 +346,15 @@ const MarketSwapForm = ({
|
|||
setShowTokenSelect={setShowTokenSelect}
|
||||
handleRepay={handleRepay}
|
||||
/>
|
||||
{ipAllowed ? (
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingExactIn || loadingExactOut}
|
||||
useMargin={useMargin}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
inputSymbol={inputBank?.name}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingExactIn || loadingExactOut}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MarketSwapForm
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
inputSymbol: string | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: JupiterV6RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { setShowSettingsModal } = TopBarStore()
|
||||
// const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
const tokenPositionsFull = useTokenPositionsFull([outputBank, inputBank])
|
||||
|
||||
// check if the borrowed amount exceeds the net borrow limit in the current period
|
||||
const borrowExceedsLimitInPeriod = useMemo(() => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
if (!mangoAccount || !inputBank || !remainingBorrowsInPeriod) return false
|
||||
|
||||
const balance = mangoAccount.getTokenDepositsUi(inputBank)
|
||||
const remainingBalance = balance - amountIn.toNumber()
|
||||
const borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||
const borrowAmountNotional = borrowAmount * inputBank.uiPrice
|
||||
return borrowAmountNotional > remainingBorrowsInPeriod
|
||||
}, [amountIn, inputBank, mangoAccountAddress, remainingBorrowsInPeriod])
|
||||
|
||||
const disabled =
|
||||
!amountIn.toNumber() ||
|
||||
!amountOut ||
|
||||
!selectedRoute ||
|
||||
!!selectedRoute.error
|
||||
|
||||
return (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<SecondaryConnectButton
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
isLarge
|
||||
/>
|
||||
)}
|
||||
{tokenPositionsFull ? (
|
||||
<div className="pb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href={''} onClick={() => setShowSettingsModal(true)}>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{borrowExceedsLimitInPeriod &&
|
||||
remainingBorrowsInPeriod &&
|
||||
timeToNextPeriod ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={t('error-borrow-exceeds-limit', {
|
||||
remaining: formatCurrencyValue(remainingBorrowsInPeriod),
|
||||
resetTime: dayjs().to(dayjs().add(timeToNextPeriod, 'second')),
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{(selectedRoute === null && amountIn.gt(0)) ||
|
||||
(selectedRoute && !!selectedRoute.error) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
const POPULAR_TOKENS = [
|
||||
{ name: 'USDC', mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' },
|
||||
{ name: 'SOL', mint: 'So11111111111111111111111111111111111111112' },
|
||||
{ name: 'mSOL', mint: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So' },
|
||||
{ name: 'wBTC', mint: '3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh' },
|
||||
]
|
||||
|
||||
const PopularSwapTokens = ({
|
||||
setSwapToken,
|
||||
}: {
|
||||
setSwapToken: (token: string) => void
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-wrap">
|
||||
{POPULAR_TOKENS.map((token) => (
|
||||
<button
|
||||
className="m-1 rounded-md border border-th-fgd-4 px-2 py-0.5 text-sm text-th-fgd-3 focus:outline-none md:hover:border-th-fgd-2 md:hover:text-th-fgd-2"
|
||||
onClick={() => setSwapToken(token.mint)}
|
||||
key={token.mint}
|
||||
>
|
||||
{token.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PopularSwapTokens
|
|
@ -162,7 +162,7 @@ const SwapForm = () => {
|
|||
{swapOrTrigger === 'swap' ? (
|
||||
<>
|
||||
<div className="flex justify-end pb-3 pt-4">
|
||||
<div className="flex justify-between px-6">
|
||||
<div className="flex justify-between px-4 md:px-6">
|
||||
<Switch
|
||||
checked={walletSwap}
|
||||
onChange={() => setWalletSwap(!walletSwap)}
|
||||
|
@ -177,24 +177,24 @@ const SwapForm = () => {
|
|||
</div>
|
||||
</div>
|
||||
{walletSwap ? (
|
||||
<div className="px-6">
|
||||
<div className="px-4 md:px-6">
|
||||
<WalletSwapForm setShowTokenSelect={setShowTokenSelect} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-6">
|
||||
<div className="px-4 md:px-6">
|
||||
<MarketSwapForm setShowTokenSelect={setShowTokenSelect} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="px-6 pt-4">
|
||||
<div className="px-4 pt-4 md:px-6">
|
||||
<TriggerSwapForm
|
||||
showTokenSelect={showTokenSelect}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="px-6 pb-6">
|
||||
<div className="px-4 pb-6 md:px-6">
|
||||
{inputBank && !walletSwap ? (
|
||||
<TokenVaultWarnings bank={inputBank} type="swap" />
|
||||
) : null}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import { formatCurrencyValue } from 'utils/numbers'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import Decimal from 'decimal.js'
|
||||
import { JupiterV6RouteInfo } from 'types/jupiter'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import TopBarStore from '@store/topBarStore'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useRemainingBorrowsInPeriod from 'hooks/useRemainingBorrowsInPeriod'
|
||||
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
|
||||
import { useMemo } from 'react'
|
||||
import Button from '@components/shared/Button'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
|
||||
import Link from 'next/link'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: JupiterV6RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
const { setShowSettingsModal } = TopBarStore()
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
const tokenPositionsFull = useTokenPositionsFull([outputBank, inputBank])
|
||||
|
||||
// check if the borrowed amount exceeds the net borrow limit in the current period
|
||||
const borrowExceedsLimitInPeriod = useMemo(() => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
if (!mangoAccount || !inputBank || !remainingBorrowsInPeriod) return false
|
||||
|
||||
const balance = mangoAccount.getTokenDepositsUi(inputBank)
|
||||
const remainingBalance = balance - amountIn.toNumber()
|
||||
const borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||
const borrowAmountNotional = borrowAmount * inputBank.uiPrice
|
||||
return borrowAmountNotional > remainingBorrowsInPeriod
|
||||
}, [amountIn, inputBank, mangoAccountAddress, remainingBorrowsInPeriod])
|
||||
|
||||
const disabled =
|
||||
!amountIn.toNumber() ||
|
||||
!amountOut ||
|
||||
!selectedRoute ||
|
||||
!!selectedRoute.error
|
||||
|
||||
return ipAllowed ? (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<SecondaryConnectButton
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
isLarge
|
||||
/>
|
||||
)}
|
||||
{tokenPositionsFull ? (
|
||||
<div className="pb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href={''} onClick={() => setShowSettingsModal(true)}>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{borrowExceedsLimitInPeriod &&
|
||||
remainingBorrowsInPeriod &&
|
||||
timeToNextPeriod ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={t('error-borrow-exceeds-limit', {
|
||||
remaining: formatCurrencyValue(remainingBorrowsInPeriod),
|
||||
resetTime: dayjs().to(dayjs().add(timeToNextPeriod, 'second')),
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{(selectedRoute === null && amountIn.gt(0)) ||
|
||||
(selectedRoute && !!selectedRoute.error) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwapFormSubmitButton
|
|
@ -17,6 +17,7 @@ import Input from '@components/forms/Input'
|
|||
import { getInputTokenBalance } from './TriggerSwapForm'
|
||||
import { walletBalanceForToken } from '@components/DepositForm'
|
||||
import TokenReduceOnlyDesc from '@components/shared/TokenReduceOnlyDesc'
|
||||
import PopularSwapTokens from './PopularSwapTokens'
|
||||
|
||||
export type SwapFormTokenListType =
|
||||
| 'input'
|
||||
|
@ -349,7 +350,7 @@ const SwapFormTokenList = ({
|
|||
>
|
||||
<XMarkIcon className="h-6 w-6" />
|
||||
</IconButton>
|
||||
<div className="relative mb-4">
|
||||
<div className="relative">
|
||||
<Input
|
||||
className="pl-10"
|
||||
type="text"
|
||||
|
@ -361,13 +362,24 @@ const SwapFormTokenList = ({
|
|||
/>
|
||||
<MagnifyingGlassIcon className="absolute left-3 top-3.5 h-5 w-5 text-th-fgd-3" />
|
||||
</div>
|
||||
<div className="flex justify-between rounded bg-th-bkg-2 p-2">
|
||||
{!type?.includes('reduce') ? (
|
||||
<div className="pt-2">
|
||||
<PopularSwapTokens setSwapToken={handleTokenSelect} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-4 flex justify-between rounded bg-th-bkg-2 p-2">
|
||||
<p className="text-xs text-th-fgd-4">{t('token')}</p>
|
||||
{!type?.includes('output') ? (
|
||||
<p className="text-xs text-th-fgd-4">{t('max')}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="thin-scroll h-[calc(100%-128px)] overflow-auto py-2">
|
||||
<div
|
||||
className={`thin-scroll ${
|
||||
!type?.includes('reduce')
|
||||
? 'h-[calc(100%-170px)]'
|
||||
: 'h-[calc(100%-128px)]'
|
||||
} overflow-auto py-2`}
|
||||
>
|
||||
{sortedTokens?.length ? (
|
||||
sortedTokens.map((token) => (
|
||||
<TokenItem
|
||||
|
|
|
@ -63,18 +63,6 @@ const SwapSummaryInfo = ({
|
|||
: Math.trunc(simulatedHealthRatio)
|
||||
}, [inputBank, outputBank, amountInFormValue, amountOutFormValue])
|
||||
|
||||
const estSlippage = useMemo(() => {
|
||||
const { group } = mangoStore.getState()
|
||||
const amountIn = parseFloat(amountInFormValue) || 0
|
||||
if (!group || !inputBank || amountIn <= 0) return 0
|
||||
const value = amountIn * inputBank.uiPrice
|
||||
const slippage = group.getPriceImpactByTokenIndex(
|
||||
inputBank.tokenIndex,
|
||||
value,
|
||||
)
|
||||
return slippage
|
||||
}, [amountInFormValue, inputBank])
|
||||
|
||||
const handleSetMargin = () => {
|
||||
set((s) => {
|
||||
s.swap.margin = !s.swap.margin
|
||||
|
@ -117,16 +105,6 @@ const SwapSummaryInfo = ({
|
|||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{estSlippage ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-th-fgd-3">{t('trade:est-slippage')}</p>
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
{estSlippage.toFixed(2)}%
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -188,6 +188,7 @@ const SwapTokenChart = () => {
|
|||
swapOrTrigger,
|
||||
} = mangoStore((s) => s.swap)
|
||||
const { inputCoingeckoId, outputCoingeckoId } = useJupiterSwapData()
|
||||
const { isDesktop } = useViewport()
|
||||
const [baseTokenId, setBaseTokenId] = useState(inputCoingeckoId)
|
||||
const [quoteTokenId, setQuoteTokenId] = useState(outputCoingeckoId)
|
||||
const [mouseData, setMouseData] = useState<SwapChartDataItem>()
|
||||
|
@ -498,8 +499,11 @@ const SwapTokenChart = () => {
|
|||
)
|
||||
}, [amountIn, amountOut, swapMode])
|
||||
|
||||
const chartNumberHeight = isDesktop ? 48 : 40
|
||||
const chartNumberWidth = isDesktop ? 35 : 27
|
||||
|
||||
return (
|
||||
<ContentBox hideBorder hidePadding className="h-full px-6 py-3">
|
||||
<ContentBox hideBorder hidePadding className="h-full px-4 py-3 md:px-6">
|
||||
{isLoading || isFetching ? (
|
||||
<>
|
||||
<SheenLoader className="w-[148px] rounded-md">
|
||||
|
@ -539,11 +543,11 @@ const SwapTokenChart = () => {
|
|||
) : null}
|
||||
{mouseData ? (
|
||||
<>
|
||||
<div className="mb-1 flex flex-col font-display text-5xl text-th-fgd-1 md:flex-row md:items-end">
|
||||
<div className="mb-1 flex flex-col font-display text-4xl text-th-fgd-1 md:flex-row md:items-end md:text-5xl">
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={35}
|
||||
height={chartNumberHeight}
|
||||
width={chartNumberWidth}
|
||||
play
|
||||
numbers={formatNumericValue(mouseData.price)}
|
||||
/>
|
||||
|
@ -552,27 +556,25 @@ const SwapTokenChart = () => {
|
|||
<FormatNumericValue value={mouseData.price} />
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={`ml-0 mt-2 flex items-center text-sm md:ml-3 md:mt-0`}
|
||||
>
|
||||
<Change change={calculateChartChange()} suffix="%" />
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-th-fgd-4">
|
||||
{dayjs(mouseData.time).format('DD MMM YY, h:mma')}
|
||||
</p>
|
||||
<div className="flex space-x-3">
|
||||
<Change change={calculateChartChange()} suffix="%" />
|
||||
<p className="text-sm text-th-fgd-4">
|
||||
{dayjs(mouseData.time).format('DD MMM YY, h:mma')}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mb-1 flex flex-col font-display text-5xl text-th-fgd-1 md:flex-row md:items-end">
|
||||
<div className="mb-1 flex flex-col font-display text-4xl text-th-fgd-1 md:flex-row md:items-end md:text-5xl">
|
||||
{loadingSwapPrice ? (
|
||||
<SheenLoader className="mt-2 w-[180px] rounded-md">
|
||||
<div className="h-[40px] bg-th-bkg-2" />
|
||||
<div className="h-9 bg-th-bkg-2 md:h-10" />
|
||||
</SheenLoader>
|
||||
) : animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={35}
|
||||
height={chartNumberHeight}
|
||||
width={chartNumberWidth}
|
||||
play
|
||||
numbers={formatNumericValue(
|
||||
chartData[chartData.length - 1].price,
|
||||
|
@ -585,22 +587,20 @@ const SwapTokenChart = () => {
|
|||
/>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={`ml-0 mt-2 flex items-center text-sm md:ml-3 md:mt-0`}
|
||||
>
|
||||
<Change change={calculateChartChange()} suffix="%" />
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-th-fgd-4">
|
||||
{dayjs(chartData[chartData.length - 1].time).format(
|
||||
'DD MMM YY, h:mma',
|
||||
)}
|
||||
</p>
|
||||
<div className="flex space-x-3">
|
||||
<Change change={calculateChartChange()} suffix="%" />
|
||||
<p className="text-sm text-th-fgd-4">
|
||||
{dayjs(chartData[chartData.length - 1].time).format(
|
||||
'DD MMM YY, h:mma',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 h-40 w-auto md:h-96">
|
||||
<div className="mt-2 h-44 w-auto sm:h-52 md:h-96">
|
||||
<div className="absolute right-0 top-[2px] -mb-2 flex items-center justify-end space-x-4">
|
||||
<FavoriteSwapButton
|
||||
inputToken={inputBank!.name}
|
||||
|
|
|
@ -16,14 +16,8 @@ import { JupiterV6RouteInfo } from 'types/jupiter'
|
|||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { DEFAULT_PERCENTAGE_VALUES } from './PercentageSelectButtons'
|
||||
import BuyTokenInput from './BuyTokenInput'
|
||||
import Button from '@components/shared/Button'
|
||||
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { floorToDecimal } from 'utils/numbers'
|
||||
|
@ -32,6 +26,7 @@ import WalletSellTokenInput from './WalletSellTokenInput'
|
|||
import { walletBalanceForToken } from '@components/DepositForm'
|
||||
import WalletSwapSlider from './WalletSwapSlider'
|
||||
import ButtonGroup from '@components/forms/ButtonGroup'
|
||||
import SwapFormSubmitButton from './SwapFormSubmitButton'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -42,7 +37,6 @@ type WalletSwapFormProps = {
|
|||
const set = mangoStore.getState().set
|
||||
|
||||
const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
//initial state is undefined null is returned on error
|
||||
const [selectedRoute, setSelectedRoute] =
|
||||
useState<JupiterV6RouteInfo | null>()
|
||||
|
@ -52,7 +46,6 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
const [isDraggingSlider, setIsDraggingSlider] = useState(false)
|
||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
const {
|
||||
margin: useMargin,
|
||||
slippage,
|
||||
inputBank,
|
||||
outputBank,
|
||||
|
@ -84,7 +77,6 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
!isDraggingSlider
|
||||
),
|
||||
})
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||
|
||||
|
@ -360,81 +352,15 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
loading={loadingExactIn}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
/>
|
||||
{ipAllowed ? (
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingExactIn || loadingExactOut}
|
||||
useMargin={useMargin}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
inputSymbol={inputBank?.name}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingExactIn || loadingExactOut}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WalletSwapForm
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
inputSymbol: string | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: JupiterV6RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { connected } = useWallet()
|
||||
|
||||
const disabled =
|
||||
(connected && !amountIn.toNumber()) || !amountOut || !selectedRoute
|
||||
|
||||
return (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<SecondaryConnectButton
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
isLarge
|
||||
/>
|
||||
)}
|
||||
{(selectedRoute === null && amountIn.gt(0)) ||
|
||||
(selectedRoute && !!selectedRoute.error) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ const fetchIpGeolocation = async () => {
|
|||
}
|
||||
|
||||
export default function useIpAddress() {
|
||||
const [ipAllowed, setIpAllowed] = useState(false)
|
||||
const [spotAllowed, setSpotAllowed] = useState(false)
|
||||
const [ipAllowed, setIpAllowed] = useState(true)
|
||||
const [spotAllowed, setSpotAllowed] = useState(true)
|
||||
const [ipCountry, setIpCountry] = useState('')
|
||||
|
||||
const ipCountryCode = useQuery<string, Error>(
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 32 KiB |
|
@ -9,6 +9,7 @@
|
|||
"perp-volume": "Perp Volume",
|
||||
"pnl-liquidation-fee": "Positive PnL Liquidation Fee",
|
||||
"settle-pnl-factor": "Settle PnL Factor",
|
||||
"show-as-apr": "Show as APR",
|
||||
"six-hourly": "6-Hourly",
|
||||
"tooltip-base-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over a perp base position.",
|
||||
"tooltip-funding-limits": "The minimum and maximum funding rates (in percent per day).",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "Margin Swap",
|
||||
"mango-swap": "Mango Swap",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
|
@ -41,9 +41,10 @@
|
|||
"show-swap-history": "Show Swap History – Prices shown are from CoinGecko and may not match your swap price",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-into-1": "Borrow against your collateral and swap with up to 5x leverage.",
|
||||
"swap-into-2": "Swap your Mango assets via the top DEXs on Solana and get the best possible price.",
|
||||
"swap-into-3": "Use the slider to set your swap size. Margin can be switched off in swap settings.",
|
||||
"swap-into-1": "Swap with up to 5x leverage",
|
||||
"swap-into-2": "Powered by Jupiter",
|
||||
"swap-into-3": "Trigger orders for stop-loss and take-profit",
|
||||
"swap-into-4": "Wallet Swap to swap tokens in your wallet",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"perp-volume": "Perp Volume",
|
||||
"pnl-liquidation-fee": "Positive PnL Liquidation Fee",
|
||||
"settle-pnl-factor": "Settle PnL Factor",
|
||||
"show-as-apr": "Show as APR",
|
||||
"six-hourly": "6-Hourly",
|
||||
"tooltip-base-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over a perp base position.",
|
||||
"tooltip-funding-limits": "The minimum and maximum funding rates (in percent per day).",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "Margin Swap",
|
||||
"mango-swap": "Mango Swap",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
|
@ -41,9 +41,10 @@
|
|||
"show-swap-history": "Show Swap History – Prices shown are from CoinGecko and may not match your swap price",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-into-1": "Borrow against your collateral and swap with up to 5x leverage.",
|
||||
"swap-into-2": "Swap your Mango assets via the top DEXs on Solana and get the best possible price.",
|
||||
"swap-into-3": "Use the slider to set your swap size. Margin can be switched off in swap settings.",
|
||||
"swap-into-1": "Swap with up to 5x leverage",
|
||||
"swap-into-2": "Powered by Jupiter",
|
||||
"swap-into-3": "Trigger orders for stop-loss and take-profit",
|
||||
"swap-into-4": "Wallet Swap to swap tokens in your wallet",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"perp-volume": "Perp Volume",
|
||||
"pnl-liquidation-fee": "Positive PnL Liquidation Fee",
|
||||
"settle-pnl-factor": "Settle PnL Factor",
|
||||
"show-as-apr": "Show as APR",
|
||||
"six-hourly": "6-Hourly",
|
||||
"tooltip-base-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over a perp base position.",
|
||||
"tooltip-funding-limits": "The minimum and maximum funding rates (in percent per day).",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "Margin Swap",
|
||||
"mango-swap": "Mango Swap",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
|
@ -41,9 +41,10 @@
|
|||
"show-swap-history": "Show Swap History – Prices shown are from CoinGecko and may not match your swap price",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-into-1": "Borrow against your collateral and swap with up to 5x leverage.",
|
||||
"swap-into-2": "Swap your Mango assets via the top DEXs on Solana and get the best possible price.",
|
||||
"swap-into-3": "Use the slider to set your swap size. Margin can be switched off in swap settings.",
|
||||
"swap-into-1": "Swap with up to 5x leverage",
|
||||
"swap-into-2": "Powered by Jupiter",
|
||||
"swap-into-3": "Trigger orders for stop-loss and take-profit",
|
||||
"swap-into-4": "Wallet Swap to swap tokens in your wallet",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"perp-volume": "合约交易量",
|
||||
"pnl-liquidation-fee": "正数盈亏清算费用",
|
||||
"settle-pnl-factor": "结清盈亏因素",
|
||||
"show-as-apr": "Show as APR",
|
||||
"six-hourly": "6-小时",
|
||||
"tooltip-base-liquidation-fee": "当清算者接管持仓时,被清算者将支付此费用。",
|
||||
"tooltip-funding-limits": "资金费用的最高与最低(一天)。",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"insufficient-balance": "{{symbol}}余额不够",
|
||||
"insufficient-collateral": "质押品不够",
|
||||
"margin": "保证金",
|
||||
"margin-swap": "杠杆换币",
|
||||
"mango-swap": "Mango Swap",
|
||||
"max-slippage": "最多下滑",
|
||||
"maximum-cost": "最大成本",
|
||||
"minimum-received": "最小获取",
|
||||
|
@ -43,9 +43,10 @@
|
|||
"show-swap-history": "显示换币纪录–价格来自CoinGecko而也许跟换币价格不同",
|
||||
"slippage": "下滑",
|
||||
"swap-history": "换币纪录",
|
||||
"swap-into-1": "以你的质押品来借贷并进行高达5倍杠杆换币。",
|
||||
"swap-into-2": "通过 Solana 上的顶级 DEX 交换您的 Mango 资产,并获得最优惠的价格。",
|
||||
"swap-into-3": "使用滑块设置交换大小。在换币设置中可以关闭杠杆功能。",
|
||||
"swap-into-1": "Swap with up to 5x leverage",
|
||||
"swap-into-2": "Powered by Jupiter",
|
||||
"swap-into-3": "Trigger orders for stop-loss and take-profit",
|
||||
"swap-into-4": "Wallet Swap to swap tokens in your wallet",
|
||||
"swap-route": "换币路线",
|
||||
"tooltip-borrow-balance": "您将使用您的{{balance}} {{token}} 余额并借入{{borrowAmount}} {{token}} 来执行此交换。当前的{{token}} 可变借贷利率为{{rate}}%",
|
||||
"tooltip-borrow-no-balance": "您将借入 {{borrowAmount}} {{token}} 来执行此交换。当前的 {{token}} 可变借贷利率为 {{rate}}%",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"perp-volume": "合約交易量",
|
||||
"pnl-liquidation-fee": "正數盈虧清算費用",
|
||||
"settle-pnl-factor": "結清盈虧因素",
|
||||
"show-as-apr": "Show as APR",
|
||||
"six-hourly": "6-小時",
|
||||
"tooltip-base-liquidation-fee": "當清算者接管持倉時,被清算者將支付此費用。",
|
||||
"tooltip-funding-limits": "資金費用的最高與最低(一天)。",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"insufficient-balance": "{{symbol}}餘額不夠",
|
||||
"insufficient-collateral": "質押品不夠",
|
||||
"margin": "保證金",
|
||||
"margin-swap": "槓桿換幣",
|
||||
"mango-swap": "Mango Swap",
|
||||
"max-slippage": "最多下滑",
|
||||
"maximum-cost": "最大成本",
|
||||
"minimum-received": "最小獲取",
|
||||
|
@ -43,9 +43,10 @@
|
|||
"show-swap-history": "顯示換幣紀錄–價格來自CoinGecko而也許跟換幣價格不同",
|
||||
"slippage": "下滑",
|
||||
"swap-history": "換幣紀錄",
|
||||
"swap-into-1": "以你的質押品來借貸並進行高達5倍槓桿換幣。",
|
||||
"swap-into-2": "通過 Solana 上的頂級 DEX 交換您的 Mango 資產,並獲得最優惠的價格。",
|
||||
"swap-into-3": "使用滑塊設置交換大小。在換幣設置中可以關閉槓桿功能。",
|
||||
"swap-into-1": "Swap with up to 5x leverage",
|
||||
"swap-into-2": "Powered by Jupiter",
|
||||
"swap-into-3": "Trigger orders for stop-loss and take-profit",
|
||||
"swap-into-4": "Wallet Swap to swap tokens in your wallet",
|
||||
"swap-route": "換幣路線",
|
||||
"tooltip-borrow-balance": "您將使用您的 {{balance}} {{token}} 餘額並借入 {{borrowAmount}} {{token}} 來執行此交換。當前的 {{token}} 可變借貸利率為 {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "您將借入 {{borrowAmount}} {{token}} 來執行此交換。當前的 {{token}} 可變借貸利率為 {{rate}}%",
|
||||
|
|
|
@ -144,6 +144,7 @@ export const CUSTOM_TOKEN_ICONS: { [key: string]: boolean } = {
|
|||
eth: true,
|
||||
ethpo: true,
|
||||
'eth (portal)': true,
|
||||
guac: true,
|
||||
hnt: true,
|
||||
jitosol: true,
|
||||
kin: true,
|
||||
|
|
|
@ -79,7 +79,10 @@ const roundValue = (
|
|||
})
|
||||
}
|
||||
|
||||
const digits2 = new Intl.NumberFormat('en', { maximumFractionDigits: 2 })
|
||||
const digits2 = new Intl.NumberFormat('en', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})
|
||||
const digits3 = new Intl.NumberFormat('en', { maximumFractionDigits: 3 })
|
||||
const digits4 = new Intl.NumberFormat('en', { maximumFractionDigits: 4 })
|
||||
const digits5 = new Intl.NumberFormat('en', { maximumFractionDigits: 5 })
|
||||
|
|
Loading…
Reference in New Issue