merge
This commit is contained in:
commit
4d71a9d5c5
|
@ -10,6 +10,8 @@
|
|||
"rules": {
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"@next/next/no-img-element": 0,
|
||||
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
|
||||
"react-hooks/exhaustive-deps": "warn", // Checks effect dependencies
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
2,
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ const HydrateStore = () => {
|
|||
useEffect(() => {
|
||||
const connection = mangoStore.getState().connection
|
||||
const client = mangoStore.getState().client
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
if (!mangoAccount) return
|
||||
|
||||
|
@ -68,12 +69,9 @@ const HydrateStore = () => {
|
|||
// newMangoAccount.spotOpenOrdersAccounts =
|
||||
// mangoAccount.spotOpenOrdersAccounts
|
||||
// newMangoAccount.advancedOrders = mangoAccount.advancedOrders
|
||||
mangoStore.setState({
|
||||
mangoAccount: {
|
||||
...mangoStore.getState().mangoAccount,
|
||||
current: newMangoAccount,
|
||||
lastSlot: context.slot,
|
||||
},
|
||||
set((s) => {
|
||||
s.mangoAccount.current = newMangoAccount
|
||||
s.mangoAccount.lastSlot = context.slot
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +88,7 @@ const HydrateStore = () => {
|
|||
const ReadOnlyMangoAccount = () => {
|
||||
const router = useRouter()
|
||||
const groupLoaded = mangoStore((s) => s.groupLoaded)
|
||||
const ma = router.query?.mangoAccount
|
||||
const ma = router.query?.address
|
||||
|
||||
useEffect(() => {
|
||||
if (!groupLoaded) return
|
||||
|
|
|
@ -62,7 +62,7 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<span className="ml-3 text-lg font-bold text-th-fgd-1">
|
||||
<span className="ml-3 font-display text-lg text-th-fgd-1">
|
||||
Mango
|
||||
</span>
|
||||
</Transition>
|
||||
|
|
|
@ -244,7 +244,7 @@ const UserSetup = ({ onClose }: { onClose: () => void }) => {
|
|||
</div>
|
||||
<div className="col-span-1 flex flex-col items-center justify-center p-6 pt-24">
|
||||
<UserSetupTransition show={showSetupStep === 0}>
|
||||
<h2 className="mb-4 text-5xl lg:text-6xl">
|
||||
<h2 className="mb-4 font-display text-5xl tracking-normal lg:text-6xl">
|
||||
{t('onboarding:intro-heading')}
|
||||
</h2>
|
||||
<p className="mb-4 text-base">{t('onboarding:intro-desc')}</p>
|
||||
|
@ -280,7 +280,7 @@ const UserSetup = ({ onClose }: { onClose: () => void }) => {
|
|||
<UserSetupTransition delay show={showSetupStep === 1}>
|
||||
{showSetupStep === 1 ? (
|
||||
<div>
|
||||
<h2 className="mb-6 text-5xl lg:text-6xl">
|
||||
<h2 className="mb-6 font-display text-5xl tracking-normal lg:text-6xl">
|
||||
{t('onboarding:connect-wallet')}
|
||||
</h2>
|
||||
<p className="mb-2 text-base">{t('onboarding:choose-wallet')}</p>
|
||||
|
@ -333,7 +333,7 @@ const UserSetup = ({ onClose }: { onClose: () => void }) => {
|
|||
{showSetupStep === 2 ? (
|
||||
<div>
|
||||
<div className="pb-6">
|
||||
<h2 className="mb-4 text-5xl lg:text-6xl">
|
||||
<h2 className="mb-4 font-display text-5xl tracking-normal lg:text-6xl">
|
||||
{t('onboarding:create-account')}
|
||||
</h2>
|
||||
<p className="text-base">
|
||||
|
@ -386,7 +386,7 @@ const UserSetup = ({ onClose }: { onClose: () => void }) => {
|
|||
<UserSetupTransition delay show={showSetupStep === 3}>
|
||||
{showSetupStep === 3 ? (
|
||||
<div className="relative">
|
||||
<h2 className="mb-6 text-5xl lg:text-6xl">
|
||||
<h2 className="mb-6 font-display text-5xl tracking-normal lg:text-6xl">
|
||||
{t('onboarding:fund-account')}
|
||||
</h2>
|
||||
<UserSetupTransition show={depositToken.length > 0}>
|
||||
|
@ -542,7 +542,7 @@ const UserSetup = ({ onClose }: { onClose: () => void }) => {
|
|||
<UserSetupTransition delay show={showSetupStep === 4}>
|
||||
{showSetupStep === 4 ? (
|
||||
<div className="relative">
|
||||
<h2 className="mb-4 text-5xl lg:text-6xl">
|
||||
<h2 className="mb-4 font-display text-5xl tracking-normal lg:text-6xl">
|
||||
{t('onboarding:your-profile')}
|
||||
</h2>
|
||||
<p className="text-base">{t('onboarding:profile-desc')}</p>
|
||||
|
@ -595,7 +595,7 @@ const UserSetupTransition = ({
|
|||
return (
|
||||
<Transition
|
||||
appear
|
||||
className="h-full w-full max-w-md"
|
||||
className="h-full w-full max-w-lg"
|
||||
show={show}
|
||||
enter={`transition ease-in duration-300 ${delay ? 'delay-300' : ''}`}
|
||||
enterFrom="opacity-0"
|
||||
|
|
|
@ -81,19 +81,19 @@ const AccountPage = () => {
|
|||
INITIAL_ANIMATION_SETTINGS
|
||||
)
|
||||
|
||||
const leverage = useMemo(() => {
|
||||
if (!group || !mangoAccount) return 0
|
||||
const liabsValue = mangoAccount
|
||||
.getLiabsValue(group, HealthType.init)!
|
||||
.toNumber()
|
||||
const totalCollateral = mangoAccount
|
||||
.getAssetsValue(group, HealthType.init)!
|
||||
.toNumber()
|
||||
// const leverage = useMemo(() => {
|
||||
// if (!group || !mangoAccount) return 0
|
||||
// const liabsValue = mangoAccount
|
||||
// .getLiabsValue(group, HealthType.init)!
|
||||
// .toNumber()
|
||||
// const totalCollateral = mangoAccount
|
||||
// .getAssetsValue(group, HealthType.init)!
|
||||
// .toNumber()
|
||||
|
||||
if (isNaN(liabsValue / totalCollateral)) {
|
||||
return 0
|
||||
} else return liabsValue / totalCollateral
|
||||
}, [mangoAccount, group])
|
||||
// if (isNaN(liabsValue / totalCollateral)) {
|
||||
// return 0
|
||||
// } else return liabsValue / totalCollateral
|
||||
// }, [mangoAccount, group])
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccount) {
|
||||
|
@ -234,12 +234,12 @@ const AccountPage = () => {
|
|||
{t('account-value')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<div className="mb-2 flex items-center text-5xl font-bold text-th-fgd-1">
|
||||
<div className="mb-2 flex items-center font-display text-5xl text-th-fgd-1">
|
||||
{animationSettings['number-scroll'] ? (
|
||||
group && mangoAccount ? (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={32}
|
||||
width={35}
|
||||
play
|
||||
delay={0.05}
|
||||
duration={1}
|
||||
|
@ -248,7 +248,7 @@ const AccountPage = () => {
|
|||
) : (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={32}
|
||||
width={36}
|
||||
play
|
||||
delay={0.05}
|
||||
duration={1}
|
||||
|
@ -319,8 +319,8 @@ const AccountPage = () => {
|
|||
<AccountActions />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-5 border-b border-th-bkg-3">
|
||||
<div className="col-span-5 flex border-t border-th-bkg-3 py-3 pl-6 md:border-t-0 lg:col-span-1">
|
||||
<div className="grid grid-cols-4 border-b border-th-bkg-3">
|
||||
<div className="col-span-4 flex border-t border-th-bkg-3 py-3 pl-6 md:border-t-0 lg:col-span-1">
|
||||
<div id="account-step-four">
|
||||
<Tooltip
|
||||
maxWidth="20rem"
|
||||
|
@ -357,7 +357,7 @@ const AccountPage = () => {
|
|||
</div>
|
||||
}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
|
||||
<p className="tooltip-underline text-sm font-normal text-th-fgd-3 xl:text-base">
|
||||
{t('health')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
|
@ -366,7 +366,7 @@ const AccountPage = () => {
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-5 flex border-t border-th-bkg-3 py-3 pl-6 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div className="col-span-4 flex border-t border-th-bkg-3 py-3 pl-6 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div id="account-step-five">
|
||||
<Tooltip
|
||||
content="The value of collateral you have to open new trades or borrows. When your free collateral reaches $0 you won't be able to make withdrawals."
|
||||
|
@ -412,7 +412,7 @@ const AccountPage = () => {
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-5 flex border-t border-th-bkg-3 py-3 pl-6 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
{/* <div className="col-span-5 flex border-t border-th-bkg-3 py-3 pl-6 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div id="account-step-six">
|
||||
<Tooltip
|
||||
content="Total position size divided by total collateral."
|
||||
|
@ -428,9 +428,9 @@ const AccountPage = () => {
|
|||
{leverage.toFixed(2)}x
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<button
|
||||
className={`col-span-5 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 pr-4 lg:col-span-1 lg:border-l lg:border-t-0 ${
|
||||
className={`col-span-4 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 pr-4 lg:col-span-1 lg:border-l lg:border-t-0 ${
|
||||
performanceData.length > 4
|
||||
? 'default-transition cursor-pointer md:hover:bg-th-bkg-2'
|
||||
: 'cursor-default'
|
||||
|
@ -460,7 +460,7 @@ const AccountPage = () => {
|
|||
) : null}
|
||||
</button>
|
||||
<button
|
||||
className={`col-span-5 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left lg:col-span-1 lg:border-l lg:border-t-0 ${
|
||||
className={`col-span-4 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left lg:col-span-1 lg:border-l lg:border-t-0 ${
|
||||
interestTotalValue > 1 || interestTotalValue < -1
|
||||
? 'default-transition cursor-pointer md:hover:bg-th-bkg-2'
|
||||
: 'cursor-default'
|
||||
|
|
|
@ -9,6 +9,7 @@ import { useUnsettledSpotBalances } from 'hooks/useUnsettledSpotBalances'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
|
||||
|
||||
const TABS = [
|
||||
'balances',
|
||||
|
@ -52,7 +53,7 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
|
|||
const swapHistory = mangoStore((s) => s.mangoAccount.stats.swapHistory.data)
|
||||
const loading = mangoStore((s) => s.mangoAccount.stats.swapHistory.loading)
|
||||
const unsettledSpotBalances = useUnsettledSpotBalances()
|
||||
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
|
||||
const unsettledPerpPositions = useUnsettledPerpPositions()
|
||||
switch (activeTab) {
|
||||
case 'balances':
|
||||
return <TokenList />
|
||||
|
@ -64,9 +65,7 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
|
|||
return (
|
||||
<UnsettledTrades
|
||||
unsettledSpotBalances={unsettledSpotBalances}
|
||||
unsettledPerpPositions={perpPositions.filter(
|
||||
(p) => !p.basePositionLots.toNumber()
|
||||
)}
|
||||
unsettledPerpPositions={unsettledPerpPositions}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
|
|
|
@ -5,10 +5,10 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
|||
import Tooltip from '@components/shared/Tooltip'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import {
|
||||
BoltIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronRightIcon,
|
||||
LinkIcon,
|
||||
NoSymbolIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import mangoStore, { LiquidationFeedItem } from '@store/mangoStore'
|
||||
import dayjs from 'dayjs'
|
||||
|
@ -288,14 +288,14 @@ const ActivityFeedTable = ({
|
|||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<BoltIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p>No account activity found...</p>
|
||||
<NoSymbolIcon className="mb-1 h-6 w-6 text-th-fgd-4" />
|
||||
<p>{t('activity:no-activity')}</p>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<LinkIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p>Connect to view your account activity</p>
|
||||
<p>{t('activity:connect-activity')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from '@blockworks-foundation/mango-v4'
|
||||
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
|
||||
import Button from '../shared/Button'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import DepositModal from '../modals/DepositModal'
|
||||
import WithdrawModal from '../modals/WithdrawModal'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -21,18 +21,18 @@ const MangoAccountSummary = () => {
|
|||
const [showDepositModal, setShowDepositModal] = useState(false)
|
||||
const [showWithdrawModal, setShowWithdrawModal] = useState(false)
|
||||
|
||||
const leverage = useMemo(() => {
|
||||
if (!group || !mangoAccount) return 0
|
||||
const liabsValue = mangoAccount
|
||||
.getLiabsValue(group, HealthType.init)
|
||||
.toNumber()
|
||||
const totalCollateral = mangoAccount
|
||||
.getAssetsValue(group, HealthType.init)
|
||||
.toNumber()
|
||||
if (isNaN(liabsValue / totalCollateral)) {
|
||||
return 0
|
||||
} else return liabsValue / totalCollateral
|
||||
}, [mangoAccount])
|
||||
// const leverage = useMemo(() => {
|
||||
// if (!group || !mangoAccount) return 0
|
||||
// const liabsValue = mangoAccount
|
||||
// .getLiabsValue(group, HealthType.init)
|
||||
// .toNumber()
|
||||
// const totalCollateral = mangoAccount
|
||||
// .getAssetsValue(group, HealthType.init)
|
||||
// .toNumber()
|
||||
// if (isNaN(liabsValue / totalCollateral)) {
|
||||
// return 0
|
||||
// } else return liabsValue / totalCollateral
|
||||
// }, [mangoAccount])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -88,12 +88,12 @@ const MangoAccountSummary = () => {
|
|||
: `$${(0).toFixed(2)}`}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
{/* <div>
|
||||
<p className="text-sm text-th-fgd-3">{t('leverage')}</p>
|
||||
<p className="font-mono text-sm text-th-fgd-1">
|
||||
{leverage.toFixed(2)}x
|
||||
</p>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
|
|
|
@ -44,7 +44,7 @@ export const walletBalanceForToken = (
|
|||
token: string
|
||||
): { maxAmount: number; maxDecimals: number } => {
|
||||
const group = mangoStore.getState().group
|
||||
const bank = group?.banksMapByName.get(token)![0]
|
||||
const bank = group?.banksMapByName.get(token)?.[0]
|
||||
|
||||
let walletToken
|
||||
if (bank) {
|
||||
|
@ -97,7 +97,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
const setMax = useCallback(() => {
|
||||
setInputAmount(tokenMax.maxAmount.toString())
|
||||
setSizePercentage('100')
|
||||
}, [tokenMax, selectedToken])
|
||||
}, [tokenMax])
|
||||
|
||||
const handleSizePercentage = useCallback(
|
||||
(percentage: string) => {
|
||||
|
@ -108,7 +108,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
|
||||
setInputAmount(amount.toString())
|
||||
},
|
||||
[tokenMax, selectedToken]
|
||||
[tokenMax]
|
||||
)
|
||||
|
||||
const handleSelectToken = (token: string) => {
|
||||
|
@ -152,7 +152,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
}
|
||||
|
||||
onClose()
|
||||
}, [bank, wallet, inputAmount])
|
||||
}, [bank, wallet, inputAmount, onClose])
|
||||
|
||||
// TODO extract into a shared hook for UserSetup.tsx
|
||||
const banks = useMemo(() => {
|
||||
|
@ -175,6 +175,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
|
||||
const exceedsAlphaMax = useMemo(() => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const group = mangoStore.getState().group
|
||||
if (!group || !mangoAccount) return
|
||||
if (
|
||||
mangoAccount.owner.toString() ===
|
||||
|
@ -182,7 +183,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
)
|
||||
return false
|
||||
const accountValue = toUiDecimalsForQuote(
|
||||
mangoAccount.getEquity(group)!.toNumber()
|
||||
mangoAccount.getEquity(group).toNumber()
|
||||
)
|
||||
return (
|
||||
parseFloat(inputAmount) > ALPHA_DEPOSIT_LIMIT ||
|
||||
|
@ -320,7 +321,9 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
<p className="font-mono">{bank!.initAssetWeight.toFixed(2)}x</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<p>{t('collateral-value')}</p>
|
||||
<Tooltip content={t('tooltip-collateral-value')}>
|
||||
<p className="tooltip-underline">{t('collateral-value')}</p>
|
||||
</Tooltip>
|
||||
<p className="font-mono">
|
||||
{formatFixedDecimals(
|
||||
bank!.uiPrice! *
|
||||
|
|
|
@ -84,7 +84,7 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
const setMax = useCallback(() => {
|
||||
setInputAmount(borrowAmount.toFixed(bank?.mintDecimals))
|
||||
setSizePercentage('100')
|
||||
}, [bank, borrowAmount, selectedToken])
|
||||
}, [bank, borrowAmount])
|
||||
|
||||
const handleSizePercentage = useCallback(
|
||||
(percentage: string) => {
|
||||
|
@ -97,7 +97,7 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
|
||||
setInputAmount(amount.toFixed(bank?.mintDecimals))
|
||||
},
|
||||
[bank, borrowAmount, selectedToken]
|
||||
[bank, borrowAmount]
|
||||
)
|
||||
|
||||
const handleSelectToken = (token: string) => {
|
||||
|
@ -105,43 +105,47 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
setShowTokenList(false)
|
||||
}
|
||||
|
||||
const handleDeposit = useCallback(async () => {
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const actions = mangoStore.getState().actions
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const handleDeposit = useCallback(
|
||||
async (amount: string) => {
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const actions = mangoStore.getState().actions
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
|
||||
if (!mangoAccount || !group || !bank || !wallet) return
|
||||
if (!mangoAccount || !group || !bank || !wallet) return
|
||||
console.log('inputAmount: ', amount)
|
||||
|
||||
try {
|
||||
setSubmitting(true)
|
||||
const tx = await client.tokenDeposit(
|
||||
group,
|
||||
mangoAccount,
|
||||
bank.mint,
|
||||
parseFloat(inputAmount)
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
type: 'success',
|
||||
txid: tx,
|
||||
})
|
||||
try {
|
||||
setSubmitting(true)
|
||||
const tx = await client.tokenDeposit(
|
||||
group,
|
||||
mangoAccount,
|
||||
bank.mint,
|
||||
parseFloat(amount)
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
type: 'success',
|
||||
txid: tx,
|
||||
})
|
||||
|
||||
await actions.reloadMangoAccount()
|
||||
actions.fetchWalletTokens(wallet.adapter as unknown as Wallet)
|
||||
setSubmitting(false)
|
||||
} catch (e: any) {
|
||||
notify({
|
||||
title: 'Transaction failed',
|
||||
description: e.message,
|
||||
txid: e?.txid,
|
||||
type: 'error',
|
||||
})
|
||||
console.error('Error repaying:', e)
|
||||
}
|
||||
await actions.reloadMangoAccount()
|
||||
actions.fetchWalletTokens(wallet.adapter as unknown as Wallet)
|
||||
setSubmitting(false)
|
||||
} catch (e: any) {
|
||||
notify({
|
||||
title: 'Transaction failed',
|
||||
description: e.message,
|
||||
txid: e?.txid,
|
||||
type: 'error',
|
||||
})
|
||||
console.error('Error repaying:', e)
|
||||
}
|
||||
|
||||
onClose()
|
||||
}, [bank, wallet])
|
||||
onClose()
|
||||
},
|
||||
[bank, wallet, onClose]
|
||||
)
|
||||
|
||||
const banks = useMemo(() => {
|
||||
const banks =
|
||||
|
@ -160,7 +164,7 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
}).filter((b) => b.borrowAmount > 0)
|
||||
: []
|
||||
return banks
|
||||
}, [group?.banksMapByName, walletTokens, mangoAccount])
|
||||
}, [group?.banksMapByName, mangoAccount])
|
||||
|
||||
useEffect(() => {
|
||||
if (!token && banks.length) {
|
||||
|
@ -295,7 +299,7 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
|||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleDeposit}
|
||||
onClick={() => handleDeposit(inputAmount)}
|
||||
className="flex w-full items-center justify-center"
|
||||
disabled={!inputAmount || showInsufficientBalance}
|
||||
size="large"
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Bank, Serum3Market } from '@blockworks-foundation/mango-v4'
|
|||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import Decimal from 'decimal.js'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -17,7 +16,7 @@ import {
|
|||
trimDecimals,
|
||||
} from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
||||
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||
import { LinkButton } from './Button'
|
||||
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
|
@ -218,9 +217,9 @@ const Balance = ({ bank }: { bank: Bank }) => {
|
|||
(balance > 0 && type === 'quote') || (balance < 0 && type === 'base')
|
||||
? 'buy'
|
||||
: 'sell'
|
||||
price = calculateMarketPrice(orderbook, balance, side)
|
||||
price = calculateLimitPriceForMarketOrder(orderbook, balance, side)
|
||||
} else {
|
||||
price = new Decimal(tradeForm.price).toNumber()
|
||||
price = Number(tradeForm.price)
|
||||
}
|
||||
|
||||
let minOrderDecimals: number
|
||||
|
|
|
@ -39,7 +39,7 @@ const Button: FunctionComponent<ButtonCombinedProps> = ({
|
|||
: size === 'large'
|
||||
? 'h-12 px-6'
|
||||
: 'h-8 px-3'
|
||||
} default-transition font-bold ${
|
||||
} default-transition font-display ${
|
||||
theme === 'High Contrast' && !secondary
|
||||
? 'text-th-bkg-1'
|
||||
: 'text-th-fgd-1'
|
||||
|
|
|
@ -10,14 +10,12 @@ import {
|
|||
ResponsiveContainer,
|
||||
} from 'recharts'
|
||||
import FlipNumbers from 'react-flip-numbers'
|
||||
|
||||
import LineChartIcon from '../icons/LineChartIcon'
|
||||
import ContentBox from '../shared/ContentBox'
|
||||
import SheenLoader from '../shared/SheenLoader'
|
||||
import { COLORS } from '../../styles/colors'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { IconButton } from './Button'
|
||||
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
|
||||
import { ArrowLeftIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
import { FadeInFadeOut } from './Transitions'
|
||||
import ChartRangeButtons from './ChartRangeButtons'
|
||||
import Change from './Change'
|
||||
|
@ -26,6 +24,7 @@ import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
|||
import { formatFixedDecimals } from 'utils/numbers'
|
||||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||
import { AxisDomain } from 'recharts/types/util/types'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -33,6 +32,7 @@ interface DetailedAreaChartProps {
|
|||
data: any[]
|
||||
daysToShow?: string
|
||||
domain?: AxisDomain
|
||||
heightClass?: string
|
||||
hideChange?: boolean
|
||||
hideChart?: () => void
|
||||
loading?: boolean
|
||||
|
@ -58,6 +58,7 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
data,
|
||||
daysToShow = '1',
|
||||
domain,
|
||||
heightClass,
|
||||
hideChange,
|
||||
hideChart,
|
||||
loading,
|
||||
|
@ -70,6 +71,7 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
xKey,
|
||||
yKey,
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const [mouseData, setMouseData] = useState<any>(null)
|
||||
const { theme } = useTheme()
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
|
@ -98,17 +100,23 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
return 0
|
||||
}
|
||||
|
||||
const flipGradientCoords = useMemo(
|
||||
() => data[0][yKey] <= 0 && data[data.length - 1][yKey] < data[0][yKey],
|
||||
[data]
|
||||
)
|
||||
const flipGradientCoords = useMemo(() => {
|
||||
if (!data.length) return
|
||||
return data[0][yKey] <= 0 && data[data.length - 1][yKey] < data[0][yKey]
|
||||
}, [data])
|
||||
|
||||
console.log('title', title?.replace(/\s/g, ''))
|
||||
|
||||
return (
|
||||
<FadeInFadeOut show={true}>
|
||||
<ContentBox hideBorder hidePadding>
|
||||
{loading ? (
|
||||
<SheenLoader className="flex flex-1">
|
||||
<div className="h-[448px] w-full rounded-lg bg-th-bkg-2" />
|
||||
<div
|
||||
className={`${
|
||||
heightClass ? heightClass : 'h-96'
|
||||
} w-full rounded-lg bg-th-bkg-2`}
|
||||
/>
|
||||
</SheenLoader>
|
||||
) : data.length ? (
|
||||
<div className="relative">
|
||||
|
@ -134,12 +142,12 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
small
|
||||
? 'h-8 items-center text-2xl'
|
||||
: 'mb-1 items-end text-4xl'
|
||||
} font-bold text-th-fgd-1`}
|
||||
} font-display text-th-fgd-1`}
|
||||
>
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={small ? 24 : 40}
|
||||
width={small ? 15 : 26}
|
||||
width={small ? 17 : 30}
|
||||
play
|
||||
numbers={
|
||||
prefix +
|
||||
|
@ -179,12 +187,12 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
small
|
||||
? 'h-8 items-center text-2xl'
|
||||
: 'mb-1 items-end text-4xl'
|
||||
} font-bold text-th-fgd-1`}
|
||||
} font-display text-th-fgd-1`}
|
||||
>
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={small ? 24 : 40}
|
||||
width={small ? 15 : 26}
|
||||
width={small ? 17 : 30}
|
||||
play
|
||||
numbers={
|
||||
prefix +
|
||||
|
@ -223,7 +231,9 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="-mt-1 h-96 w-auto">
|
||||
<div
|
||||
className={`-mt-1 ${heightClass ? heightClass : 'h-96'} w-auto`}
|
||||
>
|
||||
{setDaysToShow ? (
|
||||
<div className="absolute -top-1 right-0 -mb-2 flex justify-end">
|
||||
<ChartRangeButtons
|
||||
|
@ -249,7 +259,7 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="gradientArea"
|
||||
id={`gradientArea-${title?.replace(/\s/g, '')}`}
|
||||
x1="0"
|
||||
y1={flipGradientCoords ? '1' : '0'}
|
||||
x2="0"
|
||||
|
@ -285,7 +295,7 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
: COLORS.DOWN[theme]
|
||||
}
|
||||
strokeWidth={1.5}
|
||||
fill="url(#gradientArea)"
|
||||
fill={`url(#gradientArea-${title?.replace(/\s/g, '')})`}
|
||||
/>
|
||||
<XAxis
|
||||
axisLine={false}
|
||||
|
@ -323,10 +333,14 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-96 items-center justify-center rounded-lg bg-th-bkg-2 p-4 text-th-fgd-3">
|
||||
<div
|
||||
className={`flex ${
|
||||
heightClass ? heightClass : 'h-96'
|
||||
} items-center justify-center p-4 text-th-fgd-3`}
|
||||
>
|
||||
<div className="">
|
||||
<LineChartIcon className="mx-auto h-10 w-10 text-th-fgd-4" />
|
||||
<p className="text-th-fgd-4">Chart not available</p>
|
||||
<NoSymbolIcon className="mx-auto mb-1 h-6 w-6 text-th-fgd-4" />
|
||||
<p className="text-th-fgd-4">{t('chart-unavailable')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/20/solid'
|
||||
import { CLUSTER } from '@store/mangoStore'
|
||||
|
||||
type ExplorerLinkProps = {
|
||||
address: string
|
||||
anchorData?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ExplorerLink = ({ address }: ExplorerLinkProps) => {
|
||||
const cluster = 'devnet'
|
||||
const ExplorerLink = ({
|
||||
address,
|
||||
anchorData = false,
|
||||
className = '',
|
||||
}: ExplorerLinkProps) => {
|
||||
return (
|
||||
<a
|
||||
href={
|
||||
'https://explorer.solana.com/address/' + address + '?cluster=' + cluster
|
||||
}
|
||||
className="ml-1 text-yellow-400 hover:underline"
|
||||
href={`https://explorer.solana.com/address/${address}${
|
||||
anchorData ? '/anchor-account' : ''
|
||||
}?cluster=${CLUSTER}`}
|
||||
className={`flex items-center break-all text-th-fgd-2 hover:text-th-fgd-3 ${className}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{address}
|
||||
<ArrowTopRightOnSquareIcon className="ml-2 h-5 w-5 whitespace-nowrap" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -31,23 +31,24 @@ const HealthImpact = ({
|
|||
{t('health-impact')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<div className="flex items-center space-x-1.5 font-mono">
|
||||
<p className={`text-th-fgd-2 ${small ? 'text-xs' : 'text-sm'}`}>
|
||||
{currentMaintHealth}%
|
||||
</p>
|
||||
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />
|
||||
<p
|
||||
className={`${
|
||||
maintProjectedHealth < 50 && maintProjectedHealth > 15
|
||||
? 'text-th-warning'
|
||||
: maintProjectedHealth <= 15
|
||||
? 'text-th-down'
|
||||
: 'text-th-up'
|
||||
} ${small ? 'text-xs' : 'text-sm'}`}
|
||||
>
|
||||
{maintProjectedHealth}%
|
||||
</p>
|
||||
{/* <span
|
||||
{currentMaintHealth ? (
|
||||
<div className="flex items-center space-x-1.5 font-mono">
|
||||
<p className={`text-th-fgd-2 ${small ? 'text-xs' : 'text-sm'}`}>
|
||||
{currentMaintHealth}%
|
||||
</p>
|
||||
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />
|
||||
<p
|
||||
className={`${
|
||||
maintProjectedHealth < 50 && maintProjectedHealth > 15
|
||||
? 'text-th-warning'
|
||||
: maintProjectedHealth <= 15
|
||||
? 'text-th-down'
|
||||
: 'text-th-up'
|
||||
} ${small ? 'text-xs' : 'text-sm'}`}
|
||||
>
|
||||
{maintProjectedHealth}%
|
||||
</p>
|
||||
{/* <span
|
||||
className={`text-xs ${
|
||||
maintProjectedHealth >= currentMaintHealth!
|
||||
? 'text-th-up'
|
||||
|
@ -57,7 +58,10 @@ const HealthImpact = ({
|
|||
({maintProjectedHealth >= currentMaintHealth! ? '+' : ''}
|
||||
{maintProjectedHealth - currentMaintHealth!}%)
|
||||
</span> */}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs">–</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import Image from 'next/image'
|
||||
import { ReactElement, useEffect, useState } from 'react'
|
||||
|
||||
const LogoWithFallback = ({
|
||||
fallback,
|
||||
alt,
|
||||
src,
|
||||
...props
|
||||
}: {
|
||||
fallback: ReactElement
|
||||
alt: string
|
||||
src: string
|
||||
[x: string]: string | boolean | ReactElement
|
||||
}) => {
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setError(false)
|
||||
}, [src])
|
||||
|
||||
return (
|
||||
<>
|
||||
{!error ? (
|
||||
<Image
|
||||
alt={alt}
|
||||
onError={(e) => {
|
||||
console.warn('error', e)
|
||||
setError(true)
|
||||
}}
|
||||
src={src}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
fallback
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LogoWithFallback
|
|
@ -15,11 +15,11 @@ const MaxAmountButton = ({
|
|||
}) => {
|
||||
return (
|
||||
<LinkButton
|
||||
className={`no-underline ${className}`}
|
||||
className={`font-normal no-underline ${className}`}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="mr-1 font-normal text-th-fgd-4">{label}:</span>
|
||||
<p className="mr-1 text-th-fgd-4">{label}:</p>
|
||||
<span className="font-mono text-th-fgd-2 underline">{value}</span>
|
||||
</LinkButton>
|
||||
)
|
||||
|
|
|
@ -45,7 +45,7 @@ const TabButtons: FunctionComponent<TabButtonsProps> = ({
|
|||
key={`${label}${i}`}
|
||||
onClick={() => onChange(label)}
|
||||
>
|
||||
<span className="">{t(label)} </span>
|
||||
<span className="font-medium">{t(label)} </span>
|
||||
{count ? (
|
||||
<div
|
||||
className={`ml-1.5 rounded ${
|
||||
|
|
|
@ -25,8 +25,6 @@ const PerpMarketsTable = () => {
|
|||
const { theme } = useTheme()
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
// const bids = mangoStore((s) => s.selectedMarket.bidsAccount)
|
||||
// const asks = mangoStore((s) => s.selectedMarket.asksAccount)
|
||||
const rate = usePerpFundingRate()
|
||||
|
||||
return (
|
||||
|
@ -63,15 +61,13 @@ const PerpMarketsTable = () => {
|
|||
const chartData = coingeckoData ? coingeckoData.prices : undefined
|
||||
|
||||
let fundingRate
|
||||
if (
|
||||
rate.isSuccess
|
||||
// && bids instanceof BookSide &&
|
||||
// asks instanceof BookSide
|
||||
) {
|
||||
const marketRate = rate.data.find(
|
||||
if (rate.isSuccess && market instanceof PerpMarket) {
|
||||
const marketRate = rate?.data?.find(
|
||||
(r) => r.market_index === market.perpMarketIndex
|
||||
)
|
||||
fundingRate = `${marketRate?.funding_apr.toFixed(2)}%`
|
||||
fundingRate = marketRate
|
||||
? `${marketRate.funding_rate_hourly.toFixed(4)}%`
|
||||
: '–'
|
||||
} else {
|
||||
fundingRate = '–'
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ const TokenStats = () => {
|
|||
const { t } = useTranslation(['common', 'token'])
|
||||
const actions = mangoStore((s) => s.actions)
|
||||
const tokenStats = mangoStore((s) => s.tokenStats.data)
|
||||
const initialStatsLoad = mangoStore((s) => s.tokenStats.initialLoad)
|
||||
const loadingStats = mangoStore((s) => s.tokenStats.loading)
|
||||
const [showTokenDetails, setShowTokenDetails] = useState('')
|
||||
const { group } = useMangoGroup()
|
||||
|
@ -47,7 +48,7 @@ const TokenStats = () => {
|
|||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (group && !tokenStats.length) {
|
||||
if (group && !initialStatsLoad) {
|
||||
actions.fetchTokenStats()
|
||||
}
|
||||
}, [group])
|
||||
|
@ -113,15 +114,15 @@ const TokenStats = () => {
|
|||
|
||||
return (
|
||||
<ContentBox hideBorder hidePadding>
|
||||
<div className="grid grid-cols-2 gap-x-6 border-b border-th-bkg-3 text-5xl">
|
||||
<div className="grid grid-cols-2">
|
||||
{loadingStats ? (
|
||||
<div className="col-span-2 py-4 px-6 md:col-span-1">
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1">
|
||||
<SheenLoader className="flex flex-1">
|
||||
<div className="h-96 w-full rounded-lg bg-th-bkg-2" />
|
||||
</SheenLoader>
|
||||
</div>
|
||||
) : totalValues.length ? (
|
||||
<div className="col-span-2 py-4 px-6 md:col-span-1">
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1">
|
||||
<DetailedAreaChart
|
||||
data={totalValues.concat([
|
||||
{
|
||||
|
@ -131,6 +132,7 @@ const TokenStats = () => {
|
|||
},
|
||||
])}
|
||||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${x.toFixed(2)}`}
|
||||
title={t('total-deposit-value')}
|
||||
|
@ -140,13 +142,13 @@ const TokenStats = () => {
|
|||
</div>
|
||||
) : null}
|
||||
{loadingStats ? (
|
||||
<div className="col-span-2 border-t border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:border-t-0 md:pl-6">
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6">
|
||||
<SheenLoader className="flex flex-1">
|
||||
<div className="h-96 w-full rounded-lg bg-th-bkg-2" />
|
||||
</SheenLoader>
|
||||
</div>
|
||||
) : totalValues.length ? (
|
||||
<div className="col-span-2 border-t border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:border-t-0 md:pl-6">
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6">
|
||||
<DetailedAreaChart
|
||||
data={totalValues.concat([
|
||||
{
|
||||
|
@ -156,6 +158,7 @@ const TokenStats = () => {
|
|||
},
|
||||
])}
|
||||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${x.toFixed(2)}`}
|
||||
title={t('total-borrow-value')}
|
||||
|
|
|
@ -45,7 +45,6 @@ import SwapSlider from './SwapSlider'
|
|||
import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
|
||||
import MaxSwapAmount from './MaxSwapAmount'
|
||||
import PercentageSelectButtons from './PercentageSelectButtons'
|
||||
import BorrowInfo from './BorrowInfo'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
|
||||
const MAX_DIGITS = 11
|
||||
|
@ -55,6 +54,8 @@ export const withValueLimit = (values: NumberFormatValues): boolean => {
|
|||
: true
|
||||
}
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
const SwapForm = () => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
const [selectedRoute, setSelectedRoute] = useState<RouteInfo>()
|
||||
|
@ -65,7 +66,6 @@ const SwapForm = () => {
|
|||
const { group } = useMangoGroup()
|
||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'Slider')
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
const {
|
||||
margin: useMargin,
|
||||
slippage,
|
||||
|
@ -152,7 +152,7 @@ const SwapForm = () => {
|
|||
useEffect(() => {
|
||||
setAmountInFormValue('')
|
||||
setAmountOutFormValue('')
|
||||
}, [useMargin])
|
||||
}, [useMargin, setAmountInFormValue, setAmountOutFormValue])
|
||||
|
||||
const handleAmountInChange = useCallback(
|
||||
(e: NumberFormatValues, info: SourceInfo) => {
|
||||
|
@ -164,7 +164,7 @@ const SwapForm = () => {
|
|||
}
|
||||
setAmountInFormValue(e.value)
|
||||
},
|
||||
[swapMode]
|
||||
[swapMode, setAmountInFormValue]
|
||||
)
|
||||
|
||||
const handleAmountOutChange = useCallback(
|
||||
|
@ -177,7 +177,7 @@ const SwapForm = () => {
|
|||
}
|
||||
setAmountOutFormValue(e.value)
|
||||
},
|
||||
[swapMode]
|
||||
[swapMode, setAmountOutFormValue]
|
||||
)
|
||||
|
||||
const handleTokenInSelect = useCallback((mintAddress: string) => {
|
||||
|
@ -215,7 +215,7 @@ const SwapForm = () => {
|
|||
setAnimateSwitchArrow(
|
||||
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1
|
||||
)
|
||||
}, [set, amountOutAsDecimal, amountInAsDecimal])
|
||||
}, [setAmountInFormValue, amountOutAsDecimal, amountInAsDecimal])
|
||||
|
||||
const maintProjectedHealth = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
|
@ -427,11 +427,11 @@ const SwapForm = () => {
|
|||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
{group && inputBank ? (
|
||||
<div className="pt-4">
|
||||
<div className="mt-4">
|
||||
<TokenVaultWarnings bank={inputBank} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="space-y-2">
|
||||
<div className="mt-4 space-y-2">
|
||||
<div id="swap-step-four">
|
||||
<HealthImpact maintProjectedHealth={maintProjectedHealth} />
|
||||
</div>
|
||||
|
@ -441,18 +441,14 @@ const SwapForm = () => {
|
|||
Est. {t('swap:slippage')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="text-right font-mono text-sm text-th-fgd-3">
|
||||
<p className="text-right font-mono text-sm text-th-fgd-2">
|
||||
{selectedRoute?.priceImpactPct
|
||||
? selectedRoute?.priceImpactPct * 100 < 0.1
|
||||
? '<0.1%'
|
||||
: `${(selectedRoute?.priceImpactPct * 100).toFixed(2)}%`
|
||||
: '0.00%'}
|
||||
: '–'}
|
||||
</p>
|
||||
</div>
|
||||
<BorrowInfo
|
||||
amount={parseFloat(amountInFormValue)}
|
||||
useMargin={useMargin}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ContentBox>
|
||||
|
|
|
@ -125,13 +125,15 @@ const SwapFormTokenList = ({
|
|||
group &&
|
||||
mangoAccount &&
|
||||
outputBank &&
|
||||
inputBank &&
|
||||
type === 'input'
|
||||
) {
|
||||
const filteredSortedTokens = mangoTokens
|
||||
.map((token) => {
|
||||
const max = getTokenInMax(
|
||||
mangoAccount,
|
||||
token.address,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
group,
|
||||
useMargin
|
||||
)
|
||||
|
|
|
@ -35,6 +35,7 @@ import { SOUND_SETTINGS_KEY } from 'utils/constants'
|
|||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { Howl } from 'howler'
|
||||
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
|
||||
type JupiterRouteInfoProps = {
|
||||
amountIn: Decimal
|
||||
|
@ -218,20 +219,20 @@ const SwapReviewRouteInfo = ({
|
|||
}
|
||||
}
|
||||
|
||||
const borrowAmount = useMemo(() => {
|
||||
const [balance, borrowAmount] = useMemo(() => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const inputBank = mangoStore.getState().swap.inputBank
|
||||
if (!mangoAccount || !inputBank) return 0
|
||||
if (!mangoAccount || !inputBank) return [0, 0]
|
||||
|
||||
const remainingBalance =
|
||||
mangoAccount.getTokenDepositsUi(inputBank) - amountIn.toNumber()
|
||||
const x = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||
console.log('borrowAmount', x)
|
||||
return x
|
||||
const balance = mangoAccount.getTokenDepositsUi(inputBank)
|
||||
const remainingBalance = balance - amountIn.toNumber()
|
||||
const borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||
|
||||
return [balance, borrowAmount]
|
||||
}, [amountIn])
|
||||
|
||||
const coinGeckoPriceDifference = useMemo(() => {
|
||||
return amountOut
|
||||
return amountOut?.toNumber()
|
||||
? floorToDecimal(
|
||||
amountIn
|
||||
.div(amountOut)
|
||||
|
@ -240,14 +241,13 @@ const SwapReviewRouteInfo = ({
|
|||
coingeckoPrices?.inputCoingeckoPrice
|
||||
)
|
||||
)
|
||||
.div(amountIn.div(amountOut)),
|
||||
.div(amountIn.div(amountOut))
|
||||
.mul(100),
|
||||
1
|
||||
)
|
||||
: new Decimal(0)
|
||||
}, [coingeckoPrices, amountIn, amountOut])
|
||||
|
||||
console.log('selectedRoute', selectedRoute)
|
||||
|
||||
return routes?.length && selectedRoute && outputTokenInfo && amountOut ? (
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<div>
|
||||
|
@ -361,7 +361,24 @@ const SwapReviewRouteInfo = ({
|
|||
{borrowAmount ? (
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-sm text-th-fgd-3">{t('borrow-amount')}</p>
|
||||
<Tooltip
|
||||
content={
|
||||
balance
|
||||
? t('swap:tooltip-borrow-balance', {
|
||||
balance: balance,
|
||||
borrowAmount: borrowAmount,
|
||||
token: inputTokenInfo?.symbol,
|
||||
})
|
||||
: t('swap:tooltip-borrow-no-balance', {
|
||||
borrowAmount: borrowAmount,
|
||||
token: inputTokenInfo?.symbol,
|
||||
})
|
||||
}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3">
|
||||
{t('borrow-amount')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="text-right font-mono text-sm text-th-fgd-1">
|
||||
~{formatFixedDecimals(borrowAmount)}{' '}
|
||||
<span className="font-body tracking-wide">
|
||||
|
@ -370,7 +387,7 @@ const SwapReviewRouteInfo = ({
|
|||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-sm text-th-fgd-3">Borrow Fee</p>
|
||||
<p className="text-sm text-th-fgd-3">{t('borrow-fee')}</p>
|
||||
<p className="text-right font-mono text-sm text-th-fgd-1">
|
||||
~
|
||||
{formatFixedDecimals(
|
||||
|
@ -383,6 +400,19 @@ const SwapReviewRouteInfo = ({
|
|||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Tooltip content={t('tooltip-borrow-rate')}>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3">
|
||||
{t('borrow-rate')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="text-right font-mono text-sm text-th-down">
|
||||
{formatDecimal(inputBank!.getBorrowRateUi(), 2, {
|
||||
fixed: true,
|
||||
})}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div className="flex justify-between">
|
||||
|
@ -551,7 +581,7 @@ const SwapReviewRouteInfo = ({
|
|||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-center justify-center pb-6">
|
||||
<div className="flex items-center justify-center py-6">
|
||||
<Button
|
||||
onClick={onSwap}
|
||||
className="flex w-full items-center justify-center text-base"
|
||||
|
|
|
@ -11,8 +11,6 @@ import {
|
|||
Text,
|
||||
} from 'recharts'
|
||||
import FlipNumbers from 'react-flip-numbers'
|
||||
|
||||
import LineChartIcon from '../icons/LineChartIcon'
|
||||
import ContentBox from '../shared/ContentBox'
|
||||
import { formatFixedDecimals } from '../../utils/numbers'
|
||||
import SheenLoader from '../shared/SheenLoader'
|
||||
|
@ -29,6 +27,8 @@ import useJupiterSwapData from './useJupiterSwapData'
|
|||
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'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -70,6 +70,7 @@ const CustomizedLabel = ({
|
|||
}
|
||||
|
||||
const SwapTokenChart = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { inputCoingeckoId, outputCoingeckoId } = useJupiterSwapData()
|
||||
const [baseTokenId, setBaseTokenId] = useState(inputCoingeckoId)
|
||||
|
@ -153,9 +154,6 @@ const SwapTokenChart = () => {
|
|||
<SheenLoader className="mt-2 w-[148px] rounded-md">
|
||||
<div className="h-[18px] bg-th-bkg-2" />
|
||||
</SheenLoader>
|
||||
<SheenLoader className="mt-4 w-full rounded-md">
|
||||
<div className="h-[308px] bg-th-bkg-2" />
|
||||
</SheenLoader>
|
||||
</>
|
||||
) : chartData?.length && baseTokenId && quoteTokenId ? (
|
||||
<div className="relative">
|
||||
|
@ -184,11 +182,11 @@ const SwapTokenChart = () => {
|
|||
) : null}
|
||||
{mouseData ? (
|
||||
<>
|
||||
<div className="mb-1 flex flex-col text-5xl font-bold text-th-fgd-1 md:flex-row md:items-end">
|
||||
<div className="mb-1 flex flex-col font-display text-5xl text-th-fgd-1 md:flex-row md:items-end">
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={32}
|
||||
width={35}
|
||||
play
|
||||
numbers={formatFixedDecimals(mouseData['price'])}
|
||||
/>
|
||||
|
@ -207,11 +205,11 @@ const SwapTokenChart = () => {
|
|||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mb-1 flex flex-col text-5xl font-bold text-th-fgd-1 md:flex-row md:items-end">
|
||||
<div className="mb-1 flex flex-col font-display text-5xl text-th-fgd-1 md:flex-row md:items-end">
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={32}
|
||||
width={35}
|
||||
play
|
||||
numbers={formatFixedDecimals(
|
||||
chartData[chartData.length - 1]['price']
|
||||
|
@ -317,10 +315,10 @@ const SwapTokenChart = () => {
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-4 flex h-full items-center justify-center rounded-lg bg-th-bkg-2 p-4 text-th-fgd-3 md:mt-0">
|
||||
<div className="mt-4 flex h-full items-center justify-center p-4 text-th-fgd-3 md:mt-0">
|
||||
<div className="">
|
||||
<LineChartIcon className="mx-auto h-10 w-10 text-th-fgd-4" />
|
||||
<p className="text-th-fgd-4">Chart not available</p>
|
||||
<NoSymbolIcon className="mx-auto mb-1 h-6 w-6 text-th-fgd-4" />
|
||||
<p className="text-th-fgd-4">{t('chart-unavailable')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -51,8 +51,6 @@ const useJupiterRoutes = ({
|
|||
slippage,
|
||||
swapMode,
|
||||
}: useJupiterPropTypes) => {
|
||||
console.log('amount: ', amount)
|
||||
|
||||
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
|
||||
|
||||
const decimals =
|
||||
|
|
|
@ -5,6 +5,7 @@ import mangoStore from '@store/mangoStore'
|
|||
import { floorToDecimal } from '../../utils/numbers'
|
||||
import useMangoAccount from '../../hooks/useMangoAccount'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
|
||||
export const getMaxWithdrawForBank = (
|
||||
group: Group,
|
||||
|
@ -26,12 +27,13 @@ export const getMaxWithdrawForBank = (
|
|||
|
||||
export const getTokenInMax = (
|
||||
mangoAccount: MangoAccount,
|
||||
inputTokenAddress: string,
|
||||
inputMint: PublicKey,
|
||||
outputMint: PublicKey,
|
||||
group: Group,
|
||||
useMargin: boolean
|
||||
) => {
|
||||
const outputBank = mangoStore.getState().swap.outputBank
|
||||
const inputBank = group.banksMapByMint.get(inputTokenAddress)?.[0]
|
||||
const inputBank = group.getFirstBankByMint(inputMint)
|
||||
const outputBank = group.getFirstBankByMint(outputMint)
|
||||
|
||||
if (!group || !inputBank || !mangoAccount || !outputBank) {
|
||||
return {
|
||||
|
@ -54,11 +56,21 @@ export const getTokenInMax = (
|
|||
group,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
1
|
||||
inputBank.uiPrice / outputBank.uiPrice
|
||||
),
|
||||
inputBank.mintDecimals
|
||||
)
|
||||
|
||||
console.log(
|
||||
'getMaxSourceUiForTokenSwap',
|
||||
mangoAccount.getMaxSourceUiForTokenSwap(
|
||||
group,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
inputBank.uiPrice / outputBank.uiPrice
|
||||
)
|
||||
)
|
||||
|
||||
const inputBankVaultBalance = floorToDecimal(
|
||||
group.getTokenVaultBalanceByMintUi(inputBank.mint),
|
||||
inputBank.mintDecimals
|
||||
|
@ -88,12 +100,14 @@ export const useTokenMax = (useMargin = true) => {
|
|||
const { mangoAccount } = useMangoAccount()
|
||||
const { group } = useMangoGroup()
|
||||
const inputBank = mangoStore((s) => s.swap.inputBank)
|
||||
const outputBank = mangoStore((s) => s.swap.outputBank)
|
||||
|
||||
const tokenInMax = useMemo(() => {
|
||||
if (mangoAccount && group && inputBank) {
|
||||
if (mangoAccount && group && inputBank && outputBank) {
|
||||
return getTokenInMax(
|
||||
mangoAccount,
|
||||
inputBank?.mint.toString(),
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
group,
|
||||
useMargin
|
||||
)
|
||||
|
@ -104,7 +118,7 @@ export const useTokenMax = (useMargin = true) => {
|
|||
amountWithBorrow: new Decimal(0),
|
||||
decimals: 6,
|
||||
}
|
||||
}, [mangoAccount, group, useMargin, inputBank])
|
||||
}, [mangoAccount, group, useMargin, inputBank, outputBank])
|
||||
|
||||
return tokenInMax
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@ const ChartTabs = ({ token }: { token: string }) => {
|
|||
const [activeDepositsTab, setActiveDepositsTab] = useState('token:deposits')
|
||||
const [activeBorrowsTab, setActiveBorrowsTab] = useState('token:borrows')
|
||||
const tokenStats = mangoStore((s) => s.tokenStats.data)
|
||||
const initialStatsLoad = mangoStore((s) => s.tokenStats.initialLoad)
|
||||
const loadingTokenStats = mangoStore((s) => s.tokenStats.loading)
|
||||
const actions = mangoStore((s) => s.actions)
|
||||
const { group } = useMangoGroup()
|
||||
|
||||
useEffect(() => {
|
||||
if (group && !tokenStats.length) {
|
||||
if (group && !initialStatsLoad) {
|
||||
actions.fetchTokenStats()
|
||||
}
|
||||
}, [group])
|
||||
|
@ -53,11 +54,12 @@ const ChartTabs = ({ token }: { token: string }) => {
|
|||
['token:deposit-rates', 0],
|
||||
]}
|
||||
/>
|
||||
<div className="px-6 pt-5 pb-2">
|
||||
<div className="px-6 py-4">
|
||||
{activeDepositsTab === 'token:deposits' ? (
|
||||
<DetailedAreaChart
|
||||
data={statsHistory}
|
||||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
// domain={[0, 'dataMax']}
|
||||
loading={loadingTokenStats}
|
||||
small
|
||||
|
@ -70,6 +72,7 @@ const ChartTabs = ({ token }: { token: string }) => {
|
|||
<DetailedAreaChart
|
||||
data={statsHistory}
|
||||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
// domain={[0, 'dataMax']}
|
||||
loading={loadingTokenStats}
|
||||
hideChange
|
||||
|
@ -95,11 +98,12 @@ const ChartTabs = ({ token }: { token: string }) => {
|
|||
['token:borrow-rates', 0],
|
||||
]}
|
||||
/>
|
||||
<div className="px-6 pt-5 pb-2">
|
||||
<div className="px-6 py-4">
|
||||
{activeBorrowsTab === 'token:borrows' ? (
|
||||
<DetailedAreaChart
|
||||
data={statsHistory}
|
||||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
// domain={[0, 'dataMax']}
|
||||
loading={loadingTokenStats}
|
||||
small
|
||||
|
@ -112,6 +116,7 @@ const ChartTabs = ({ token }: { token: string }) => {
|
|||
<DetailedAreaChart
|
||||
data={statsHistory}
|
||||
daysToShow={'999'}
|
||||
heightClass="h-64"
|
||||
// domain={[0, 'dataMax']}
|
||||
loading={loadingTokenStats}
|
||||
small
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import { Bank } from '@blockworks-foundation/mango-v4'
|
||||
import Change from '@components/shared/Change'
|
||||
import ChartRangeButtons from '@components/shared/ChartRangeButtons'
|
||||
import {
|
||||
ArrowSmallUpIcon,
|
||||
ArrowTrendingUpIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { ArrowSmallUpIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { useCoingecko } from 'hooks/useCoingecko'
|
||||
import parse from 'html-react-parser'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import { formatFixedDecimals } from 'utils/numbers'
|
||||
const PriceChart = dynamic(() => import('@components/token/PriceChart'), {
|
||||
ssr: false,
|
||||
|
@ -51,6 +48,14 @@ const CoingeckoStats = ({
|
|||
const [chartData, setChartData] = useState<{ prices: any[] } | null>(null)
|
||||
const [loadChartData, setLoadChartData] = useState(true)
|
||||
const { isLoading: loadingPrices, data: coingeckoPrices } = useCoingecko()
|
||||
const descWidthRef = useRef<any>(null)
|
||||
|
||||
const [width, setWidth] = useState<number>(0)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!descWidthRef.current) return
|
||||
setWidth(descWidthRef.current.clientWidth)
|
||||
}, [])
|
||||
|
||||
const handleDaysToShow = async (days: string) => {
|
||||
if (days !== '1') {
|
||||
|
@ -113,20 +118,23 @@ const CoingeckoStats = ({
|
|||
className={`${
|
||||
showFullDesc ? 'h-full' : 'h-5'
|
||||
} max-w-[720px] overflow-hidden`}
|
||||
ref={descWidthRef}
|
||||
>
|
||||
{parse(coingeckoData.description.en)}
|
||||
</p>
|
||||
<span
|
||||
className="default-transition flex cursor-pointer items-end font-normal underline hover:text-th-fgd-2 md:hover:no-underline"
|
||||
onClick={() => setShowFullDesc(!showFullDesc)}
|
||||
>
|
||||
{showFullDesc ? 'Less' : 'More'}
|
||||
<ArrowSmallUpIcon
|
||||
className={`h-5 w-5 ${
|
||||
showFullDesc ? 'rotate-360' : 'rotate-180'
|
||||
} default-transition`}
|
||||
/>
|
||||
</span>
|
||||
{width === 720 ? (
|
||||
<span
|
||||
className="default-transition ml-4 flex cursor-pointer items-end font-normal underline hover:text-th-fgd-2 md:hover:no-underline"
|
||||
onClick={() => setShowFullDesc(!showFullDesc)}
|
||||
>
|
||||
{showFullDesc ? 'Less' : 'More'}
|
||||
<ArrowSmallUpIcon
|
||||
className={`h-5 w-5 ${
|
||||
showFullDesc ? 'rotate-360' : 'rotate-180'
|
||||
} default-transition`}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{!loadingChart ? (
|
||||
|
@ -148,7 +156,7 @@ const CoingeckoStats = ({
|
|||
</>
|
||||
) : bank?.name === 'USDC' || bank?.name === 'USDT' ? null : (
|
||||
<div className="flex flex-col items-center p-6">
|
||||
<ArrowTrendingUpIcon className="h-5 w-5 text-th-fgd-3" />
|
||||
<NoSymbolIcon className="mb-1 h-6 w-6 text-th-fgd-4" />
|
||||
<p className="mb-0 text-th-fgd-4">{t('token:chart-unavailable')}</p>
|
||||
</div>
|
||||
)
|
||||
|
@ -163,16 +171,30 @@ const CoingeckoStats = ({
|
|||
<div className="flex justify-between pb-4">
|
||||
<p>{t('token:market-cap')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(market_cap.usd, true)}{' '}
|
||||
{market_cap?.usd ? (
|
||||
formatFixedDecimals(market_cap.usd, true)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}{' '}
|
||||
<span className="text-th-fgd-4">
|
||||
#{coingeckoData.market_cap_rank}
|
||||
{coingeckoData.market_cap_rank
|
||||
? `#${coingeckoData.market_cap_rank}`
|
||||
: ''}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between border-t border-th-bkg-3 py-4">
|
||||
<p>{t('token:volume')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(total_volume.usd, true)}
|
||||
{total_volume?.usd ? (
|
||||
formatFixedDecimals(total_volume.usd, true)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between border-t border-th-bkg-3 py-4">
|
||||
|
@ -180,9 +202,17 @@ const CoingeckoStats = ({
|
|||
<div className="flex flex-col items-end">
|
||||
<div className="flex items-center font-mono text-th-fgd-2">
|
||||
<span className="mr-2">
|
||||
{formatFixedDecimals(ath.usd, true)}
|
||||
{ath?.usd ? (
|
||||
formatFixedDecimals(ath.usd, true)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<Change change={ath_change_percentage.usd} suffix="%" />
|
||||
{ath_change_percentage.usd ? (
|
||||
<Change change={ath_change_percentage.usd} suffix="%" />
|
||||
) : null}
|
||||
</div>
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
{dayjs(ath_date.usd).format('MMM, D, YYYY')} (
|
||||
|
@ -195,7 +225,13 @@ const CoingeckoStats = ({
|
|||
<div className="flex flex-col items-end">
|
||||
<div className="flex items-center font-mono text-th-fgd-2">
|
||||
<span className="mr-2">
|
||||
{formatFixedDecimals(atl.usd, true)}
|
||||
{atl?.usd ? (
|
||||
formatFixedDecimals(atl.usd, true)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<Change change={atl_change_percentage.usd} suffix="%" />
|
||||
</div>
|
||||
|
@ -211,7 +247,13 @@ const CoingeckoStats = ({
|
|||
<div className="flex justify-between pb-4">
|
||||
<p>{t('token:fdv')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(fully_diluted_valuation.usd, true)}
|
||||
{fully_diluted_valuation?.usd ? (
|
||||
formatFixedDecimals(fully_diluted_valuation.usd, true)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -224,7 +266,13 @@ const CoingeckoStats = ({
|
|||
>
|
||||
<p>{t('token:circulating-supply')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(circulating_supply)}
|
||||
{circulating_supply ? (
|
||||
formatFixedDecimals(circulating_supply)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
|
@ -234,14 +282,26 @@ const CoingeckoStats = ({
|
|||
>
|
||||
<p>{t('token:total-supply')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(total_supply)}
|
||||
{total_supply ? (
|
||||
formatFixedDecimals(total_supply)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
{max_supply ? (
|
||||
<div className="flex justify-between border-t border-th-bkg-3 pt-4">
|
||||
<p>{t('token:max-supply')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(max_supply)}
|
||||
{max_supply ? (
|
||||
formatFixedDecimals(max_supply)
|
||||
) : (
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('unavailable')}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
|
|
@ -116,11 +116,11 @@ const TokenPage = () => {
|
|||
<h1 className="text-base font-normal">{bank.name}</h1>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-end space-x-3 text-5xl font-bold text-th-fgd-1">
|
||||
<div className="flex items-end space-x-3 font-display text-5xl text-th-fgd-1">
|
||||
{animationSettings['number-scroll'] ? (
|
||||
<FlipNumbers
|
||||
height={48}
|
||||
width={32}
|
||||
width={35}
|
||||
play
|
||||
delay={0.05}
|
||||
duration={1}
|
||||
|
@ -192,9 +192,7 @@ const TokenPage = () => {
|
|||
<p className="mb-2">
|
||||
{t('token:token-not-found-desc', { token: token })}
|
||||
</p>
|
||||
<Link href="/">
|
||||
<a>{t('token:go-to-account')}</a>
|
||||
</Link>
|
||||
<Link href="/">{t('token:go-to-account')}</Link>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -5,7 +5,6 @@ import useOraclePrice from 'hooks/useOraclePrice'
|
|||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useMemo } from 'react'
|
||||
import { formatFixedDecimals } from 'utils/numbers'
|
||||
import MarketSelectDropdown from './MarketSelectDropdown'
|
||||
import PerpFundingRate from './PerpFundingRate'
|
||||
|
||||
|
@ -42,7 +41,7 @@ const AdvancedMarketHeader = () => {
|
|||
<div className="text-xs text-th-fgd-4">{t('trade:oracle-price')}</div>
|
||||
<div className="font-mono text-xs text-th-fgd-2">
|
||||
{oraclePrice ? (
|
||||
`$${formatFixedDecimals(oraclePrice)}`
|
||||
`$${oraclePrice}`
|
||||
) : (
|
||||
<span className="text-th-fgd-4">–</span>
|
||||
)}
|
||||
|
|
|
@ -22,7 +22,7 @@ import NumberFormat, {
|
|||
} from 'react-number-format'
|
||||
import { notify } from 'utils/notifications'
|
||||
import SpotSlider from './SpotSlider'
|
||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
||||
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||
import Image from 'next/legacy/image'
|
||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import Loading from '@components/shared/Loading'
|
||||
|
@ -38,18 +38,20 @@ import useJupiterMints from 'hooks/useJupiterMints'
|
|||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import Slippage from './Slippage'
|
||||
import { formatFixedDecimals, getDecimalCount } from 'utils/numbers'
|
||||
import LogoWithFallback from '@components/shared/LogoWithFallback'
|
||||
|
||||
const TABS: [string, number][] = [
|
||||
['Limit', 0],
|
||||
['Market', 0],
|
||||
]
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
const AdvancedTradeForm = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const set = mangoStore.getState().set
|
||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
const { selectedMarket, price: marketPrice } = useSelectedMarket()
|
||||
const { selectedMarket, price: oraclePrice } = useSelectedMarket()
|
||||
const [useMargin, setUseMargin] = useState(true)
|
||||
const [placingOrder, setPlacingOrder] = useState(false)
|
||||
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'Slider')
|
||||
|
@ -92,14 +94,11 @@ const AdvancedTradeForm = () => {
|
|||
return ''
|
||||
}, [quoteSymbol, mangoTokens])
|
||||
|
||||
const setTradeType = useCallback(
|
||||
(tradeType: 'Limit' | 'Market') => {
|
||||
set((s) => {
|
||||
s.tradeForm.tradeType = tradeType
|
||||
})
|
||||
},
|
||||
[set]
|
||||
)
|
||||
const setTradeType = useCallback((tradeType: 'Limit' | 'Market') => {
|
||||
set((s) => {
|
||||
s.tradeForm.tradeType = tradeType
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handlePriceChange = useCallback(
|
||||
(e: NumberFormatValues, info: SourceInfo) => {
|
||||
|
@ -113,7 +112,7 @@ const AdvancedTradeForm = () => {
|
|||
}
|
||||
})
|
||||
},
|
||||
[set]
|
||||
[]
|
||||
)
|
||||
|
||||
const handleBaseSizeChange = useCallback(
|
||||
|
@ -122,8 +121,8 @@ const AdvancedTradeForm = () => {
|
|||
set((s) => {
|
||||
const price =
|
||||
s.tradeForm.tradeType === 'Market'
|
||||
? marketPrice
|
||||
: parseFloat(s.tradeForm.price)
|
||||
? oraclePrice
|
||||
: Number(s.tradeForm.price)
|
||||
|
||||
s.tradeForm.baseSize = e.value
|
||||
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
||||
|
@ -133,7 +132,7 @@ const AdvancedTradeForm = () => {
|
|||
}
|
||||
})
|
||||
},
|
||||
[set, marketPrice]
|
||||
[oraclePrice]
|
||||
)
|
||||
|
||||
const handleQuoteSizeChange = useCallback(
|
||||
|
@ -142,8 +141,8 @@ const AdvancedTradeForm = () => {
|
|||
set((s) => {
|
||||
const price =
|
||||
s.tradeForm.tradeType === 'Market'
|
||||
? marketPrice
|
||||
: parseFloat(s.tradeForm.price)
|
||||
? oraclePrice
|
||||
: Number(s.tradeForm.price)
|
||||
|
||||
s.tradeForm.quoteSize = e.value
|
||||
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
||||
|
@ -153,59 +152,81 @@ const AdvancedTradeForm = () => {
|
|||
}
|
||||
})
|
||||
},
|
||||
[set, marketPrice]
|
||||
[oraclePrice]
|
||||
)
|
||||
|
||||
const handlePostOnlyChange = useCallback(
|
||||
(postOnly: boolean) => {
|
||||
const handlePostOnlyChange = useCallback((postOnly: boolean) => {
|
||||
set((s) => {
|
||||
s.tradeForm.postOnly = postOnly
|
||||
if (s.tradeForm.ioc === true) {
|
||||
s.tradeForm.ioc = !postOnly
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleIocChange = useCallback((ioc: boolean) => {
|
||||
set((s) => {
|
||||
s.tradeForm.ioc = ioc
|
||||
if (s.tradeForm.postOnly === true) {
|
||||
s.tradeForm.postOnly = !ioc
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleSetSide = useCallback((side: 'buy' | 'sell') => {
|
||||
set((s) => {
|
||||
s.tradeForm.side = side
|
||||
})
|
||||
}, [])
|
||||
|
||||
/*
|
||||
* Updates the limit price on page load
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (tradeForm.price === undefined) {
|
||||
const group = mangoStore.getState().group
|
||||
if (!group || !oraclePrice) return
|
||||
|
||||
set((s) => {
|
||||
s.tradeForm.postOnly = postOnly
|
||||
if (s.tradeForm.ioc === true) {
|
||||
s.tradeForm.ioc = !postOnly
|
||||
}
|
||||
s.tradeForm.price = oraclePrice.toString()
|
||||
})
|
||||
},
|
||||
[set]
|
||||
)
|
||||
|
||||
const handleIocChange = useCallback(
|
||||
(ioc: boolean) => {
|
||||
set((s) => {
|
||||
s.tradeForm.ioc = ioc
|
||||
if (s.tradeForm.postOnly === true) {
|
||||
s.tradeForm.postOnly = !ioc
|
||||
}
|
||||
})
|
||||
},
|
||||
[set]
|
||||
)
|
||||
|
||||
const handleSetSide = useCallback(
|
||||
(side: 'buy' | 'sell') => {
|
||||
set((s) => {
|
||||
s.tradeForm.side = side
|
||||
})
|
||||
},
|
||||
[set]
|
||||
)
|
||||
}
|
||||
}, [oraclePrice, tradeForm.price])
|
||||
|
||||
/*
|
||||
* Updates the price and the quote size when a Market order is selected
|
||||
*/
|
||||
useEffect(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!group || !marketPrice || !selectedMarket) return
|
||||
let tickSize: number
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
const market = group.getSerum3ExternalMarket(
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
tickSize = market.tickSize
|
||||
} else {
|
||||
tickSize = selectedMarket.tickSize
|
||||
if (
|
||||
tradeForm.tradeType === 'Market' &&
|
||||
oraclePrice &&
|
||||
selectedMarket &&
|
||||
group
|
||||
) {
|
||||
let tickSize: number
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
const market = group.getSerum3ExternalMarket(
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
tickSize = market.tickSize
|
||||
} else {
|
||||
tickSize = selectedMarket.tickSize
|
||||
}
|
||||
if (!isNaN(parseFloat(tradeForm.baseSize))) {
|
||||
const baseSize = new Decimal(tradeForm.baseSize)?.toNumber()
|
||||
const quoteSize = baseSize * oraclePrice
|
||||
set((s) => {
|
||||
s.tradeForm.price = oraclePrice.toFixed(getDecimalCount(tickSize))
|
||||
s.tradeForm.quoteSize = quoteSize.toFixed(getDecimalCount(tickSize))
|
||||
})
|
||||
} else {
|
||||
set((s) => {
|
||||
s.tradeForm.price = oraclePrice.toFixed(getDecimalCount(tickSize))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
set((s) => {
|
||||
s.tradeForm.price = marketPrice.toFixed(getDecimalCount(tickSize))
|
||||
})
|
||||
}, [set, marketPrice, selectedMarket])
|
||||
}, [oraclePrice, selectedMarket, tradeForm])
|
||||
|
||||
const handlePlaceOrder = useCallback(async () => {
|
||||
const client = mangoStore.getState().client
|
||||
|
@ -218,11 +239,15 @@ const AdvancedTradeForm = () => {
|
|||
if (!group || !mangoAccount) return
|
||||
setPlacingOrder(true)
|
||||
try {
|
||||
const baseSize = new Decimal(tradeForm.baseSize).toNumber()
|
||||
let price = new Decimal(tradeForm.price).toNumber()
|
||||
const baseSize = Number(tradeForm.baseSize)
|
||||
let price = Number(tradeForm.price)
|
||||
if (tradeForm.tradeType === 'Market') {
|
||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||
price = calculateMarketPrice(orderbook, baseSize, tradeForm.side)
|
||||
price = calculateLimitPriceForMarketOrder(
|
||||
orderbook,
|
||||
baseSize,
|
||||
tradeForm.side
|
||||
)
|
||||
}
|
||||
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
|
@ -243,7 +268,6 @@ const AdvancedTradeForm = () => {
|
|||
Date.now(),
|
||||
10
|
||||
)
|
||||
actions.reloadMangoAccount()
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
|
@ -272,7 +296,6 @@ const AdvancedTradeForm = () => {
|
|||
undefined,
|
||||
undefined
|
||||
)
|
||||
actions.reloadMangoAccount()
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
|
@ -291,43 +314,53 @@ const AdvancedTradeForm = () => {
|
|||
} finally {
|
||||
setPlacingOrder(false)
|
||||
}
|
||||
}, [t])
|
||||
}, [])
|
||||
|
||||
const maintProjectedHealth = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
if (!mangoAccount || !group || !Number(tradeForm.baseSize)) return 100
|
||||
if (
|
||||
!mangoAccount ||
|
||||
!group ||
|
||||
!Number.isInteger(Number(tradeForm.baseSize))
|
||||
)
|
||||
return 100
|
||||
|
||||
let simulatedHealthRatio = 0
|
||||
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
simulatedHealthRatio =
|
||||
tradeForm.side === 'sell'
|
||||
? mangoAccount.simHealthRatioWithSerum3AskUiChanges(
|
||||
group,
|
||||
parseFloat(tradeForm.baseSize),
|
||||
selectedMarket.serumMarketExternal,
|
||||
HealthType.maint
|
||||
)
|
||||
: mangoAccount.simHealthRatioWithSerum3BidUiChanges(
|
||||
group,
|
||||
parseFloat(tradeForm.baseSize),
|
||||
selectedMarket.serumMarketExternal,
|
||||
HealthType.maint
|
||||
)
|
||||
} else if (selectedMarket instanceof PerpMarket) {
|
||||
simulatedHealthRatio =
|
||||
tradeForm.side === 'sell'
|
||||
? mangoAccount.simHealthRatioWithPerpAskUiChanges(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex,
|
||||
parseFloat(tradeForm.baseSize)
|
||||
)
|
||||
: mangoAccount.simHealthRatioWithPerpBidUiChanges(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex,
|
||||
parseFloat(tradeForm.baseSize)
|
||||
)
|
||||
try {
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
simulatedHealthRatio =
|
||||
tradeForm.side === 'sell'
|
||||
? mangoAccount.simHealthRatioWithSerum3AskUiChanges(
|
||||
group,
|
||||
parseFloat(tradeForm.baseSize),
|
||||
selectedMarket.serumMarketExternal,
|
||||
HealthType.maint
|
||||
)
|
||||
: mangoAccount.simHealthRatioWithSerum3BidUiChanges(
|
||||
group,
|
||||
parseFloat(tradeForm.baseSize),
|
||||
selectedMarket.serumMarketExternal,
|
||||
HealthType.maint
|
||||
)
|
||||
} else if (selectedMarket instanceof PerpMarket) {
|
||||
simulatedHealthRatio =
|
||||
tradeForm.side === 'sell'
|
||||
? mangoAccount.simHealthRatioWithPerpAskUiChanges(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex,
|
||||
parseFloat(tradeForm.baseSize),
|
||||
Number(tradeForm.price)
|
||||
)
|
||||
: mangoAccount.simHealthRatioWithPerpBidUiChanges(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex,
|
||||
parseFloat(tradeForm.baseSize),
|
||||
Number(tradeForm.price)
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Error calculating projected health: ', e)
|
||||
}
|
||||
|
||||
return simulatedHealthRatio > 100
|
||||
|
@ -399,11 +432,16 @@ const AdvancedTradeForm = () => {
|
|||
</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="default-transition flex items-center rounded-md rounded-b-none border border-th-input-border bg-th-input-bkg p-2 text-sm font-bold text-th-fgd-1 md:hover:z-10 md:hover:border-th-input-border-hover lg:text-base">
|
||||
{baseLogoURI ? (
|
||||
<Image alt="" width="24" height="24" src={baseLogoURI} />
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
)}
|
||||
<LogoWithFallback
|
||||
alt=""
|
||||
className="z-10 drop-shadow-md"
|
||||
width={'24'}
|
||||
height={'24'}
|
||||
src={baseLogoURI || `/icons/${baseSymbol?.toLowerCase()}.svg`}
|
||||
fallback={
|
||||
<QuestionMarkCircleIcon className={`h-5 w-5 text-th-fgd-3`} />
|
||||
}
|
||||
/>
|
||||
<NumberFormat
|
||||
inputMode="decimal"
|
||||
thousandSeparator=","
|
||||
|
|
|
@ -4,6 +4,7 @@ import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
|||
import Image from 'next/legacy/image'
|
||||
import { useMemo } from 'react'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import LogoWithFallback from '@components/shared/LogoWithFallback'
|
||||
|
||||
const MarketLogos = ({
|
||||
market,
|
||||
|
@ -40,12 +41,13 @@ const MarketLogos = ({
|
|||
return {
|
||||
baseLogoURI,
|
||||
quoteLogoURI,
|
||||
name: market.name.split(/-|\//)[0],
|
||||
}
|
||||
}, [group, mangoTokens, market])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative mr-1.5 ${small ? 'h-4' : 'h-5'} ${
|
||||
className={`relative ${small ? 'mr-1.5 h-4' : 'mr-2 h-5'} ${
|
||||
market instanceof Serum3Market
|
||||
? small
|
||||
? 'w-[27px]'
|
||||
|
@ -55,20 +57,19 @@ const MarketLogos = ({
|
|||
: 'w-[20px]'
|
||||
}`}
|
||||
>
|
||||
<div className="absolute left-0 top-0">
|
||||
{logos.baseLogoURI ? (
|
||||
<Image
|
||||
alt=""
|
||||
className="z-10 drop-shadow-md"
|
||||
width={small ? '16' : '20'}
|
||||
height={small ? '16' : '20'}
|
||||
src={logos.baseLogoURI}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon
|
||||
className={`${small ? 'h-4 w-4' : 'h-5 w-5'} text-th-fgd-3`}
|
||||
/>
|
||||
)}
|
||||
<div className="absolute left-0 top-0 z-10">
|
||||
<LogoWithFallback
|
||||
alt=""
|
||||
className="drop-shadow-md"
|
||||
width={small ? '16' : '20'}
|
||||
height={small ? '16' : '20'}
|
||||
src={logos.baseLogoURI || `/icons/${logos?.name?.toLowerCase()}.svg`}
|
||||
fallback={
|
||||
<QuestionMarkCircleIcon
|
||||
className={`${small ? 'h-4 w-4' : 'h-5 w-5'} text-th-fgd-3`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute right-0 top-0">
|
||||
{logos.quoteLogoURI && market instanceof Serum3Market ? (
|
||||
|
|
|
@ -1,61 +1,95 @@
|
|||
import {
|
||||
PerpMarket,
|
||||
PerpOrder,
|
||||
PerpOrderType,
|
||||
Serum3Market,
|
||||
Serum3OrderType,
|
||||
Serum3SelfTradeBehavior,
|
||||
Serum3Side,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
import Input from '@components/forms/Input'
|
||||
import { IconButton } from '@components/shared/Button'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import SideBadge from '@components/shared/SideBadge'
|
||||
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import { LinkIcon, NoSymbolIcon, TrashIcon } from '@heroicons/react/20/solid'
|
||||
import {
|
||||
CheckIcon,
|
||||
LinkIcon,
|
||||
NoSymbolIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { Order } from '@project-serum/serum/lib/market'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { ChangeEvent, useCallback, useState } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { formatFixedDecimals, getDecimalCount } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import MarketLogos from './MarketLogos'
|
||||
import TableMarketName from './TableMarketName'
|
||||
|
||||
const OpenOrders = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { connected } = useWallet()
|
||||
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
|
||||
const [cancelId, setCancelId] = useState<string>('')
|
||||
const [modifyOrderId, setModifyOrderId] = useState<string | undefined>(
|
||||
undefined
|
||||
)
|
||||
const [modifiedOrderSize, setModifiedOrderSize] = useState('')
|
||||
const [modifiedOrderPrice, setModifiedOrderPrice] = useState('')
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
const findSerum3MarketPkInOpenOrders = (o: Order): string | undefined => {
|
||||
const openOrders = mangoStore.getState().mangoAccount.openOrders
|
||||
let foundedMarketPk: string | undefined = undefined
|
||||
for (const [marketPk, orders] of Object.entries(openOrders)) {
|
||||
for (const order of orders) {
|
||||
if (order.orderId.eq(o.orderId)) {
|
||||
foundedMarketPk = marketPk
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return foundedMarketPk
|
||||
}
|
||||
|
||||
const handleCancelSerumOrder = useCallback(
|
||||
async (o: Order) => {
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const selectedMarket = mangoStore.getState().selectedMarket.current
|
||||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (!group || !mangoAccount) return
|
||||
const marketPk = findSerum3MarketPkInOpenOrders(o)
|
||||
if (!marketPk) return
|
||||
const market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(marketPk)
|
||||
)
|
||||
|
||||
setCancelId(o.orderId.toString())
|
||||
try {
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
const tx = await client.serum3CancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
selectedMarket!.serumMarketExternal,
|
||||
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
o.orderId
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
}
|
||||
const tx = await client.serum3CancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
market!.serumMarketExternal,
|
||||
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
o.orderId
|
||||
)
|
||||
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
} catch (e: any) {
|
||||
console.error('Error canceling', e)
|
||||
notify({
|
||||
|
@ -68,7 +102,74 @@ const OpenOrders = () => {
|
|||
setCancelId('')
|
||||
}
|
||||
},
|
||||
[t]
|
||||
[t, openOrders]
|
||||
)
|
||||
|
||||
const modifyOrder = useCallback(
|
||||
async (o: PerpOrder | Order) => {
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const actions = mangoStore.getState().actions
|
||||
const baseSize = modifiedOrderSize ? Number(modifiedOrderSize) : o.size
|
||||
const price = modifiedOrderPrice ? Number(modifiedOrderPrice) : o.price
|
||||
if (!group || !mangoAccount) return
|
||||
try {
|
||||
let tx = ''
|
||||
if (o instanceof PerpOrder) {
|
||||
tx = await client.modifyPerpOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
o.perpMarketIndex,
|
||||
o.orderId,
|
||||
o.side,
|
||||
price,
|
||||
Math.abs(baseSize),
|
||||
undefined, // maxQuoteQuantity
|
||||
Date.now(),
|
||||
PerpOrderType.limit,
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
} else {
|
||||
const marketPk = findSerum3MarketPkInOpenOrders(o)
|
||||
if (!marketPk) return
|
||||
const market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(marketPk)
|
||||
)
|
||||
tx = await client.modifySerum3Order(
|
||||
group,
|
||||
o.orderId,
|
||||
mangoAccount,
|
||||
market.serumMarketExternal,
|
||||
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
price,
|
||||
baseSize,
|
||||
Serum3SelfTradeBehavior.decrementTake,
|
||||
Serum3OrderType.limit,
|
||||
Date.now(),
|
||||
10
|
||||
)
|
||||
}
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
} catch (e: any) {
|
||||
console.error('Error canceling', e)
|
||||
notify({
|
||||
title: t('trade:cancel-order-error'),
|
||||
description: e.message,
|
||||
txid: e.txid,
|
||||
type: 'error',
|
||||
})
|
||||
} finally {
|
||||
cancelEditOrderForm()
|
||||
}
|
||||
},
|
||||
[t, modifiedOrderSize, modifiedOrderPrice]
|
||||
)
|
||||
|
||||
const handleCancelPerpOrder = useCallback(
|
||||
|
@ -76,25 +177,22 @@ const OpenOrders = () => {
|
|||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const selectedMarket = mangoStore.getState().selectedMarket.current
|
||||
const actions = mangoStore.getState().actions
|
||||
if (!group || !mangoAccount) return
|
||||
setCancelId(o.orderId.toString())
|
||||
try {
|
||||
if (selectedMarket instanceof PerpMarket) {
|
||||
const tx = await client.perpCancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
o.perpMarketIndex,
|
||||
o.orderId
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
}
|
||||
const tx = await client.perpCancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
o.perpMarketIndex,
|
||||
o.orderId
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
txid: tx,
|
||||
})
|
||||
} catch (e: any) {
|
||||
console.error('Error canceling', e)
|
||||
notify({
|
||||
|
@ -110,18 +208,29 @@ const OpenOrders = () => {
|
|||
[t]
|
||||
)
|
||||
|
||||
const showEditOrderForm = (order: Order | PerpOrder) => {
|
||||
setModifyOrderId(order.orderId.toString())
|
||||
setModifiedOrderSize(order.size.toString())
|
||||
setModifiedOrderPrice(order.price.toString())
|
||||
}
|
||||
const cancelEditOrderForm = () => {
|
||||
setModifyOrderId(undefined)
|
||||
setModifiedOrderSize('')
|
||||
setModifiedOrderPrice('')
|
||||
}
|
||||
|
||||
return connected ? (
|
||||
Object.values(openOrders).flat().length ? (
|
||||
showTableView ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">{t('market')}</Th>
|
||||
<Th className="text-right">{t('trade:side')}</Th>
|
||||
<Th className="text-right">{t('trade:size')}</Th>
|
||||
<Th className="text-right">{t('price')}</Th>
|
||||
<Th className="text-right">{t('value')}</Th>
|
||||
<Th className="text-right"></Th>
|
||||
<Th className="w-[16.67%] text-left">{t('market')}</Th>
|
||||
<Th className="w-[16.67%] text-right">{t('trade:side')}</Th>
|
||||
<Th className="w-[16.67%] text-right">{t('trade:size')}</Th>
|
||||
<Th className="w-[16.67%] text-right">{t('price')}</Th>
|
||||
<Th className="w-[16.67%] text-right">{t('value')}</Th>
|
||||
<Th className="w-[16.67%] text-right"></Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -158,53 +267,106 @@ const OpenOrders = () => {
|
|||
key={`${o.side}${o.size}${o.price}`}
|
||||
className="my-1 p-2"
|
||||
>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market!} />
|
||||
{market?.name}
|
||||
</div>
|
||||
<Td className="w-[16.67%]">
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<Td className="w-[16.67%] text-right">
|
||||
<SideBadge side={o.side} />
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
{o.size.toLocaleString(undefined, {
|
||||
maximumFractionDigits: getDecimalCount(minOrderSize),
|
||||
})}
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<span className="font-mono">
|
||||
{o.price.toLocaleString(undefined, {
|
||||
minimumFractionDigits: getDecimalCount(tickSize),
|
||||
maximumFractionDigits: getDecimalCount(tickSize),
|
||||
})}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quoteSymbol}
|
||||
</span>
|
||||
</span>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
{modifyOrderId !== o.orderId.toString() ? (
|
||||
<>
|
||||
<Td className="w-[16.67%] text-right font-mono">
|
||||
{o.size.toLocaleString(undefined, {
|
||||
maximumFractionDigits:
|
||||
getDecimalCount(minOrderSize),
|
||||
})}
|
||||
</Td>
|
||||
<Td className="w-[16.67%] text-right">
|
||||
<span className="font-mono">
|
||||
{o.price.toLocaleString(undefined, {
|
||||
minimumFractionDigits:
|
||||
getDecimalCount(tickSize),
|
||||
maximumFractionDigits:
|
||||
getDecimalCount(tickSize),
|
||||
})}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quoteSymbol}
|
||||
</span>
|
||||
</span>
|
||||
</Td>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Td className="w-[16.67%]">
|
||||
<Input
|
||||
className="default-transition h-7 w-full rounded-none border-b-2 border-l-0 border-r-0 border-t-0 border-th-bkg-4 bg-transparent px-0 text-right hover:border-th-fgd-3 focus:border-th-active focus:outline-none"
|
||||
type="text"
|
||||
value={modifiedOrderSize}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setModifiedOrderSize(e.target.value)
|
||||
}
|
||||
/>
|
||||
</Td>
|
||||
<Td className="w-[16.67%]">
|
||||
<Input
|
||||
autoFocus
|
||||
className="default-transition h-7 w-full rounded-none border-b-2 border-l-0 border-r-0 border-t-0 border-th-bkg-4 bg-transparent px-0 text-right hover:border-th-fgd-3 focus:border-th-active focus:outline-none"
|
||||
type="text"
|
||||
value={modifiedOrderPrice}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setModifiedOrderPrice(e.target.value)
|
||||
}
|
||||
/>
|
||||
</Td>
|
||||
</>
|
||||
)}
|
||||
<Td className="w-[16.67%] text-right">
|
||||
{formatFixedDecimals(o.size * o.price, true)}
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('cancel')}>
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() =>
|
||||
o instanceof PerpOrder
|
||||
? handleCancelPerpOrder(o)
|
||||
: handleCancelSerumOrder(o)
|
||||
}
|
||||
size="small"
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Td className="w-[16.67%]">
|
||||
<div className="flex justify-end space-x-2">
|
||||
{modifyOrderId !== o.orderId.toString() ? (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={() => showEditOrderForm(o)}
|
||||
size="small"
|
||||
>
|
||||
<PencilIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
<Tooltip content={t('cancel')}>
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() =>
|
||||
o instanceof PerpOrder
|
||||
? handleCancelPerpOrder(o)
|
||||
: handleCancelSerumOrder(o)
|
||||
}
|
||||
size="small"
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={() => modifyOrder(o)}
|
||||
size="small"
|
||||
>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={cancelEditOrderForm}
|
||||
size="small"
|
||||
>
|
||||
<XMarkIcon className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
</TrBody>
|
||||
|
@ -251,16 +413,9 @@ const OpenOrders = () => {
|
|||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={`${o.side}${o.size}${o.price}`}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<div>
|
||||
<div className="mb-0.5 flex items-center space-x-2">
|
||||
<p className="whitespace-nowrap text-sm text-th-fgd-1">
|
||||
{market.name}
|
||||
</p>
|
||||
<SideBadge side={o.side} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<TableMarketName market={market} />
|
||||
<SideBadge side={o.side} />
|
||||
</div>
|
||||
<div className="flex items-center space-x-3 pl-8">
|
||||
<div className="text-right">
|
||||
|
@ -283,20 +438,36 @@ const OpenOrders = () => {
|
|||
{quoteSymbol}
|
||||
</p>
|
||||
</div>
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() =>
|
||||
o instanceof PerpOrder
|
||||
? handleCancelPerpOrder(o)
|
||||
: handleCancelSerumOrder(o)
|
||||
}
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
<div className="flex items-center space-x-2">
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() =>
|
||||
o instanceof PerpOrder
|
||||
? handleCancelPerpOrder(o)
|
||||
: handleCancelSerumOrder(o)
|
||||
}
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<PencilIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
disabled={cancelId === o.orderId.toString()}
|
||||
onClick={() =>
|
||||
o instanceof PerpOrder
|
||||
? handleCancelPerpOrder(o)
|
||||
: handleCancelSerumOrder(o)
|
||||
}
|
||||
>
|
||||
{cancelId === o.orderId.toString() ? (
|
||||
<Loading className="h-4 w-4" />
|
||||
) : (
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -324,10 +324,10 @@ const Orderbook = () => {
|
|||
|
||||
if (!market || !group) return
|
||||
|
||||
let previousBidInfo: AccountInfo<Buffer> | null = null
|
||||
let previousAskInfo: AccountInfo<Buffer> | null = null
|
||||
let bidSubscriptionId: number
|
||||
let askSubscriptionId: number
|
||||
let previousBidInfo: AccountInfo<Buffer> | undefined = undefined
|
||||
let previousAskInfo: AccountInfo<Buffer> | undefined = undefined
|
||||
let bidSubscriptionId: number | undefined = undefined
|
||||
let askSubscriptionId: number | undefined = undefined
|
||||
|
||||
const bidsPk =
|
||||
market instanceof Market ? market['_decoded'].bids : market.bids
|
||||
|
@ -391,10 +391,10 @@ const Orderbook = () => {
|
|||
)
|
||||
}
|
||||
return () => {
|
||||
if (bidSubscriptionId) {
|
||||
if (typeof bidSubscriptionId !== 'undefined') {
|
||||
connection.removeAccountChangeListener(bidSubscriptionId)
|
||||
}
|
||||
if (askSubscriptionId) {
|
||||
if (typeof askSubscriptionId !== 'undefined') {
|
||||
connection.removeAccountChangeListener(askSubscriptionId)
|
||||
}
|
||||
}
|
||||
|
@ -572,6 +572,7 @@ const OrderbookRow = ({
|
|||
minOrderSize: number
|
||||
tickSize: number
|
||||
}) => {
|
||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||
const element = useRef<HTMLDivElement>(null)
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
|
@ -608,14 +609,29 @@ const OrderbookRow = ({
|
|||
const set = mangoStore.getState().set
|
||||
set((state) => {
|
||||
state.tradeForm.price = formattedPrice.toFixed()
|
||||
if (tradeForm.baseSize && tradeForm.tradeType === 'Limit') {
|
||||
const quoteSize = floorToDecimal(
|
||||
formattedPrice.mul(new Decimal(tradeForm.baseSize)),
|
||||
getDecimalCount(tickSize)
|
||||
)
|
||||
state.tradeForm.quoteSize = quoteSize.toFixed()
|
||||
}
|
||||
})
|
||||
}, [formattedPrice])
|
||||
}, [formattedPrice, tradeForm])
|
||||
|
||||
// const handleSizeClick = () => {
|
||||
// set((state) => {
|
||||
// state.tradeForm.baseSize = Number(formattedSize)
|
||||
// })
|
||||
// }
|
||||
const handleSizeClick = useCallback(() => {
|
||||
const set = mangoStore.getState().set
|
||||
set((state) => {
|
||||
state.tradeForm.baseSize = formattedSize.toString()
|
||||
if (formattedSize && tradeForm.price) {
|
||||
const quoteSize = floorToDecimal(
|
||||
formattedSize.mul(new Decimal(tradeForm.price)),
|
||||
getDecimalCount(tickSize)
|
||||
)
|
||||
state.tradeForm.quoteSize = quoteSize.toString()
|
||||
}
|
||||
})
|
||||
}, [formattedSize, tradeForm])
|
||||
|
||||
const groupingDecimalCount = useMemo(
|
||||
() => getDecimalCount(grouping),
|
||||
|
@ -632,11 +648,13 @@ const OrderbookRow = ({
|
|||
<div
|
||||
className={`relative flex h-[24px] cursor-pointer justify-between border-b border-b-th-bkg-1 text-sm`}
|
||||
ref={element}
|
||||
onClick={handlePriceClick}
|
||||
>
|
||||
<>
|
||||
<div className="flex w-full items-center justify-between text-th-fgd-3 hover:bg-th-bkg-2">
|
||||
<div className="flex w-full justify-start pl-2">
|
||||
<div className="flex h-full w-full items-center justify-between text-th-fgd-3 hover:bg-th-bkg-2">
|
||||
<div
|
||||
className="flex h-full w-full items-center justify-start pl-2 hover:underline"
|
||||
onClick={handleSizeClick}
|
||||
>
|
||||
<div
|
||||
style={{ fontFeatureSettings: 'zero 1' }}
|
||||
className={`z-10 w-full text-right font-mono text-xs ${
|
||||
|
@ -647,8 +665,13 @@ const OrderbookRow = ({
|
|||
{formattedSize.toFixed(minOrderSizeDecimals)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`z-10 w-full pr-4 text-right font-mono text-xs`}>
|
||||
{formattedPrice.toFixed(groupingDecimalCount)}
|
||||
<div
|
||||
className={`z-10 flex h-full w-full items-center pr-4 hover:underline`}
|
||||
onClick={handlePriceClick}
|
||||
>
|
||||
<div className="w-full text-right font-mono text-xs">
|
||||
{formattedPrice.toFixed(groupingDecimalCount)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,13 +4,14 @@ import mangoStore from '@store/mangoStore'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
// import { notify } from 'utils/notifications'
|
||||
|
||||
const PerpButtonGroup = () => {
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const tradeFormPrice = mangoStore((s) => s.tradeForm.price)
|
||||
|
||||
const leverageMax = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
|
@ -21,23 +22,25 @@ const PerpButtonGroup = () => {
|
|||
if (side === 'buy') {
|
||||
return mangoAccount.getMaxQuoteForPerpBidUi(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex
|
||||
selectedMarket.perpMarketIndex,
|
||||
Number(tradeFormPrice)
|
||||
)
|
||||
} else {
|
||||
return mangoAccount.getMaxBaseForPerpAskUi(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex
|
||||
selectedMarket.perpMarketIndex,
|
||||
Number(tradeFormPrice)
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('PerpSlider: ', e)
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Error calculating max leverage.',
|
||||
})
|
||||
console.error('Error calculating max leverage perp btn grp: ', e)
|
||||
// notify({
|
||||
// type: 'error',
|
||||
// title: 'Error calculating max leverage.',
|
||||
// })
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount])
|
||||
}, [side, selectedMarket, mangoAccount, tradeFormPrice])
|
||||
|
||||
const handleSizePercentage = useCallback(
|
||||
(percentage: string) => {
|
||||
|
@ -50,9 +53,7 @@ const PerpButtonGroup = () => {
|
|||
s.tradeForm.quoteSize = size.toString()
|
||||
|
||||
if (Number(s.tradeForm.price)) {
|
||||
s.tradeForm.baseSize = (
|
||||
size / parseFloat(s.tradeForm.price)
|
||||
).toString()
|
||||
s.tradeForm.baseSize = (size / Number(s.tradeForm.price)).toString()
|
||||
} else {
|
||||
s.tradeForm.baseSize = ''
|
||||
}
|
||||
|
@ -61,13 +62,13 @@ const PerpButtonGroup = () => {
|
|||
|
||||
if (Number(s.tradeForm.price)) {
|
||||
s.tradeForm.quoteSize = (
|
||||
size * parseFloat(s.tradeForm.price)
|
||||
size * Number(s.tradeForm.price)
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
[side, selectedMarket, mangoAccount]
|
||||
[leverageMax]
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -27,31 +27,33 @@ export const usePerpFundingRate = () => {
|
|||
}
|
||||
)
|
||||
|
||||
return res
|
||||
return Array.isArray(res?.data) ? res : { isSuccess: false, data: null }
|
||||
}
|
||||
|
||||
const PerpFundingRate = () => {
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const rate = usePerpFundingRate()
|
||||
|
||||
// const bids = mangoStore((s) => s.selectedMarket.bidsAccount)
|
||||
// const asks = mangoStore((s) => s.selectedMarket.asksAccount)
|
||||
|
||||
const fundingRate = useMemo(() => {
|
||||
if (rate.isSuccess && selectedMarket instanceof PerpMarket) {
|
||||
const marketRate = rate?.data.find(
|
||||
const marketRate = rate?.data?.find(
|
||||
(r) => r.market_index === selectedMarket.perpMarketIndex
|
||||
)
|
||||
return marketRate?.funding_rate_hourly
|
||||
}
|
||||
}, [rate])
|
||||
}, [rate, selectedMarket])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="font-mono text-xs text-th-fgd-2">
|
||||
{selectedMarket instanceof PerpMarket && fundingRate
|
||||
? fundingRate.toFixed(4)
|
||||
: '-'}
|
||||
%
|
||||
{selectedMarket instanceof PerpMarket && fundingRate ? (
|
||||
`${fundingRate.toFixed(4)}%`
|
||||
) : (
|
||||
<span className="text-th-fgd-4">-</span>
|
||||
)}
|
||||
</div>
|
||||
{/* <div className="font-mono text-xs text-th-fgd-2">
|
||||
{selectedMarket instanceof PerpMarket &&
|
||||
|
|
|
@ -4,7 +4,6 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
|||
import { LinkIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import Decimal from 'decimal.js'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -14,9 +13,9 @@ import {
|
|||
numberFormat,
|
||||
trimDecimals,
|
||||
} from 'utils/numbers'
|
||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
||||
import MarketLogos from './MarketLogos'
|
||||
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||
import PerpSideBadge from './PerpSideBadge'
|
||||
import TableMarketName from './TableMarketName'
|
||||
|
||||
const PerpPositions = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
|
@ -29,10 +28,14 @@ const PerpPositions = () => {
|
|||
const tradeForm = mangoStore.getState().tradeForm
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
let price = new Decimal(tradeForm.price).toNumber()
|
||||
let price = Number(tradeForm.price)
|
||||
if (tradeForm.tradeType === 'Market') {
|
||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||
price = calculateMarketPrice(orderbook, positionSize, tradeForm.side)
|
||||
price = calculateLimitPriceForMarketOrder(
|
||||
orderbook,
|
||||
positionSize,
|
||||
tradeForm.side
|
||||
)
|
||||
}
|
||||
const newSide = positionSize > 0 ? 'sell' : 'buy'
|
||||
|
||||
|
@ -64,8 +67,8 @@ const PerpPositions = () => {
|
|||
<Th className="text-right">{t('trade:size')}</Th>
|
||||
<Th className="text-right">{t('notional')}</Th>
|
||||
<Th className="text-right">{t('trade:entry-price')}</Th>
|
||||
<Th className="text-right">Redeemable P&L</Th>
|
||||
<Th className="text-right">Realized P&L</Th>
|
||||
<Th className="text-right">Redeemable PnL</Th>
|
||||
<Th className="text-right">Realized PnL</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -84,15 +87,12 @@ const PerpPositions = () => {
|
|||
|
||||
if (!basePosition) return null
|
||||
|
||||
const unsettledPnl = position.getEquityUi(market)
|
||||
const unsettledPnl = position.getEquityUi(group, market)
|
||||
|
||||
return (
|
||||
<TrBody key={`${position.marketIndex}`} className="my-1 p-2">
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
{market?.name}
|
||||
</div>
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<PerpSideBadge basePosition={basePosition} />
|
||||
|
@ -124,7 +124,7 @@ const PerpPositions = () => {
|
|||
<div>
|
||||
$
|
||||
{numberFormat.format(
|
||||
position.getEntryPrice(market).toNumber()
|
||||
position.getAverageEntryPriceUi(market)
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
|
|
|
@ -4,7 +4,7 @@ import mangoStore from '@store/mangoStore'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
// import { notify } from 'utils/notifications'
|
||||
|
||||
const PerpSlider = () => {
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
|
@ -28,20 +28,22 @@ const PerpSlider = () => {
|
|||
if (side === 'buy') {
|
||||
return mangoAccount.getMaxQuoteForPerpBidUi(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex
|
||||
selectedMarket.perpMarketIndex,
|
||||
Number(tradeForm.price)
|
||||
)
|
||||
} else {
|
||||
return mangoAccount.getMaxBaseForPerpAskUi(
|
||||
group,
|
||||
selectedMarket.perpMarketIndex
|
||||
selectedMarket.perpMarketIndex,
|
||||
Number(tradeForm.price)
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('PerpSlider: ', e)
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Error calculating max leverage.',
|
||||
})
|
||||
console.error('Error calculating max leverage for PerpSlider: ', e)
|
||||
// notify({
|
||||
// type: 'error',
|
||||
// title: 'Error calculating max leverage.',
|
||||
// })
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount])
|
||||
|
@ -54,7 +56,7 @@ const PerpSlider = () => {
|
|||
const price =
|
||||
s.tradeForm.tradeType === 'Market'
|
||||
? marketPrice
|
||||
: parseFloat(s.tradeForm.price)
|
||||
: Number(s.tradeForm.price)
|
||||
|
||||
if (s.tradeForm.side === 'buy') {
|
||||
s.tradeForm.quoteSize = val
|
||||
|
|
|
@ -4,6 +4,7 @@ import useMarkPrice from 'hooks/useMarkPrice'
|
|||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useMemo } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { calculateSlippage } from 'utils/tradeForm'
|
||||
|
||||
const Slippage = () => {
|
||||
|
@ -13,27 +14,29 @@ const Slippage = () => {
|
|||
const { selectedMarket } = useSelectedMarket()
|
||||
|
||||
const slippage = useMemo(() => {
|
||||
if (tradeForm.tradeType === 'Market' && markPrice && selectedMarket) {
|
||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||
return calculateSlippage(
|
||||
orderbook,
|
||||
Number(tradeForm.baseSize),
|
||||
tradeForm.side,
|
||||
markPrice
|
||||
)
|
||||
try {
|
||||
if (tradeForm.tradeType === 'Market' && markPrice && selectedMarket) {
|
||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||
return calculateSlippage(
|
||||
orderbook,
|
||||
Number(tradeForm.baseSize),
|
||||
tradeForm.side,
|
||||
markPrice
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
notify({ type: 'info', title: 'Unable to calculate slippage' })
|
||||
}
|
||||
return 0
|
||||
}, [tradeForm, markPrice, selectedMarket])
|
||||
|
||||
return slippage ? (
|
||||
<div className="flex justify-between">
|
||||
<div className="flex justify-between text-xs">
|
||||
<Tooltip content={t('trade:tooltip-slippage')}>
|
||||
<p className="tooltip-underline mr-4 mb-1 text-xs">
|
||||
{t('trade:est-slippage')}
|
||||
</p>
|
||||
<p className="tooltip-underline mr-4">{t('trade:est-slippage')}</p>
|
||||
</Tooltip>
|
||||
<p
|
||||
className={`text-xs ${
|
||||
className={`${
|
||||
slippage <= 1
|
||||
? 'text-th-success'
|
||||
: slippage <= 3
|
||||
|
|
|
@ -4,7 +4,7 @@ import mangoStore from '@store/mangoStore'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
// import { notify } from 'utils/notifications'
|
||||
|
||||
const SpotButtonGroup = () => {
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
|
@ -30,11 +30,11 @@ const SpotButtonGroup = () => {
|
|||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('SpotSlider: ', e)
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Error calculating max leverage.',
|
||||
})
|
||||
console.error('Error calculating max leverage: spot btn group: ', e)
|
||||
// notify({
|
||||
// type: 'error',
|
||||
// title: 'Error calculating max leverage.',
|
||||
// })
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount])
|
||||
|
@ -50,9 +50,7 @@ const SpotButtonGroup = () => {
|
|||
s.tradeForm.quoteSize = size.toString()
|
||||
|
||||
if (Number(s.tradeForm.price)) {
|
||||
s.tradeForm.baseSize = (
|
||||
size / parseFloat(s.tradeForm.price)
|
||||
).toString()
|
||||
s.tradeForm.baseSize = (size / Number(s.tradeForm.price)).toString()
|
||||
} else {
|
||||
s.tradeForm.baseSize = ''
|
||||
}
|
||||
|
@ -61,7 +59,7 @@ const SpotButtonGroup = () => {
|
|||
|
||||
if (Number(s.tradeForm.price)) {
|
||||
s.tradeForm.quoteSize = (
|
||||
size * parseFloat(s.tradeForm.price)
|
||||
size * Number(s.tradeForm.price)
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import mangoStore from '@store/mangoStore'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
// import { notify } from 'utils/notifications'
|
||||
|
||||
const SpotSlider = () => {
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
|
@ -30,11 +30,11 @@ const SpotSlider = () => {
|
|||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('SpotSlider: ', e)
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Error calculating max leverage.',
|
||||
})
|
||||
console.error('Error calculating max leverage for spot slider: ', e)
|
||||
// notify({
|
||||
// type: 'error',
|
||||
// title: 'Error calculating max leverage.',
|
||||
// })
|
||||
return 0
|
||||
}
|
||||
}, [side, selectedMarket, mangoAccount])
|
||||
|
@ -47,7 +47,7 @@ const SpotSlider = () => {
|
|||
const price =
|
||||
s.tradeForm.tradeType === 'Market'
|
||||
? marketPrice
|
||||
: parseFloat(s.tradeForm.price)
|
||||
: Number(s.tradeForm.price)
|
||||
|
||||
if (s.tradeForm.side === 'buy') {
|
||||
s.tradeForm.quoteSize = val
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import Link from 'next/link'
|
||||
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!} />
|
||||
{market.name}
|
||||
</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!} />
|
||||
{market.name}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default TableMarketName
|
|
@ -8,29 +8,27 @@ import { useUnsettledSpotBalances } from 'hooks/useUnsettledSpotBalances'
|
|||
import PerpPositions from './PerpPositions'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
|
||||
|
||||
const TradeInfoTabs = () => {
|
||||
const [selectedTab, setSelectedTab] = useState('balances')
|
||||
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
|
||||
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
|
||||
const unsettledSpotBalances = useUnsettledSpotBalances()
|
||||
const unsettledPerpPositions = useUnsettledPerpPositions()
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.lg : false
|
||||
|
||||
const tabsWithCount: [string, number][] = useMemo(() => {
|
||||
const unsettledTradeCount =
|
||||
Object.values(unsettledSpotBalances).flat().length +
|
||||
perpPositions.filter((p) => !p.basePositionLots.toNumber())?.length
|
||||
unsettledPerpPositions?.length
|
||||
return [
|
||||
['balances', 0],
|
||||
['trade:orders', Object.values(openOrders).flat().length],
|
||||
['trade:unsettled', unsettledTradeCount],
|
||||
[
|
||||
'Positions',
|
||||
perpPositions.filter((p) => p.basePositionLots.toNumber())?.length,
|
||||
],
|
||||
['Positions', unsettledPerpPositions.length],
|
||||
]
|
||||
}, [openOrders, perpPositions, unsettledSpotBalances])
|
||||
}, [openOrders, unsettledPerpPositions, unsettledSpotBalances])
|
||||
|
||||
return (
|
||||
<div className="hide-scroll h-full overflow-y-scroll pb-5">
|
||||
|
@ -48,9 +46,7 @@ const TradeInfoTabs = () => {
|
|||
{selectedTab === 'trade:unsettled' ? (
|
||||
<UnsettledTrades
|
||||
unsettledSpotBalances={unsettledSpotBalances}
|
||||
unsettledPerpPositions={perpPositions.filter(
|
||||
(p) => !p.basePositionLots.toNumber()
|
||||
)}
|
||||
unsettledPerpPositions={unsettledPerpPositions}
|
||||
/>
|
||||
) : null}
|
||||
{selectedTab === 'Positions' ? <PerpPositions /> : null}
|
||||
|
|
|
@ -9,11 +9,11 @@ import Tooltip from '@components/shared/Tooltip'
|
|||
import Loading from '@components/shared/Loading'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import MarketLogos from './MarketLogos'
|
||||
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { PerpMarket, PerpPosition } from '@blockworks-foundation/mango-v4'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import TableMarketName from './TableMarketName'
|
||||
|
||||
const UnsettledTrades = ({
|
||||
unsettledSpotBalances,
|
||||
|
@ -45,7 +45,6 @@ const UnsettledTrades = ({
|
|||
new PublicKey(mktAddress)
|
||||
)
|
||||
actions.fetchOpenOrders()
|
||||
actions.reloadMangoAccount()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Successfully settled funds',
|
||||
|
@ -76,7 +75,7 @@ const UnsettledTrades = ({
|
|||
try {
|
||||
const mangoAccounts = await client.getAllMangoAccounts(group)
|
||||
const perpPosition = mangoAccount.getPerpPosition(market.perpMarketIndex)
|
||||
const mangoAccountPnl = perpPosition?.getEquityUi(market)
|
||||
const mangoAccountPnl = perpPosition?.getEquityUi(group, market)
|
||||
|
||||
if (mangoAccountPnl === undefined)
|
||||
throw new Error('Unable to get account P&L')
|
||||
|
@ -86,8 +85,9 @@ const UnsettledTrades = ({
|
|||
.map((m) => ({
|
||||
mangoAccount: m,
|
||||
pnl:
|
||||
m?.getPerpPosition(market.perpMarketIndex)?.getEquityUi(market) ||
|
||||
0,
|
||||
m
|
||||
?.getPerpPosition(market.perpMarketIndex)
|
||||
?.getEquityUi(group, market) || 0,
|
||||
}))
|
||||
.sort((a, b) => sign * (a.pnl - b.pnl))
|
||||
console.log(
|
||||
|
@ -143,8 +143,7 @@ const UnsettledTrades = ({
|
|||
<thead>
|
||||
<TrHead>
|
||||
<Th className="bg-th-bkg-1 text-left">{t('market')}</Th>
|
||||
<Th className="bg-th-bkg-1 text-right">{t('trade:base')}</Th>
|
||||
<Th className="bg-th-bkg-1 text-right">{t('trade:quote')}</Th>
|
||||
<Th className="bg-th-bkg-1 text-right">{t('trade:amount')}</Th>
|
||||
<Th className="bg-th-bkg-1 text-right" />
|
||||
</TrHead>
|
||||
</thead>
|
||||
|
@ -159,23 +158,24 @@ const UnsettledTrades = ({
|
|||
return (
|
||||
<TrBody key={mktAddress} className="text-sm">
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<span>{market ? market.name : ''}</span>
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<div className="flex justify-end">
|
||||
<div>
|
||||
{unsettledSpotBalances[mktAddress].base || 0.0}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{base}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
{unsettledSpotBalances[mktAddress].quote || 0.0}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quote}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
{unsettledSpotBalances[mktAddress].base || 0.0}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{base}
|
||||
</span>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
{unsettledSpotBalances[mktAddress].quote || 0.0}{' '}
|
||||
<span className="font-body tracking-wide text-th-fgd-4">
|
||||
{quote}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('trade:settle-funds')}>
|
||||
|
@ -202,16 +202,10 @@ const UnsettledTrades = ({
|
|||
return (
|
||||
<TrBody key={position.marketIndex} className="text-sm">
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<span>{market ? market.name : ''}</span>
|
||||
</div>
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
<span></span>
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
{position.getEquityUi(market)}
|
||||
{position.getEquityUi(group, market)}
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex justify-end">
|
||||
|
@ -248,10 +242,7 @@ const UnsettledTrades = ({
|
|||
key={mktAddress}
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<span>{market ? market.name : ''}</span>
|
||||
</div>
|
||||
<TableMarketName market={market} />
|
||||
<div className="flex items-center space-x-3">
|
||||
{unsettledSpotBalances[mktAddress].base ? (
|
||||
<span className="font-mono text-sm">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
import useMangoGroup from './useMangoGroup'
|
||||
import useSelectedMarket from './useSelectedMarket'
|
||||
|
||||
|
@ -9,12 +10,15 @@ export default function useOraclePrice() {
|
|||
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
|
||||
return price && market ? price.toFixed(getDecimalCount(market.tickSize)) : ''
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import useMangoGroup from './useMangoGroup'
|
||||
|
||||
const useUnsettledPerpPositions = () => {
|
||||
const { group } = useMangoGroup()
|
||||
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
|
||||
|
||||
return perpPositions.filter((p) => {
|
||||
const market = group?.getPerpMarketByMarketIndex(p.marketIndex)
|
||||
if (!market) return false
|
||||
return p.getPnl(market).toNumber() > 0
|
||||
})
|
||||
}
|
||||
|
||||
export default useUnsettledPerpPositions
|
|
@ -12,7 +12,7 @@
|
|||
"postinstall": "tar -xzC public -f vendor/charting_library.tgz;tar -xzC public -f vendor/datafeeds.tgz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-v4": "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#ts/test",
|
||||
"@blockworks-foundation/mango-v4": "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#main",
|
||||
"@headlessui/react": "^1.6.6",
|
||||
"@heroicons/react": "^2.0.10",
|
||||
"@project-serum/anchor": "0.25.0",
|
||||
|
|
|
@ -27,6 +27,12 @@ import MangoProvider from '@components/MangoProvider'
|
|||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import EnhancedWalletProvider from '@components/wallet/EnhancedWalletProvider'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { useRouter } from 'next/router'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import Head from 'next/head'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
|
||||
// Do not add hooks to this component that will cause unnecessary rerenders
|
||||
// Top level state hydrating/updating should go in MangoProvider
|
||||
|
@ -65,6 +71,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
<EnhancedWalletProvider>
|
||||
<ThemeProvider defaultTheme="Mango Classic">
|
||||
<ViewportProvider>
|
||||
<PageTitle />
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
|
@ -80,3 +87,37 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
}
|
||||
|
||||
export default appWithTranslation(MyApp)
|
||||
|
||||
const PageTitle = () => {
|
||||
const router = useRouter()
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { group } = useMangoGroup()
|
||||
|
||||
const [market, price] = useMemo(() => {
|
||||
if (!selectedMarket || !group) return []
|
||||
if (selectedMarket instanceof PerpMarket) {
|
||||
return [selectedMarket, selectedMarket.uiPrice]
|
||||
} else {
|
||||
const price = group.getFirstBankByTokenIndex(
|
||||
selectedMarket.baseTokenIndex
|
||||
).uiPrice
|
||||
const market = group.getSerum3ExternalMarket(
|
||||
selectedMarket.serumMarketExternal
|
||||
)
|
||||
return [market, price]
|
||||
}
|
||||
}, [selectedMarket, group])
|
||||
|
||||
const marketTitleString =
|
||||
market && selectedMarket && router.pathname == '/trade'
|
||||
? `${price?.toFixed(getDecimalCount(market.tickSize))} ${
|
||||
selectedMarket.name
|
||||
} - `
|
||||
: ''
|
||||
|
||||
return (
|
||||
<Head>
|
||||
<title>{marketTitleString}Mango Markets</title>
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,14 +6,10 @@ class MyDocument extends Document {
|
|||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<link
|
||||
{/* <link
|
||||
href="https://fonts.googleapis.com/css2?family=Lato:wght@200;300;400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
{/* <link
|
||||
href="https://fonts.googleapis.com/css?family=IBM+Plex+Mono&display=swap"
|
||||
rel="stylesheet"
|
||||
></link> */}
|
||||
/> */}
|
||||
<Script
|
||||
src="/datafeeds/udf/dist/bundle.js"
|
||||
strategy="beforeInteractive"
|
||||
|
|
|
@ -0,0 +1,521 @@
|
|||
import { Bank, toUiDecimals, I80F48 } from '@blockworks-foundation/mango-v4'
|
||||
import ExplorerLink from '@components/shared/ExplorerLink'
|
||||
import { coder } from '@project-serum/anchor/dist/cjs/spl/token'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import type { NextPage } from 'next'
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import Image from 'next/image'
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { Disclosure } from '@headlessui/react'
|
||||
import MarketLogos from '@components/trade/MarketLogos'
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const Dashboard: NextPage = () => {
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
// const client = mangoStore(s => s.client)
|
||||
|
||||
// const handleClickScroll = (id: string) => {
|
||||
// const element = document.getElementById(id)
|
||||
// if (element) {
|
||||
// element.scrollIntoView({ behavior: 'smooth' })
|
||||
// }
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12">
|
||||
<div className="col-span-12 lg:col-span-8 lg:col-start-3 xl:col-span-6 xl:col-start-4">
|
||||
<div className="p-8 pb-20 md:pb-16 lg:p-10">
|
||||
<h1>Dashboard</h1>
|
||||
{group ? (
|
||||
<div className="mt-4">
|
||||
<h2 className="mb-2">Group</h2>
|
||||
<ExplorerLink
|
||||
address={group?.publicKey.toString()}
|
||||
anchorData
|
||||
></ExplorerLink>
|
||||
<h3 className="mt-6 mb-3 text-base text-th-fgd-3">Banks</h3>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{Array.from(group.banksMapByMint).map(([mintAddress, banks]) =>
|
||||
banks.map((bank) => {
|
||||
const logoUri = mangoTokens.length
|
||||
? mangoTokens.find((t) => t.address === mintAddress)
|
||||
?.logoURI
|
||||
: ''
|
||||
return (
|
||||
<Disclosure key={bank.publicKey.toString()}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`default-transition flex w-full items-center justify-between border-t border-th-bkg-3 p-4 md:hover:bg-th-bkg-2 ${
|
||||
open ? 'bg-th-bkg-2' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{logoUri ? (
|
||||
<Image
|
||||
alt=""
|
||||
width="20"
|
||||
height="20"
|
||||
src={logoUri}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
)}
|
||||
<p className="ml-2 text-th-fgd-2">
|
||||
{bank.name} Bank
|
||||
</p>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} h-5 w-5 text-th-fgd-3`}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
<KeyValuePair
|
||||
label="Mint"
|
||||
value={<ExplorerLink address={mintAddress} />}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Bank"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={bank.publicKey.toString()}
|
||||
anchorData
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Vault"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={bank.vault.toString()}
|
||||
anchorData
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={bank.oracle.toString()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Token Index"
|
||||
value={bank.tokenIndex}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle Price"
|
||||
value={`$${bank.uiPrice}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Stable Price"
|
||||
value={`$${group.toUiPrice(
|
||||
I80F48.fromNumber(
|
||||
bank.stablePriceModel.stablePrice
|
||||
),
|
||||
bank.mintDecimals
|
||||
)}`}
|
||||
/>
|
||||
<VaultData bank={bank} />
|
||||
<KeyValuePair
|
||||
label="Loan Fee Rate"
|
||||
value={`${(
|
||||
10000 * bank.loanFeeRate.toNumber()
|
||||
).toFixed(2)} bps`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Loan origination fee rate"
|
||||
value={`${(
|
||||
10000 * bank.loanOriginationFeeRate.toNumber()
|
||||
).toFixed(2)} bps`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Collected fees native"
|
||||
value={bank.collectedFeesNative.toNumber()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Liquidation fee"
|
||||
value={`${(
|
||||
10000 * bank.liquidationFee.toNumber()
|
||||
).toFixed(2)} bps`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Dust"
|
||||
value={bank.dust.toNumber()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Deposits"
|
||||
value={toUiDecimals(
|
||||
bank.indexedDeposits
|
||||
.mul(bank.depositIndex)
|
||||
.toNumber(),
|
||||
bank.mintDecimals
|
||||
)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Borrows"
|
||||
value={toUiDecimals(
|
||||
bank.indexedBorrows
|
||||
.mul(bank.borrowIndex)
|
||||
.toNumber(),
|
||||
bank.mintDecimals
|
||||
)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Avg Utilization"
|
||||
value={`${
|
||||
bank.avgUtilization.toNumber() * 100
|
||||
}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Maint Asset/Liab Weight"
|
||||
value={`${bank.maintAssetWeight.toFixed(2)}/
|
||||
${bank.maintLiabWeight.toFixed(2)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Init Asset/Liab Weight"
|
||||
value={`${bank.initAssetWeight.toFixed(2)}/
|
||||
${bank.initLiabWeight.toFixed(2)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Scaled Init Asset/Liab Weight"
|
||||
value={`${bank
|
||||
.scaledInitAssetWeight()
|
||||
.toFixed(4)}/
|
||||
${bank.scaledInitLiabWeight().toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Deposit weight scale start quote"
|
||||
value={bank.depositWeightScaleStartQuote}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Borrow weight scale start quote"
|
||||
value={bank.borrowWeightScaleStartQuote}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Rate params"
|
||||
value={
|
||||
<span className="text-right">
|
||||
{`${(100 * bank.rate0.toNumber()).toFixed(
|
||||
2
|
||||
)}% @ ${(
|
||||
100 * bank.util0.toNumber()
|
||||
).toFixed()}% util, `}
|
||||
{`${(100 * bank.rate1.toNumber()).toFixed(
|
||||
2
|
||||
)}% @ ${(
|
||||
100 * bank.util1.toNumber()
|
||||
).toFixed()}% util, `}
|
||||
{`${(100 * bank.maxRate.toNumber()).toFixed(
|
||||
2
|
||||
)}% @ 100% util`}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Deposit rate"
|
||||
value={`${bank.getDepositRateUi()}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Borrow rate"
|
||||
value={`${bank.getBorrowRateUi()}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Last index update"
|
||||
value={new Date(
|
||||
1000 * bank.indexLastUpdated.toNumber()
|
||||
).toUTCString()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Last rates updated"
|
||||
value={new Date(
|
||||
1000 * bank.bankRateLastUpdated.toNumber()
|
||||
).toUTCString()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Last stable price updated"
|
||||
value={new Date(
|
||||
1000 *
|
||||
bank.stablePriceModel.lastUpdateTimestamp.toNumber()
|
||||
).toUTCString()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Stable Price: delay interval"
|
||||
value={`${bank.stablePriceModel.delayIntervalSeconds}s`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Stable Price: growth limits"
|
||||
value={`${(
|
||||
100 * bank.stablePriceModel.delayGrowthLimit
|
||||
).toFixed(2)}% delay / ${(
|
||||
100 * bank.stablePriceModel.stableGrowthLimit
|
||||
).toFixed(2)}% stable`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle: Conf Filter"
|
||||
value={`${(
|
||||
100 * bank.oracleConfig.confFilter.toNumber()
|
||||
).toFixed(2)}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle: Max Staleness"
|
||||
value={`${bank.oracleConfig.maxStalenessSlots} slots`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="netBorrowsInWindow / netBorrowLimitPerWindowQuote"
|
||||
value={`${bank.netBorrowsInWindow.toNumber()} / ${bank.netBorrowLimitPerWindowQuote.toNumber()}`}
|
||||
/>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h3 className="mt-6 mb-3 text-base text-th-fgd-3">
|
||||
Perp Markets
|
||||
</h3>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{Array.from(group.perpMarketsMapByOracle).map(
|
||||
([oracle, perpMarket]) => {
|
||||
return (
|
||||
<Disclosure key={oracle.toString()}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`default-transition flex w-full items-center justify-between border-t border-th-bkg-3 p-4 md:hover:bg-th-bkg-2 ${
|
||||
open ? 'bg-th-bkg-2' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={perpMarket} />
|
||||
<p className="text-th-fgd-2">
|
||||
{perpMarket.name}
|
||||
</p>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} h-5 w-5 text-th-fgd-3`}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
<KeyValuePair
|
||||
label="Perp Market"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={perpMarket.publicKey.toString()}
|
||||
anchorData
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Bids"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={perpMarket.bids.toString()}
|
||||
anchorData
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Asks"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={perpMarket.asks.toString()}
|
||||
anchorData
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Event Queue"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={perpMarket.eventQueue.toString()}
|
||||
anchorData
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={perpMarket.oracle.toString()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Perp Market Index"
|
||||
value={perpMarket.perpMarketIndex}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle Price"
|
||||
value={`$${perpMarket.uiPrice}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Stable Price"
|
||||
value={`$${group.toUiPrice(
|
||||
I80F48.fromNumber(
|
||||
perpMarket.stablePriceModel.stablePrice
|
||||
),
|
||||
perpMarket.baseDecimals
|
||||
)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Open Interest"
|
||||
value={`${perpMarket.openInterest} lots`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Lot Sizes"
|
||||
value={`${perpMarket.baseLotSize} base /
|
||||
${perpMarket.quoteLotSize} quote`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Maint Asset/Liab Weight"
|
||||
value={`${perpMarket.maintAssetWeight.toFixed(
|
||||
4
|
||||
)}/
|
||||
${perpMarket.maintLiabWeight.toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Init Asset/Liab Weight"
|
||||
value={`${perpMarket.initAssetWeight.toFixed(
|
||||
4
|
||||
)}/
|
||||
${perpMarket.initLiabWeight.toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Liquidation Fee"
|
||||
value={`${(
|
||||
100 * perpMarket.liquidationFee.toNumber()
|
||||
).toFixed(4)}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Trading Fees"
|
||||
value={`${(
|
||||
10000 * perpMarket.makerFee.toNumber()
|
||||
).toFixed(2)} bps maker / ${(
|
||||
10000 * perpMarket.takerFee.toNumber()
|
||||
).toFixed(2)} bps taker`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Funding Limits"
|
||||
value={`${(
|
||||
100 * perpMarket.minFunding.toNumber()
|
||||
).toFixed(2)}% to ${(
|
||||
100 * perpMarket.maxFunding.toNumber()
|
||||
).toFixed(2)}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Fees Accrued"
|
||||
value={`$${toUiDecimals(
|
||||
perpMarket.feesAccrued,
|
||||
6
|
||||
)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Fees Settled"
|
||||
value={`$${toUiDecimals(
|
||||
perpMarket.feesSettled,
|
||||
6
|
||||
)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle: Conf Filter"
|
||||
value={`${(
|
||||
100 *
|
||||
perpMarket.oracleConfig.confFilter.toNumber()
|
||||
).toFixed(2)}%`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Oracle: Max Staleness"
|
||||
value={`${perpMarket.oracleConfig.maxStalenessSlots} slots`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Trusted Market"
|
||||
value={`${perpMarket.trustedMarket}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Group Insurance Fund"
|
||||
value={`${perpMarket.groupInsuranceFund}`}
|
||||
/>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
'Loading'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const KeyValuePair = ({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string
|
||||
value: number | ReactNode | string
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex justify-between border-t border-th-bkg-3 p-4 xl:py-1.5">
|
||||
<span className="mr-4 whitespace-nowrap text-th-fgd-3">{label}</span>
|
||||
{value}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const VaultData = ({ bank }: { bank: Bank }) => {
|
||||
const [vault, setVault] = useState<any>()
|
||||
const client = mangoStore((s) => s.client)
|
||||
|
||||
const getVaultData = useCallback(async () => {
|
||||
const res = await client.program.provider.connection.getAccountInfo(
|
||||
bank.vault
|
||||
)
|
||||
const v = res?.data ? coder().accounts.decode('token', res.data) : undefined
|
||||
|
||||
setVault(v)
|
||||
}, [bank.vault])
|
||||
|
||||
useEffect(() => {
|
||||
getVaultData()
|
||||
}, [getVaultData])
|
||||
|
||||
return (
|
||||
<KeyValuePair
|
||||
label="Vault balance"
|
||||
value={
|
||||
vault ? toUiDecimals(vault.amount.toNumber(), bank.mintDecimals) : '...'
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
|
@ -0,0 +1,232 @@
|
|||
import ExplorerLink from '@components/shared/ExplorerLink'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import type { NextPage } from 'next'
|
||||
import { ReactNode } from 'react'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import {
|
||||
toUiDecimalsForQuote,
|
||||
HealthType,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const Dashboard: NextPage = () => {
|
||||
const { group } = useMangoGroup()
|
||||
// const { mangoTokens } = useJupiterMints()
|
||||
// const client = mangoStore(s => s.client)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12">
|
||||
<div className="col-span-12 border-b border-th-bkg-3 lg:col-span-8 lg:col-start-3 xl:col-span-6 xl:col-start-4">
|
||||
<div className="p-8 pb-20 md:pb-16 lg:p-10">
|
||||
<h1>Dashboard</h1>
|
||||
{group && mangoAccount ? (
|
||||
<div className="mt-4">
|
||||
<h2 className="mb-6">Mango Account</h2>
|
||||
|
||||
<KeyValuePair
|
||||
label="Address"
|
||||
value={
|
||||
<ExplorerLink address={mangoAccount.publicKey.toString()} />
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Owner"
|
||||
value={<ExplorerLink address={mangoAccount.owner.toString()} />}
|
||||
/>
|
||||
<KeyValuePair label="Name" value={mangoAccount.name.toString()} />
|
||||
<KeyValuePair
|
||||
label="Delegate"
|
||||
value={
|
||||
<ExplorerLink address={mangoAccount.delegate.toString()} />
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Account Number"
|
||||
value={mangoAccount.accountNum.toString()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Being Liquidated"
|
||||
value={mangoAccount.beingLiquidated.toString()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Init Health"
|
||||
value={`$${toUiDecimalsForQuote(
|
||||
mangoAccount.getHealth(group, HealthType.init)
|
||||
).toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Maint Health"
|
||||
value={`$${toUiDecimalsForQuote(
|
||||
mangoAccount.getHealth(group, HealthType.maint)
|
||||
).toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Perp Settle Health"
|
||||
value={`$${toUiDecimalsForQuote(
|
||||
mangoAccount.getPerpSettleHealth(group)
|
||||
).toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Net Deposits"
|
||||
value={`$${toUiDecimalsForQuote(
|
||||
mangoAccount.netDeposits
|
||||
).toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Perp Spot Transfers"
|
||||
value={mangoAccount.perpSpotTransfers.toNumber()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Health Region Begin Init Health"
|
||||
value={mangoAccount.healthRegionBeginInitHealth.toNumber()}
|
||||
/>
|
||||
|
||||
<h3 className="mt-4">Token Active Positions</h3>
|
||||
{mangoAccount.tokensActive().map((token) => {
|
||||
const bank = group.getFirstBankByTokenIndex(token.tokenIndex)
|
||||
return (
|
||||
<div key={token.tokenIndex} className="mt-6">
|
||||
<KeyValuePair label="Token's Bank Name" value={bank.name} />
|
||||
<KeyValuePair
|
||||
label="Balance UI"
|
||||
value={token.balanceUi(bank)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Value at oracle price"
|
||||
value={`$${token.balanceUi(bank) * bank.uiPrice}`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
<h3 className="mt-4">Serum3 Active Positions</h3>
|
||||
{mangoAccount.serum3Active().map((serum) => {
|
||||
const market = group.getSerum3MarketByMarketIndex(
|
||||
serum.marketIndex
|
||||
)
|
||||
const extMarket = group.getSerum3ExternalMarket(
|
||||
market.serumMarketExternal
|
||||
)
|
||||
return (
|
||||
<div key={serum.marketIndex} className="mt-6">
|
||||
<KeyValuePair
|
||||
label="Serum Market"
|
||||
value={
|
||||
<ExplorerLink address={market.publicKey.toString()} />
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Serum External Market"
|
||||
value={
|
||||
<ExplorerLink
|
||||
address={extMarket.publicKey.toString()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<KeyValuePair label="Name" value={market.name} />
|
||||
<KeyValuePair
|
||||
label="Market Index"
|
||||
value={serum.marketIndex}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
<h3 className="mt-4">Perp Active Positions</h3>
|
||||
{mangoAccount.perpActive().map((perp) => {
|
||||
const market = group.getPerpMarketByMarketIndex(
|
||||
perp.marketIndex
|
||||
)
|
||||
return (
|
||||
<div key={perp.marketIndex} className="mt-6">
|
||||
<KeyValuePair
|
||||
label="Market"
|
||||
value={
|
||||
<ExplorerLink address={market.publicKey.toString()} />
|
||||
}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Market Index"
|
||||
value={perp.marketIndex}
|
||||
/>
|
||||
<KeyValuePair label="Name" value={market.name} />
|
||||
<KeyValuePair
|
||||
label="Base Position Ui"
|
||||
value={perp.getBasePositionUi(market)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Quote Position UI"
|
||||
value={`$${toUiDecimalsForQuote(
|
||||
perp.quotePositionNative
|
||||
).toFixed(4)}`}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Quote Running Native"
|
||||
value={perp.quoteRunningNative.toNumber()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Taker Quote Lots"
|
||||
value={perp.takerQuoteLots.toNumber()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Unsettled Funding"
|
||||
value={perp.getUnsettledFunding(market).toNumber()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Equity UI"
|
||||
value={perp.getEquityUi(group, market)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Has open orders"
|
||||
value={perp.hasOpenOrders().toString()}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Avg Entry Price UI"
|
||||
value={perp.getAverageEntryPriceUi(market)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Break even price UI"
|
||||
value={perp.getBreakEvenPriceUi(market)}
|
||||
/>
|
||||
<KeyValuePair
|
||||
label="Pnl"
|
||||
value={perp.getPnl(market).toNumber()}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
'Loading'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const KeyValuePair = ({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string
|
||||
value: number | ReactNode | string
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex justify-between border-t border-th-bkg-3 py-4 xl:py-1.5">
|
||||
<span className="mr-4 whitespace-nowrap text-th-fgd-3">{label}</span>
|
||||
{value}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
|
@ -10,6 +10,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
'onboarding',
|
||||
'profile',
|
||||
'token',
|
||||
'trade',
|
||||
])),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import TokenPage from '@components/token/TokenPage'
|
||||
import { CLUSTER } from '@store/mangoStore'
|
||||
import type { NextPage } from 'next'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import { LISTED_TOKENS } from 'utils/tokens'
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
|
@ -12,8 +12,14 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
}
|
||||
|
||||
export const getStaticPaths = async () => {
|
||||
const paths = LISTED_TOKENS.map((token) => ({
|
||||
params: { token: token },
|
||||
const url =
|
||||
CLUSTER === 'devnet'
|
||||
? 'https://api.jup.ag/api/tokens/devnet'
|
||||
: 'https://cache.jup.ag/tokens'
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
const paths = data.map((t: any) => ({
|
||||
params: { token: t.symbol },
|
||||
}))
|
||||
|
||||
return { paths, fallback: false }
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import {
|
||||
Group,
|
||||
PerpMarket,
|
||||
Serum3Market,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
import TradeAdvancedPage from '@components/trade/TradeAdvancedPage'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import mangoStore, { DEFAULT_TRADE_FORM } from '@store/mangoStore'
|
||||
// import mangoStore from '@store/mangoStore'
|
||||
import type { NextPage } from 'next'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
|
@ -20,6 +25,23 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
}
|
||||
}
|
||||
|
||||
const getOraclePriceForMarket = (
|
||||
group: Group,
|
||||
mkt: Serum3Market | PerpMarket
|
||||
): number => {
|
||||
let price: number
|
||||
if (mkt instanceof Serum3Market) {
|
||||
const baseBank = group.getFirstBankByTokenIndex(mkt.baseTokenIndex)
|
||||
|
||||
price = baseBank.uiPrice
|
||||
} else if (mkt) {
|
||||
price = mkt._uiPrice
|
||||
} else {
|
||||
price = 0
|
||||
}
|
||||
return price
|
||||
}
|
||||
|
||||
const Trade: NextPage = () => {
|
||||
const router = useRouter()
|
||||
const { name: marketName } = router.query
|
||||
|
@ -34,10 +56,15 @@ const Trade: NextPage = () => {
|
|||
const mkt =
|
||||
serumMarkets.find((m) => m.name === marketName) ||
|
||||
perpMarkets.find((m) => m.name === marketName)
|
||||
|
||||
if (mkt) {
|
||||
set((s) => {
|
||||
s.selectedMarket.name = marketName
|
||||
s.selectedMarket.current = mkt
|
||||
s.tradeForm = {
|
||||
...DEFAULT_TRADE_FORM,
|
||||
price: getOraclePriceForMarket(group, mkt).toString(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,23 +1,25 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M5.19951 22.1422C5.39263 21.9491 5.65815 21.8364 5.93977 21.8364H31.4786C31.9453 21.8364 32.1787 22.3997 31.8488 22.7296L26.8038 27.7746C26.6107 27.9677 26.3451 28.0804 26.0635 28.0804H0.52463C0.0579459 28.0804 -0.175396 27.5171 0.154501 27.1872L5.19951 22.1422Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M5.19951 3.30576C5.40067 3.11265 5.6662 3 5.93977 3H31.4786C31.9453 3 32.1787 3.56324 31.8488 3.89314L26.8038 8.93814C26.6107 9.13125 26.3451 9.2439 26.0635 9.2439H0.52463C0.0579459 9.2439 -0.175396 8.68066 0.154501 8.35077L5.19951 3.30576Z" fill="url(#paint1_linear)"/>
|
||||
<path d="M26.8038 12.6637C26.6107 12.4706 26.3451 12.3579 26.0635 12.3579H0.52463C0.0579459 12.3579 -0.175396 12.9211 0.154501 13.251L5.19951 18.2961C5.39263 18.4892 5.65815 18.6018 5.93977 18.6018H31.4786C31.9453 18.6018 32.1787 18.0386 31.8488 17.7087L26.8038 12.6637Z" fill="url(#paint2_linear)"/>
|
||||
<g clip-path="url(#clip0_2582_2263)">
|
||||
<path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16345 24.8366 0 16 0C7.16345 0 0 7.16345 0 16C0 24.8366 7.16345 32 16 32Z" fill="black"/>
|
||||
<path d="M6.15479 12.2139H6.18078C6.17324 12.2168 6.16359 12.2168 6.15479 12.2139Z" fill="#308D8A"/>
|
||||
<path d="M9.24648 19.7458C9.36718 19.6251 9.53314 19.5547 9.70917 19.5547H25.6719C25.9636 19.5547 26.1095 19.9067 25.9033 20.1129L22.75 23.2663C22.6293 23.387 22.4633 23.4574 22.2873 23.4574H6.3245C6.0328 23.4574 5.88695 23.1053 6.09315 22.8991L9.24648 19.7458Z" fill="url(#paint0_linear_2582_2263)"/>
|
||||
<path d="M9.24648 7.97236C9.37221 7.85166 9.53817 7.78125 9.70917 7.78125H25.6719C25.9636 7.78125 26.1095 8.1333 25.9033 8.33949L22.75 11.4928C22.6293 11.6135 22.4633 11.6839 22.2873 11.6839H6.3245C6.0328 11.6839 5.88695 11.3319 6.09315 11.1257L9.24648 7.97236Z" fill="url(#paint1_linear_2582_2263)"/>
|
||||
<path d="M22.75 13.8215C22.6293 13.7008 22.4633 13.6304 22.2873 13.6304H6.3245C6.0328 13.6304 5.88695 13.9824 6.09315 14.1886L9.24648 17.3419C9.36718 17.4626 9.53314 17.533 9.70917 17.533H25.6719C25.9636 17.533 26.1095 17.181 25.9033 16.9748L22.75 13.8215Z" fill="url(#paint2_linear_2582_2263)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="29.0389" y1="-0.0137359" x2="11.3639" y2="33.8409" gradientUnits="userSpaceOnUse">
|
||||
<linearGradient id="paint0_linear_2582_2263" x1="24.147" y1="5.8975" x2="13.0995" y2="27.058" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFA3"/>
|
||||
<stop offset="1" stop-color="#DC1FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="21.3105" y1="-4.04864" x2="3.63559" y2="29.8059" gradientUnits="userSpaceOnUse">
|
||||
<linearGradient id="paint1_linear_2582_2263" x1="19.3165" y1="3.37558" x2="8.26896" y2="24.536" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFA3"/>
|
||||
<stop offset="1" stop-color="#DC1FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear" x1="25.1501" y1="-2.04394" x2="7.47521" y2="31.8107" gradientUnits="userSpaceOnUse">
|
||||
<linearGradient id="paint2_linear_2582_2263" x1="21.7164" y1="4.62866" x2="10.6689" y2="25.7891" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00FFA3"/>
|
||||
<stop offset="1" stop-color="#DC1FFF"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<clipPath id="clip0_2582_2263">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.0 KiB |
|
@ -5,6 +5,7 @@
|
|||
"advanced-filters": "Advanced Filters",
|
||||
"asset-liquidated": "Asset Liquidated",
|
||||
"asset-returned": "Asset Returned",
|
||||
"connect-activity": "Connect to view your account activity",
|
||||
"credit": "Credit",
|
||||
"debit": "Debit",
|
||||
"deposit": "Deposit",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"liquidation": "Liquidation",
|
||||
"liquidations": "Liquidations",
|
||||
"liquidation-details": "Liquidation Details",
|
||||
"no-activity": "No account activity",
|
||||
"perps": "Perps",
|
||||
"perp_trade": "Perp",
|
||||
"reset-filters": "Reset Filters",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"borrow-value": "Borrow Value",
|
||||
"buy": "Buy",
|
||||
"cancel": "Cancel",
|
||||
"chart-unavailable": "Chart Unavailable",
|
||||
"clear-all": "Clear All",
|
||||
"close-account": "Close Account",
|
||||
"close-account-desc": "Are you sure? Closing your account is irreversible",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"health-tooltip": "Projects the health of your account before you make a trade. The first value is your current account health and the second your projected account health.",
|
||||
"insufficient-sol": "Solana requires 0.0294 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"insufficient-sol": "Solana requires 0.0432 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
"learn": "Learn",
|
||||
|
@ -103,7 +104,8 @@
|
|||
"token": "Token",
|
||||
"tokens": "Tokens",
|
||||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance annually",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"advanced-filters": "Advanced Filters",
|
||||
"asset-liquidated": "Asset Liquidated",
|
||||
"asset-returned": "Asset Returned",
|
||||
"connect-activity": "Connect to view your account activity",
|
||||
"credit": "Credit",
|
||||
"debit": "Debit",
|
||||
"deposit": "Deposit",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"liquidation": "Liquidation",
|
||||
"liquidations": "Liquidations",
|
||||
"liquidation-details": "Liquidation Details",
|
||||
"no-activity": "No account activity",
|
||||
"perps": "Perps",
|
||||
"perp_trade": "Perp",
|
||||
"reset-filters": "Reset Filters",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"borrow-value": "Borrow Value",
|
||||
"buy": "Buy",
|
||||
"cancel": "Cancel",
|
||||
"chart-unavailable": "Chart Unavailable",
|
||||
"clear-all": "Clear All",
|
||||
"close-account": "Close Account",
|
||||
"close-account-desc": "Are you sure? Closing your account is irreversible",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"health-tooltip": "Projects the health of your account before you make a trade. The first value is your current account health and the second your projected account health.",
|
||||
"insufficient-sol": "Solana requires 0.0294 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"insufficient-sol": "Solana requires 0.0432 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
"learn": "Learn",
|
||||
|
@ -103,7 +104,8 @@
|
|||
"token": "Token",
|
||||
"tokens": "Tokens",
|
||||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance annually",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"advanced-filters": "Advanced Filters",
|
||||
"asset-liquidated": "Asset Liquidated",
|
||||
"asset-returned": "Asset Returned",
|
||||
"connect-activity": "Connect to view your account activity",
|
||||
"credit": "Credit",
|
||||
"debit": "Debit",
|
||||
"deposit": "Deposit",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"liquidation": "Liquidation",
|
||||
"liquidations": "Liquidations",
|
||||
"liquidation-details": "Liquidation Details",
|
||||
"no-activity": "No account activity",
|
||||
"perps": "Perps",
|
||||
"perp_trade": "Perp",
|
||||
"reset-filters": "Reset Filters",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"borrow-value": "Borrow Value",
|
||||
"buy": "Buy",
|
||||
"cancel": "Cancel",
|
||||
"chart-unavailable": "Chart Unavailable",
|
||||
"clear-all": "Clear All",
|
||||
"close-account": "Close Account",
|
||||
"close-account-desc": "Are you sure? Closing your account is irreversible",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"health-tooltip": "Projects the health of your account before you make a trade. The first value is your current account health and the second your projected account health.",
|
||||
"insufficient-sol": "Solana requires 0.0294 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"insufficient-sol": "Solana requires 0.0432 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
"learn": "Learn",
|
||||
|
@ -103,7 +104,8 @@
|
|||
"token": "Token",
|
||||
"tokens": "Tokens",
|
||||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance annually",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"advanced-filters": "Advanced Filters",
|
||||
"asset-liquidated": "Asset Liquidated",
|
||||
"asset-returned": "Asset Returned",
|
||||
"connect-activity": "Connect to view your account activity",
|
||||
"credit": "Credit",
|
||||
"debit": "Debit",
|
||||
"deposit": "Deposit",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"liquidation": "Liquidation",
|
||||
"liquidations": "Liquidations",
|
||||
"liquidation-details": "Liquidation Details",
|
||||
"no-activity": "No account activity",
|
||||
"perps": "Perps",
|
||||
"perp_trade": "Perp",
|
||||
"reset-filters": "Reset Filters",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"borrow-value": "Borrow Value",
|
||||
"buy": "Buy",
|
||||
"cancel": "Cancel",
|
||||
"chart-unavailable": "Chart Unavailable",
|
||||
"clear-all": "Clear All",
|
||||
"close-account": "Close Account",
|
||||
"close-account-desc": "Are you sure? Closing your account is irreversible",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"health-tooltip": "Projects the health of your account before you make a trade. The first value is your current account health and the second your projected account health.",
|
||||
"insufficient-sol": "Solana requires 0.0294 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"insufficient-sol": "Solana requires 0.0432 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
"learn": "Learn",
|
||||
|
@ -103,7 +104,8 @@
|
|||
"token": "Token",
|
||||
"tokens": "Tokens",
|
||||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance annually",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"advanced-filters": "Advanced Filters",
|
||||
"asset-liquidated": "Asset Liquidated",
|
||||
"asset-returned": "Asset Returned",
|
||||
"connect-activity": "Connect to view your account activity",
|
||||
"credit": "Credit",
|
||||
"debit": "Debit",
|
||||
"deposit": "Deposit",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"liquidation": "Liquidation",
|
||||
"liquidations": "Liquidations",
|
||||
"liquidation-details": "Liquidation Details",
|
||||
"no-activity": "No account activity",
|
||||
"perps": "Perps",
|
||||
"perp_trade": "Perp",
|
||||
"reset-filters": "Reset Filters",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"borrow-value": "Borrow Value",
|
||||
"buy": "Buy",
|
||||
"cancel": "Cancel",
|
||||
"chart-unavailable": "Chart Unavailable",
|
||||
"clear-all": "Clear All",
|
||||
"close-account": "Close Account",
|
||||
"close-account-desc": "Are you sure? Closing your account is irreversible",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"health": "Health",
|
||||
"health-impact": "Health Impact",
|
||||
"health-tooltip": "Projects the health of your account before you make a trade. The first value is your current account health and the second your projected account health.",
|
||||
"insufficient-sol": "Solana requires 0.0294 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"insufficient-sol": "Solana requires 0.0432 SOL rent to create a Mango Account. This will be returned if you close your account.",
|
||||
"interest-earned": "Interest Earned",
|
||||
"interest-earned-paid": "Interest Earned",
|
||||
"learn": "Learn",
|
||||
|
@ -103,7 +104,8 @@
|
|||
"token": "Token",
|
||||
"tokens": "Tokens",
|
||||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance annually",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
@ -1,4 +1,22 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400&display=swap');
|
||||
/* @import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400&display=swap'); */
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: normal;
|
||||
src: url('/fonts/TT_Commons_Pro_Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: medium;
|
||||
src: url('/fonts/TT_Commons_Pro_Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: bold;
|
||||
src: url('/fonts/TT_Commons_Pro_DemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
:root:not(.theme-dark) {
|
||||
--black: #fff;
|
||||
|
@ -57,7 +75,7 @@ html .chart-page .chart-container-border {
|
|||
}
|
||||
|
||||
html .chart-page {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-family: 'TT Commons', sans-serif;
|
||||
}
|
||||
|
||||
html .loading-indicator {
|
||||
|
@ -156,7 +174,7 @@ html.theme-dark .chart-page .chart-container-border {
|
|||
}
|
||||
|
||||
html.theme-dark .chart-page {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-family: 'TT Commons', sans-serif;
|
||||
}
|
||||
|
||||
html.theme-dark .loading-indicator {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { subscribeWithSelector } from 'zustand/middleware'
|
|||
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
||||
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
||||
import { Orderbook as SpotOrderBook } from '@project-serum/serum'
|
||||
import { Orderbook } from '@project-serum/serum'
|
||||
import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react'
|
||||
import {
|
||||
MangoClient,
|
||||
|
@ -33,15 +33,16 @@ import {
|
|||
LAST_ACCOUNT_KEY,
|
||||
OUTPUT_TOKEN_DEFAULT,
|
||||
} from '../utils/constants'
|
||||
import { Orderbook, SpotBalances } from 'types'
|
||||
import { OrderbookL2, SpotBalances } from 'types'
|
||||
import spotBalancesUpdater from './spotBalancesUpdater'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
|
||||
import perpPositionsUpdater from './perpPositionsUpdater'
|
||||
|
||||
const GROUP = new PublicKey('DLdcpC6AsAJ9xeKMR3WhHrN5sM5o7GVVXQhQ5vwisTtz')
|
||||
const GROUP = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
|
||||
|
||||
export const connection = new web3.Connection(
|
||||
'https://mango.rpcpool.com/0f9acc0d45173b51bf7d7e09c1e5',
|
||||
process.env.NEXT_PUBLIC_ENDPOINT ||
|
||||
'https://mango.rpcpool.com/0f9acc0d45173b51bf7d7e09c1e5',
|
||||
'processed'
|
||||
)
|
||||
const options = AnchorProvider.defaultOptions()
|
||||
|
@ -53,8 +54,9 @@ const DEFAULT_CLIENT = MangoClient.connect(
|
|||
DEFAULT_PROVIDER,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
null,
|
||||
'get-program-accounts'
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
}
|
||||
)
|
||||
|
||||
export interface TotalInterestDataItem {
|
||||
|
@ -173,6 +175,26 @@ export interface TokenStatsItem {
|
|||
// wallet_pk: '',
|
||||
// }
|
||||
|
||||
interface TradeForm {
|
||||
side: 'buy' | 'sell'
|
||||
price: string | undefined
|
||||
baseSize: string
|
||||
quoteSize: string
|
||||
tradeType: 'Market' | 'Limit'
|
||||
postOnly: boolean
|
||||
ioc: boolean
|
||||
}
|
||||
|
||||
export const DEFAULT_TRADE_FORM: TradeForm = {
|
||||
side: 'buy',
|
||||
price: undefined,
|
||||
baseSize: '',
|
||||
quoteSize: '',
|
||||
tradeType: 'Limit',
|
||||
postOnly: false,
|
||||
ioc: false,
|
||||
}
|
||||
|
||||
export type MangoStore = {
|
||||
activityFeed: {
|
||||
feed: Array<DepositWithdrawFeedItem | LiquidationFeedItem>
|
||||
|
@ -211,9 +233,9 @@ export type MangoStore = {
|
|||
name: string
|
||||
current: Serum3Market | PerpMarket | undefined
|
||||
fills: any
|
||||
bidsAccount: BookSide | SpotOrderBook | undefined
|
||||
asksAccount: BookSide | SpotOrderBook | undefined
|
||||
orderbook: Orderbook
|
||||
bidsAccount: BookSide | Orderbook | undefined
|
||||
asksAccount: BookSide | Orderbook | undefined
|
||||
orderbook: OrderbookL2
|
||||
markPrice: number
|
||||
}
|
||||
serumMarkets: Serum3Market[]
|
||||
|
@ -237,18 +259,11 @@ export type MangoStore = {
|
|||
}
|
||||
set: (x: (x: MangoStore) => void) => void
|
||||
tokenStats: {
|
||||
initialLoad: boolean
|
||||
loading: boolean
|
||||
data: TokenStatsItem[]
|
||||
}
|
||||
tradeForm: {
|
||||
side: 'buy' | 'sell'
|
||||
price: string
|
||||
baseSize: string
|
||||
quoteSize: string
|
||||
tradeType: 'Market' | 'Limit'
|
||||
postOnly: boolean
|
||||
ioc: boolean
|
||||
}
|
||||
tradeForm: TradeForm
|
||||
wallet: {
|
||||
tokens: TokenAccount[]
|
||||
nfts: {
|
||||
|
@ -357,18 +372,11 @@ const mangoStore = create<MangoStore>()(
|
|||
amountOut: '',
|
||||
},
|
||||
tokenStats: {
|
||||
initialLoad: false,
|
||||
loading: false,
|
||||
data: [],
|
||||
},
|
||||
tradeForm: {
|
||||
side: 'buy',
|
||||
price: '',
|
||||
baseSize: '',
|
||||
quoteSize: '',
|
||||
tradeType: 'Limit',
|
||||
postOnly: false,
|
||||
ioc: false,
|
||||
},
|
||||
tradeForm: DEFAULT_TRADE_FORM,
|
||||
wallet: {
|
||||
tokens: [],
|
||||
nfts: {
|
||||
|
@ -555,6 +563,7 @@ const mangoStore = create<MangoStore>()(
|
|||
}
|
||||
})
|
||||
} catch (e) {
|
||||
notify({ type: 'info', title: 'Unable to refresh data' })
|
||||
console.error('Error fetching group', e)
|
||||
}
|
||||
},
|
||||
|
@ -668,6 +677,9 @@ const mangoStore = create<MangoStore>()(
|
|||
const set = get().set
|
||||
const client = get().client
|
||||
const group = get().group
|
||||
if (!providedMangoAccount) {
|
||||
await get().actions.reloadMangoAccount()
|
||||
}
|
||||
const mangoAccount =
|
||||
providedMangoAccount || get().mangoAccount.current
|
||||
|
||||
|
@ -755,8 +767,7 @@ const mangoStore = create<MangoStore>()(
|
|||
fetchTokenStats: async () => {
|
||||
const set = get().set
|
||||
const group = get().group
|
||||
const stats = get().tokenStats.data
|
||||
if (stats.length || !group) return
|
||||
if (!group) return
|
||||
set((state) => {
|
||||
state.tokenStats.loading = true
|
||||
})
|
||||
|
@ -768,6 +779,7 @@ const mangoStore = create<MangoStore>()(
|
|||
|
||||
set((state) => {
|
||||
state.tokenStats.data = data
|
||||
state.tokenStats.initialLoad = true
|
||||
state.tokenStats.loading = false
|
||||
})
|
||||
} catch {
|
||||
|
@ -813,6 +825,7 @@ const mangoStore = create<MangoStore>()(
|
|||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
prioritizationFee: 2,
|
||||
postSendTxCallback: ({ txid }: { txid: string }) => {
|
||||
notify({
|
||||
|
@ -822,8 +835,7 @@ const mangoStore = create<MangoStore>()(
|
|||
txid: txid,
|
||||
})
|
||||
},
|
||||
},
|
||||
'get-program-accounts'
|
||||
}
|
||||
)
|
||||
set((s) => {
|
||||
s.client = client
|
||||
|
@ -934,6 +946,10 @@ const mangoStore = create<MangoStore>()(
|
|||
)
|
||||
|
||||
mangoStore.subscribe((state) => state.mangoAccount.current, spotBalancesUpdater)
|
||||
mangoStore.subscribe(
|
||||
(state) => state.mangoAccount.openOrderAccounts,
|
||||
spotBalancesUpdater
|
||||
)
|
||||
mangoStore.subscribe(
|
||||
(state) => state.mangoAccount.current,
|
||||
perpPositionsUpdater
|
||||
|
|
|
@ -3,8 +3,31 @@
|
|||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
src: url('/fonts/roboto.woff');
|
||||
font-family: 'TT Mono';
|
||||
src: url('/fonts/TT_Commons_Pro_Mono_Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons Expanded';
|
||||
src: url('/fonts/TT_Commons_Pro_Expanded_DemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: normal;
|
||||
src: url('/fonts/TT_Commons_Pro_Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: medium;
|
||||
src: url('/fonts/TT_Commons_Pro_Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'TT Commons';
|
||||
font-weight: bold;
|
||||
src: url('/fonts/TT_Commons_Pro_DemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
|
|
|
@ -5,9 +5,9 @@ module.exports = {
|
|||
],
|
||||
theme: {
|
||||
fontFamily: {
|
||||
display: ['Lato, sans-serif'],
|
||||
body: ['Lato, sans-serif'],
|
||||
mono: ['Roboto Mono'],
|
||||
display: ['TT Commons Expanded, sans-serif'],
|
||||
body: 'TT Commons, sans-serif',
|
||||
mono: ['TT Mono, mono'],
|
||||
},
|
||||
extend: {
|
||||
animation: {
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface ChartTradeType {
|
|||
marketAddress: string
|
||||
}
|
||||
|
||||
export interface Orderbook {
|
||||
export interface OrderbookL2 {
|
||||
bids: number[][]
|
||||
asks: number[][]
|
||||
}
|
||||
|
|
|
@ -38,9 +38,7 @@ export const PROFILE_CATEGORIES = [
|
|||
'yolo',
|
||||
]
|
||||
|
||||
const baseUrl = 'https://event-history-api-candles.herokuapp.com'
|
||||
|
||||
export const CHART_DATA_FEED = `${baseUrl}/tv`
|
||||
export const CHART_DATA_FEED = `https://dry-ravine-67635.herokuapp.com/tv`
|
||||
|
||||
export const DEFAULT_MARKET_NAME = 'SOL/USDC'
|
||||
|
||||
|
|
|
@ -105,15 +105,3 @@ export const fetchNftsFromHolaplexIndexer = async (owner: PublicKey) => {
|
|||
|
||||
export const formatTokenSymbol = (symbol: string) =>
|
||||
symbol === 'MSOL' ? 'mSOL' : symbol === 'SOETH' ? 'soETH' : symbol
|
||||
|
||||
export const LISTED_TOKENS: string[] = [
|
||||
'BTC',
|
||||
'DUST',
|
||||
'ETH',
|
||||
'RAY',
|
||||
'soETH',
|
||||
'SOL',
|
||||
'MSOL',
|
||||
'USDC',
|
||||
'USDT',
|
||||
]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Orderbook } from 'types'
|
||||
import { OrderbookL2 } from 'types'
|
||||
|
||||
export const calculateMarketPrice = (
|
||||
orderBook: Orderbook,
|
||||
export const calculateLimitPriceForMarketOrder = (
|
||||
orderBook: OrderbookL2,
|
||||
size: number,
|
||||
side: 'buy' | 'sell'
|
||||
): number => {
|
||||
|
@ -29,7 +29,7 @@ export const calculateMarketPrice = (
|
|||
}
|
||||
|
||||
export const calculateSlippage = (
|
||||
orderBook: Orderbook,
|
||||
orderBook: OrderbookL2,
|
||||
size: number,
|
||||
side: 'buy' | 'sell',
|
||||
markPrice: number
|
||||
|
@ -38,10 +38,17 @@ export const calculateSlippage = (
|
|||
const ba = orderBook?.asks?.length > 0 && Number(orderBook.asks[0][0])
|
||||
const referencePrice = bb && ba ? (bb + ba) / 2 : markPrice
|
||||
|
||||
const estimatedPrice = calculateMarketPrice(orderBook, Number(size), side)
|
||||
if (Number(size)) {
|
||||
const estimatedPrice = calculateLimitPriceForMarketOrder(
|
||||
orderBook,
|
||||
Number(size),
|
||||
side
|
||||
)
|
||||
|
||||
const slippageAbs =
|
||||
Number(size) > 0 ? Math.abs(estimatedPrice - referencePrice) : 0
|
||||
const slippageRel = (slippageAbs / referencePrice) * 100
|
||||
return slippageRel
|
||||
const slippageAbs =
|
||||
Number(size) > 0 ? Math.abs(estimatedPrice - referencePrice) : 0
|
||||
const slippageRel = (slippageAbs / referencePrice) * 100
|
||||
return slippageRel
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
|
64
yarn.lock
64
yarn.lock
|
@ -50,9 +50,9 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@blockworks-foundation/mango-v4@https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#ts/test":
|
||||
"@blockworks-foundation/mango-v4@https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#main":
|
||||
version "0.0.1-beta.6"
|
||||
resolved "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#c54fdbee5cc7be5e9d5367996b7986e370e609ff"
|
||||
resolved "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#2cd8c048f3eb25b40a4124464a95240f362488b1"
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.25.0"
|
||||
"@project-serum/serum" "^0.13.65"
|
||||
|
@ -622,9 +622,9 @@
|
|||
bignumber.js "^9.0.1"
|
||||
|
||||
"@solana/buffer-layout@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734"
|
||||
integrity sha512-lR0EMP2HC3+Mxwd4YcnZb0smnaDw7Bl2IQWZiTevRH5ZZBZn6VRWn3/92E3qdU4SSImJkA6IDHawOHAnx/qUvQ==
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
|
||||
integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
|
||||
dependencies:
|
||||
buffer "~6.0.3"
|
||||
|
||||
|
@ -1098,9 +1098,9 @@
|
|||
"@wallet-standard/base" "^1.0.0"
|
||||
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.63.1":
|
||||
version "1.70.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.70.0.tgz#14ad207f431861397db85921aad8df4e8374e7c8"
|
||||
integrity sha512-HwdI9LaHaszfpzgxJI44iP68mJWUeqK1TeSheKQsGkH5zlVyGWGmim50MyDWu2vXiuL8Akf2xEMSrDYyLordgg==
|
||||
version "1.70.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.70.1.tgz#4a2df47cc32a0f67be5161e772b2ceb6512281fa"
|
||||
integrity sha512-AnaqCF1cJ3w7d0yhvLGAKAcRI+n5o+ursQihhoTe4cUh8/9d4gbT73SoHYElS7e67OtAgLmSfbcC5hcOAgdvnQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@noble/ed25519" "^1.7.0"
|
||||
|
@ -1558,9 +1558,9 @@
|
|||
integrity sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==
|
||||
|
||||
"@types/node@*":
|
||||
version "18.11.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.11.tgz#1d455ac0211549a8409d3cdb371cd55cc971e8dc"
|
||||
integrity sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==
|
||||
version "18.11.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d"
|
||||
integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==
|
||||
|
||||
"@types/node@17.0.23":
|
||||
version "17.0.23"
|
||||
|
@ -2209,21 +2209,19 @@ axe-core@^4.4.3:
|
|||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f"
|
||||
integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==
|
||||
|
||||
axios@^0.21.0, axios@^0.21.1:
|
||||
axios@^0.21.0:
|
||||
version "0.21.4"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383"
|
||||
integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==
|
||||
axios@^0.25.0:
|
||||
version "0.25.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a"
|
||||
integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.0"
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
follow-redirects "^1.14.7"
|
||||
|
||||
axobject-query@^2.2.0:
|
||||
version "2.2.0"
|
||||
|
@ -3742,7 +3740,7 @@ flatted@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
|
||||
integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
|
||||
|
||||
follow-redirects@^1.14.0, follow-redirects@^1.15.0:
|
||||
follow-redirects@^1.14.0, follow-redirects@^1.14.7:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||
|
@ -4301,7 +4299,7 @@ jmespath@^0.15.0:
|
|||
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
|
||||
integrity sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==
|
||||
|
||||
joi@^17.4.0:
|
||||
joi@^17.6.0:
|
||||
version "17.7.0"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3"
|
||||
integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==
|
||||
|
@ -5653,7 +5651,7 @@ rxjs@6, rxjs@^6.6.3:
|
|||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
rxjs@^7.1.0:
|
||||
rxjs@^7.5.4:
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2"
|
||||
integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==
|
||||
|
@ -5851,9 +5849,9 @@ split@0.3:
|
|||
through "2"
|
||||
|
||||
start-server-and-test@^1.14.0:
|
||||
version "1.15.1"
|
||||
resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.1.tgz#8e28e48a31884b7c7b19950502e92161eec6293b"
|
||||
integrity sha512-ixhMbUAmym+7k3kqhjhTt6/0eUwE3dbNZPWgcfEUJ5uStaHbT0m8VB+049V5V9X0ueEyxAkwnkkR2SEhCKst5g==
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.2.tgz#3c4f9b358a0dc5ae03a96dd7d7ae9e25a3b24165"
|
||||
integrity sha512-t5xJX04Hg7hqxiKHMJBz/n4zIMsE6G7hpAcerFAH+4Vh9le/LeyFcJERJM7WLiPygWF9TOg33oroJF1XOzJtYQ==
|
||||
dependencies:
|
||||
arg "^5.0.2"
|
||||
bluebird "3.7.2"
|
||||
|
@ -5862,7 +5860,7 @@ start-server-and-test@^1.14.0:
|
|||
execa "5.1.1"
|
||||
lazy-ass "1.6.0"
|
||||
ps-tree "1.2.0"
|
||||
wait-on "6.0.0"
|
||||
wait-on "6.0.1"
|
||||
|
||||
stream-browserify@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
@ -6544,16 +6542,16 @@ void-elements@3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
|
||||
|
||||
wait-on@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7"
|
||||
integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw==
|
||||
wait-on@6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e"
|
||||
integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==
|
||||
dependencies:
|
||||
axios "^0.21.1"
|
||||
joi "^17.4.0"
|
||||
axios "^0.25.0"
|
||||
joi "^17.6.0"
|
||||
lodash "^4.17.21"
|
||||
minimist "^1.2.5"
|
||||
rxjs "^7.1.0"
|
||||
rxjs "^7.5.4"
|
||||
|
||||
walktour@^5.1.1:
|
||||
version "5.1.1"
|
||||
|
|
Loading…
Reference in New Issue