conflicts and translations
This commit is contained in:
commit
bcae2a3d70
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn lint-staged
|
|
@ -21,6 +21,7 @@ import WithdrawModal from './WithdrawModal'
|
|||
import { ExpandableRow } from './TableElements'
|
||||
import MobileTableHeader from './mobile/MobileTableHeader'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { TransactionSignature } from '@solana/web3.js'
|
||||
|
||||
const BalancesTable = ({ showZeroBalances = false }) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
@ -100,7 +101,8 @@ const BalancesTable = ({ showZeroBalances = false }) => {
|
|||
const spotMarkets = Object.values(markets).filter(
|
||||
(mkt) => mkt instanceof Market
|
||||
) as Market[]
|
||||
const txids = await mangoClient.settleAll(
|
||||
|
||||
const txids: TransactionSignature[] = await mangoClient.settleAll(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
spotMarkets,
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
import {
|
||||
FunctionComponent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import useMangoStore, { MNGO_INDEX } from '../stores/useMangoStore'
|
||||
import { XCircleIcon } from '@heroicons/react/outline'
|
||||
import Button from './Button'
|
||||
import Modal from './Modal'
|
||||
import { ElementTitle } from './styles'
|
||||
import { notify } from '../utils/notifications'
|
||||
//import { useTranslation } from 'next-i18next'
|
||||
import { CheckCircleIcon } from '@heroicons/react/solid'
|
||||
import {
|
||||
getMultipleAccounts,
|
||||
nativeToUi,
|
||||
zeroKey,
|
||||
ZERO_BN,
|
||||
ZERO_I80F48,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import usePerpPositions from '../hooks/usePerpPositions'
|
||||
import { useOpenOrders } from '../hooks/useOpenOrders'
|
||||
import { formatUsdValue } from '../utils'
|
||||
|
||||
interface CloseAccountModalProps {
|
||||
accountName?: string
|
||||
lamports?: number
|
||||
isOpen: boolean
|
||||
onClose?: (x?) => void
|
||||
}
|
||||
|
||||
const CloseAccountModal: FunctionComponent<CloseAccountModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
}) => {
|
||||
//const { t } = useTranslation('common')
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
|
||||
const { openPositions, unsettledPositions } = usePerpPositions()
|
||||
const [hasBorrows, setHasBorrows] = useState(false)
|
||||
const [hasOpenPositions, setHasOpenPositions] = useState(false)
|
||||
const [totalAccountSOL, setTotalAccountSOL] = useState(0)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const connection = useMangoStore((s) => s.connection.current)
|
||||
const client = useMangoStore((s) => s.connection.client)
|
||||
const openOrders = useOpenOrders()
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
|
||||
const fetchTotalAccountSOL = useCallback(async () => {
|
||||
if (!mangoAccount) {
|
||||
return
|
||||
}
|
||||
const accountKeys = [
|
||||
mangoAccount.publicKey,
|
||||
...mangoAccount.spotOpenOrders.filter((oo) => !oo.equals(zeroKey)),
|
||||
...(!mangoAccount.advancedOrdersKey.equals(zeroKey)
|
||||
? [mangoAccount.advancedOrdersKey]
|
||||
: []),
|
||||
]
|
||||
const accounts = await getMultipleAccounts(connection, accountKeys)
|
||||
const lamports =
|
||||
accounts.reduce((total, account) => {
|
||||
return total + account.accountInfo.lamports
|
||||
}, 0) * 0.000000001
|
||||
|
||||
setTotalAccountSOL(lamports)
|
||||
}, [mangoAccount])
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccount) {
|
||||
if (mangoAccount.borrows.some((b) => b.gt(ZERO_I80F48))) {
|
||||
setHasBorrows(true)
|
||||
}
|
||||
if (openPositions.length || unsettledPositions.length) {
|
||||
setHasOpenPositions(true)
|
||||
}
|
||||
}
|
||||
|
||||
fetchTotalAccountSOL()
|
||||
}, [mangoAccount])
|
||||
|
||||
const mngoAccrued = useMemo(() => {
|
||||
return mangoAccount
|
||||
? mangoAccount.perpAccounts.reduce((acc, perpAcct) => {
|
||||
return perpAcct.mngoAccrued.add(acc)
|
||||
}, ZERO_BN)
|
||||
: ZERO_BN
|
||||
}, [mangoAccount])
|
||||
|
||||
const closeAccount = async () => {
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
try {
|
||||
const txids = await client.emptyAndCloseMangoAccount(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
mangoCache,
|
||||
MNGO_INDEX,
|
||||
wallet
|
||||
)
|
||||
|
||||
await actions.fetchAllMangoAccounts()
|
||||
const mangoAccounts = useMangoStore.getState().mangoAccounts
|
||||
|
||||
setMangoStore((state) => {
|
||||
state.selectedMangoAccount.current = mangoAccounts.length
|
||||
? mangoAccounts[0]
|
||||
: null
|
||||
})
|
||||
|
||||
onClose()
|
||||
for (const txid of txids) {
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
txid,
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Error deleting account:', err)
|
||||
notify({
|
||||
title: 'Error deleting account',
|
||||
description: `${err.message}`,
|
||||
txid: err.txid,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose} isOpen={isOpen && mangoAccount !== undefined}>
|
||||
<Modal.Header>
|
||||
<div className="flex items-center">
|
||||
<ElementTitle noMarignBottom>Close your account</ElementTitle>
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<div className="text-th-fgd-2 text-center my-4">
|
||||
You can close your Mango account and recover the small amount of SOL
|
||||
used to cover rent exemption.
|
||||
</div>
|
||||
<div className="text-th-fgd-2 text-center my-4">
|
||||
To close your account you must:
|
||||
</div>
|
||||
|
||||
<div className="bg-th-bkg-4 overflow-none p-2 sm:p-6 rounded-lg">
|
||||
<div className="flex items-center text-th-fgd-2 mb-4 ">
|
||||
{!hasBorrows ? (
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
) : (
|
||||
<XCircleIcon className="h-4 w-4 mr-1.5 text-th-red" />
|
||||
)}{' '}
|
||||
Close all borrows
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2 mb-4">
|
||||
{!hasOpenPositions ? (
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
) : (
|
||||
<XCircleIcon className="h-4 w-4 mr-1.5 text-th-red" />
|
||||
)}{' '}
|
||||
Close and settle all Perp positons
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
{openOrders && !openOrders.length ? (
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
) : (
|
||||
<XCircleIcon className="h-4 w-4 mr-1.5 text-th-red" />
|
||||
)}{' '}
|
||||
Close all open orders
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-th-fgd-2 text-center my-4">
|
||||
By closing your account you will:
|
||||
</div>
|
||||
<div className="bg-th-bkg-4 overflow-wrap p-2 sm:p-6 rounded-lg">
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
Delete your Mango account
|
||||
</div>
|
||||
{mangoAccount &&
|
||||
mangoAccount.getAssetsVal(mangoGroup, mangoCache).gt(ZERO_I80F48) ? (
|
||||
<div className="flex items-center text-th-fgd-2 mt-4">
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
Withdraw assets worth{' '}
|
||||
{formatUsdValue(+mangoAccount.computeValue(mangoGroup, mangoCache))}
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<div className="flex items-center text-th-fgd-2 mt-4">
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
Recover {totalAccountSOL.toFixed(3)} SOL
|
||||
</div>
|
||||
{!mngoAccrued.isZero() ? (
|
||||
<div className="flex items-center text-th-fgd-2 mt-4">
|
||||
<CheckCircleIcon className="h-4 w-4 mr-1.5 text-th-green" />
|
||||
Claim{' '}
|
||||
{mangoGroup
|
||||
? nativeToUi(
|
||||
mngoAccrued.toNumber(),
|
||||
mangoGroup.tokens[MNGO_INDEX].decimals
|
||||
).toFixed(3)
|
||||
: 0}{' '}
|
||||
MNGO in unclaimed rewards
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
<div className="text-th-fgd-2 text-center my-4">Goodbye 👋</div>
|
||||
<Button
|
||||
onClick={() => closeAccount()}
|
||||
disabled={
|
||||
(openOrders && openOrders.length > 0) ||
|
||||
hasBorrows ||
|
||||
hasOpenPositions
|
||||
}
|
||||
className="mt-1 w-full"
|
||||
>
|
||||
Close Account
|
||||
</Button>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default CloseAccountModal
|
|
@ -70,7 +70,7 @@ const DepositModal: FunctionComponent<DepositModalProps> = ({
|
|||
notify({
|
||||
title: t('deposit-successful'),
|
||||
type: 'success',
|
||||
txid: response.toString(),
|
||||
txid: response instanceof Array ? response[1] : response,
|
||||
})
|
||||
setSubmitting(false)
|
||||
onClose()
|
||||
|
|
|
@ -18,9 +18,9 @@ class ErrorBoundary extends React.Component<
|
|||
// You can also log the error to an error reporting service
|
||||
// logErrorToMyService(error, errorInfo)
|
||||
|
||||
if (process.env.NEXT_ERROR_WEBHOOK_URL) {
|
||||
if (process.env.NEXT_PUBLIC_ERROR_WEBHOOK_URL) {
|
||||
try {
|
||||
fetch(process.env.NEXT_ERROR_WEBHOOK_URL, {
|
||||
fetch(process.env.NEXT_PUBLIC_ERROR_WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
|
|
|
@ -6,7 +6,7 @@ import { XIcon } from '@heroicons/react/solid'
|
|||
// const GLOBAL_NOTIFICATION_KEY = 'globalNotification-0.1'
|
||||
|
||||
const GlobalNotification = () => {
|
||||
const [show, setShow] = useState(true)
|
||||
const [show, setShow] = useState(false) // update this to true when we have a notification
|
||||
|
||||
if (show) {
|
||||
return (
|
||||
|
|
|
@ -275,7 +275,7 @@ const JupiterForm: FunctionComponent = () => {
|
|||
|
||||
const getWalletTokenPrices = async () => {
|
||||
const ids = walletTokensWithInfos.map(
|
||||
(token) => token.item.extensions.coingeckoId
|
||||
(token) => token.item.extensions?.coingeckoId
|
||||
)
|
||||
const response = await fetch(
|
||||
`https://api.coingecko.com/api/v3/simple/price?ids=${ids.toString()}&vs_currencies=usd`
|
||||
|
@ -391,15 +391,15 @@ const JupiterForm: FunctionComponent = () => {
|
|||
</div>
|
||||
{walletTokensWithInfos
|
||||
.sort((a, b) => {
|
||||
const aId = a.item.extensions.coingeckoId
|
||||
const bId = b.item.extensions.coingeckoId
|
||||
const aId = a.item.extensions?.coingeckoId
|
||||
const bId = b.item.extensions?.coingeckoId
|
||||
return (
|
||||
b.uiBalance * walletTokenPrices[bId]?.usd -
|
||||
a.uiBalance * walletTokenPrices[aId]?.usd
|
||||
)
|
||||
})
|
||||
.map((token) => {
|
||||
const geckoId = token.item.extensions.coingeckoId
|
||||
const geckoId = token.item.extensions?.coingeckoId
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer default-transition flex items-center justify-between px-4 py-2 hover:bg-th-bkg-4"
|
||||
|
@ -743,9 +743,9 @@ const JupiterForm: FunctionComponent = () => {
|
|||
(formValue?.amount / outAmountUi)) *
|
||||
100 <=
|
||||
0
|
||||
? 'cheaper'
|
||||
: 'more expensive'
|
||||
} than CoinGecko`}</span>
|
||||
? t('swap:cheaper')
|
||||
: t('swap:more-expensive')
|
||||
} CoinGecko`}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -2,9 +2,15 @@ import { useEffect, useState } from 'react'
|
|||
import { TranslateIcon } from '@heroicons/react/outline'
|
||||
import DropMenu from './DropMenu'
|
||||
import { useRouter } from 'next/router'
|
||||
import dayjs from 'dayjs'
|
||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
require('dayjs/locale/en')
|
||||
require('dayjs/locale/es')
|
||||
require('dayjs/locale/zh')
|
||||
require('dayjs/locale/zh-tw')
|
||||
|
||||
export const LANGS = [
|
||||
{ locale: 'en', name: 'english', description: 'english' },
|
||||
{ locale: 'es', name: 'spanish', description: 'spanish' },
|
||||
|
@ -29,6 +35,7 @@ const LanguageSwitch = () => {
|
|||
const handleLangChange = (e) => {
|
||||
setSavedLanguage(e)
|
||||
router.push({ pathname, query }, asPath, { locale: e })
|
||||
dayjs.locale(e == 'zh_tw' ? 'zh-tw' : e)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -16,6 +16,7 @@ const ManualRefresh = ({ className = '' }) => {
|
|||
await actions.fetchMangoGroup()
|
||||
if (mangoAccount) {
|
||||
await actions.reloadMangoAccount()
|
||||
actions.reloadOrders()
|
||||
actions.fetchTradeHistory()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ function calculateFundingRate(perpStats, perpMarket) {
|
|||
}
|
||||
|
||||
function parseOpenInterest(perpMarket: PerpMarket) {
|
||||
if (!(perpMarket instanceof PerpMarket)) return 0
|
||||
if (!perpMarket || !(perpMarket instanceof PerpMarket)) return 0
|
||||
|
||||
return perpMarket.baseLotsToNumber(perpMarket.openInterest) / 2
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ export default function MarketMenuItem({ menuTitle = '', linksArray = [] }) {
|
|||
<div
|
||||
onMouseEnter={() => onHover(openState, 'onMouseEnter')}
|
||||
onMouseLeave={() => onHover(openState, 'onMouseLeave')}
|
||||
className="cursor-pointer flex flex-col h-10"
|
||||
className="cursor-pointer flex flex-col h-10 px-3"
|
||||
>
|
||||
<div className="default-transition flex items-center h-10 text-th-fgd-3 hover:text-th-primary focus:outline-none">
|
||||
<SymbolIcon
|
||||
|
|
|
@ -165,7 +165,7 @@ export default function MarketPosition() {
|
|||
</ElementTitle>
|
||||
) : null}
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="font-normal text-th-fgd-3 leading-4">{t('size')}</div>
|
||||
<div className="font-normal text-th-fgd-3 leading-4">{t('side')}</div>
|
||||
{initialLoad ? (
|
||||
<DataLoader />
|
||||
) : (
|
||||
|
|
|
@ -75,8 +75,8 @@ const MarketSelect = () => {
|
|||
}}
|
||||
className="border-l-[20px] border-th-bkg-4"
|
||||
/>
|
||||
<div className="flex items-center justify-between pl-3 w-full">
|
||||
<div className="flex items-center space-x-6">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex items-center">
|
||||
{sortedMarkets
|
||||
.filter((m) => !hiddenMarkets.includes(m.baseAsset))
|
||||
.map((s) => (
|
||||
|
|
|
@ -63,7 +63,11 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
|
|||
actions.fetchWalletTokens()
|
||||
actions.fetchAllMangoAccounts()
|
||||
setSubmitting(false)
|
||||
onAccountCreation(response)
|
||||
onAccountCreation(response[0])
|
||||
notify({
|
||||
title: 'Mango Account Created',
|
||||
txid: response[1],
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
setSubmitting(false)
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
InformationCircleIcon,
|
||||
XCircleIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import useMangoStore, { CLUSTER } from '../stores/useMangoStore'
|
||||
import { Notification, notify } from '../utils/notifications'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Loading from './Loading'
|
||||
|
@ -103,7 +103,7 @@ const Notification = ({ notification }: { notification: Notification }) => {
|
|||
hideNotification()
|
||||
}
|
||||
},
|
||||
parsedTitle || type === 'confirm' || type === 'error' ? 30000 : 8000
|
||||
parsedTitle || type === 'confirm' || type === 'error' ? 20000 : 8000
|
||||
)
|
||||
|
||||
return () => {
|
||||
|
@ -117,7 +117,7 @@ const Notification = ({ notification }: { notification: Notification }) => {
|
|||
<div
|
||||
className={`max-w-sm w-full bg-th-bkg-3 border border-th-bkg-4 shadow-lg rounded-md mt-2 pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden`}
|
||||
>
|
||||
<div className={`flex items-center p-4 relative`}>
|
||||
<div className={`flex items-center px-2 py-2.5 relative`}>
|
||||
<div className={`flex-shrink-0`}>
|
||||
{type === 'success' ? (
|
||||
<CheckCircleIcon className={`text-th-green h-7 w-7 mr-1`} />
|
||||
|
@ -133,20 +133,24 @@ const Notification = ({ notification }: { notification: Notification }) => {
|
|||
)}
|
||||
</div>
|
||||
<div className={`ml-2 flex-1`}>
|
||||
<div className={`font-bold text-base text-th-fgd-1`}>
|
||||
<div className={`font-bold text-normal text-th-fgd-1`}>
|
||||
{parsedTitle || title}
|
||||
</div>
|
||||
{description ? (
|
||||
<p className={`mb-0 mt-0.5 text-th-fgd-3`}>{description}</p>
|
||||
<p className={`mb-0 mt-0.5 text-th-fgd-3 leading-tight`}>
|
||||
{description}
|
||||
</p>
|
||||
) : null}
|
||||
{txid ? (
|
||||
<a
|
||||
href={'https://explorer.solana.com/tx/' + txid}
|
||||
className="flex items-center mt-0.5 text-sm"
|
||||
href={
|
||||
'https://explorer.solana.com/tx/' + txid + '?cluster=' + CLUSTER
|
||||
}
|
||||
className="flex items-center mt-1 text-sm"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="break-all flex-1">
|
||||
<div className="break-all flex-1 text-xs">
|
||||
{type === 'error'
|
||||
? txid
|
||||
: `${txid.slice(0, 14)}...${txid.slice(txid.length - 14)}`}
|
||||
|
|
|
@ -46,7 +46,7 @@ export default function RecentMarketTrades() {
|
|||
|
||||
useInterval(async () => {
|
||||
fetchTradesForChart()
|
||||
}, 5000)
|
||||
}, 2000)
|
||||
|
||||
return !isMobile ? (
|
||||
<>
|
||||
|
|
|
@ -29,6 +29,7 @@ const CUSTOM_NODE = NODE_URLS.find((n) => n.label === 'Custom')
|
|||
export const NODE_URL_KEY = 'node-url-key-0.5'
|
||||
export const DEFAULT_MARKET_KEY = 'defaultMarket'
|
||||
export const ORDERBOOK_FLASH_KEY = 'showOrderbookFlash'
|
||||
export const DEFAULT_SPOT_MARGIN_KEY = 'defaultSpotMargin'
|
||||
export const initialMarket = {
|
||||
base: 'BTC',
|
||||
kind: 'perp',
|
||||
|
@ -43,6 +44,7 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|||
NODE_URL_KEY,
|
||||
NODE_URLS[0].value
|
||||
)
|
||||
|
||||
const [defaultMarket] = useLocalStorageState(
|
||||
DEFAULT_MARKET_KEY,
|
||||
initialMarket
|
||||
|
@ -51,6 +53,12 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|||
ORDERBOOK_FLASH_KEY,
|
||||
true
|
||||
)
|
||||
|
||||
const [defaultSpotMargin, setDefaultSpotMargin] = useLocalStorageState(
|
||||
DEFAULT_SPOT_MARGIN_KEY,
|
||||
false
|
||||
)
|
||||
|
||||
const rpcEndpoint =
|
||||
NODE_URLS.find((node) => node.value === rpcEndpointUrl) || CUSTOM_NODE
|
||||
return (
|
||||
|
@ -96,6 +104,14 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|||
onChange={(checked) => setShowOrderbookFlash(checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-th-bkg-4 flex items-center justify-between py-3 text-th-fgd-1">
|
||||
<span>{t('default-spot-margin')}</span>
|
||||
<Switch
|
||||
checked={defaultSpotMargin}
|
||||
onChange={(checked) => setDefaultSpotMargin(checked)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<SettingsContent
|
||||
|
|
|
@ -19,7 +19,7 @@ const SwapSettingsModal = ({
|
|||
slippage: number
|
||||
setSlippage: (x) => void
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'swap'])
|
||||
const [tempSlippage, setTempSlippage] = useState(slippage)
|
||||
const [inputValue, setInputValue] = useState(
|
||||
tempSlippage ? tempSlippage.toString() : ''
|
||||
|
@ -45,10 +45,12 @@ const SwapSettingsModal = ({
|
|||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} hideClose>
|
||||
<Modal.Header>
|
||||
<h2 className="font-bold text-th-fgd-1 text-lg">Slippage Settings</h2>
|
||||
<h2 className="font-bold text-th-fgd-1 text-lg">
|
||||
{t('swap:slippage-settings')}
|
||||
</h2>
|
||||
</Modal.Header>
|
||||
<div className="flex justify-between mb-2">
|
||||
<div className="text-th-fgd-1 text-xs">Slippage</div>
|
||||
<div className="text-th-fgd-1 text-xs">{t('swap:slippage')}</div>
|
||||
<LinkButton
|
||||
className="font-normal text-th-fgd-3 text-xs"
|
||||
onClick={() => setShowCustomSlippageForm(!showCustomSlippageForm)}
|
||||
|
@ -75,7 +77,7 @@ const SwapSettingsModal = ({
|
|||
/>
|
||||
)}
|
||||
<Button className="mt-6 w-full" onClick={handleSave}>
|
||||
Save
|
||||
{t('save')}
|
||||
</Button>
|
||||
</Modal>
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ import { AreaChart, Area, XAxis, YAxis, Tooltip } from 'recharts'
|
|||
import useDimensions from 'react-cool-dimensions'
|
||||
import { IconButton } from './Button'
|
||||
import { LineChartIcon } from './icons'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -40,6 +41,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
const [daysToShow, setDaysToShow] = useState(1)
|
||||
const [topHolders, setTopHolders] = useState(null)
|
||||
const { observe, width, height } = useDimensions()
|
||||
const { t } = useTranslation(['common', 'swap'])
|
||||
|
||||
const getTopHolders = async (inputMint, outputMint) => {
|
||||
const inputResponse = await fetch(
|
||||
|
@ -317,7 +319,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
) : (
|
||||
<div className="bg-th-bkg-3 mt-4 md:mt-0 p-4 rounded-md text-center text-th-fgd-3">
|
||||
<LineChartIcon className="h-6 mx-auto text-th-primary w-6" />
|
||||
Chart not available
|
||||
{t('swap:chart-not-available')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -395,7 +397,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{inputTokenInfo.market_cap_rank ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Market Cap Rank
|
||||
{t('swap:market-cap-rank')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
#{inputTokenInfo.market_cap_rank}
|
||||
|
@ -406,7 +408,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
inputTokenInfo.market_data?.market_cap?.usd !== 0 ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Market Cap
|
||||
{t('swap:market-cap')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
$
|
||||
|
@ -419,7 +421,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{inputTokenInfo.market_data.total_volume?.usd ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
24h Volume
|
||||
{t('daily-volume')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
$
|
||||
|
@ -432,7 +434,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{inputTokenInfo.market_data?.circulating_supply ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Token Supply
|
||||
{t('swap:token-supply')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
{numberCompacter.format(
|
||||
|
@ -441,7 +443,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
</div>
|
||||
{inputTokenInfo.market_data?.max_supply ? (
|
||||
<div className="text-th-fgd-2 text-xs">
|
||||
Max Supply:{' '}
|
||||
{t('swap:max-supply')}:{' '}
|
||||
{numberCompacter.format(
|
||||
inputTokenInfo.market_data.max_supply
|
||||
)}
|
||||
|
@ -452,7 +454,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{inputTokenInfo.market_data?.ath?.usd ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
All-Time High
|
||||
{t('swap:ath')}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
|
@ -490,7 +492,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{inputTokenInfo.market_data?.atl?.usd ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
All-Time Low
|
||||
{t('swap:atl')}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
|
@ -568,7 +570,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
</div>
|
||||
) : (
|
||||
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
|
||||
Input token information is not available.
|
||||
{t('swap:input-info-unavailable')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -646,7 +648,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{outputTokenInfo.market_cap_rank ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Market Cap Rank
|
||||
{t('swap:market-cap-rank')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
#{outputTokenInfo.market_cap_rank}
|
||||
|
@ -657,7 +659,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
outputTokenInfo.market_data?.market_cap?.usd !== 0 ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Market Cap
|
||||
{t('swap:market-cap')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
$
|
||||
|
@ -670,7 +672,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{outputTokenInfo.market_data.total_volume?.usd ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
24h Volume
|
||||
{t('daily-volume')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
$
|
||||
|
@ -683,7 +685,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{outputTokenInfo.market_data?.circulating_supply ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Token Supply
|
||||
{t('swap:token-supply')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
{numberCompacter.format(
|
||||
|
@ -692,7 +694,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
</div>
|
||||
{outputTokenInfo.market_data?.max_supply ? (
|
||||
<div className="text-th-fgd-2 text-xs">
|
||||
Max Supply:{' '}
|
||||
{t('swap:max-supply')}:{' '}
|
||||
{numberCompacter.format(
|
||||
outputTokenInfo.market_data.max_supply
|
||||
)}
|
||||
|
@ -703,7 +705,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{outputTokenInfo.market_data?.ath?.usd ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
All-Time High
|
||||
{t('swap:ath')}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
|
@ -741,7 +743,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{outputTokenInfo.market_data?.atl?.usd ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
All-Time Low
|
||||
{t('swap:atl')}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="font-bold text-th-fgd-1 text-lg">
|
||||
|
@ -819,7 +821,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
</div>
|
||||
) : (
|
||||
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
|
||||
Output token information is not available.
|
||||
{t('swap:output-info-unavailable')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,6 @@ import { numberCompacter, numberFormatter } from './SwapTokenInfo'
|
|||
import Button, { IconButton } from './Button'
|
||||
import Input from './Input'
|
||||
import { SearchIcon, XIcon } from '@heroicons/react/outline'
|
||||
import { time } from 'console'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -151,7 +150,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
(t) => t?.extensions?.coingeckoId === insight.id
|
||||
)
|
||||
return (
|
||||
<Disclosure>
|
||||
<Disclosure key={insight.id}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div
|
||||
|
|
|
@ -51,8 +51,7 @@ const TVChartContainer = () => {
|
|||
const openOrders = useOpenOrders()
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
const selectedMarginAccount =
|
||||
useMangoStore.getState().selectedMangoAccount.current
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const selectedMarketPrice = useMangoStore((s) => s.selectedMarket.markPrice)
|
||||
const [lines, setLines] = useState(new Map())
|
||||
const [moveInProgress, toggleMoveInProgress] = useState(false)
|
||||
|
@ -563,7 +562,7 @@ const TVChartContainer = () => {
|
|||
useInterval(() => {
|
||||
if (showOrderLines) {
|
||||
if (
|
||||
selectedMarginAccount &&
|
||||
mangoAccount &&
|
||||
connected &&
|
||||
!moveInProgress &&
|
||||
!orderInProgress &&
|
||||
|
@ -589,7 +588,7 @@ const TVChartContainer = () => {
|
|||
lines?.size != openOrdersInSelectedMarket ||
|
||||
matches != openOrdersInSelectedMarket ||
|
||||
(lines?.size > 0 && lines?.size != matches) ||
|
||||
(lines?.size > 0 && !selectedMarginAccount) ||
|
||||
(lines?.size > 0 && !mangoAccount) ||
|
||||
priceReset
|
||||
) {
|
||||
if (priceReset) {
|
||||
|
@ -604,7 +603,7 @@ const TVChartContainer = () => {
|
|||
} else if (lines?.size > 0) {
|
||||
setLines(deleteLines())
|
||||
}
|
||||
}, [100])
|
||||
}, [500])
|
||||
|
||||
return <div id={defaultProps.containerId} className="tradingview-chart" />
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ const handleUsdDustTicks = (v) =>
|
|||
|
||||
const AccountInterest = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
|
@ -81,8 +82,10 @@ const AccountInterest = () => {
|
|||
)
|
||||
|
||||
const mangoAccountPk = useMemo(() => {
|
||||
return mangoAccount.publicKey.toString()
|
||||
}, [mangoAccount])
|
||||
if (connected) {
|
||||
return mangoAccount.publicKey.toString()
|
||||
}
|
||||
}, [connected, mangoAccount])
|
||||
|
||||
const token = useMemo(() => {
|
||||
if (selectedAsset) {
|
||||
|
@ -287,16 +290,24 @@ const AccountInterest = () => {
|
|||
})
|
||||
}
|
||||
})
|
||||
if (dailyInterest.length <= 1) {
|
||||
const chartFunding = []
|
||||
hourlyInterestStats[selectedAsset].forEach((a) => {
|
||||
chartFunding.push({
|
||||
funding: a.total_funding,
|
||||
time: a.time,
|
||||
|
||||
if (dailyInterest.length === 1) {
|
||||
const chartInterest = []
|
||||
filtered.forEach((a) => {
|
||||
chartInterest.push({
|
||||
time: new Date(a.time).getTime(),
|
||||
interest:
|
||||
a.borrow_interest > 0
|
||||
? a.borrow_interest * -1
|
||||
: a.deposit_interest,
|
||||
value:
|
||||
a.borrow_interest > 0
|
||||
? a.borrow_interest * a.price * -1
|
||||
: a.deposit_interest * a.price,
|
||||
})
|
||||
})
|
||||
setShowHours(true)
|
||||
setChartData(chartFunding.reverse())
|
||||
setChartData(chartInterest.reverse())
|
||||
} else {
|
||||
setShowHours(false)
|
||||
setChartData(dailyInterest.reverse())
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
import { useEffect, useMemo, useState } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import useMangoStore from '../../stores/useMangoStore'
|
||||
import { Table, Td, Th, TrBody, TrHead } from '../TableElements'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { isEmpty } from 'lodash'
|
||||
import usePagination from '../../hooks/usePagination'
|
||||
import { numberCompactFormatter } from '../../utils/'
|
||||
import Pagination from '../Pagination'
|
||||
import Chart from '../Chart'
|
||||
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
dayjs.extend(utc)
|
||||
import { exportDataToCSV } from '../../utils/export'
|
||||
import { SaveIcon } from '@heroicons/react/outline'
|
||||
import Button from '../Button'
|
||||
|
||||
export const handleDustTicks = (v) =>
|
||||
v < 0.000001
|
||||
? v === 0
|
||||
? 0
|
||||
: v.toExponential()
|
||||
: numberCompactFormatter.format(v)
|
||||
|
||||
const AccountPerformance = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const [hourlyPerformanceStats, setHourlyPerformanceStats] = useState<any>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [chartData, setChartData] = useState([])
|
||||
const {
|
||||
paginatedData,
|
||||
setData,
|
||||
totalPages,
|
||||
nextPage,
|
||||
previousPage,
|
||||
page,
|
||||
firstPage,
|
||||
lastPage,
|
||||
} = usePagination(hourlyPerformanceStats)
|
||||
|
||||
const mangoAccountPk = useMemo(() => {
|
||||
return mangoAccount.publicKey.toString()
|
||||
}, [mangoAccount])
|
||||
|
||||
const exportPerformanceDataToCSV = () => {
|
||||
const dataToExport = hourlyPerformanceStats.map((row) => {
|
||||
const timestamp = new Date(row.time)
|
||||
return {
|
||||
timestamp: `${timestamp.toLocaleDateString()} ${timestamp.toLocaleTimeString()}`,
|
||||
account_equity: row.account_equity,
|
||||
pnl: row.pnl,
|
||||
}
|
||||
})
|
||||
|
||||
const title = `${
|
||||
mangoAccount.name || mangoAccount.publicKey
|
||||
}-Performance-${new Date().toLocaleDateString()}`
|
||||
const headers = ['Timestamp', 'Account Equity', 'PNL']
|
||||
|
||||
exportDataToCSV(dataToExport, title, headers, t)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(hourlyPerformanceStats)) {
|
||||
setData(hourlyPerformanceStats)
|
||||
}
|
||||
}, [hourlyPerformanceStats])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchHourlyPerformanceStats = async () => {
|
||||
setLoading(true)
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v3/stats/account-performance?mango-account=${mangoAccountPk}`
|
||||
)
|
||||
const parsedResponse = await response.json()
|
||||
const entries: any = Object.entries(parsedResponse)
|
||||
|
||||
const stats = entries
|
||||
.map(([key, value]) => {
|
||||
return { ...value, time: key }
|
||||
})
|
||||
.filter((x) => x)
|
||||
.reverse()
|
||||
|
||||
setLoading(false)
|
||||
setHourlyPerformanceStats(stats)
|
||||
}
|
||||
|
||||
fetchHourlyPerformanceStats()
|
||||
}, [mangoAccountPk])
|
||||
|
||||
useEffect(() => {
|
||||
if (hourlyPerformanceStats) {
|
||||
setChartData(hourlyPerformanceStats.slice().reverse())
|
||||
}
|
||||
}, [hourlyPerformanceStats])
|
||||
|
||||
const increaseYAxisWidth = !!chartData.find((data) => data.value < 0.001)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between pb-4">
|
||||
<div className="text-th-fgd-1 text-lg">{t('account-performance')}</div>
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className={`float-right text-xs h-8 pt-0 pb-0 pl-3 pr-3`}
|
||||
onClick={exportPerformanceDataToCSV}
|
||||
>
|
||||
<div className={`flex items-center whitespace-nowrap`}>
|
||||
<SaveIcon className={`h-4 w-4 mr-1.5`} />
|
||||
{t('export-data')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{mangoAccount ? (
|
||||
<div>
|
||||
<>
|
||||
{!isEmpty(hourlyPerformanceStats) && !loading ? (
|
||||
<>
|
||||
{chartData.length > 0 ? (
|
||||
<div className="flex flex-col sm:flex-row space-x-0 sm:space-x-4 w-full">
|
||||
<div
|
||||
className="border border-th-bkg-4 relative mb-6 p-4 rounded-md w-full sm:w-1/2"
|
||||
style={{ height: '330px' }}
|
||||
>
|
||||
<Chart
|
||||
title={t('account-equity-chart-title')}
|
||||
xAxis="time"
|
||||
yAxis="account_equity"
|
||||
data={chartData}
|
||||
labelFormat={(x) => x && x.toFixed(6 + 1)}
|
||||
tickFormat={handleDustTicks}
|
||||
type="area"
|
||||
yAxisWidth={increaseYAxisWidth ? 70 : 50}
|
||||
showAll
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="border border-th-bkg-4 relative mb-6 p-4 rounded-md w-full sm:w-1/2"
|
||||
style={{ height: '330px' }}
|
||||
>
|
||||
<Chart
|
||||
title={t('account-pnl-chart-title')}
|
||||
xAxis="time"
|
||||
yAxis="pnl"
|
||||
data={chartData}
|
||||
labelFormat={(x) => x && x.toFixed(6 + 1)}
|
||||
tickFormat={handleDustTicks}
|
||||
type="area"
|
||||
yAxisWidth={increaseYAxisWidth ? 70 : 50}
|
||||
showAll
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div>
|
||||
<div>
|
||||
{paginatedData.length ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>{t('time')}</Th>
|
||||
<Th>{t('account-equity')}</Th>
|
||||
<Th>{t('account-pnl')}</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paginatedData.map((stat, index) => {
|
||||
// @ts-ignore
|
||||
const utc = dayjs.utc(stat.time).format()
|
||||
return (
|
||||
<TrBody index={index} key={stat.time}>
|
||||
<Td>{dayjs(utc).format('DD/MM/YY, h:mma')}</Td>
|
||||
<Td>{stat.account_equity.toFixed(6 + 1)}</Td>
|
||||
<Td>{stat.pnl.toFixed(6 + 1)}</Td>
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<div className="flex justify-center w-full bg-th-bkg-3 py-4 text-th-fgd-3">
|
||||
{t('no-performance-history')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Pagination
|
||||
page={page}
|
||||
totalPages={totalPages}
|
||||
nextPage={nextPage}
|
||||
lastPage={lastPage}
|
||||
firstPage={firstPage}
|
||||
previousPage={previousPage}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : loading ? (
|
||||
<div className="pt-8 space-y-2">
|
||||
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
|
||||
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
|
||||
<div className="animate-pulse bg-th-bkg-3 h-12 rounded-md w-full" />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<div>{t('connect-wallet')}</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountPerformance
|
|
@ -709,26 +709,17 @@ export const MsolMonoIcon = ({ className }) => {
|
|||
viewBox="0 0 32 32"
|
||||
fill="currentColor"
|
||||
>
|
||||
<g clipPath="url(#clip0_1551_2618)">
|
||||
<path
|
||||
opacity="0.4"
|
||||
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"
|
||||
/>
|
||||
<path
|
||||
opacity="0.4"
|
||||
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"
|
||||
/>
|
||||
<path
|
||||
opacity="0.4"
|
||||
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"
|
||||
/>
|
||||
<path d="M11.9267 5.932C10.3455 6.44311 9.11805 7.29156 8.24423 8.47733C7.39123 9.64267 6.96472 11.0942 6.96472 12.832C6.96472 13.6089 7.03754 14.1404 7.18317 14.4267C7.32881 14.7129 7.40163 14.8867 7.40163 14.948C5.17549 14.948 4.06242 14.0178 4.06242 12.1573C4.06242 10.8898 4.55133 9.62222 5.52917 8.35467C6.52782 7.06667 7.79692 6.024 9.3365 5.22667C10.8969 4.40889 12.4469 4 13.9864 4C14.7562 4 15.5468 4.14311 16.3582 4.42933L15.6404 20.5907L22.5061 4.42933H27L23.7232 27H19.2293L21.2266 13.2307L15.3596 27H11.0841L11.7083 13.1693L6.49661 27H4L11.9267 5.932Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1551_2618">
|
||||
<rect width="32" height="32" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<path
|
||||
opacity=".4"
|
||||
d="M16 32c8.837 0 16-7.163 16-16S24.837 0 16 0 0 7.163 0 16s7.163 16 16 16Z"
|
||||
/>
|
||||
<path d="M5.295 11.89c-.213-.2-.014-.448.16-.585.22-.205.437-.428.643-.625.917-.933 1.834-1.84 2.87-2.632.141-.466.793-.08.518.295-1.294 1.03-2.466 2.212-3.636 3.382l.016-.015a.078.078 0 0 0 .01-.01c-.147.162-.368.384-.58.19ZM22.924 12.238a.293.293 0 0 1 .055-.424c1.16-1.27 2.286-2.564 3.505-3.778.109-.149.372-.165.47.012.097.16.012.327-.109.445-.272.305-.58.575-.858.873a56.611 56.611 0 0 0-2.06 2.284c-.279.221-.595.942-1.003.588Z" />
|
||||
<path d="M9.337 8.42a.285.285 0 0 1-.271-.271.29.29 0 0 1 .313-.294c.365-.05.69-.08 1.05-.1 5.329-.367 10.695-.216 16.017.06.156-.012.294.13.296.282-.009.41-.499.284-.768.281-5.53-.287-11.112-.445-16.637.043ZM22.591 12.474c-.087-.004-.168-.007-.236 0-3.076-.173-6.166-.152-9.253-.13-2.196.015-4.392.03-6.577-.024a.794.794 0 0 0-.262-.09 2.704 2.704 0 0 0-.42-.105 8.084 8.084 0 0 0-.166-.033.765.765 0 0 0-.3-.059c-.004 0-.009 0-.014-.002a.096.096 0 0 0 .013.002h-.004a1.667 1.667 0 0 0-.192-.021.27.27 0 0 1 .126-.195c.187-.131.42-.136.638-.106.907.117 1.816.091 2.727.066.547-.013 1.093-.029 1.64-.012 1.268.024 2.539.014 3.81.003 1.586-.013 3.175-.026 4.761.032.71-.013 1.42.014 2.129.042.636.025 1.273.05 1.908.047a.296.296 0 0 1 .367.23c.019.315-.248.36-.523.36-.056 0-.115-.003-.172-.005Z" />
|
||||
<path d="M6.155 12.214h.026a.04.04 0 0 1-.026 0ZM5.613 12.068a.74.74 0 0 0-.235-.037c-.004 0-.01 0-.014-.002.005 0 .01 0 .013.002h-.004a1.256 1.256 0 0 0-.234-.023h-.024a.334.334 0 0 1 .042-.347c.102-.13.183-.329.374-.332.317.002.37.364.168.554a.257.257 0 0 1-.054.194l-.032-.01Z" />
|
||||
<path d="M6.758 12.372c-.267-.008-.55-.032-.81-.036-.278-.026-.593.052-.847-.084-.392-.274.243-1.066.593-.99.222.049.317.355.124.5.336.01.66.024.996.043.367.042.312.595-.056.567ZM22.53 14.357c-5.527-.212-11.058-.005-16.587-.07-.35-.014-.362-.552 0-.565 5.669.045 11.338-.126 17.004.07.364-.01.394.55.02.567a7.46 7.46 0 0 1-.437-.002ZM26.04 18.47c-3.308-.163-6.632-.075-9.947-.118-1.267-.019-2.526-.094-3.791-.059-.902-.014-1.81.174-2.707.11-.33-.088-.26-.581.093-.557 1.822-.06 3.652-.217 5.48-.077 2.419.036 4.84 0 7.26.06 1.219.01 2.442-.046 3.656.073.253.008.658-.076.683.277-.01.388-.472.29-.735.29h.008Z" />
|
||||
<path d="m9.386 18.325-.024-.021-.012-.01c.013.01.026.022.042.032-1.46-1.162-2.606-2.638-3.858-4.004-.3-.235.051-.672.333-.459 1.095 1.098 1.992 2.376 3.175 3.387.243.244.509.453.76.68.224.263-.16.633-.416.395ZM26.464 18.45a.288.288 0 0 1-.151-.47c-1.025-1.296-2.23-2.447-3.45-3.556-.556-.19 0-.905.37-.437a31.314 31.314 0 0 1 3.367 3.41c.16.185.252.34.384.532.158.33-.209.608-.52.521ZM9.463 20.506a.287.287 0 0 1-.21-.341c.042-.188.226-.235.396-.21.225-.042.453-.071.68-.088 5.335-.307 10.704-.06 16.052-.087.35.01.366.552.003.564-5.637.048-11.319-.27-16.92.162ZM5.983 24.456c-.399.057-.502-.528-.084-.566.623.038 1.238-.021 1.86-.047 5.11-.078 10.22.014 15.332.045.32.07.263.568-.064.56-5.699-.014-11.39-.14-17.084.005l.04.003Z" />
|
||||
<path d="M22.95 24.273c-.312-.243.043-.55.235-.73l-.02.018c1.058-1.139 1.992-2.392 3.09-3.492.026-.339.559-.323.562.02a.491.491 0 0 1-.181.403c-.883.871-1.624 1.872-2.46 2.788-.267.322-.558.63-.836.94a.295.295 0 0 1-.39.053ZM5.667 24.265c-.481-.222-.016-.658.21-.87.627-.7 1.282-1.425 1.971-2.056.43-.438.883-.85 1.341-1.257.084-.08.192-.164.31-.162.312-.013.402.445.106.55-1.26 1.1-2.417 2.32-3.534 3.55-.099.144-.189.313-.404.245Z" />
|
||||
<path d="M9.29 20.183c5.717-.486 11.512-.144 17.263-.182-1.033 1.226-1.943 2.582-2.963 3.824l-.004.004c-.1.105-.23.244-.302.389-5.3-.04-10.603-.145-15.903-.053-.557.026-1.105.08-1.662.07 1.13-1.405 2.3-2.794 3.572-4.052ZM26.65 18.126c-.06 0-.117.005-.169.003-1.264-.136-2.536-.074-3.804-.085-2.517-.067-5.035-.026-7.552-.067-1.895-.16-3.793.017-5.684.085-.231-.221-.469-.43-.691-.676-1.074-1.004-1.92-2.239-2.876-3.369 5.636.063 11.274-.156 16.908.08.073.002.147.003.22.003a.43.43 0 0 0 .129.07c1.24 1.237 2.468 2.518 3.519 3.956ZM26.655 8.219c.05 0 .11.005.171.01-1.163 1.225-2.252 2.516-3.372 3.783a.288.288 0 0 0-.075.08c-.673.004-1.35-.023-2.025-.052a37.57 37.57 0 0 0-2.274-.046c-1.694-.064-3.39-.05-5.086-.035-1.357.012-2.713.023-4.069-.004-.584-.018-1.167 0-1.75.014-.974.028-1.944.056-2.914-.073-.024-.005-.045-.007-.07-.01l.008-.013c1.206-1.248 2.417-2.506 3.745-3.612 5.882-.532 11.822-.358 17.71-.042Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
ChartBarIcon,
|
||||
CurrencyDollarIcon,
|
||||
CalculatorIcon,
|
||||
} from '@heroicons/react/solid'
|
||||
import { ChartBarIcon, CurrencyDollarIcon } from '@heroicons/react/solid'
|
||||
import { BtcMonoIcon, TradeIcon } from '../icons'
|
||||
import useMangoGroupConfig from '../../hooks/useMangoGroupConfig'
|
||||
import MarketsModal from '../MarketsModal'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const StyledBarItemLabel = ({ children, ...props }) => (
|
||||
|
@ -19,36 +12,25 @@ const StyledBarItemLabel = ({ children, ...props }) => (
|
|||
|
||||
const BottomBar = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const [showMarketsModal, setShowMarketsModal] = useState(false)
|
||||
const [sortedMarkets, setSortedMarkets] = useState([])
|
||||
const { asPath } = useRouter()
|
||||
const groupConfig = useMangoGroupConfig()
|
||||
|
||||
useEffect(() => {
|
||||
const markets = []
|
||||
const allMarkets = [...groupConfig.spotMarkets, ...groupConfig.perpMarkets]
|
||||
allMarkets.forEach((market) => {
|
||||
const base = market.name.slice(0, -5)
|
||||
const found = markets.find((b) => b.baseAsset === base)
|
||||
if (!found) {
|
||||
markets.push({ baseAsset: base, markets: [market] })
|
||||
} else {
|
||||
found.markets.push(market)
|
||||
}
|
||||
})
|
||||
setSortedMarkets(markets)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-th-bkg-1 default-transition grid grid-cols-5 grid-rows-1 py-2.5">
|
||||
<div
|
||||
className="col-span-1 cursor-pointer default-transition flex flex-col items-center text-th-fgd-3 hover:text-th-primary"
|
||||
onClick={() => setShowMarketsModal(true)}
|
||||
<div className="bg-th-bkg-1 default-transition grid grid-cols-4 grid-rows-1 py-2.5">
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/select',
|
||||
}}
|
||||
>
|
||||
<BtcMonoIcon className="h-4 mb-1 w-4" />
|
||||
<StyledBarItemLabel>{t('markets')}</StyledBarItemLabel>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
asPath === '/select' ? 'text-th-primary' : 'text-th-fgd-3'
|
||||
} col-span-1 cursor-pointer default-transition flex flex-col items-center hover:text-th-primary`}
|
||||
>
|
||||
<BtcMonoIcon className="h-4 mb-1 w-4" />
|
||||
<StyledBarItemLabel>{t('markets')}</StyledBarItemLabel>
|
||||
</div>
|
||||
</Link>
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/market',
|
||||
|
@ -58,7 +40,7 @@ const BottomBar = () => {
|
|||
>
|
||||
<div
|
||||
className={`${
|
||||
asPath === '/' || asPath.includes('market')
|
||||
asPath === '/' || asPath.startsWith('/market')
|
||||
? 'text-th-primary'
|
||||
: 'text-th-fgd-3'
|
||||
} col-span-1 cursor-pointer default-transition flex flex-col items-center hover:text-th-primary`}
|
||||
|
@ -87,26 +69,7 @@ const BottomBar = () => {
|
|||
<StyledBarItemLabel>{t('stats')}</StyledBarItemLabel>
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="/risk-calculator">
|
||||
<div
|
||||
className={`${
|
||||
asPath === '/risk-calculator'
|
||||
? 'text-th-primary'
|
||||
: 'text-th-fgd-3'
|
||||
} col-span-1 cursor-pointer default-transition flex flex-col items-center hover:text-th-primary`}
|
||||
>
|
||||
<CalculatorIcon className="h-4 mb-1 w-4" />
|
||||
<StyledBarItemLabel>{t('calculator')}</StyledBarItemLabel>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
{showMarketsModal ? (
|
||||
<MarketsModal
|
||||
isOpen={showMarketsModal}
|
||||
onClose={() => setShowMarketsModal(false)}
|
||||
markets={sortedMarkets}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import RecentMarketTrades from '../RecentMarketTrades'
|
|||
import FloatingElement from '../FloatingElement'
|
||||
import Swipeable from './Swipeable'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Link from 'next/link'
|
||||
|
||||
const TVChartContainer = dynamic(
|
||||
() => import('../../components/TradingView/index'),
|
||||
|
@ -54,27 +55,29 @@ const MobileTradePage = () => {
|
|||
return (
|
||||
<div className="pb-14 pt-4 px-2">
|
||||
<div className="relative">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
width="30"
|
||||
height="30"
|
||||
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
|
||||
className="mr-2"
|
||||
/>
|
||||
<Link href="/select">
|
||||
<div className="flex items-center">
|
||||
<div className="font-semibold pr-0.5 text-xl">{baseSymbol}</div>
|
||||
<span className="text-th-fgd-4 text-xl">
|
||||
{isPerpMarket ? '-' : '/'}
|
||||
</span>
|
||||
<div className="font-semibold pl-0.5 text-xl">
|
||||
{isPerpMarket ? 'PERP' : groupConfig.quoteSymbol}
|
||||
<img
|
||||
alt=""
|
||||
width="30"
|
||||
height="30"
|
||||
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
|
||||
className="mr-2"
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<div className="font-semibold pr-0.5 text-xl">{baseSymbol}</div>
|
||||
<span className="text-th-fgd-4 text-xl">
|
||||
{isPerpMarket ? '-' : '/'}
|
||||
</span>
|
||||
<div className="font-semibold pl-0.5 text-xl">
|
||||
{isPerpMarket ? 'PERP' : groupConfig.quoteSymbol}
|
||||
</div>
|
||||
</div>
|
||||
<span className="border border-th-primary ml-2 px-1 py-0.5 rounded text-xs text-th-primary">
|
||||
{initLeverage}x
|
||||
</span>
|
||||
</div>
|
||||
<span className="border border-th-primary ml-2 px-1 py-0.5 rounded text-xs text-th-primary">
|
||||
{initLeverage}x
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PerpMarket } from '@blockworks-foundation/mango-client'
|
||||
import { useState } from 'react'
|
||||
import { useState, useMemo } from 'react'
|
||||
import useMangoGroupConfig from '../../hooks/useMangoGroupConfig'
|
||||
import useMangoStore from '../../stores/useMangoStore'
|
||||
import Chart from '../Chart'
|
||||
|
@ -7,6 +7,7 @@ import BN from 'bn.js'
|
|||
import { perpContractPrecision } from '../../utils'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Select from '../Select'
|
||||
import { marketsSelector } from '../../stores/selectors'
|
||||
|
||||
function calculateFundingRate(
|
||||
oldestLongFunding,
|
||||
|
@ -42,44 +43,57 @@ export default function StatsPerps({ perpStats }) {
|
|||
const selectedMarketConfig = marketConfigs.find(
|
||||
(m) => m.name === selectedAsset
|
||||
)
|
||||
const markets = Object.values(
|
||||
useMangoStore.getState().selectedMangoGroup.markets
|
||||
).filter((m) => m instanceof PerpMarket) as PerpMarket[]
|
||||
const selectedMarket = markets.find((m) =>
|
||||
m.publicKey.equals(selectedMarketConfig.publicKey)
|
||||
)
|
||||
let selectedStatsData = perpStats.filter(
|
||||
(stat) => stat.name === selectedAsset
|
||||
)
|
||||
let markets = useMangoStore(marketsSelector)
|
||||
markets = Object.values(markets)
|
||||
|
||||
if (selectedAsset == 'SOL-PERP') {
|
||||
const startTimestamp = 1632160800000
|
||||
selectedStatsData = selectedStatsData.filter(
|
||||
(stat) => new Date(stat.hourly).getTime() >= startTimestamp
|
||||
const perpMarkets = useMemo(() => {
|
||||
return markets.filter((m) => m instanceof PerpMarket) as PerpMarket[]
|
||||
}, [markets])
|
||||
|
||||
const selectedMarket = useMemo(() => {
|
||||
return perpMarkets.find((m) =>
|
||||
m.publicKey.equals(selectedMarketConfig.publicKey)
|
||||
)
|
||||
}
|
||||
}, [selectedMarketConfig, perpMarkets])
|
||||
|
||||
const perpsData = selectedStatsData.map((x) => {
|
||||
return {
|
||||
fundingRate: calculateFundingRate(
|
||||
x.oldestLongFunding,
|
||||
x.oldestShortFunding,
|
||||
x.latestLongFunding,
|
||||
x.latestShortFunding,
|
||||
selectedMarket,
|
||||
x.baseOraclePrice
|
||||
),
|
||||
openInterest: selectedMarket.baseLotsToNumber(x.openInterest) / 2,
|
||||
time: x.hourly,
|
||||
const perpsData = useMemo(() => {
|
||||
if (perpStats.length === 0) return []
|
||||
|
||||
let selectedStatsData = perpStats.filter(
|
||||
(stat) => stat.name === selectedAsset
|
||||
)
|
||||
|
||||
if (selectedAsset == 'SOL-PERP') {
|
||||
const startTimestamp = 1632160800000
|
||||
selectedStatsData = selectedStatsData.filter(
|
||||
(stat) => new Date(stat.hourly).getTime() >= startTimestamp
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
if (selectedAsset === 'BTC-PERP') {
|
||||
const index = perpsData.findIndex(
|
||||
(x) => x.time === '2021-09-15T05:00:00.000Z'
|
||||
)
|
||||
perpsData.splice(index, 1)
|
||||
}
|
||||
const perpsData = selectedStatsData.map((x) => {
|
||||
return {
|
||||
fundingRate: calculateFundingRate(
|
||||
x.oldestLongFunding,
|
||||
x.oldestShortFunding,
|
||||
x.latestLongFunding,
|
||||
x.latestShortFunding,
|
||||
selectedMarket,
|
||||
x.baseOraclePrice
|
||||
),
|
||||
openInterest: selectedMarket.baseLotsToNumber(x.openInterest) / 2,
|
||||
time: x.hourly,
|
||||
}
|
||||
})
|
||||
|
||||
if (selectedAsset === 'BTC-PERP') {
|
||||
const index = perpsData.findIndex(
|
||||
(x) => x.time === '2021-09-15T05:00:00.000Z'
|
||||
)
|
||||
perpsData.splice(index, 1)
|
||||
}
|
||||
|
||||
return perpsData
|
||||
}, [selectedAsset, perpStats, selectedMarket])
|
||||
|
||||
const progress =
|
||||
1 -
|
||||
|
|
|
@ -33,8 +33,11 @@ import EstPriceImpact from './EstPriceImpact'
|
|||
import useFees from '../../hooks/useFees'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import useSrmAccount from '../../hooks/useSrmAccount'
|
||||
import { useLocalStorageStringState } from '../../hooks/useLocalStorageState'
|
||||
import useLocalStorageState, {
|
||||
useLocalStorageStringState,
|
||||
} from '../../hooks/useLocalStorageState'
|
||||
import InlineNotification from '../InlineNotification'
|
||||
import { DEFAULT_SPOT_MARGIN_KEY } from '../SettingsModal'
|
||||
|
||||
const MAX_SLIPPAGE_KEY = 'maxSlippage'
|
||||
|
||||
|
@ -64,7 +67,11 @@ export default function AdvancedTradeForm({
|
|||
const market = useMangoStore((s) => s.selectedMarket.current)
|
||||
const isPerpMarket = market instanceof PerpMarket
|
||||
const [reduceOnly, setReduceOnly] = useState(false)
|
||||
const [spotMargin, setSpotMargin] = useState(true)
|
||||
const [defaultSpotMargin] = useLocalStorageState(
|
||||
DEFAULT_SPOT_MARGIN_KEY,
|
||||
false
|
||||
)
|
||||
const [spotMargin, setSpotMargin] = useState(defaultSpotMargin)
|
||||
const [positionSizePercent, setPositionSizePercent] = useState('')
|
||||
const [insufficientSol, setInsufficientSol] = useState(false)
|
||||
const { takerFee, makerFee } = useFees()
|
||||
|
@ -170,7 +177,7 @@ export default function AdvancedTradeForm({
|
|||
}
|
||||
}, [set, tradeType, side])
|
||||
|
||||
const { max, deposits, borrows, spotMax } = useMemo(() => {
|
||||
const { max, deposits, borrows, spotMax, reduceMax } = useMemo(() => {
|
||||
if (!mangoAccount) return { max: 0 }
|
||||
const priceOrDefault = price
|
||||
? I80F48.fromNumber(price)
|
||||
|
@ -211,6 +218,14 @@ export default function AdvancedTradeForm({
|
|||
priceOrDefault
|
||||
)
|
||||
|
||||
let reduceMax
|
||||
if (market && market instanceof PerpMarket) {
|
||||
reduceMax =
|
||||
Math.abs(market?.baseLotsToNumber(perpAccount?.basePosition)) || 0
|
||||
} else {
|
||||
reduceMax = 0
|
||||
}
|
||||
|
||||
if (maxQuote.toNumber() <= 0) return { max: 0 }
|
||||
// multiply the maxQuote by a scaler value to account for
|
||||
// srm fees or rounding issues in getMaxLeverageForMarket
|
||||
|
@ -220,8 +235,17 @@ export default function AdvancedTradeForm({
|
|||
: (maxQuote.toNumber() * maxScaler) /
|
||||
mangoGroup.getPrice(marketIndex, mangoCache).toNumber()
|
||||
|
||||
return { max: scaledMax, deposits, borrows, spotMax }
|
||||
}, [mangoAccount, mangoGroup, mangoCache, marketIndex, market, side, price])
|
||||
return { max: scaledMax, deposits, borrows, spotMax, reduceMax }
|
||||
}, [
|
||||
mangoAccount,
|
||||
mangoGroup,
|
||||
mangoCache,
|
||||
marketIndex,
|
||||
market,
|
||||
side,
|
||||
price,
|
||||
reduceOnly,
|
||||
])
|
||||
|
||||
const onChangeSide = (side) => {
|
||||
setPositionSizePercent('')
|
||||
|
@ -402,19 +426,23 @@ export default function AdvancedTradeForm({
|
|||
setIoc(checked)
|
||||
}
|
||||
const reduceOnChange = (checked) => {
|
||||
handleSetPositionSize(positionSizePercent, spotMargin, checked)
|
||||
setReduceOnly(checked)
|
||||
}
|
||||
const marginOnChange = (checked) => {
|
||||
setSpotMargin(checked)
|
||||
if (positionSizePercent) {
|
||||
handleSetPositionSize(positionSizePercent, checked)
|
||||
handleSetPositionSize(positionSizePercent, checked, reduceOnly)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSetPositionSize = (percent, spotMargin) => {
|
||||
const handleSetPositionSize = (percent, spotMargin, reduceOnly) => {
|
||||
setPositionSizePercent(percent)
|
||||
const baseSizeMax =
|
||||
spotMargin || marketConfig.kind === 'perp' ? max : spotMax
|
||||
const baseSizeMax = reduceOnly
|
||||
? reduceMax
|
||||
: spotMargin || marketConfig.kind === 'perp'
|
||||
? max
|
||||
: spotMax
|
||||
const baseSize = baseSizeMax * (parseInt(percent) / 100)
|
||||
const step = parseFloat(minOrderSize)
|
||||
const roundedSize = (Math.floor(baseSize / step) * step).toFixed(
|
||||
|
@ -493,10 +521,8 @@ export default function AdvancedTradeForm({
|
|||
}
|
||||
|
||||
const estimatedSize =
|
||||
perpAccount && reduceOnly
|
||||
? Math.abs(
|
||||
(market as PerpMarket).baseLotsToNumber(perpAccount.basePosition)
|
||||
)
|
||||
perpAccount && reduceOnly && market instanceof PerpMarket
|
||||
? Math.abs(market.baseLotsToNumber(perpAccount.basePosition))
|
||||
: baseSize
|
||||
estimatedPrice = estimateMarketPrice(orderbook, estimatedSize || 0, side)
|
||||
|
||||
|
@ -645,7 +671,18 @@ export default function AdvancedTradeForm({
|
|||
)
|
||||
}
|
||||
}
|
||||
notify({ title: t('successfully-placed'), txid })
|
||||
if (txid instanceof Array) {
|
||||
for (const [index, id] of txid.entries()) {
|
||||
notify({
|
||||
title:
|
||||
index === 0 ? 'Transaction successful' : t('successfully-placed'),
|
||||
txid: id,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
notify({ title: t('successfully-placed'), txid })
|
||||
}
|
||||
|
||||
setPrice('')
|
||||
onSetBaseSize('')
|
||||
} catch (e) {
|
||||
|
@ -840,7 +877,7 @@ export default function AdvancedTradeForm({
|
|||
<div className="col-span-12 -mt-1">
|
||||
<ButtonGroup
|
||||
activeValue={positionSizePercent}
|
||||
onChange={(p) => handleSetPositionSize(p, spotMargin)}
|
||||
onChange={(p) => handleSetPositionSize(p, spotMargin, reduceOnly)}
|
||||
unit="%"
|
||||
values={
|
||||
isMobile
|
||||
|
|
|
@ -13,7 +13,7 @@ const EstPriceImpact = ({
|
|||
return (
|
||||
<div className={`text-th-fgd-3 text-xs`}>
|
||||
<div className="flex justify-between mb-1">
|
||||
{t('slippage')}
|
||||
{t('est-slippage')}
|
||||
<span
|
||||
className={`font-bold opacity-80 ml-2 ${
|
||||
priceImpactRel <= 0.005
|
||||
|
|
|
@ -27,7 +27,7 @@ const TradeType = ({
|
|||
className={`font-normal w-full bg-th-bkg-1 border border-th-bkg-4 px-2 h-10 hover:border-th-primary rounded-md focus:outline-none focus:border-th-primary`}
|
||||
>
|
||||
<div className={`flex items-center justify-between space-x-4`}>
|
||||
<span>{t(value.toLowerCase().replaceAll(' ', '-'))}</span>
|
||||
<span>{t(value?.toLowerCase()?.replaceAll(' ', '-'))}</span>
|
||||
{open ? (
|
||||
<ChevronUpIcon className={`h-5 w-5 mr-1 text-th-primary`} />
|
||||
) : (
|
||||
|
@ -48,7 +48,7 @@ const TradeType = ({
|
|||
selected && `text-th-primary`
|
||||
}`}
|
||||
>
|
||||
{t(type.toLowerCase().replaceAll(' ', '-'))}
|
||||
{t(type?.toLowerCase()?.replaceAll(' ', '-'))}
|
||||
</div>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
|
|
|
@ -19,7 +19,6 @@ import {
|
|||
} from '../stores/selectors'
|
||||
|
||||
const SECONDS = 1000
|
||||
const _SLOW_REFRESH_INTERVAL = 20 * SECONDS
|
||||
|
||||
function decodeBook(market, accInfo: AccountInfo<Buffer>): number[][] {
|
||||
if (market && accInfo?.data) {
|
||||
|
@ -49,18 +48,34 @@ const useHydrateStore = () => {
|
|||
const connection = useMangoStore(connectionSelector)
|
||||
const mangoAccount = useMangoStore(mangoAccountSelector)
|
||||
|
||||
// Fetches mango group as soon as page loads
|
||||
useEffect(() => {
|
||||
actions.fetchMangoGroup()
|
||||
}, [actions])
|
||||
|
||||
useInterval(() => {
|
||||
actions.fetchMangoGroup()
|
||||
}, 120 * SECONDS)
|
||||
|
||||
useInterval(() => {
|
||||
actions.fetchMangoGroupCache()
|
||||
}, 12 * SECONDS)
|
||||
|
||||
useInterval(() => {
|
||||
if (mangoAccount) {
|
||||
actions.fetchTradeHistory()
|
||||
actions.reloadOrders()
|
||||
actions.updateOpenOrders()
|
||||
}
|
||||
}, 60 * SECONDS)
|
||||
|
||||
useInterval(() => {
|
||||
if (mangoAccount) {
|
||||
actions.reloadMangoAccount()
|
||||
actions.fetchWalletTokens()
|
||||
}
|
||||
}, 90 * SECONDS)
|
||||
|
||||
useInterval(() => {
|
||||
actions.fetchMangoGroup()
|
||||
}, 120 * SECONDS)
|
||||
|
||||
useEffect(() => {
|
||||
const market = markets[marketConfig.publicKey.toString()]
|
||||
setMangoStore((state) => {
|
||||
|
@ -81,18 +96,34 @@ const useHydrateStore = () => {
|
|||
console.log('in mango account WS useEffect')
|
||||
const subscriptionId = connection.onAccountChange(
|
||||
mangoAccount.publicKey,
|
||||
(info) => {
|
||||
console.log('mango account WS update: ', info)
|
||||
(info, context) => {
|
||||
if (info?.lamports === 0) return
|
||||
|
||||
const decodedMangoAccount = MangoAccountLayout.decode(info?.data)
|
||||
const newMangoAccount = Object.assign(mangoAccount, decodedMangoAccount)
|
||||
const lastSeenSlot =
|
||||
useMangoStore.getState().selectedMangoAccount.lastSlot
|
||||
const mangoAccountLastUpdated = new Date(
|
||||
useMangoStore.getState().selectedMangoAccount.lastUpdatedAt
|
||||
)
|
||||
const newUpdatedAt = new Date()
|
||||
const timeDiff =
|
||||
mangoAccountLastUpdated.getTime() - newUpdatedAt.getTime()
|
||||
|
||||
// const lastSlot = useMangoStore.getState().connection.slot
|
||||
// only updated mango account if it's been more than 1 second since last update
|
||||
if (Math.abs(timeDiff / 1000) >= 1 && context.slot > lastSeenSlot) {
|
||||
console.log('mango account WS update: ', info)
|
||||
const decodedMangoAccount = MangoAccountLayout.decode(info?.data)
|
||||
const newMangoAccount = Object.assign(
|
||||
mangoAccount,
|
||||
decodedMangoAccount
|
||||
)
|
||||
|
||||
setMangoStore((state) => {
|
||||
state.selectedMangoAccount.current = newMangoAccount
|
||||
state.selectedMangoAccount.lastUpdatedAt = new Date().toISOString()
|
||||
})
|
||||
setMangoStore((state) => {
|
||||
state.selectedMangoAccount.lastSlot = context.slot
|
||||
state.selectedMangoAccount.current = newMangoAccount
|
||||
state.selectedMangoAccount.lastUpdatedAt =
|
||||
newUpdatedAt.toISOString()
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -160,7 +191,7 @@ const useHydrateStore = () => {
|
|||
// fetch filled trades for selected market
|
||||
useInterval(() => {
|
||||
actions.loadMarketFills()
|
||||
}, _SLOW_REFRESH_INTERVAL)
|
||||
}, 20 * SECONDS)
|
||||
|
||||
useEffect(() => {
|
||||
actions.loadMarketFills()
|
||||
|
|
|
@ -55,7 +55,7 @@ export const collectPerpPosition = (
|
|||
console.error(e)
|
||||
}
|
||||
|
||||
const basePosition = perpMarket.baseLotsToNumber(perpAccount.basePosition)
|
||||
const basePosition = perpMarket?.baseLotsToNumber(perpAccount.basePosition)
|
||||
const indexPrice = mangoGroup.getPrice(marketIndex, mangoCache).toNumber()
|
||||
const notionalSize = Math.abs(basePosition * indexPrice)
|
||||
const unrealizedPnl = basePosition * (indexPrice - breakEvenPrice)
|
||||
|
|
|
@ -11,10 +11,8 @@ import {
|
|||
BitpieWalletAdapter,
|
||||
} from '../utils/wallet-adapters'
|
||||
import { WalletAdapter } from '../@types/types'
|
||||
import useInterval from './useInterval'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const SECONDS = 1000
|
||||
const ASSET_URL =
|
||||
'https://cdn.jsdelivr.net/gh/solana-labs/oyster@main/assets/wallets'
|
||||
|
||||
|
@ -67,7 +65,6 @@ export default function useWallet() {
|
|||
providerUrl: selectedProviderUrl,
|
||||
} = useMangoStore((state) => state.wallet)
|
||||
const endpoint = useMangoStore((state) => state.connection.endpoint)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const [savedProviderUrl, setSavedProviderUrl] = useLocalStorageState(
|
||||
PROVIDER_LOCAL_STORAGE_KEY,
|
||||
|
@ -148,18 +145,5 @@ export default function useWallet() {
|
|||
})
|
||||
}, [wallet, setMangoStore])
|
||||
|
||||
useInterval(() => {
|
||||
if (connected && mangoAccount) {
|
||||
actions.fetchWalletTokens()
|
||||
actions.fetchTradeHistory()
|
||||
}
|
||||
}, 90 * SECONDS)
|
||||
|
||||
useInterval(() => {
|
||||
if (connected && mangoAccount) {
|
||||
actions.reloadMangoAccount()
|
||||
}
|
||||
}, 90 * SECONDS)
|
||||
|
||||
return { connected, wallet }
|
||||
}
|
||||
|
|
|
@ -8,18 +8,13 @@
|
|||
"devnet": "NEXT_PUBLIC_CLUSTER=devnet NEXT_PUBLIC_ENDPOINT=https://mango.devnet.rpcpool.com/ NEXT_PUBLIC_GROUP=devnet.2 next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"prepare": "husky install",
|
||||
"type-check": "tsc --pretty --noEmit",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext ts --ext tsx --ext js --quiet",
|
||||
"test": "jest",
|
||||
"test-all": "yarn lint && yarn type-check && yarn test"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged",
|
||||
"pre-push": ""
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.@(ts|tsx)": [
|
||||
"yarn lint",
|
||||
|
@ -27,7 +22,7 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-client": "latest",
|
||||
"@blockworks-foundation/mango-client": "git+https://github.com/blockworks-foundation/mango-client-v3.git",
|
||||
"@headlessui/react": "^1.2.0",
|
||||
"@heroicons/react": "^1.0.0",
|
||||
"@jup-ag/react-hook": "^1.0.0-beta.4",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
CurrencyDollarIcon,
|
||||
DuplicateIcon,
|
||||
|
@ -6,6 +6,7 @@ import {
|
|||
ExternalLinkIcon,
|
||||
LinkIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import useMangoStore, { serumProgramId } from '../stores/useMangoStore'
|
||||
import { copyToClipboard } from '../utils'
|
||||
|
@ -17,6 +18,7 @@ import AccountsModal from '../components/AccountsModal'
|
|||
import AccountOverview from '../components/account_page/AccountOverview'
|
||||
import AccountInterest from '../components/account_page/AccountInterest'
|
||||
import AccountFunding from '../components/account_page/AccountFunding'
|
||||
import AccountPerformance from '../components/account_page/AccountPerformance'
|
||||
import AccountNameModal from '../components/AccountNameModal'
|
||||
import Button from '../components/Button'
|
||||
import EmptyState from '../components/EmptyState'
|
||||
|
@ -30,6 +32,13 @@ import { useTranslation } from 'next-i18next'
|
|||
import Select from '../components/Select'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import CloseAccountModal from '../components/CloseAccountModal'
|
||||
import {
|
||||
actionsSelector,
|
||||
mangoAccountSelector,
|
||||
mangoGroupSelector,
|
||||
walletConnectedSelector,
|
||||
} from '../stores/selectors'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
|
@ -40,21 +49,29 @@ export async function getStaticProps({ locale }) {
|
|||
}
|
||||
}
|
||||
|
||||
const TABS = ['Portfolio', 'Orders', 'History', 'Interest', 'Funding']
|
||||
const TABS = [
|
||||
'Portfolio',
|
||||
'Orders',
|
||||
'History',
|
||||
'Interest',
|
||||
'Funding',
|
||||
'Performance',
|
||||
]
|
||||
|
||||
export default function Account() {
|
||||
const { t } = useTranslation('common')
|
||||
const [showAccountsModal, setShowAccountsModal] = useState(false)
|
||||
const [showNameModal, setShowNameModal] = useState(false)
|
||||
const [showCloseAccountModal, setShowCloseAccountModal] = useState(false)
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
const [resetOnLeave, setResetOnLeave] = useState(false)
|
||||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const connected = useMangoStore(walletConnectedSelector)
|
||||
const mangoAccount = useMangoStore(mangoAccountSelector)
|
||||
const mangoClient = useMangoStore((s) => s.connection.client)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoGroup = useMangoStore(mangoGroupSelector)
|
||||
const wallet = useMangoStore((s) => s.wallet.current)
|
||||
const isLoading = useMangoStore((s) => s.selectedMangoAccount.initialLoad)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const actions = useMangoStore(actionsSelector)
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const [viewIndex, setViewIndex] = useState(0)
|
||||
const [activeTab, setActiveTab] = useState(TABS[0])
|
||||
|
@ -74,6 +91,9 @@ export default function Account() {
|
|||
const handleCloseNameModal = useCallback(() => {
|
||||
setShowNameModal(false)
|
||||
}, [])
|
||||
const handleCloseCloseAccountModal = useCallback(() => {
|
||||
setShowCloseAccountModal(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
async function loadUnownedMangoAccount() {
|
||||
|
@ -167,7 +187,7 @@ export default function Account() {
|
|||
{t('account-address-warning')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 grid-rows-1 gap-2">
|
||||
<div className="grid grid-cols-4 grid-rows-1 gap-2">
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowNameModal(true)}
|
||||
|
@ -177,6 +197,15 @@ export default function Account() {
|
|||
{mangoAccount?.name ? t('edit-name') : t('add-name')}
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowCloseAccountModal(true)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<TrashIcon className="h-4 w-4 mr-1.5" />
|
||||
Close Account
|
||||
</div>
|
||||
</Button>
|
||||
<a
|
||||
className="bg-th-bkg-4 col-span-1 default-transition flex font-bold h-8 items-center justify-center pl-3 pr-3 rounded-full text-th-fgd-1 text-xs hover:text-th-fgd-1 hover:brightness-[1.15] focus:outline-none"
|
||||
href={`https://explorer.solana.com/address/${mangoAccount?.publicKey}`}
|
||||
|
@ -281,6 +310,13 @@ export default function Account() {
|
|||
onClose={handleCloseNameModal}
|
||||
/>
|
||||
) : null}
|
||||
{showCloseAccountModal ? (
|
||||
<CloseAccountModal
|
||||
accountName={mangoAccount?.name}
|
||||
isOpen={showCloseAccountModal}
|
||||
onClose={handleCloseCloseAccountModal}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -297,6 +333,8 @@ const TabContent = ({ activeTab }) => {
|
|||
return <AccountInterest />
|
||||
case 'Funding':
|
||||
return <AccountFunding />
|
||||
case 'Performance':
|
||||
return <AccountPerformance />
|
||||
default:
|
||||
return <AccountOverview />
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import useMangoGroupConfig from '../hooks/useMangoGroupConfig'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import useMangoStore, { serumProgramId } from '../stores/useMangoStore'
|
||||
import {
|
||||
getMarketByBaseSymbolAndKind,
|
||||
getMarketIndexBySymbol,
|
||||
|
@ -17,9 +17,12 @@ import IntroTips, { SHOW_TOUR_KEY } from '../components/IntroTips'
|
|||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from '../components/TradePageGrid'
|
||||
import {
|
||||
actionsSelector,
|
||||
mangoAccountSelector,
|
||||
marketConfigSelector,
|
||||
walletConnectedSelector,
|
||||
} from '../stores/selectors'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
|
@ -36,12 +39,46 @@ const PerpMarket = () => {
|
|||
const groupConfig = useMangoGroupConfig()
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const connected = useMangoStore(walletConnectedSelector)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const mangoAccount = useMangoStore(mangoAccountSelector)
|
||||
const mangoClient = useMangoStore((s) => s.connection.client)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const marketConfig = useMangoStore(marketConfigSelector)
|
||||
const actions = useMangoStore(actionsSelector)
|
||||
const router = useRouter()
|
||||
const { pubkey } = router.query
|
||||
const { width } = useViewport()
|
||||
const hideTips = width ? width < breakpoints.md : false
|
||||
|
||||
useEffect(() => {
|
||||
async function loadUnownedMangoAccount() {
|
||||
try {
|
||||
const unownedMangoAccountPubkey = new PublicKey(pubkey)
|
||||
if (mangoGroup) {
|
||||
const unOwnedMangoAccount = await mangoClient.getMangoAccount(
|
||||
unownedMangoAccountPubkey,
|
||||
serumProgramId
|
||||
)
|
||||
console.log('unOwnedMangoAccount: ', unOwnedMangoAccount)
|
||||
|
||||
setMangoStore((state) => {
|
||||
state.selectedMangoAccount.current = unOwnedMangoAccount
|
||||
state.selectedMangoAccount.initialLoad = false
|
||||
state.wallet.connected = true
|
||||
})
|
||||
actions.fetchTradeHistory()
|
||||
actions.reloadOrders()
|
||||
// setResetOnLeave(true)
|
||||
}
|
||||
} catch (error) {
|
||||
router.push('/account')
|
||||
}
|
||||
}
|
||||
|
||||
if (pubkey) {
|
||||
loadUnownedMangoAccount()
|
||||
}
|
||||
}, [pubkey, mangoClient, mangoGroup])
|
||||
|
||||
useEffect(() => {
|
||||
const name = decodeURIComponent(router.asPath).split('name=')[1]
|
||||
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
|
||||
|
|
|
@ -1295,9 +1295,6 @@ export default function RiskCalculator() {
|
|||
IN TESTING (Use at your own risk): Please report any bugs or
|
||||
comments in our #dev-ui discord channel.
|
||||
</p>
|
||||
{/* <p className="mb-0">
|
||||
Stay healthy to keep the dream alive. We hear that one mango smoothie a day keeps the liquidators away!
|
||||
</p> */}
|
||||
</div>
|
||||
{scenarioBars?.rowData.length > 0 ? (
|
||||
<div className="rounded-lg bg-th-bkg-2">
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ChevronRightIcon } from '@heroicons/react/solid'
|
||||
import useMangoGroupConfig from '../hooks/useMangoGroupConfig'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import Link from 'next/link'
|
||||
import { formatUsdValue } from '../utils'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import PageBodyContainer from '../components/PageBodyContainer'
|
||||
import TopBar from '../components/TopBar'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common', 'tv-chart'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const SelectMarket = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const groupConfig = useMangoGroupConfig()
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
|
||||
|
||||
const [markets, setMarkets] = useState([])
|
||||
useEffect(() => {
|
||||
const markets = []
|
||||
const allMarkets = [...groupConfig.spotMarkets, ...groupConfig.perpMarkets]
|
||||
allMarkets.forEach((market) => {
|
||||
const base = market.name.slice(0, -5)
|
||||
const found = markets.find((b) => b.baseAsset === base)
|
||||
if (!found) {
|
||||
markets.push({ baseAsset: base, markets: [market] })
|
||||
} else {
|
||||
found.markets.push(market)
|
||||
}
|
||||
})
|
||||
setMarkets(markets)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
||||
<TopBar />
|
||||
<PageBodyContainer>
|
||||
<div className="font-bold py-4 text-2xl text-th-fgd-1">
|
||||
{t('markets')}
|
||||
</div>
|
||||
{markets.map((mkt) => (
|
||||
<div key={mkt.baseAsset}>
|
||||
<div className="bg-th-bkg-3 flex items-center justify-between px-2.5 py-2">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
alt=""
|
||||
src={`/assets/icons/${mkt.baseAsset.toLowerCase()}.svg`}
|
||||
className={`h-5 mr-2.5 w-auto`}
|
||||
/>
|
||||
<span className="text-th-fgd-2">{mkt.baseAsset}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y divide-th-bkg-4">
|
||||
{mangoGroup
|
||||
? mkt.markets.map((m) => (
|
||||
<div
|
||||
className={`flex items-center justify-between px-2.5 text-xs`}
|
||||
key={m.name}
|
||||
>
|
||||
<Link href={`/market?name=${m.name}`} key={m.name}>
|
||||
<a className="cursor-pointer default-transition flex h-12 items-center justify-between text-th-fgd-2 hover:text-th-primary w-full">
|
||||
{m.name}
|
||||
<div className="flex items-center">
|
||||
<span className="text-right w-20">
|
||||
{formatUsdValue(
|
||||
mangoGroup
|
||||
.getPrice(m.marketIndex, mangoCache)
|
||||
.toNumber()
|
||||
)}
|
||||
</span>
|
||||
<ChevronRightIcon className="h-4 ml-1 w-5 text-th-fgd-2" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* spacer so last market can be selected albeit bottom bar overlay */}
|
||||
<p className="flex h-12 md:hidden"></p>
|
||||
</PageBodyContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SelectMarket
|
|
@ -4,7 +4,6 @@ import useMangoStore from '../stores/useMangoStore'
|
|||
import PageBodyContainer from '../components/PageBodyContainer'
|
||||
import TopBar from '../components/TopBar'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
// import { useTranslation } from 'next-i18next'
|
||||
import {
|
||||
actionsSelector,
|
||||
connectionSelector,
|
||||
|
@ -13,6 +12,7 @@ import {
|
|||
} from '../stores/selectors'
|
||||
import JupiterForm from '../components/JupiterForm'
|
||||
import { zeroKey } from '@blockworks-foundation/mango-client'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
|
@ -24,7 +24,7 @@ export async function getStaticProps({ locale }) {
|
|||
}
|
||||
|
||||
export default function Swap() {
|
||||
// const { t } = useTranslation(['common', 'swap'])
|
||||
const { t } = useTranslation(['common', 'swap'])
|
||||
const connection = useMangoStore(connectionSelector)
|
||||
const connected = useMangoStore(walletConnectedSelector)
|
||||
const wallet = useMangoStore(walletSelector)
|
||||
|
@ -58,11 +58,11 @@ export default function Swap() {
|
|||
<h1
|
||||
className={`mb-1.5 md:mb-0 text-th-fgd-1 text-2xl font-semibold`}
|
||||
>
|
||||
Swap
|
||||
{t('swap')}
|
||||
</h1>
|
||||
<div className="flex flex-col md:items-end">
|
||||
<p className="mb-0 text-xs">
|
||||
Swap between 100s of tokens at the best rates.
|
||||
{t('swap:swap-between-hundreds')}
|
||||
</p>
|
||||
<a
|
||||
className="mb-0 text-th-fgd-2 text-xs"
|
||||
|
|
|
@ -1,30 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 498 498" enable-background="new 0 0 498 498" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<circle cx="249" cy="249" r="249"/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="88.5831" y1="415.184" x2="415.3678" y2="88.3993">
|
||||
<stop offset="0" style="stop-color:#C947E6"/>
|
||||
<stop offset="3.078728e-002" style="stop-color:#C04EE4"/>
|
||||
<stop offset="0.1719" style="stop-color:#9C6BDB"/>
|
||||
<stop offset="0.304" style="stop-color:#827FD5"/>
|
||||
<stop offset="0.4227" style="stop-color:#728CD1"/>
|
||||
<stop offset="0.5161" style="stop-color:#6D90D0"/>
|
||||
<stop offset="0.6074" style="stop-color:#6598CD"/>
|
||||
<stop offset="0.7504" style="stop-color:#4FAEC3"/>
|
||||
<stop offset="0.9262" style="stop-color:#2CD3B4"/>
|
||||
<stop offset="1" style="stop-color:#1BE4AD"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M435.9,117.4l-61.2,60.3c-1,1-2.3,1.5-3.6,1.5H72.2c-5.3,0-7.9-6.4-4.2-10.1l61.2-60.3
|
||||
c1-1,2.3-1.5,3.6-1.5h298.9C437,107.3,439.7,113.7,435.9,117.4z M435.9,277.6l-61.2-60.3c-1-1-2.3-1.5-3.6-1.5H72.2
|
||||
c-5.3,0-7.9,6.4-4.2,10.1l61.2,60.3c1,1,2.3,1.5,3.6,1.5h298.9C437,287.7,439.7,281.3,435.9,277.6z M431.7,324.4H132.9
|
||||
c-1.4,0-2.7,0.5-3.6,1.5L68,386.2c-3.8,3.7-1.1,10.1,4.2,10.1h298.9c1.4,0,2.7-0.5,3.6-1.5l61.2-60.3
|
||||
C439.7,330.8,437,324.4,431.7,324.4z"/>
|
||||
</g>
|
||||
<path fill="#FFFFFF" d="M275.3,373.7l34.2-157.3l-62.8,157.3h-47.1l0.7-141.2l-44.1,141.2h-27.5l74.5-229.2
|
||||
c-33.6,11-54.2,37-52.5,75c0.7,15.7,4.1,20.1,5.3,23.1c-24.1,0.8-36.2-8.3-37.2-30c-1.9-43,53.9-89.1,105.5-89.1
|
||||
c10,0,18.6,0.9,26.9,3.9l0.1,192.4l73-191.7h53l-52.5,245.5H275.3z"/>
|
||||
</g>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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="#C8ECE1"/>
|
||||
<path d="M5.29524 11.8893C5.08172 11.6904 5.28095 11.442 5.45579 11.3054C5.67519 11.0999 5.89249 10.8767 6.09844 10.68C7.01512 9.74696 7.93219 8.84079 8.96785 8.04847C9.10908 7.58198 9.76138 7.96741 9.48569 8.34273C8.19239 9.37206 7.01974 10.5548 5.85046 11.7253L5.86601 11.7098C5.86983 11.7066 5.87335 11.7031 5.87652 11.6992C5.72941 11.8619 5.50791 12.0842 5.29524 11.8893Z" fill="#308D8A"/>
|
||||
<path d="M22.9244 12.238C22.899 12.207 22.8803 12.1712 22.8693 12.1327C22.8583 12.0942 22.8552 12.0539 22.8603 12.0142C22.8654 11.9745 22.8786 11.9363 22.899 11.9018C22.9194 11.8674 22.9467 11.8374 22.979 11.8139C24.1386 10.5446 25.2647 9.25007 26.4844 8.03585C26.5928 7.88744 26.8559 7.87108 26.9538 8.04801C27.0513 8.20815 26.9664 8.37498 26.845 8.49266C26.573 8.7978 26.2658 9.0681 25.9867 9.36648C25.2722 10.1041 24.5997 10.8758 23.9272 11.65C23.6482 11.8715 23.3321 12.5923 22.9244 12.238Z" fill="#308D8A"/>
|
||||
<path d="M9.33668 8.4205C9.26579 8.41715 9.19873 8.3875 9.14859 8.33723C9.09853 8.28702 9.06909 8.21982 9.06596 8.14899C9.06525 8.10853 9.073 8.06843 9.08872 8.03116C9.10443 7.9939 9.12776 7.96034 9.1572 7.93268C9.18671 7.90494 9.22155 7.88368 9.25974 7.87031C9.29786 7.85687 9.33839 7.85161 9.37871 7.85474C9.74351 7.8056 10.0693 7.77488 10.4294 7.75511C15.7577 7.38782 21.1236 7.53915 26.4455 7.81527C26.6019 7.80261 26.7398 7.94555 26.7423 8.09687C26.733 8.50661 26.2433 8.38053 25.9744 8.37847C20.4444 8.09054 14.8624 7.93339 9.33668 8.4205Z" fill="#308D8A"/>
|
||||
<path d="M22.5912 12.4739C22.5042 12.4704 22.4231 12.4671 22.355 12.4739C19.2788 12.3011 16.1887 12.3225 13.102 12.3436C10.9056 12.3591 8.71029 12.3734 6.52514 12.3196C6.44358 12.2749 6.35482 12.2449 6.26288 12.2309C6.12583 12.1844 5.98528 12.149 5.84258 12.125C5.78751 12.1133 5.73204 12.1027 5.67698 12.0922C5.6547 12.0838 5.63495 12.0771 5.61225 12.0699C5.53645 12.0456 5.45732 12.0333 5.37771 12.0334C5.37309 12.0334 5.36806 12.0334 5.36344 12.0313C5.36773 12.0323 5.37208 12.033 5.37646 12.0334H5.37183C5.30842 12.0224 5.24443 12.0151 5.18018 12.0115C5.18544 11.9718 5.19949 11.9338 5.22131 11.9002C5.24312 11.8666 5.27214 11.8383 5.30627 11.8174C5.49288 11.6858 5.72658 11.6812 5.94387 11.7106C6.85131 11.8278 7.75996 11.8022 8.67118 11.7774C9.2176 11.7635 9.76402 11.7484 10.3104 11.7648C11.5793 11.7892 12.8498 11.7787 14.1204 11.7682C15.7075 11.7551 17.2962 11.7425 18.8824 11.7997C19.5915 11.7871 20.3018 11.8139 21.0109 11.8417C21.6472 11.8674 22.2836 11.8922 22.919 11.8888C22.9584 11.8783 22.9995 11.8761 23.0397 11.8822C23.08 11.8883 23.1186 11.9026 23.153 11.9243C23.1875 11.946 23.2172 11.9746 23.2401 12.0083C23.263 12.0419 23.2788 12.0799 23.2864 12.1199C23.3048 12.4335 23.0384 12.4798 22.7631 12.4798C22.7068 12.4781 22.6479 12.4759 22.5912 12.4739Z" fill="#308D8A"/>
|
||||
<path d="M6.15479 12.2139H6.18078C6.17324 12.2168 6.16359 12.2168 6.15479 12.2139Z" fill="#308D8A"/>
|
||||
<path d="M5.61291 12.0675C5.53719 12.0429 5.458 12.0307 5.37839 12.0313C5.37377 12.0313 5.36873 12.0313 5.36411 12.0288C5.36873 12.0288 5.37377 12.0288 5.37713 12.0313H5.37251C5.29553 12.0162 5.21729 12.0083 5.13883 12.0078H5.11487C5.08916 11.9518 5.07948 11.8897 5.0869 11.8285C5.09431 11.7673 5.11854 11.7093 5.1569 11.6611C5.25903 11.5308 5.34015 11.3324 5.53138 11.3286C5.84828 11.3311 5.90123 11.6926 5.6995 11.883C5.70398 11.9173 5.70149 11.9523 5.69217 11.9856C5.68286 12.019 5.66691 12.0501 5.64528 12.0771L5.61291 12.0675Z" fill="#308D8A"/>
|
||||
<path d="M6.75799 12.3719C6.49068 12.3643 6.20866 12.3403 5.94808 12.3365C5.67026 12.3097 5.35504 12.3878 5.10119 12.2525C4.70905 11.9781 5.34411 11.1858 5.69422 11.2614C5.91613 11.3106 6.01112 11.6174 5.8182 11.7628C6.15443 11.7713 6.47849 11.7864 6.81431 11.8048C7.1812 11.8469 7.12616 12.4 6.75799 12.3719Z" fill="#308D8A"/>
|
||||
<path d="M22.53 14.3575C17.003 14.1453 11.4722 14.3516 5.94313 14.2865C5.59386 14.2726 5.58083 13.7346 5.94313 13.7219C11.6118 13.767 17.2813 13.5959 22.9474 13.7913C23.3114 13.7829 23.3408 14.3428 22.9663 14.3587C22.8196 14.363 22.6738 14.3617 22.53 14.3575Z" fill="#308D8A"/>
|
||||
<path d="M26.0399 18.4691C22.7317 18.3068 19.4084 18.3947 16.0926 18.3518C14.8258 18.3333 13.567 18.2576 12.3015 18.293C11.3999 18.2787 10.4912 18.467 9.59518 18.4035C9.26522 18.3152 9.33498 17.8218 9.68762 17.8458C11.5101 17.786 13.34 17.6289 15.1671 17.7688C17.5868 17.8054 20.0077 17.7688 22.4278 17.8281C23.6467 17.8382 24.8702 17.7835 26.0844 17.9025C26.3366 17.9105 26.7422 17.826 26.7674 18.1791C26.7573 18.567 26.2954 18.4682 26.0315 18.4691H26.0399Z" fill="#308D8A"/>
|
||||
<path d="M9.3859 18.3246L9.36151 18.3036L9.34978 18.2944C9.36279 18.3045 9.37623 18.3158 9.3918 18.3259C7.93289 17.1638 6.78594 15.6881 5.53386 14.3225C5.23461 14.0867 5.58513 13.65 5.86715 13.8631C6.96245 14.9613 7.85894 16.239 9.04165 17.2503C9.28542 17.4941 9.55067 17.7034 9.80112 17.9299C10.0256 18.1927 9.64105 18.5634 9.3859 18.3246Z" fill="#308D8A"/>
|
||||
<path d="M26.4637 18.4502C26.4154 18.4389 26.3707 18.4153 26.3342 18.3817C26.2977 18.348 26.2705 18.3054 26.2553 18.2582C26.2401 18.2109 26.2374 18.1605 26.2475 18.1119C26.2576 18.0632 26.2801 18.0181 26.3128 17.9807C25.2877 16.6845 24.0831 15.5333 22.8639 14.4237C22.3074 14.2337 22.8639 13.5192 23.2337 13.9874C24.4412 15.036 25.5665 16.1756 26.5999 17.3961C26.7592 17.5819 26.8521 17.7378 26.984 17.929C27.1425 18.2589 26.7752 18.5372 26.4637 18.4502Z" fill="#308D8A"/>
|
||||
<path d="M9.46332 20.5063C9.39064 20.4881 9.32792 20.4421 9.28867 20.3783C9.24934 20.3144 9.23661 20.2377 9.25318 20.1646C9.29521 19.9766 9.47889 19.9304 9.64906 19.9544C9.87434 19.913 10.1016 19.8837 10.3299 19.8665C15.6636 19.5597 21.0326 19.8077 26.3814 19.7799C26.7307 19.7905 26.7475 20.3318 26.3835 20.3445C20.7472 20.3916 15.0655 20.0738 9.46332 20.5063Z" fill="#308D8A"/>
|
||||
<path d="M5.9828 24.4555C5.58435 24.5126 5.48137 23.928 5.89874 23.8897C6.52246 23.928 7.13653 23.8687 7.75818 23.8426C12.8682 23.7645 17.9799 23.8569 23.0908 23.8884C23.4102 23.9582 23.3538 24.456 23.0273 24.4488C17.328 24.4336 11.6372 24.3071 5.94329 24.4534L5.9828 24.4555Z" fill="#308D8A"/>
|
||||
<path d="M22.9506 24.2727C22.6379 24.0302 22.9927 23.7234 23.1852 23.5422C23.1788 23.5489 23.1721 23.5548 23.1658 23.5607C24.2233 22.4221 25.1569 21.1691 26.2543 20.0687C26.2812 19.73 26.8137 19.7455 26.8167 20.0881C26.8204 20.1649 26.8059 20.2415 26.7746 20.3117C26.7431 20.3818 26.6956 20.4436 26.6359 20.492C25.7533 21.3633 25.0118 22.3636 24.1762 23.2796C23.9089 23.6015 23.6181 23.91 23.3398 24.2202C23.2928 24.2753 23.227 24.3112 23.1552 24.3209C23.0834 24.3306 23.0106 24.3134 22.9506 24.2727Z" fill="#308D8A"/>
|
||||
<path d="M5.66685 24.2647C5.18602 24.0428 5.6513 23.6074 5.877 23.3952C6.5041 22.6941 7.15895 21.9695 7.84823 21.339C8.27859 20.9011 8.73086 20.4888 9.18903 20.0819C9.27308 20.0025 9.38067 19.918 9.49921 19.9197C9.81146 19.9075 9.90099 20.3652 9.6051 20.4699C8.34416 21.5698 7.18753 22.7891 6.07076 24.0197C5.9724 24.1643 5.88205 24.3333 5.66685 24.2647Z" fill="#308D8A"/>
|
||||
<path d="M9.2907 20.1835C15.0073 19.6967 20.8017 20.0394 26.5529 20.0014C25.5203 21.2271 24.6099 22.5827 23.5902 23.8248L23.5862 23.8294C23.4863 23.9339 23.3553 24.0725 23.2837 24.2176C17.9833 24.1785 12.6806 24.0725 7.38137 24.1645C6.82422 24.191 6.27591 24.2456 5.71875 24.2346C6.84854 22.8303 8.01902 21.4412 9.2907 20.1835Z" fill="#308D8A"/>
|
||||
<path d="M26.65 18.1263C26.5901 18.1263 26.5328 18.1305 26.4813 18.1291C25.2173 17.9928 23.9454 18.0555 22.6775 18.0435C20.1604 17.977 17.6417 18.0182 15.1246 17.977C13.2301 17.8178 11.3322 17.9942 9.44077 18.0621C9.20995 17.8408 8.97208 17.6318 8.74957 17.3864C7.67615 16.3821 6.82972 15.1474 5.87354 14.0173C11.5097 14.08 17.1476 13.8609 22.7816 14.0963C22.855 14.0986 22.9293 14.1 23.0028 14.1C23.0417 14.1311 23.0849 14.155 23.1309 14.1709C24.3708 15.4066 25.5985 16.6878 26.65 18.1263Z" fill="#308D8A"/>
|
||||
<path d="M26.6547 8.21904C26.7059 8.21904 26.7652 8.22416 26.8262 8.22921C25.6627 9.45432 24.574 10.7453 23.4545 12.0116C23.4246 12.0331 23.399 12.0603 23.3791 12.0918C22.7057 12.096 22.0297 12.0686 21.354 12.0403C20.5968 12.0093 19.8377 11.9791 19.0804 11.994C17.3863 11.9304 15.6898 11.9448 13.9943 11.9591C12.6373 11.9707 11.2807 11.9824 9.92507 11.955C9.34146 11.9369 8.75792 11.955 8.17431 11.9694C7.20109 11.9968 6.23057 12.0251 5.26094 11.8956C5.23715 11.8914 5.21606 11.8891 5.19092 11.8863C5.19361 11.8826 5.19585 11.8775 5.19855 11.8733C6.40475 10.625 7.61588 9.36749 8.94416 8.26079C14.8261 7.72902 20.766 7.90253 26.6547 8.21904Z" fill="#308D8A"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 8.4 KiB |
|
@ -7,9 +7,14 @@
|
|||
"account-address-warning": "Do not send tokens directly to your account address.",
|
||||
"account-details-tip-desc": "When you make your first deposit we'll set you up with a Mango Account. You'll need at least 0.0035 SOL in your wallet to cover the rent/cost of creating the account.",
|
||||
"account-details-tip-title": "Account Details",
|
||||
"account-equity": "Account Equity",
|
||||
"account-equity-chart-title": "Account Equity",
|
||||
"account-health-tip-desc": "To avoid liquidation you must keep your account health above 0%. To increase the health of your account, reduce borrows or deposit funds.",
|
||||
"account-health-tip-title": "Account Health",
|
||||
"account-name": "Account Name",
|
||||
"account-performance": "Account Performance",
|
||||
"account-pnl": "Account PNL",
|
||||
"account-pnl-chart-title": "Account PNL",
|
||||
"account-risk": "Account Risk",
|
||||
"account-value": "Account Value",
|
||||
"accounts": "Accounts",
|
||||
|
@ -94,6 +99,7 @@
|
|||
"data-refresh-tip-title": "Manual Data Refresh",
|
||||
"date": "Date",
|
||||
"default-market": "Default Market",
|
||||
"default-spot-margin": "Trade with margin by default",
|
||||
"delay-displaying-recent": "There may be a delay in displaying the latest activity.",
|
||||
"deposit": "Deposit",
|
||||
"deposit-before": "You need more {{tokenSymbol}} in your wallet to fully repay your borrow",
|
||||
|
@ -121,6 +127,7 @@
|
|||
"enter-name": "Enter an account name",
|
||||
"equity": "Equity",
|
||||
"est-period-end": "Est Period End",
|
||||
"est-slippage": "Est. Slippage",
|
||||
"estimated-liq-price": "Est. Liq. Price",
|
||||
"explorer": "Explorer",
|
||||
"export-data": "Export CSV",
|
||||
|
@ -215,11 +222,11 @@
|
|||
"name-your-account": "Name Your Account",
|
||||
"net": "Net",
|
||||
"net-balance": "Net Balance",
|
||||
"new": "New",
|
||||
"new-alert": "New Alert",
|
||||
"net-interest-value": "Net Interest Value",
|
||||
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
|
||||
"new": "New",
|
||||
"new-account": "New",
|
||||
"new-alert": "New Alert",
|
||||
"next": "Next",
|
||||
"no-account-found": "No Account Found",
|
||||
"no-address": "No {{tokenSymbol}} wallet address found",
|
||||
|
@ -248,6 +255,7 @@
|
|||
"orderbook-animation": "Orderbook Animation",
|
||||
"orders": "Orders",
|
||||
"performance-insights": "Performance Insights",
|
||||
"performance": "Performance",
|
||||
"period-progress": "Period Progress",
|
||||
"perp": "Perp",
|
||||
"perp-fees": "Mango Perp Fees",
|
||||
|
@ -274,7 +282,6 @@
|
|||
"profile-menu-tip-title": "Profile Menu",
|
||||
"profit-price": "Profit Price",
|
||||
"quantity": "Quantity",
|
||||
"rate": "Rate",
|
||||
"rates": "Deposit/Borrow Rates",
|
||||
"read-more": "Read More",
|
||||
"recent": "Recent",
|
||||
|
@ -311,7 +318,6 @@
|
|||
"show-zero": "Show zero balances",
|
||||
"side": "Side",
|
||||
"size": "Size",
|
||||
"slippage": "Est. Slippage",
|
||||
"spanish": "Español",
|
||||
"spread": "Spread",
|
||||
"stats": "Stats",
|
||||
|
|
|
@ -2,17 +2,27 @@
|
|||
"ata-deposit": "Deposit",
|
||||
"ata-deposit-details": "{{cost}} SOL for {{count}} ATA Account",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL for {{count}} ATA Accounts",
|
||||
"ath": "All-Time High",
|
||||
"atl": "All-Time Low",
|
||||
"bal": "Bal:",
|
||||
"best-swap": "Best Swap",
|
||||
"chart-not-available": "Chart not available",
|
||||
"cheaper": "cheaper than",
|
||||
"fees-paid-to": "Fees paid to {{feeRecipient}}",
|
||||
"from": "from",
|
||||
"get-started": "Before you get started...",
|
||||
"got-it": "Got It",
|
||||
"heres-how": "Here's how",
|
||||
"input-info-unavailable": "Input token information is not available.",
|
||||
"jupiter-error": "Error in Jupiter – Try changing your input",
|
||||
"market-cap": "Market Cap",
|
||||
"market-cap-rank": "Market Cap Rank",
|
||||
"max-supply": "Max Supply",
|
||||
"minimum-received": "Minimum Received",
|
||||
"more-expensive": "more expensive than",
|
||||
"need-ata-account": "You need to have an Associated Token Account.",
|
||||
"other-routes": "{{numberOfRoutes}} other routes",
|
||||
"output-info-unavailable": "Output token information is not available.",
|
||||
"pay": "Pay",
|
||||
"price-impact": "Price Impact",
|
||||
"price-info": "Price Info",
|
||||
|
@ -22,12 +32,16 @@
|
|||
"serum-details": "{{cost}} SOL for {{count}} Serum OpenOrders Account",
|
||||
"serum-details_plural": "{{cost}} SOL for {{count}} Serum OpenOrders Accounts",
|
||||
"serum-requires-openorders": "Serum requires an OpenOrders account for each token. You can close the account and recover the SOL later.",
|
||||
"slippage": "Slippage",
|
||||
"slippage-settings": "Slippage Settings",
|
||||
"swap-between-hundreds": "Swap between 100s of tokens at the best rates.",
|
||||
"swap-details": "Swap Details",
|
||||
"swap-fee": "Swap Fee",
|
||||
"swap-in-wallet": "Swaps interact directly with your connected wallet, not your Mango Account.",
|
||||
"swap-successful": "Swap Successful",
|
||||
"swapping": "Swapping...",
|
||||
"to": "to",
|
||||
"token-supply": "Token Supply",
|
||||
"transaction-fee": "Transaction Fee",
|
||||
"unavailable": "Unavailable",
|
||||
"you-pay": "You pay",
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
"enter-name": "Ingrese un nombre de cuenta",
|
||||
"equity": "Capital",
|
||||
"est-period-end": "Est Period End",
|
||||
"est-slippage": "Deslizamiento estimado",
|
||||
"estimated-liq-price": "Est. Liq. Price",
|
||||
"explorer": "Explorador",
|
||||
"export-data": "Export CSV",
|
||||
|
@ -213,11 +214,11 @@
|
|||
"name-your-account": "Nombra tu cuenta",
|
||||
"net": "Neto",
|
||||
"net-balance": "Balance neto",
|
||||
"new": "Nuevo",
|
||||
"new-alert": "New Alert",
|
||||
"net-interest-value": "Valor de interés neto",
|
||||
"net-interest-value-desc": "Calculado en el momento en que se ganó / pagó. Esto podría ser útil al momento de impuestos.",
|
||||
"new": "Nuevo",
|
||||
"new-account": "Nuevo",
|
||||
"new-alert": "New Alert",
|
||||
"next": "Próximo",
|
||||
"no-account-found": "No Account Found",
|
||||
"no-address": "No ${tokenSymbol} dirección de billetera encontrada",
|
||||
|
@ -272,7 +273,6 @@
|
|||
"profile-menu-tip-title": "Menú de perfil",
|
||||
"profit-price": "Precio de beneficio",
|
||||
"quantity": "Cantidad",
|
||||
"rate": "Rate",
|
||||
"rates": "Tasas de depósito / préstamo",
|
||||
"read-more": "Leer más",
|
||||
"recent": "Reciente",
|
||||
|
@ -309,7 +309,6 @@
|
|||
"show-zero": "Mostrar saldos cero",
|
||||
"side": "Lado",
|
||||
"size": "Tamaño",
|
||||
"slippage": "Deslizamiento estimado",
|
||||
"spanish": "Español",
|
||||
"spread": "Propago",
|
||||
"stats": "Estadisticas",
|
||||
|
|
|
@ -2,17 +2,27 @@
|
|||
"ata-deposit": "Deposit",
|
||||
"ata-deposit-details": "{{cost}} SOL for {{count}} ATA Account",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL for {{count}} ATA Accounts",
|
||||
"ath": "All-Time High",
|
||||
"atl": "All-Time Low",
|
||||
"bal": "Bal:",
|
||||
"best-swap": "Best Swap",
|
||||
"chart-not-available": "Chart not available",
|
||||
"cheaper": "cheaper than",
|
||||
"fees-paid-to": "Fees paid to {{feeRecipient}}",
|
||||
"from": "from",
|
||||
"get-started": "Before you get started...",
|
||||
"got-it": "Got It",
|
||||
"heres-how": "Here's how",
|
||||
"input-info-unavailable": "Input token information is not available.",
|
||||
"jupiter-error": "Error in Jupiter – try changing your input",
|
||||
"market-cap": "Market Cap",
|
||||
"market-cap-rank": "Market Cap Rank",
|
||||
"max-supply": "Max Supply",
|
||||
"minimum-received": "Minimum Received",
|
||||
"more-expensive": "more expensive than",
|
||||
"need-ata-account": "You need to have an Associated Token Account.",
|
||||
"other-routes": "{{numberOfRoutes}} other routes",
|
||||
"output-info-unavailable": "Output token information is not available.",
|
||||
"pay": "Pay",
|
||||
"price-impact": "Price Impact",
|
||||
"price-info": "Price Info",
|
||||
|
@ -22,12 +32,16 @@
|
|||
"serum-details": "{{cost}} SOL for {{count}} Serum OpenOrders Account",
|
||||
"serum-details_plural": "{{cost}} SOL for {{count}} Serum OpenOrders Accounts",
|
||||
"serum-requires-openorders": "Serum requires an OpenOrders account for each token. You can close the account and recover the SOL later.",
|
||||
"slippage": "Slippage",
|
||||
"slippage-settings": "Slippage Settings",
|
||||
"swap-between-hundreds": "Swap between 100s of tokens at the best rates.",
|
||||
"swap-details": "Swap Details",
|
||||
"swap-fee": "Swap Fee",
|
||||
"swap-in-wallet": "Swaps interact directly with your connected wallet, not your Mango Account.",
|
||||
"swap-successful": "Swap Successful",
|
||||
"swapping": "Swapping...",
|
||||
"to": "to",
|
||||
"token-supply": "Token Supply",
|
||||
"transaction-fee": "Transaction Fee",
|
||||
"unavailable": "Unavailable",
|
||||
"you-pay": "You pay",
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
"enter-name": "输入帐户标签",
|
||||
"equity": "余额",
|
||||
"est-period-end": "预计期末时间",
|
||||
"est-slippage": "预计下滑",
|
||||
"estimated-liq-price": "预计清算价格",
|
||||
"explorer": "浏览器",
|
||||
"export-data": "Export CSV",
|
||||
|
@ -213,11 +214,11 @@
|
|||
"name-your-account": "给帐户标签",
|
||||
"net": "净",
|
||||
"net-balance": "净余额",
|
||||
"new": "新子帐户",
|
||||
"new-alert": "创建警报",
|
||||
"net-interest-value": "Net Interest Value",
|
||||
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
|
||||
"new": "新子帐户",
|
||||
"new-account": "新子帐户",
|
||||
"new-alert": "创建警报",
|
||||
"next": "前往",
|
||||
"no-account-found": "您没有帐户",
|
||||
"no-address": "没有{{tokenSymbol}}钱包地址",
|
||||
|
@ -272,7 +273,6 @@
|
|||
"profile-menu-tip-title": "个人资料菜单",
|
||||
"profit-price": "止盈价格",
|
||||
"quantity": "数量",
|
||||
"rate": "排放率",
|
||||
"rates": "存款/借贷利率",
|
||||
"read-more": "看更多资料",
|
||||
"recent": "最近",
|
||||
|
@ -309,7 +309,6 @@
|
|||
"show-zero": "显示零余额",
|
||||
"side": "方向",
|
||||
"size": "数量",
|
||||
"slippage": "预计下滑",
|
||||
"spanish": "Español",
|
||||
"spread": "点差",
|
||||
"stats": "统计",
|
||||
|
|
|
@ -2,32 +2,46 @@
|
|||
"ata-deposit": "押金",
|
||||
"ata-deposit-details": "{{cost}} SOL为 {{count}} ATA帐户",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL为 {{count}} ATA帐户",
|
||||
"ath": "历史高价",
|
||||
"atl": "历史低价",
|
||||
"bal": "余额:",
|
||||
"best-swap": "Best Swap",
|
||||
"best-swap": "最佳换币",
|
||||
"chart-not-available": "无法显示图表",
|
||||
"cheaper": "低于",
|
||||
"fees-paid-to": "费用缴给{{feeRecipient}}",
|
||||
"from": "从",
|
||||
"get-started": "开始前...",
|
||||
"got-it": "明白",
|
||||
"heres-how": "了解更多",
|
||||
"input-info-unavailable": "获取付出币种资料时出错",
|
||||
"jupiter-error": "Jupiter出错,请更改输入",
|
||||
"market-cap": "总市值",
|
||||
"market-cap-rank": "总市值排名",
|
||||
"max-supply": "最大供应量",
|
||||
"minimum-received": "最好获得",
|
||||
"more-expensive": "高于",
|
||||
"need-ata-account": "您必有一个关联币种帐户(ATA)。",
|
||||
"other-routes": "{{numberOfRoutes}} other routes",
|
||||
"other-routes": "{{numberOfRoutes}}条其他路线",
|
||||
"output-info-unavailable": "获取收到币种资料时出错",
|
||||
"pay": "付出",
|
||||
"price-impact": "Price Impact",
|
||||
"price-impact": "价格影响",
|
||||
"price-info": "价格细节",
|
||||
"rate": "Rate",
|
||||
"rate": "率",
|
||||
"receive": "收到",
|
||||
"routes-found": "找到{{numberOfRoutes}}条路线",
|
||||
"serum-details": "{{cost}} SOL为 {{count}} Serum OpenOrders帐户",
|
||||
"serum-details_plural": "{{cost}} SOL为 {{count}} Serum OpenOrders帐户",
|
||||
"serum-requires-openorders": "Serum要求每个币种有一个OpenOrders帐户。以后可以关闭帐户二恢复SOL押金。",
|
||||
"swap-details": "Swap Details",
|
||||
"slippage": "滑点",
|
||||
"slippage-settings": "滑点设定",
|
||||
"swap-between-hundreds": "以最好价格来换几百个币种。",
|
||||
"swap-details": "换币细节",
|
||||
"swap-fee": "换币费用",
|
||||
"swap-in-wallet": "换币会在您被连结的钱包中进行而不在您的Mango帐户中。",
|
||||
"swap-successful": "交易成功",
|
||||
"swapping": "正在交易...",
|
||||
"to": "到",
|
||||
"token-supply": "流通供应量",
|
||||
"transaction-fee": "交易费用",
|
||||
"unavailable": "无资料",
|
||||
"you-pay": "您付出",
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
"enter-name": "輸入帳戶標籤",
|
||||
"equity": "餘額",
|
||||
"est-period-end": "預計期末時間",
|
||||
"est-slippage": "預計下滑",
|
||||
"estimated-liq-price": "預計清算價格",
|
||||
"explorer": "瀏覽器",
|
||||
"export-data": "Export CSV",
|
||||
|
@ -213,11 +214,11 @@
|
|||
"name-your-account": "給帳戶標籤",
|
||||
"net": "淨",
|
||||
"net-balance": "淨餘額",
|
||||
"new": "新子帳戶",
|
||||
"new-alert": "創建警報",
|
||||
"net-interest-value": "Net Interest Value",
|
||||
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
|
||||
"new": "新子帳戶",
|
||||
"new-account": "新子帳戶",
|
||||
"new-alert": "創建警報",
|
||||
"next": "前往",
|
||||
"no-account-found": "您沒有帳戶",
|
||||
"no-address": "沒有{{tokenSymbol}}錢包地址",
|
||||
|
@ -272,7 +273,6 @@
|
|||
"profile-menu-tip-title": "個人資料菜單",
|
||||
"profit-price": "止盈價格",
|
||||
"quantity": "數量",
|
||||
"rate": "排放率",
|
||||
"rates": "存款/借貸利率",
|
||||
"read-more": "看更多資料",
|
||||
"recent": "最近",
|
||||
|
@ -309,7 +309,6 @@
|
|||
"show-zero": "顯示零餘額",
|
||||
"side": "方向",
|
||||
"size": "數量",
|
||||
"slippage": "預計下滑",
|
||||
"spanish": "Español",
|
||||
"spread": "點差",
|
||||
"stats": "統計",
|
||||
|
|
|
@ -2,32 +2,46 @@
|
|||
"ata-deposit": "押金",
|
||||
"ata-deposit-details": "{{cost}} SOL為 {{count}} ATA帳戶",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL為 {{count}} ATA帳戶",
|
||||
"ath": "歷史高價",
|
||||
"atl": "歷史低價",
|
||||
"bal": "餘額:",
|
||||
"best-swap": "Best Swap",
|
||||
"best-swap": "最佳換幣",
|
||||
"chart-not-available": "無法顯示圖表",
|
||||
"cheaper": "低於",
|
||||
"fees-paid-to": "費用繳給{{feeRecipient}}",
|
||||
"from": "從",
|
||||
"get-started": "開始前...",
|
||||
"got-it": "明白",
|
||||
"heres-how": "了解更多",
|
||||
"input-info-unavailable": "獲取付出幣種資料時出錯",
|
||||
"jupiter-error": "Jupiter出錯,請更改輸入",
|
||||
"market-cap": "總市值",
|
||||
"market-cap-rank": "總市值排名",
|
||||
"max-supply": "最大供應量",
|
||||
"minimum-received": "最好獲得",
|
||||
"more-expensive": "高於",
|
||||
"need-ata-account": "您必有一個關聯幣種帳戶(ATA)。",
|
||||
"other-routes": "{{numberOfRoutes}} other routes",
|
||||
"other-routes": "{{numberOfRoutes}}條其他路線",
|
||||
"output-info-unavailable": "獲取收到幣種資料時出錯",
|
||||
"pay": "付出",
|
||||
"price-impact": "Price Impact",
|
||||
"price-impact": "價格影響",
|
||||
"price-info": "價格細節",
|
||||
"rate": "Rate",
|
||||
"rate": "率",
|
||||
"receive": "收到",
|
||||
"routes-found": "找到{{numberOfRoutes}}條路線",
|
||||
"serum-details": "{{cost}} SOL為 {{count}} Serum OpenOrders帳戶",
|
||||
"serum-details_plural": "{{cost}} SOL為 {{count}} Serum OpenOrders帳戶",
|
||||
"serum-requires-openorders": "Serum要求每個幣種有一個OpenOrders帳戶。以後可以關閉帳戶二恢復SOL押金。",
|
||||
"swap-details": "Swap Details",
|
||||
"slippage": "滑點",
|
||||
"slippage-settings": "滑點設定",
|
||||
"swap-between-hundreds": "以最好價格來換幾百個幣種。",
|
||||
"swap-details": "換幣細節",
|
||||
"swap-fee": "換幣費用",
|
||||
"swap-in-wallet": "換幣會在您被連結的錢包中進行而不在您的Mango帳戶中。",
|
||||
"swap-successful": "換幣成功",
|
||||
"swapping": "正在換幣...",
|
||||
"to": "到",
|
||||
"token-supply": "流通供應量",
|
||||
"transaction-fee": "交易費用",
|
||||
"unavailable": "無資料",
|
||||
"you-pay": "您付出",
|
||||
|
|
|
@ -52,8 +52,8 @@ export const ENDPOINTS: EndpointInfo[] = [
|
|||
]
|
||||
|
||||
type ClusterType = 'mainnet' | 'devnet'
|
||||
|
||||
const CLUSTER = (process.env.NEXT_PUBLIC_CLUSTER as ClusterType) || 'mainnet'
|
||||
const DEFAULT_MANGO_GROUP_NAME = process.env.NEXT_PUBLIC_GROUP || 'mainnet.1'
|
||||
export const CLUSTER = DEFAULT_MANGO_GROUP_NAME.split('.')[0] as ClusterType
|
||||
const ENDPOINT = ENDPOINTS.find((e) => e.name === CLUSTER)
|
||||
|
||||
export const WEBSOCKET_CONNECTION = new Connection(
|
||||
|
@ -61,7 +61,6 @@ export const WEBSOCKET_CONNECTION = new Connection(
|
|||
'processed' as Commitment
|
||||
)
|
||||
|
||||
const DEFAULT_MANGO_GROUP_NAME = process.env.NEXT_PUBLIC_GROUP || 'mainnet.1'
|
||||
export const DEFAULT_MANGO_GROUP_CONFIG = Config.ids().getGroup(
|
||||
CLUSTER,
|
||||
DEFAULT_MANGO_GROUP_NAME
|
||||
|
@ -78,8 +77,8 @@ export const serumProgramId = new PublicKey(defaultMangoGroupIds.serumProgramId)
|
|||
const mangoGroupPk = new PublicKey(defaultMangoGroupIds.publicKey)
|
||||
|
||||
// Used to retry loading the MangoGroup and MangoAccount if an rpc node error occurs
|
||||
// let mangoGroupRetryAttempt = 0
|
||||
// let mangoAccountRetryAttempt = 0
|
||||
let mangoGroupRetryAttempt = 0
|
||||
let mangoAccountRetryAttempt = 0
|
||||
|
||||
export const INITIAL_STATE = {
|
||||
WALLET: {
|
||||
|
@ -162,7 +161,8 @@ interface MangoStore extends State {
|
|||
selectedMangoAccount: {
|
||||
current: MangoAccount | null
|
||||
initialLoad: boolean
|
||||
lastUpdatedAt: number
|
||||
lastUpdatedAt: string
|
||||
lastSlot: number
|
||||
}
|
||||
tradeForm: {
|
||||
side: 'buy' | 'sell'
|
||||
|
@ -191,6 +191,8 @@ interface MangoStore extends State {
|
|||
tradeHistory: any[]
|
||||
set: (x: any) => void
|
||||
actions: {
|
||||
fetchAllMangoAccounts: () => Promise<void>
|
||||
fetchMangoGroup: () => Promise<void>
|
||||
[key: string]: (args?) => void
|
||||
}
|
||||
alerts: {
|
||||
|
@ -263,7 +265,8 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
selectedMangoAccount: {
|
||||
current: null,
|
||||
initialLoad: true,
|
||||
lastUpdatedAt: 0,
|
||||
lastUpdatedAt: '0',
|
||||
lastSlot: 0,
|
||||
},
|
||||
tradeForm: {
|
||||
side: 'buy',
|
||||
|
@ -337,7 +340,7 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
const mangoGroup = get().selectedMangoGroup.current
|
||||
const mangoClient = get().connection.client
|
||||
const wallet = get().wallet.current
|
||||
// const actions = get().actions
|
||||
const actions = get().actions
|
||||
const walletPk = wallet?.publicKey
|
||||
|
||||
if (!walletPk) return
|
||||
|
@ -370,17 +373,17 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
// if (mangoAccountRetryAttempt < 2) {
|
||||
// actions.fetchAllMangoAccounts()
|
||||
// mangoAccountRetryAttempt++
|
||||
// }
|
||||
// mangoAccountRetryAttempt = 0
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Unable to load mango account',
|
||||
description: err.message,
|
||||
})
|
||||
console.log('Could not get margin accounts for wallet', err)
|
||||
if (mangoAccountRetryAttempt < 2) {
|
||||
actions.fetchAllMangoAccounts()
|
||||
mangoAccountRetryAttempt++
|
||||
} else {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Unable to load mango account',
|
||||
description: err.message,
|
||||
})
|
||||
console.log('Could not get margin accounts for wallet', err)
|
||||
}
|
||||
})
|
||||
},
|
||||
async fetchMangoGroup() {
|
||||
|
@ -389,17 +392,19 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
const selectedMarketConfig = get().selectedMarket.config
|
||||
const mangoClient = get().connection.client
|
||||
const connection = get().connection.current
|
||||
// const actions = get().actions
|
||||
const actions = get().actions
|
||||
|
||||
return mangoClient
|
||||
.getMangoGroup(mangoGroupPk)
|
||||
.then(async (mangoGroup) => {
|
||||
mangoGroup.loadCache(connection).then((mangoCache) => {
|
||||
set((state) => {
|
||||
state.selectedMangoGroup.cache = mangoCache
|
||||
})
|
||||
})
|
||||
mangoGroup.loadRootBanks(connection).then(() => {
|
||||
mangoGroup.loadCache(connection).then((mangoCache) => {
|
||||
set((state) => {
|
||||
state.selectedMangoGroup.current = mangoGroup
|
||||
state.selectedMangoGroup.cache = mangoCache
|
||||
})
|
||||
set((state) => {
|
||||
state.selectedMangoGroup.current = mangoGroup
|
||||
})
|
||||
})
|
||||
const allMarketConfigs = getAllMarkets(mangoGroupConfig)
|
||||
|
@ -470,17 +475,17 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
// if (mangoGroupRetryAttempt < 2) {
|
||||
// actions.fetchMangoGroup()
|
||||
// mangoGroupRetryAttempt++
|
||||
// }
|
||||
// mangoGroupRetryAttempt = 0
|
||||
notify({
|
||||
title: 'Failed to load mango group. Please refresh',
|
||||
description: `${err}`,
|
||||
type: 'error',
|
||||
})
|
||||
console.log('Could not get mango group: ', err)
|
||||
if (mangoGroupRetryAttempt < 2) {
|
||||
actions.fetchMangoGroup()
|
||||
mangoGroupRetryAttempt++
|
||||
} else {
|
||||
notify({
|
||||
title: 'Failed to load mango group. Please refresh',
|
||||
description: `${err}`,
|
||||
type: 'error',
|
||||
})
|
||||
console.log('Could not get mango group: ', err)
|
||||
}
|
||||
})
|
||||
},
|
||||
async fetchTradeHistory(mangoAccount = null) {
|
||||
|
@ -522,16 +527,18 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
const connection = get().connection.current
|
||||
const mangoClient = get().connection.client
|
||||
|
||||
const reloadedMangoAccount = await mangoAccount.reloadFromSlot(
|
||||
connection,
|
||||
mangoClient.lastSlot
|
||||
)
|
||||
const [reloadedMangoAccount, lastSlot] =
|
||||
await mangoAccount.reloadFromSlot(connection, mangoClient.lastSlot)
|
||||
const lastSeenSlot = get().selectedMangoAccount.lastSlot
|
||||
|
||||
set((state) => {
|
||||
state.selectedMangoAccount.current = reloadedMangoAccount
|
||||
state.selectedMangoAccount.lastUpdatedAt = new Date().toISOString()
|
||||
})
|
||||
console.log('reloaded mango account', reloadedMangoAccount)
|
||||
if (lastSlot > lastSeenSlot) {
|
||||
set((state) => {
|
||||
state.selectedMangoAccount.current = reloadedMangoAccount
|
||||
state.selectedMangoAccount.lastUpdatedAt = new Date().toISOString()
|
||||
state.selectedMangoAccount.lastSlot = lastSlot
|
||||
})
|
||||
console.log('reloaded mango account', reloadedMangoAccount)
|
||||
}
|
||||
},
|
||||
async reloadOrders() {
|
||||
const mangoAccount = get().selectedMangoAccount.current
|
||||
|
|
|
@ -69,7 +69,7 @@ module.exports = {
|
|||
'bkg-3': '#2A2440',
|
||||
'bkg-4': '#37324D',
|
||||
'fgd-1': '#E5E3EC',
|
||||
'fgd-2': '#E5E3EC',
|
||||
'fgd-2': '#D2CEDE',
|
||||
'fgd-3': '#C1BED3',
|
||||
'fgd-4': '#706C81',
|
||||
},
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "preserve",
|
||||
"isolatedModules": true
|
||||
"isolatedModules": true,
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": ["node_modules", ".next", "out", "public/datafeeds"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"]
|
||||
|
|
|
@ -30,7 +30,12 @@ export async function deposit({
|
|||
Number(amount)
|
||||
)
|
||||
} else {
|
||||
return await mangoClient.initMangoAccountAndDeposit(
|
||||
const existingAccounts = await mangoClient.getMangoAccountsForOwner(
|
||||
mangoGroup,
|
||||
wallet.publicKey,
|
||||
false
|
||||
)
|
||||
return await mangoClient.createMangoAccountAndDeposit(
|
||||
mangoGroup,
|
||||
wallet,
|
||||
mangoGroup.tokens[tokenIndex].rootBank,
|
||||
|
@ -38,6 +43,7 @@ export async function deposit({
|
|||
mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0].vault,
|
||||
fromTokenAcc.publicKey,
|
||||
Number(amount),
|
||||
existingAccounts.length,
|
||||
accountName
|
||||
)
|
||||
}
|
||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -1284,10 +1284,9 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@blockworks-foundation/mango-client@latest":
|
||||
version "3.2.24"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.24.tgz#c46f2cff3dca06d88e304610643f40fddbf0ee76"
|
||||
integrity sha512-1rWvmMBK1wGVLG7UfTpHI2FPnXfRNTLFWifGa9q/FkRLp07GzGDY3fGhquJW2NxGv2KPwzX/YJEAHX+2x485OA==
|
||||
"@blockworks-foundation/mango-client@git+https://github.com/blockworks-foundation/mango-client-v3.git":
|
||||
version "3.3.1"
|
||||
resolved "git+https://github.com/blockworks-foundation/mango-client-v3.git#339894c3fdaadec231c9b5fa213c3a61ef78afcd"
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.16.2"
|
||||
"@project-serum/serum" "0.13.55"
|
||||
|
@ -2263,9 +2262,9 @@
|
|||
integrity sha512-qVCiT93utxN0cawScyQuNx8H82vBvZXSClZfgOu3l3dRRlRO6FjKEZlaPgXG9XUFjIAOsA4kAJY101vobHeJLQ==
|
||||
|
||||
"@types/express-serve-static-core@^4.17.9":
|
||||
version "4.17.27"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.27.tgz#7a776191e47295d2a05962ecbb3a4ce97e38b401"
|
||||
integrity sha512-e/sVallzUTPdyOTiqi8O8pMdBBphscvI6E4JYaKlja4Lm+zh7UFSSdW5VMkRbhDtmrONqOUHOXRguPsDckzxNA==
|
||||
version "4.17.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
|
||||
integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/qs" "*"
|
||||
|
|
Loading…
Reference in New Issue