merge main
This commit is contained in:
commit
bdd03b0e0c
|
@ -33,12 +33,14 @@ export const fetchChartData = async (
|
|||
const found = a.find((price: any) => price.time === c[0])
|
||||
if (found) {
|
||||
if (['usd-coin', 'tether'].includes(quoteTokenId)) {
|
||||
found.price = found.inputPrice / c[4]
|
||||
found.price = found.p1 / c[4]
|
||||
found.p2 = c[4]
|
||||
} else {
|
||||
found.price = c[4] / found.inputPrice
|
||||
found.price = c[4] / found.p1
|
||||
found.p2 = c[4]
|
||||
}
|
||||
} else {
|
||||
a.push({ time: c[0], inputPrice: c[4] })
|
||||
a.push({ time: c[0], p1: c[4] })
|
||||
}
|
||||
return a
|
||||
}, [])
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
import Decimal from 'decimal.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Image from 'next/legacy/image'
|
||||
import { Wallet } from '@project-serum/anchor'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import NumberFormat, {
|
||||
NumberFormatValues,
|
||||
|
@ -64,7 +63,7 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected, wallet } = useWallet()
|
||||
const { connected, publicKey } = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
|
||||
const bank = useMemo(() => {
|
||||
|
@ -130,7 +129,7 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const actions = mangoStore.getState().actions
|
||||
if (!mangoAccount || !group) return
|
||||
if (!mangoAccount || !group || !publicKey) return
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const tx = await client.tokenWithdraw(
|
||||
|
@ -146,7 +145,7 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
txid: tx,
|
||||
})
|
||||
await actions.reloadMangoAccount()
|
||||
actions.fetchWalletTokens(wallet!.adapter as unknown as Wallet)
|
||||
actions.fetchWalletTokens(publicKey)
|
||||
setSubmitting(false)
|
||||
onSuccess()
|
||||
} catch (e: any) {
|
||||
|
@ -159,7 +158,7 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
})
|
||||
setSubmitting(false)
|
||||
}
|
||||
}, [bank, inputAmount, onSuccess, wallet])
|
||||
}, [bank, inputAmount, onSuccess, publicKey])
|
||||
|
||||
const banks = useMemo(() => {
|
||||
if (mangoAccount) {
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
ExclamationCircleIcon,
|
||||
LinkIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { Wallet } from '@project-serum/anchor'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import Decimal from 'decimal.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -121,7 +120,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
return logoURI
|
||||
}, [bank?.mint, mangoTokens])
|
||||
|
||||
const { connected, wallet } = useWallet()
|
||||
const { connected, publicKey } = useWallet()
|
||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||
|
||||
const tokenMax = useMemo(() => {
|
||||
|
@ -158,7 +157,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
const actions = mangoStore.getState().actions
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
|
||||
if (!mangoAccount || !group || !bank) return
|
||||
if (!mangoAccount || !group || !bank || !publicKey) return
|
||||
|
||||
setSubmitting(true)
|
||||
try {
|
||||
|
@ -175,7 +174,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
})
|
||||
|
||||
await actions.reloadMangoAccount()
|
||||
actions.fetchWalletTokens(wallet!.adapter as unknown as Wallet)
|
||||
actions.fetchWalletTokens(publicKey)
|
||||
setSubmitting(false)
|
||||
onSuccess()
|
||||
} catch (e: any) {
|
||||
|
@ -188,7 +187,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
console.error('Error depositing:', e)
|
||||
setSubmitting(false)
|
||||
}
|
||||
}, [bank, wallet, inputAmount])
|
||||
}, [bank, publicKey, inputAmount])
|
||||
|
||||
// TODO extract into a shared hook for UserSetup.tsx
|
||||
const banks = useMemo(() => {
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
ExclamationCircleIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { Wallet } from '@project-serum/anchor'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import Decimal from 'decimal.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -74,7 +73,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
return logoURI
|
||||
}, [bank, mangoTokens])
|
||||
|
||||
const { connected, wallet } = useWallet()
|
||||
const { connected, publicKey } = useWallet()
|
||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||
|
||||
const walletBalance = useMemo(() => {
|
||||
|
@ -124,7 +123,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
const actions = mangoStore.getState().actions
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
|
||||
if (!mangoAccount || !group || !bank || !wallet) return
|
||||
if (!mangoAccount || !group || !bank || !publicKey) return
|
||||
console.log('inputAmount: ', amount)
|
||||
|
||||
setSubmitting(true)
|
||||
|
@ -142,7 +141,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
})
|
||||
|
||||
await actions.reloadMangoAccount()
|
||||
actions.fetchWalletTokens(wallet.adapter as unknown as Wallet)
|
||||
actions.fetchWalletTokens(publicKey)
|
||||
setSubmitting(false)
|
||||
onSuccess()
|
||||
} catch (e: any) {
|
||||
|
@ -156,7 +155,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[bank, wallet]
|
||||
[bank, publicKey]
|
||||
)
|
||||
|
||||
const banks = useMemo(() => {
|
||||
|
|
|
@ -190,7 +190,7 @@ const TokenList = () => {
|
|||
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<p className="font-body tracking-wider">{bank.name}</p>
|
||||
<p className="font-body">{bank.name}</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
|
@ -565,9 +565,7 @@ const ActionsMenu = ({
|
|||
<div className="mr-2 flex flex-shrink-0 items-center">
|
||||
<Image alt="" width="20" height="20" src={logoURI || ''} />
|
||||
</div>
|
||||
<p className="font-body tracking-wider">
|
||||
{formatTokenSymbol(bank.name)}
|
||||
</p>
|
||||
<p className="font-body">{formatTokenSymbol(bank.name)}</p>
|
||||
</div>
|
||||
<ActionsLinkButton
|
||||
mangoAccount={mangoAccount!}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useTranslation } from 'next-i18next'
|
|||
import { useMemo, useState } from 'react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { numberCompacter } from 'utils/numbers'
|
||||
const DetailedAreaChart = dynamic(
|
||||
() => import('@components/shared/DetailedAreaChart'),
|
||||
{ ssr: false }
|
||||
|
@ -57,7 +58,7 @@ const AccountChart = ({
|
|||
loading={loading}
|
||||
prefix="$"
|
||||
setDaysToShow={handleDaysToShow}
|
||||
tickFormat={(x) => `$${x.toFixed(2)}`}
|
||||
tickFormat={(x) => `$${numberCompacter.format(x)}`}
|
||||
title={t(chartToShow)}
|
||||
xKey="time"
|
||||
yKey={yKey}
|
||||
|
|
|
@ -39,6 +39,7 @@ import dayjs from 'dayjs'
|
|||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
|
@ -55,7 +56,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
const AccountPage = () => {
|
||||
const { t } = useTranslation('common')
|
||||
// const { connected } = useWallet()
|
||||
const group = mangoStore.getState().group
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const actions = mangoStore.getState().actions
|
||||
const loadPerformanceData = mangoStore(
|
||||
|
|
|
@ -429,7 +429,7 @@ const ActivityDetails = ({
|
|||
<p className="mb-0.5 text-sm">{t('activity:asset-liquidated')}</p>
|
||||
<p className="font-mono text-th-fgd-1">
|
||||
{formatDecimal(asset_amount)}{' '}
|
||||
<span className="font-body tracking-wider">{asset_symbol}</span>
|
||||
<span className="font-body">{asset_symbol}</span>
|
||||
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
|
||||
{formatFixedDecimals(asset_price, true)}
|
||||
</p>
|
||||
|
@ -441,7 +441,7 @@ const ActivityDetails = ({
|
|||
<p className="mb-0.5 text-sm">{t('activity:asset-returned')}</p>
|
||||
<p className="font-mono text-th-fgd-1">
|
||||
{formatDecimal(liab_amount)}{' '}
|
||||
<span className="font-body tracking-wider">{liab_symbol}</span>
|
||||
<span className="font-body">{liab_symbol}</span>
|
||||
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
|
||||
{formatFixedDecimals(liab_price, true)}
|
||||
</p>
|
||||
|
|
|
@ -222,7 +222,7 @@ const ActivityFeedTable = ({
|
|||
}
|
||||
>
|
||||
<Td>
|
||||
<p className="font-body tracking-wider">
|
||||
<p className="font-body">
|
||||
{dayjs(block_datetime).format('ddd D MMM')}
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
|
@ -473,7 +473,7 @@ const MobileActivityFeedItem = ({
|
|||
<p className="mb-0.5 text-sm">{t('activity:asset-liquidated')}</p>
|
||||
<p className="font-mono text-sm text-th-fgd-1">
|
||||
{formatDecimal(activity.activity_details.asset_amount)}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
<span className="font-body">
|
||||
{activity.activity_details.asset_symbol}
|
||||
</span>
|
||||
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
|
||||
|
@ -491,7 +491,7 @@ const MobileActivityFeedItem = ({
|
|||
<p className="mb-0.5 text-sm">{t('activity:asset-returned')}</p>
|
||||
<p className="font-mono text-sm text-th-fgd-1">
|
||||
{formatDecimal(activity.activity_details.liab_amount)}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
<span className="font-body">
|
||||
{activity.activity_details.liab_symbol}
|
||||
</span>
|
||||
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
|
||||
|
|
|
@ -96,6 +96,12 @@ const MangoAccountsListModal = ({
|
|||
) : mangoAccounts.length ? (
|
||||
<div className="thin-scroll mt-4 max-h-[280px] space-y-2 overflow-y-auto">
|
||||
{mangoAccounts.map((acc) => {
|
||||
if (
|
||||
mangoAccount &&
|
||||
acc.publicKey.equals(mangoAccount.publicKey)
|
||||
) {
|
||||
acc = mangoAccount
|
||||
}
|
||||
const accountValue = toUiDecimalsForQuote(
|
||||
Number(acc.getEquity(group!))
|
||||
).toFixed(2)
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
FireIcon,
|
||||
PencilIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { Wallet } from '@project-serum/anchor'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import Decimal from 'decimal.js'
|
||||
|
@ -59,7 +58,7 @@ const UserSetupModal = ({
|
|||
}) => {
|
||||
const { t } = useTranslation(['common', 'onboarding', 'swap'])
|
||||
const { group } = useMangoGroup()
|
||||
const { connected, select, wallet, wallets } = useWallet()
|
||||
const { connected, select, wallet, wallets, publicKey } = useWallet()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const mangoAccountLoading = mangoStore((s) => s.mangoAccount.initialLoad)
|
||||
const [accountName, setAccountName] = useState('')
|
||||
|
@ -84,7 +83,7 @@ const UserSetupModal = ({
|
|||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const actions = mangoStore.getState().actions
|
||||
if (!group || !wallet) return
|
||||
if (!group || !publicKey) return
|
||||
setLoadingAccount(true)
|
||||
try {
|
||||
const tx = await client.createMangoAccount(
|
||||
|
@ -96,9 +95,9 @@ const UserSetupModal = ({
|
|||
8, // perpCount
|
||||
8 // perpOoCount
|
||||
)
|
||||
actions.fetchMangoAccounts(wallet!.adapter as unknown as Wallet)
|
||||
actions.fetchMangoAccounts(publicKey)
|
||||
if (tx) {
|
||||
actions.fetchWalletTokens(wallet!.adapter as unknown as Wallet) // need to update sol balance after account rent
|
||||
actions.fetchWalletTokens(publicKey) // need to update sol balance after account rent
|
||||
setShowSetupStep(3)
|
||||
notify({
|
||||
title: t('new-account-success'),
|
||||
|
@ -116,7 +115,7 @@ const UserSetupModal = ({
|
|||
} finally {
|
||||
setLoadingAccount(false)
|
||||
}
|
||||
}, [accountName, wallet, t])
|
||||
}, [accountName, publicKey, t])
|
||||
|
||||
const handleDeposit = useCallback(async () => {
|
||||
const client = mangoStore.getState().client
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Bank, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import { NoSymbolIcon, QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
|
@ -33,24 +33,24 @@ const BalancesTable = () => {
|
|||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const banks = useMemo(() => {
|
||||
if (!group) return []
|
||||
if (!group || !mangoAccount) return []
|
||||
|
||||
const rawBanks = Array.from(group?.banksMapByName, ([key, value]) => ({
|
||||
key,
|
||||
value,
|
||||
balance: mangoAccount?.getTokenBalanceUi(value[0]),
|
||||
balance: mangoAccount.getTokenBalanceUi(value[0]),
|
||||
}))
|
||||
const sortedBanks = mangoAccount
|
||||
? rawBanks
|
||||
.sort(
|
||||
(a, b) =>
|
||||
Math.abs(b.balance! * b.value[0].uiPrice) -
|
||||
Math.abs(a.balance! * a.value[0].uiPrice)
|
||||
Math.abs(b.balance * b.value[0].uiPrice) -
|
||||
Math.abs(a.balance * a.value[0].uiPrice)
|
||||
)
|
||||
.filter((c) => {
|
||||
return (
|
||||
Math.abs(
|
||||
floorToDecimal(c.balance!, c.value[0].mintDecimals).toNumber()
|
||||
floorToDecimal(c.balance, c.value[0].mintDecimals).toNumber()
|
||||
) > 0 ||
|
||||
spotBalances[c.value[0].mint.toString()]?.unsettled > 0 ||
|
||||
spotBalances[c.value[0].mint.toString()]?.inOrders > 0
|
||||
|
@ -59,7 +59,7 @@ const BalancesTable = () => {
|
|||
: rawBanks
|
||||
|
||||
return sortedBanks
|
||||
}, [group, mangoAccount])
|
||||
}, [group, mangoAccount, spotBalances])
|
||||
|
||||
return banks?.length ? (
|
||||
showTableView ? (
|
||||
|
@ -212,6 +212,7 @@ const BalancesTable = () => {
|
|||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p>{t('trade:no-balances')}</p>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -121,7 +121,11 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
<div className="flex items-start justify-between">
|
||||
<div className="flex flex-col md:flex-row md:items-start md:space-x-6">
|
||||
{hideChart ? (
|
||||
<IconButton className="mb-6" onClick={hideChart}>
|
||||
<IconButton
|
||||
className="mb-6"
|
||||
onClick={hideChart}
|
||||
size="medium"
|
||||
>
|
||||
<ArrowLeftIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { useTranslation } from 'next-i18next'
|
|||
import dynamic from 'next/dynamic'
|
||||
import { useMemo } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { numberCompacter } from 'utils/numbers'
|
||||
// import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
const DetailedAreaChart = dynamic(
|
||||
() => import('@components/shared/DetailedAreaChart'),
|
||||
|
@ -143,7 +144,7 @@ const MangoStats = () => {
|
|||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${x.toFixed(2)}`}
|
||||
tickFormat={(x) => `$${numberCompacter.format(x)}`}
|
||||
title={t('total-deposit-value')}
|
||||
xKey="date"
|
||||
yKey={'depositValue'}
|
||||
|
@ -169,7 +170,7 @@ const MangoStats = () => {
|
|||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${x.toFixed(2)}`}
|
||||
tickFormat={(x) => `$${numberCompacter.format(x)}`}
|
||||
title={t('total-borrow-value')}
|
||||
xKey="date"
|
||||
yKey={'borrowValue'}
|
||||
|
|
|
@ -85,7 +85,7 @@ const PerpMarketsTable = ({
|
|||
<Td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<p className="font-body tracking-wider">{market.name}</p>
|
||||
<p className="font-body">{market.name}</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
|
|
|
@ -67,7 +67,7 @@ const SpotMarketsTable = () => {
|
|||
<Td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<p className="font-body tracking-wider">{market.name}</p>
|
||||
<p className="font-body">{market.name}</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
|
|
|
@ -125,7 +125,7 @@ const TokenStats = () => {
|
|||
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<p className="font-body tracking-wider">{bank.name}</p>
|
||||
<p className="font-body">{bank.name}</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
|
|
|
@ -117,7 +117,7 @@ const SwapHistoryTable = () => {
|
|||
return (
|
||||
<TrBody key={signature}>
|
||||
<Td>
|
||||
<p className="font-body tracking-wider">
|
||||
<p className="font-body">
|
||||
{dayjs(block_datetime).format('ddd D MMM')}
|
||||
</p>
|
||||
<p className="font-body text-xs text-th-fgd-3">
|
||||
|
|
|
@ -346,22 +346,22 @@ const SwapReviewRouteInfo = ({
|
|||
{swapRate ? (
|
||||
<>
|
||||
1{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
{inputTokenInfo?.name} ≈{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{inputTokenInfo?.symbol} ≈{' '}
|
||||
</span>
|
||||
{formatFixedDecimals(amountOut.div(amountIn).toNumber())}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{outputTokenInfo?.symbol}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
1{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{outputTokenInfo?.symbol} ≈{' '}
|
||||
</span>
|
||||
{formatFixedDecimals(amountIn.div(amountOut).toNumber())}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{inputTokenInfo?.symbol}
|
||||
</span>
|
||||
</>
|
||||
|
@ -397,20 +397,42 @@ const SwapReviewRouteInfo = ({
|
|||
<p className="text-sm text-th-fgd-3">
|
||||
{t('swap:minimum-received')}
|
||||
</p>
|
||||
{outputTokenInfo?.decimals &&
|
||||
selectedRoute?.otherAmountThreshold ? (
|
||||
{outputTokenInfo?.decimals && selectedRoute ? (
|
||||
<p className="text-right font-mono text-sm text-th-fgd-2">
|
||||
{formatDecimal(
|
||||
selectedRoute.otherAmountThreshold /
|
||||
10 ** outputTokenInfo.decimals || 1,
|
||||
outputTokenInfo.decimals
|
||||
)}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
{selectedRoute.swapMode === 'ExactIn'
|
||||
? formatDecimal(
|
||||
selectedRoute.otherAmountThreshold /
|
||||
10 ** outputTokenInfo.decimals || 1,
|
||||
outputTokenInfo.decimals
|
||||
)
|
||||
: formatDecimal(
|
||||
selectedRoute.outAmount /
|
||||
10 ** outputTokenInfo.decimals || 1,
|
||||
outputTokenInfo.decimals
|
||||
)}{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{outputTokenInfo?.symbol}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
{selectedRoute?.swapMode === 'ExactOut' ? (
|
||||
<div className="flex justify-between">
|
||||
<p className="text-sm text-th-fgd-3">{t('swap:maximum-cost')}</p>
|
||||
{inputTokenInfo?.decimals && selectedRoute ? (
|
||||
<p className="text-right font-mono text-sm text-th-fgd-2">
|
||||
{formatDecimal(
|
||||
selectedRoute.otherAmountThreshold /
|
||||
10 ** inputTokenInfo.decimals || 1,
|
||||
inputTokenInfo.decimals
|
||||
)}{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{inputTokenInfo?.symbol}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex justify-between">
|
||||
<p className="text-sm text-th-fgd-3">{t('swap:price-impact')}</p>
|
||||
<p className="text-right font-mono text-sm text-th-fgd-2">
|
||||
|
@ -448,9 +470,7 @@ const SwapReviewRouteInfo = ({
|
|||
</Tooltip>
|
||||
<p className="text-right font-mono text-sm text-th-fgd-2">
|
||||
~{formatFixedDecimals(borrowAmount)}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
{inputTokenInfo?.symbol}
|
||||
</span>
|
||||
<span className="font-body">{inputTokenInfo?.symbol}</span>
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -533,10 +553,7 @@ const SwapReviewRouteInfo = ({
|
|||
.mul(inputBank!.loanOriginationFeeRate.toFixed())
|
||||
.toNumber()
|
||||
)}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
{inputBank!.name}
|
||||
</span>{' '}
|
||||
(
|
||||
<span className="font-body">{inputBank!.name}</span> (
|
||||
{formatFixedDecimals(
|
||||
inputBank!.loanOriginationFeeRate.toNumber() * 100
|
||||
)}
|
||||
|
@ -571,7 +588,7 @@ const SwapReviewRouteInfo = ({
|
|||
info.lpFee?.amount /
|
||||
Math.pow(10, feeToken.decimals)
|
||||
).toFixed(6)}{' '}
|
||||
<span className="font-body tracking-wider">
|
||||
<span className="font-body">
|
||||
{feeToken?.symbol}
|
||||
</span>{' '}
|
||||
(
|
||||
|
|
|
@ -28,10 +28,17 @@ import useLocalStorageState from 'hooks/useLocalStorageState'
|
|||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
||||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
import { ArrowsRightLeftIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
interface ChartDataItem {
|
||||
p1: number
|
||||
p2: number
|
||||
price: number
|
||||
time: number
|
||||
}
|
||||
|
||||
const CustomizedLabel = ({
|
||||
chartData,
|
||||
x,
|
||||
|
@ -79,12 +86,14 @@ const CustomizedLabel = ({
|
|||
|
||||
const SwapTokenChart = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const inputBank = mangoStore((s) => s.swap.inputBank)
|
||||
const outputBank = mangoStore((s) => s.swap.outputBank)
|
||||
const { inputCoingeckoId, outputCoingeckoId } = useJupiterSwapData()
|
||||
const [baseTokenId, setBaseTokenId] = useState(inputCoingeckoId)
|
||||
const [quoteTokenId, setQuoteTokenId] = useState(outputCoingeckoId)
|
||||
const [mouseData, setMouseData] = useState<any>(null)
|
||||
const [daysToShow, setDaysToShow] = useState('1')
|
||||
const [flipPrices, setFlipPrices] = useState(false)
|
||||
const { theme } = useTheme()
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
|
@ -100,7 +109,18 @@ const SwapTokenChart = () => {
|
|||
enabled: !!baseTokenId && !!quoteTokenId,
|
||||
}
|
||||
)
|
||||
const chartData = chartDataQuery.data
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!chartDataQuery?.data?.length) return []
|
||||
if (!flipPrices) {
|
||||
return chartDataQuery.data
|
||||
} else {
|
||||
return chartDataQuery.data.map((d: ChartDataItem) => {
|
||||
const price = d.p1 / d.p2 === d.price ? d.p2 / d.p1 : d.p1 / d.p2
|
||||
return { ...d, price: price }
|
||||
})
|
||||
}
|
||||
}, [flipPrices, chartDataQuery])
|
||||
|
||||
const handleMouseMove = (coords: any) => {
|
||||
if (coords.activePayload) {
|
||||
|
@ -149,6 +169,19 @@ const SwapTokenChart = () => {
|
|||
return 0
|
||||
}
|
||||
|
||||
const swapMarketName = useMemo(() => {
|
||||
if (!inputBank || !outputBank) return ''
|
||||
const inputSymbol = formatTokenSymbol(inputBank.name?.toUpperCase())
|
||||
const outputSymbol = formatTokenSymbol(outputBank.name?.toUpperCase())
|
||||
return ['usd-coin', 'tether'].includes(inputCoingeckoId || '')
|
||||
? !flipPrices
|
||||
? `${outputSymbol}/${inputSymbol}`
|
||||
: `${inputSymbol}/${outputSymbol}`
|
||||
: !flipPrices
|
||||
? `${inputSymbol}/${outputSymbol}`
|
||||
: `${outputSymbol}/${inputSymbol}`
|
||||
}, [flipPrices, inputBank, inputCoingeckoId, outputBank])
|
||||
|
||||
return (
|
||||
<ContentBox hideBorder hidePadding className="h-full px-6 py-3">
|
||||
{chartDataQuery?.isLoading || chartDataQuery.isFetching ? (
|
||||
|
@ -169,23 +202,13 @@ const SwapTokenChart = () => {
|
|||
<div>
|
||||
{inputBank && outputBank ? (
|
||||
<div className="mb-0.5 flex items-center">
|
||||
<p className="text-base text-th-fgd-3">
|
||||
{['usd-coin', 'tether'].includes(inputCoingeckoId || '')
|
||||
? `${formatTokenSymbol(
|
||||
outputBank?.name?.toUpperCase()
|
||||
)}/${inputBank?.name?.toUpperCase()}`
|
||||
: `${formatTokenSymbol(
|
||||
inputBank?.name?.toUpperCase()
|
||||
)}/${formatTokenSymbol(
|
||||
outputBank?.name?.toUpperCase()
|
||||
)}`}
|
||||
</p>
|
||||
{/* <div
|
||||
<p className="text-base text-th-fgd-3">{swapMarketName}</p>
|
||||
<div
|
||||
className="px-2 hover:cursor-pointer hover:text-th-active"
|
||||
onClick={handleFlipChart}
|
||||
onClick={() => setFlipPrices(!flipPrices)}
|
||||
>
|
||||
<SwitchHorizontalIcon className="h-4 w-4" />
|
||||
</div> */}
|
||||
<ArrowsRightLeftIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{mouseData ? (
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import Change from '@components/shared/Change'
|
||||
import { useCoingecko } from 'hooks/useCoingecko'
|
||||
import useOraclePrice from 'hooks/useOraclePrice'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useMemo } from 'react'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
import MarketSelectDropdown from './MarketSelectDropdown'
|
||||
import PerpFundingRate from './PerpFundingRate'
|
||||
|
||||
const AdvancedMarketHeader = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { serumOrPerpMarket, baseSymbol, price } = useSelectedMarket()
|
||||
const { data: tokenPrices } = useCoingecko()
|
||||
const oraclePrice = useOraclePrice()
|
||||
|
||||
const baseSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split('/')[0]
|
||||
}, [selectedMarket])
|
||||
const coingeckoData = useMemo(() => {
|
||||
return tokenPrices.find((asset) =>
|
||||
baseSymbol === 'soETH'
|
||||
? asset.symbol === 'ETH'
|
||||
: asset.symbol === baseSymbol
|
||||
)
|
||||
}, [baseSymbol, tokenPrices])
|
||||
|
||||
const coingeckoData = tokenPrices.find((asset) =>
|
||||
baseSymbol === 'soETH'
|
||||
? asset.symbol === 'ETH'
|
||||
: asset.symbol === baseSymbol
|
||||
)
|
||||
|
||||
const change = coingeckoData
|
||||
? ((coingeckoData.prices[coingeckoData.prices.length - 1][1] -
|
||||
coingeckoData.prices[0][1]) /
|
||||
coingeckoData.prices[0][1]) *
|
||||
100
|
||||
: 0
|
||||
const change = useMemo(() => {
|
||||
return coingeckoData
|
||||
? ((coingeckoData.prices[coingeckoData.prices.length - 1][1] -
|
||||
coingeckoData.prices[0][1]) /
|
||||
coingeckoData.prices[0][1]) *
|
||||
100
|
||||
: 0
|
||||
}, [coingeckoData])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col bg-th-bkg-1 md:h-12 md:flex-row md:items-center">
|
||||
|
@ -40,8 +39,10 @@ const AdvancedMarketHeader = () => {
|
|||
<div id="trade-step-two" className="flex-col md:ml-6">
|
||||
<div className="text-xs text-th-fgd-4">{t('trade:oracle-price')}</div>
|
||||
<div className="font-mono text-xs text-th-fgd-2">
|
||||
{oraclePrice ? (
|
||||
`$${oraclePrice}`
|
||||
{price ? (
|
||||
`$${price.toFixed(
|
||||
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
|
||||
)}`
|
||||
) : (
|
||||
<span className="text-th-fgd-4">–</span>
|
||||
)}
|
||||
|
@ -51,7 +52,7 @@ const AdvancedMarketHeader = () => {
|
|||
<div className="text-xs text-th-fgd-4">{t('rolling-change')}</div>
|
||||
<Change change={change} size="small" suffix="%" />
|
||||
</div>
|
||||
{selectedMarket instanceof PerpMarket ? (
|
||||
{serumOrPerpMarket instanceof PerpMarket ? (
|
||||
<div className="ml-6 flex-col">
|
||||
<div className="text-xs text-th-fgd-4">
|
||||
{t('trade:funding-rate')}
|
||||
|
|
|
@ -13,7 +13,7 @@ import Tooltip from '@components/shared/Tooltip'
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import Decimal from 'decimal.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import NumberFormat, {
|
||||
NumberFormatValues,
|
||||
SourceInfo,
|
||||
|
@ -31,7 +31,6 @@ import { SIZE_INPUT_UI_KEY, SOUND_SETTINGS_KEY } from 'utils/constants'
|
|||
import SpotButtonGroup from './SpotButtonGroup'
|
||||
import PerpButtonGroup from './PerpButtonGroup'
|
||||
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
||||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
import LogoWithFallback from '@components/shared/LogoWithFallback'
|
||||
|
@ -56,8 +55,6 @@ const AdvancedTradeForm = () => {
|
|||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
const { selectedMarket, price: oraclePrice } = useSelectedMarket()
|
||||
const [useMargin, setUseMargin] = useState(true)
|
||||
const [placingOrder, setPlacingOrder] = useState(false)
|
||||
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'Slider')
|
||||
|
@ -68,44 +65,14 @@ const AdvancedTradeForm = () => {
|
|||
)
|
||||
const { connected } = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
|
||||
const baseSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split(/-|\//)[0]
|
||||
}, [selectedMarket])
|
||||
|
||||
const baseLogoURI = useMemo(() => {
|
||||
if (!baseSymbol || !mangoTokens.length) return ''
|
||||
const token =
|
||||
mangoTokens.find((t) => t.symbol === baseSymbol) ||
|
||||
mangoTokens.find((t) => t.symbol?.includes(baseSymbol))
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
return ''
|
||||
}, [baseSymbol, mangoTokens])
|
||||
|
||||
const quoteBank = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!group || !selectedMarket) return
|
||||
const tokenIdx =
|
||||
selectedMarket instanceof Serum3Market
|
||||
? selectedMarket.quoteTokenIndex
|
||||
: selectedMarket?.settleTokenIndex
|
||||
return group?.getFirstBankByTokenIndex(tokenIdx)
|
||||
}, [selectedMarket])
|
||||
|
||||
const quoteSymbol = useMemo(() => {
|
||||
return quoteBank?.name
|
||||
}, [quoteBank])
|
||||
|
||||
const quoteLogoURI = useMemo(() => {
|
||||
if (!quoteSymbol || !mangoTokens.length) return ''
|
||||
const token = mangoTokens.find((t) => t.symbol === quoteSymbol)
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
return ''
|
||||
}, [quoteSymbol, mangoTokens])
|
||||
const {
|
||||
selectedMarket,
|
||||
price: oraclePrice,
|
||||
baseLogoURI,
|
||||
baseSymbol,
|
||||
quoteLogoURI,
|
||||
quoteSymbol,
|
||||
} = useSelectedMarket()
|
||||
|
||||
const setTradeType = useCallback((tradeType: 'Limit' | 'Market') => {
|
||||
set((s) => {
|
||||
|
@ -192,6 +159,16 @@ const AdvancedTradeForm = () => {
|
|||
})
|
||||
}, [])
|
||||
|
||||
const handleSetMargin = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!e.target.checked) {
|
||||
set((s) => {
|
||||
s.tradeForm.quoteSize = ''
|
||||
s.tradeForm.baseSize = ''
|
||||
})
|
||||
}
|
||||
setUseMargin(e.target.checked)
|
||||
}, [])
|
||||
|
||||
const [tickDecimals, tickSize] = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!group || !selectedMarket) return [1, 0.1]
|
||||
|
@ -485,11 +462,13 @@ const AdvancedTradeForm = () => {
|
|||
minOrderDecimals={minOrderDecimals}
|
||||
tickDecimals={tickDecimals}
|
||||
step={tradeForm.side === 'buy' ? tickSize : minOrderSize}
|
||||
useMargin={useMargin}
|
||||
/>
|
||||
) : (
|
||||
<SpotButtonGroup
|
||||
minOrderDecimals={minOrderDecimals}
|
||||
tickDecimals={tickDecimals}
|
||||
useMargin={useMargin}
|
||||
/>
|
||||
)
|
||||
) : tradeFormSizeUi === 'slider' ? (
|
||||
|
@ -548,10 +527,7 @@ const AdvancedTradeForm = () => {
|
|||
placement="left"
|
||||
content={t('trade:tooltip-enable-margin')}
|
||||
>
|
||||
<Checkbox
|
||||
checked={useMargin}
|
||||
onChange={(e) => setUseMargin(e.target.checked)}
|
||||
>
|
||||
<Checkbox checked={useMargin} onChange={handleSetMargin}>
|
||||
{t('trade:margin')}
|
||||
</Checkbox>
|
||||
</Tooltip>
|
||||
|
|
|
@ -182,7 +182,12 @@ const depth = 40
|
|||
|
||||
const Orderbook = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { selectedMarket, serumOrPerpMarket: market } = useSelectedMarket()
|
||||
const {
|
||||
selectedMarket,
|
||||
serumOrPerpMarket: market,
|
||||
baseSymbol,
|
||||
quoteSymbol,
|
||||
} = useSelectedMarket()
|
||||
|
||||
const [isScrolled, setIsScrolled] = useState(false)
|
||||
const [orderbookData, setOrderbookData] = useState<any | null>(null)
|
||||
|
@ -483,8 +488,12 @@ 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('trade:size')}</div>
|
||||
<div className="col-span-1 text-right">{t('price')}</div>
|
||||
<div className="col-span-1 text-right">
|
||||
{t('trade:size')} ({baseSymbol})
|
||||
</div>
|
||||
<div className="col-span-1 text-right">
|
||||
{t('price')} ({quoteSymbol})
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="hide-scroll relative h-full overflow-y-scroll"
|
||||
|
@ -595,7 +604,6 @@ const OrderbookRow = ({
|
|||
minOrderSize: number
|
||||
tickSize: number
|
||||
}) => {
|
||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||
const element = useRef<HTMLDivElement>(null)
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
|
@ -632,29 +640,29 @@ const OrderbookRow = ({
|
|||
const set = mangoStore.getState().set
|
||||
set((state) => {
|
||||
state.tradeForm.price = formattedPrice.toFixed()
|
||||
if (tradeForm.baseSize && tradeForm.tradeType === 'Limit') {
|
||||
if (state.tradeForm.baseSize && state.tradeForm.tradeType === 'Limit') {
|
||||
const quoteSize = floorToDecimal(
|
||||
formattedPrice.mul(new Decimal(tradeForm.baseSize)),
|
||||
formattedPrice.mul(new Decimal(state.tradeForm.baseSize)),
|
||||
getDecimalCount(tickSize)
|
||||
)
|
||||
state.tradeForm.quoteSize = quoteSize.toFixed()
|
||||
}
|
||||
})
|
||||
}, [formattedPrice, tradeForm])
|
||||
}, [formattedPrice, tickSize])
|
||||
|
||||
const handleSizeClick = useCallback(() => {
|
||||
const set = mangoStore.getState().set
|
||||
set((state) => {
|
||||
state.tradeForm.baseSize = formattedSize.toString()
|
||||
if (formattedSize && tradeForm.price) {
|
||||
if (formattedSize && state.tradeForm.price) {
|
||||
const quoteSize = floorToDecimal(
|
||||
formattedSize.mul(new Decimal(tradeForm.price)),
|
||||
formattedSize.mul(new Decimal(state.tradeForm.price)),
|
||||
getDecimalCount(tickSize)
|
||||
)
|
||||
state.tradeForm.quoteSize = quoteSize.toString()
|
||||
}
|
||||
})
|
||||
}, [formattedSize, tradeForm])
|
||||
}, [formattedSize, tickSize])
|
||||
|
||||
const groupingDecimalCount = useMemo(
|
||||
() => getDecimalCount(grouping),
|
||||
|
|
|
@ -50,15 +50,12 @@ const RecentTrades = () => {
|
|||
}
|
||||
}, [fills, previousFills, soundSettings])
|
||||
|
||||
const { selectedMarket, serumOrPerpMarket: market } = useSelectedMarket()
|
||||
|
||||
const baseSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split(/-|\//)[0]
|
||||
}, [selectedMarket])
|
||||
|
||||
const quoteSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split(/-|\//)[1]
|
||||
}, [selectedMarket])
|
||||
const {
|
||||
selectedMarket,
|
||||
serumOrPerpMarket: market,
|
||||
baseSymbol,
|
||||
quoteSymbol,
|
||||
} = useSelectedMarket()
|
||||
|
||||
// const fetchRecentTrades = useCallback(async () => {
|
||||
// if (!market) return
|
||||
|
|
|
@ -1,51 +1,31 @@
|
|||
import { Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import ButtonGroup from '@components/forms/ButtonGroup'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { trimDecimals } from 'utils/numbers'
|
||||
import { useSpotMarketMax } from './SpotSlider'
|
||||
|
||||
const SpotButtonGroup = ({
|
||||
minOrderDecimals,
|
||||
tickDecimals,
|
||||
useMargin,
|
||||
}: {
|
||||
minOrderDecimals: number
|
||||
tickDecimals: number
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
|
||||
const leverageMax = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!mangoAccount || !group || !selectedMarket) return 100
|
||||
if (!(selectedMarket instanceof Serum3Market)) return 100
|
||||
|
||||
try {
|
||||
if (side === 'buy') {
|
||||
return mangoAccount.getMaxQuoteForSerum3BidUi(
|
||||
group,
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
} else {
|
||||
return mangoAccount.getMaxBaseForSerum3AskUi(
|
||||
group,
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error calculating max leverage: spot btn group: ', e)
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount])
|
||||
const max = useSpotMarketMax(mangoAccount, selectedMarket, side, useMargin)
|
||||
|
||||
const handleSizePercentage = useCallback(
|
||||
(percentage: string) => {
|
||||
const set = mangoStore.getState().set
|
||||
setSizePercentage(percentage)
|
||||
const size = leverageMax * (Number(percentage) / 100)
|
||||
const size = max * (Number(percentage) / 100)
|
||||
|
||||
set((s) => {
|
||||
if (s.tradeForm.side === 'buy') {
|
||||
|
@ -75,7 +55,7 @@ const SpotButtonGroup = ({
|
|||
}
|
||||
})
|
||||
},
|
||||
[side, selectedMarket, mangoAccount, minOrderDecimals, tickDecimals]
|
||||
[minOrderDecimals, tickDecimals, max]
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,47 +1,69 @@
|
|||
import { Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import { MangoAccount, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import LeverageSlider from '@components/shared/LeverageSlider'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { GenericMarket } from 'types'
|
||||
import { trimDecimals } from 'utils/numbers'
|
||||
|
||||
export const useSpotMarketMax = (
|
||||
mangoAccount: MangoAccount | undefined,
|
||||
selectedMarket: GenericMarket | undefined,
|
||||
side: string,
|
||||
useMargin: boolean
|
||||
) => {
|
||||
const max = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!mangoAccount || !group || !selectedMarket) return 100
|
||||
if (!(selectedMarket instanceof Serum3Market)) return 100
|
||||
|
||||
let leverageMax = 0
|
||||
let spotMax = 0
|
||||
try {
|
||||
if (side === 'buy') {
|
||||
leverageMax = mangoAccount.getMaxQuoteForSerum3BidUi(
|
||||
group,
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
spotMax = mangoAccount.getTokenBalanceUi(
|
||||
group.getFirstBankByTokenIndex(selectedMarket.quoteTokenIndex)
|
||||
)
|
||||
} else {
|
||||
leverageMax = mangoAccount.getMaxBaseForSerum3AskUi(
|
||||
group,
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
spotMax = mangoAccount.getTokenBalanceUi(
|
||||
group.getFirstBankByTokenIndex(selectedMarket.baseTokenIndex)
|
||||
)
|
||||
}
|
||||
return useMargin ? leverageMax : Math.max(spotMax, 0)
|
||||
} catch (e) {
|
||||
console.error('Error calculating max leverage: spot btn group: ', e)
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount, useMargin])
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
const SpotSlider = ({
|
||||
minOrderDecimals,
|
||||
tickDecimals,
|
||||
step,
|
||||
useMargin,
|
||||
}: {
|
||||
minOrderDecimals: number
|
||||
tickDecimals: number
|
||||
step: number
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
const { selectedMarket, price: marketPrice } = useSelectedMarket()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||
|
||||
const leverageMax = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!mangoAccount || !group || !selectedMarket) return 100
|
||||
if (!(selectedMarket instanceof Serum3Market)) return 100
|
||||
|
||||
try {
|
||||
if (side === 'buy') {
|
||||
return mangoAccount.getMaxQuoteForSerum3BidUi(
|
||||
group,
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
} else {
|
||||
return mangoAccount.getMaxBaseForSerum3AskUi(
|
||||
group,
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error calculating max leverage for spot slider: ', e)
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount])
|
||||
const max = useSpotMarketMax(mangoAccount, selectedMarket, side, useMargin)
|
||||
|
||||
const handleSlide = useCallback(
|
||||
(val: string) => {
|
||||
|
@ -85,7 +107,7 @@ const SpotSlider = ({
|
|||
? parseFloat(tradeForm.quoteSize)
|
||||
: parseFloat(tradeForm.baseSize)
|
||||
}
|
||||
leverageMax={leverageMax}
|
||||
leverageMax={max}
|
||||
onChange={handleSlide}
|
||||
step={step}
|
||||
/>
|
||||
|
|
|
@ -5,15 +5,16 @@ import MarketLogos from './MarketLogos'
|
|||
|
||||
const TableMarketName = ({ market }: { market: PerpMarket | Serum3Market }) => {
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
|
||||
return selectedMarket?.name === market.name ? (
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market!} />
|
||||
<MarketLogos market={market} />
|
||||
<span className="whitespace-nowrap">{market.name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<Link href={`/trade?name=${market.name}`}>
|
||||
<div className="default-transition flex items-center underline md:hover:text-th-fgd-3 md:hover:no-underline">
|
||||
<MarketLogos market={market!} />
|
||||
<MarketLogos market={market} />
|
||||
<span className="whitespace-nowrap">{market.name}</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
@ -317,7 +317,7 @@ const TradeHistory = () => {
|
|||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center p-8 px-6">
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p>No trade history</p>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,6 @@ import { useViewport } from 'hooks/useViewport'
|
|||
import { breakpoints } from '../../utils/theme'
|
||||
import EditProfileModal from '@components/modals/EditProfileModal'
|
||||
import MangoAccountsListModal from '@components/modals/MangoAccountsListModal'
|
||||
import { Wallet as AnchorWallet } from '@project-serum/anchor'
|
||||
|
||||
const ConnectedMenu = () => {
|
||||
const { t } = useTranslation('common')
|
||||
|
@ -33,10 +32,10 @@ const ConnectedMenu = () => {
|
|||
const isMobile = width ? width < breakpoints.md : false
|
||||
|
||||
const onConnectFetchAccountData = async (wallet: Wallet) => {
|
||||
if (!wallet) return
|
||||
await actions.fetchMangoAccounts(wallet.adapter as unknown as AnchorWallet)
|
||||
if (!wallet.adapter.publicKey) return
|
||||
await actions.fetchMangoAccounts(wallet.adapter.publicKey)
|
||||
actions.fetchTourSettings(wallet.adapter.publicKey?.toString() as string)
|
||||
actions.fetchWalletTokens(wallet.adapter as unknown as AnchorWallet)
|
||||
actions.fetchWalletTokens(wallet.adapter.publicKey)
|
||||
actions.fetchTradeHistory()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import { Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
import useMangoGroup from './useMangoGroup'
|
||||
import useSelectedMarket from './useSelectedMarket'
|
||||
|
||||
export default function useOraclePrice() {
|
||||
const { group } = useMangoGroup()
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
|
||||
if (!group || !selectedMarket) return false
|
||||
|
||||
let price
|
||||
let market
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
price = group.getFirstBankByTokenIndex(
|
||||
selectedMarket?.baseTokenIndex
|
||||
).uiPrice
|
||||
market = group.getSerum3ExternalMarket(selectedMarket.serumMarketExternal)
|
||||
} else {
|
||||
price = selectedMarket.uiPrice
|
||||
market = selectedMarket
|
||||
}
|
||||
return price && market ? price.toFixed(getDecimalCount(market.tickSize)) : ''
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
import { Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useMemo } from 'react'
|
||||
import useJupiterMints from './useJupiterMints'
|
||||
import useMangoGroup from './useMangoGroup'
|
||||
|
||||
export default function useSelectedMarket() {
|
||||
const { group } = useMangoGroup()
|
||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
|
||||
const price: number = useMemo(() => {
|
||||
if (!group) return 0
|
||||
|
@ -31,5 +33,52 @@ export default function useSelectedMarket() {
|
|||
}
|
||||
}, [selectedMarket])
|
||||
|
||||
return { selectedMarket, price, serumOrPerpMarket }
|
||||
const baseSymbol = useMemo(() => {
|
||||
return selectedMarket?.name.split(/-|\//)[0]
|
||||
}, [selectedMarket])
|
||||
|
||||
const baseLogoURI = useMemo(() => {
|
||||
if (!baseSymbol || !mangoTokens.length) return ''
|
||||
const token =
|
||||
mangoTokens.find((t) => t.symbol === baseSymbol) ||
|
||||
mangoTokens.find((t) => t.symbol?.includes(baseSymbol))
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
return ''
|
||||
}, [baseSymbol, mangoTokens])
|
||||
|
||||
const quoteBank = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!group || !selectedMarket) return
|
||||
const tokenIdx =
|
||||
selectedMarket instanceof Serum3Market
|
||||
? selectedMarket.quoteTokenIndex
|
||||
: selectedMarket?.settleTokenIndex
|
||||
return group?.getFirstBankByTokenIndex(tokenIdx)
|
||||
}, [selectedMarket])
|
||||
|
||||
const quoteSymbol = useMemo(() => {
|
||||
return quoteBank?.name
|
||||
}, [quoteBank])
|
||||
|
||||
const quoteLogoURI = useMemo(() => {
|
||||
if (!quoteSymbol || !mangoTokens.length) return ''
|
||||
const token = mangoTokens.find((t) => t.symbol === quoteSymbol)
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
return ''
|
||||
}, [quoteSymbol, mangoTokens])
|
||||
|
||||
return {
|
||||
selectedMarket,
|
||||
price,
|
||||
serumOrPerpMarket,
|
||||
baseSymbol,
|
||||
quoteBank,
|
||||
quoteSymbol,
|
||||
baseLogoURI,
|
||||
quoteLogoURI,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"pay": "You Pay",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"pay": "You Pay",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"pay": "You Pay",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"pay": "You Pay",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"pay": "You Pay",
|
||||
|
|
|
@ -320,7 +320,7 @@ export type MangoStore = {
|
|||
) => Promise<void>
|
||||
fetchGroup: () => Promise<void>
|
||||
reloadMangoAccount: () => Promise<void>
|
||||
fetchMangoAccounts: (wallet: Wallet) => Promise<void>
|
||||
fetchMangoAccounts: (ownerPk: PublicKey) => Promise<void>
|
||||
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
|
||||
fetchOpenOrders: (ma?: MangoAccount) => Promise<void>
|
||||
fetchPerpStats: () => void
|
||||
|
@ -332,7 +332,7 @@ export type MangoStore = {
|
|||
fetchTokenStats: () => void
|
||||
fetchTourSettings: (walletPk: string) => void
|
||||
fetchTradeHistory: (offset?: number) => Promise<void>
|
||||
fetchWalletTokens: (wallet: Wallet) => Promise<void>
|
||||
fetchWalletTokens: (walletPk: PublicKey) => Promise<void>
|
||||
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
|
||||
loadMarketFills: () => Promise<void>
|
||||
updateConnection: (url: string) => void
|
||||
|
@ -665,7 +665,7 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
}
|
||||
},
|
||||
fetchMangoAccounts: async (wallet) => {
|
||||
fetchMangoAccounts: async (ownerPk: PublicKey) => {
|
||||
const set = get().set
|
||||
const actions = get().actions
|
||||
try {
|
||||
|
@ -677,7 +677,7 @@ const mangoStore = create<MangoStore>()(
|
|||
|
||||
const mangoAccounts = await client.getMangoAccountsForOwner(
|
||||
group,
|
||||
wallet.publicKey
|
||||
ownerPk
|
||||
)
|
||||
const selectedAccountIsNotInAccountsList = mangoAccounts.find(
|
||||
(x) =>
|
||||
|
@ -692,8 +692,6 @@ const mangoStore = create<MangoStore>()(
|
|||
return
|
||||
}
|
||||
|
||||
mangoAccounts.forEach((ma) => ma.reloadAccountData(client))
|
||||
|
||||
let newSelectedMangoAccount = selectedMangoAccount
|
||||
if (!selectedMangoAccount || !selectedAccountIsNotInAccountsList) {
|
||||
const lastAccount = localStorage.getItem(LAST_ACCOUNT_KEY)
|
||||
|
@ -708,19 +706,23 @@ const mangoStore = create<MangoStore>()(
|
|||
}
|
||||
|
||||
if (newSelectedMangoAccount) {
|
||||
await actions.fetchOpenOrders(newSelectedMangoAccount)
|
||||
await newSelectedMangoAccount.reloadAccountData(client)
|
||||
set((state) => {
|
||||
state.mangoAccount.current = newSelectedMangoAccount
|
||||
state.mangoAccount.initialLoad = false
|
||||
})
|
||||
actions.fetchOpenOrders(newSelectedMangoAccount)
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
mangoAccounts.map((ma) => ma.reloadAccountData(client))
|
||||
)
|
||||
|
||||
set((state) => {
|
||||
state.mangoAccounts = mangoAccounts
|
||||
state.mangoAccount.current = newSelectedMangoAccount
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('Error fetching mango accts', e)
|
||||
} finally {
|
||||
set((state) => {
|
||||
state.mangoAccount.initialLoad = false
|
||||
})
|
||||
}
|
||||
},
|
||||
fetchNfts: async (connection: Connection, ownerPk: PublicKey) => {
|
||||
|
@ -888,14 +890,14 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
}
|
||||
},
|
||||
fetchWalletTokens: async (wallet: Wallet) => {
|
||||
fetchWalletTokens: async (walletPk: PublicKey) => {
|
||||
const set = get().set
|
||||
const connection = get().connection
|
||||
|
||||
if (wallet.publicKey) {
|
||||
if (walletPk) {
|
||||
const token = await getTokenAccountsByOwnerWithWrappedSol(
|
||||
connection,
|
||||
wallet.publicKey
|
||||
walletPk
|
||||
)
|
||||
|
||||
set((state) => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import { BN } from '@project-serum/anchor'
|
||||
|
||||
export interface ChartTradeType {
|
||||
|
@ -45,3 +46,5 @@ export interface SpotTradeHistory {
|
|||
base_symbol: string
|
||||
quote_symbol: string
|
||||
}
|
||||
|
||||
export type GenericMarket = Serum3Market | PerpMarket
|
||||
|
|
Loading…
Reference in New Issue