merge main
This commit is contained in:
commit
aafa6c8e0c
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn typecheck && yarn lint
|
||||
yarn typecheck && yarn lint --quiet
|
||||
|
|
|
@ -118,13 +118,23 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
|
||||
const handleDeposit = useCallback(
|
||||
async (amount: string) => {
|
||||
//to not leave some dust on account we round amount by this number
|
||||
//with reduce only set to true we take only what is needed to be
|
||||
//deposited in need to repay borrow
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const client = mangoStore.getState().client
|
||||
const group = mangoStore.getState().group
|
||||
const actions = mangoStore.getState().actions
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
|
||||
if (!mangoAccount || !group || !bank || !publicKey) return
|
||||
console.log('inputAmount: ', amount)
|
||||
|
||||
//we don't want to left negative dust in account if someone wants to repay full amount
|
||||
const actualAmount =
|
||||
sizePercentage === '100'
|
||||
? mangoAccount.getTokenBorrowsUi(bank) < parseFloat(amount)
|
||||
? parseFloat(amount)
|
||||
: mangoAccount.getTokenBorrowsUi(bank)
|
||||
: parseFloat(amount)
|
||||
|
||||
setSubmitting(true)
|
||||
try {
|
||||
|
@ -132,7 +142,8 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
group,
|
||||
mangoAccount,
|
||||
bank.mint,
|
||||
parseFloat(amount)
|
||||
actualAmount,
|
||||
true
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
|
@ -155,7 +166,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[bank, publicKey]
|
||||
[bank, publicKey?.toBase58(), sizePercentage]
|
||||
)
|
||||
|
||||
const banks = useMemo(() => {
|
||||
|
|
|
@ -46,7 +46,7 @@ const TokenList = () => {
|
|||
const { group } = useMangoGroup()
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
const totalInterestData = mangoStore(
|
||||
(s) => s.mangoAccount.stats.interestTotals.data
|
||||
(s) => s.mangoAccount.interestTotals.data
|
||||
)
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
@ -304,7 +304,7 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
|
|||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const totalInterestData = mangoStore(
|
||||
(s) => s.mangoAccount.stats.interestTotals.data
|
||||
(s) => s.mangoAccount.interestTotals.data
|
||||
)
|
||||
const symbol = bank.name
|
||||
const oraclePrice = bank.uiPrice
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { numberCompacter } from 'utils/numbers'
|
||||
|
@ -11,19 +11,38 @@ const DetailedAreaChart = dynamic(
|
|||
|
||||
const AccountChart = ({
|
||||
chartToShow,
|
||||
data,
|
||||
hideChart,
|
||||
mangoAccountAddress,
|
||||
yKey,
|
||||
}: {
|
||||
chartToShow: string
|
||||
data: Array<any>
|
||||
hideChart: () => void
|
||||
mangoAccountAddress: string
|
||||
yKey: string
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const actions = mangoStore.getState().actions
|
||||
const [daysToShow, setDaysToShow] = useState<string>('1')
|
||||
const loading = mangoStore((s) => s.mangoAccount.stats.performance.loading)
|
||||
const loading = mangoStore((s) => s.mangoAccount.performance.loading)
|
||||
const performanceData = mangoStore((s) => s.mangoAccount.performance.data)
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccountAddress) {
|
||||
actions.fetchAccountPerformance(mangoAccountAddress, 1)
|
||||
}
|
||||
}, [actions, mangoAccountAddress])
|
||||
|
||||
const data: any = useMemo(() => {
|
||||
if (!performanceData.length) return []
|
||||
if (chartToShow === 'cumulative-interest-value') {
|
||||
performanceData.map((d) => ({
|
||||
interest_value:
|
||||
d.borrow_interest_cumulative_usd + d.deposit_interest_cumulative_usd,
|
||||
time: d.time,
|
||||
}))
|
||||
}
|
||||
return performanceData
|
||||
}, [performanceData])
|
||||
|
||||
const handleDaysToShow = async (days: string) => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
|
|
|
@ -3,11 +3,10 @@ import {
|
|||
toUiDecimalsForQuote,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import AccountActions from './AccountActions'
|
||||
import mangoStore, { PerformanceDataItem } from '@store/mangoStore'
|
||||
import { formatFixedDecimals } from '../../utils/numbers'
|
||||
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
|
||||
import FlipNumbers from 'react-flip-numbers'
|
||||
import dynamic from 'next/dynamic'
|
||||
const SimpleAreaChart = dynamic(
|
||||
|
@ -19,7 +18,8 @@ import { useTheme } from 'next-themes'
|
|||
import { IconButton } from '../shared/Button'
|
||||
import {
|
||||
ArrowsPointingOutIcon,
|
||||
ChevronRightIcon,
|
||||
ChartBarIcon,
|
||||
ClockIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import AccountTabs from './AccountTabs'
|
||||
|
@ -40,33 +40,20 @@ import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettin
|
|||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
|
||||
export async function getStaticProps({ locale }: { locale: string }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'common',
|
||||
'close-account',
|
||||
'trade',
|
||||
])),
|
||||
},
|
||||
}
|
||||
}
|
||||
import PnlHistoryModal from '@components/modals/PnlHistoryModal'
|
||||
|
||||
const AccountPage = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['common', 'account'])
|
||||
// const { connected } = useWallet()
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const actions = mangoStore.getState().actions
|
||||
const loadPerformanceData = mangoStore(
|
||||
(s) => s.mangoAccount.stats.performance.loading
|
||||
)
|
||||
const performanceData = mangoStore(
|
||||
(s) => s.mangoAccount.stats.performance.data
|
||||
const performanceInitialLoad = mangoStore(
|
||||
(s) => s.mangoAccount.performance.initialLoad
|
||||
)
|
||||
const performanceData = mangoStore((s) => s.mangoAccount.performance.data)
|
||||
const totalInterestData = mangoStore(
|
||||
(s) => s.mangoAccount.stats.interestTotals.data
|
||||
(s) => s.mangoAccount.interestTotals.data
|
||||
)
|
||||
const [chartToShow, setChartToShow] = useState<
|
||||
'account-value' | 'cumulative-interest-value' | 'pnl' | ''
|
||||
|
@ -75,6 +62,7 @@ const AccountPage = () => {
|
|||
PerformanceDataItem[]
|
||||
>([])
|
||||
const [showExpandChart, setShowExpandChart] = useState<boolean>(false)
|
||||
const [showPnlHistory, setShowPnlHistory] = useState<boolean>(false)
|
||||
const { theme } = useTheme()
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.md : false
|
||||
|
@ -87,16 +75,25 @@ const AccountPage = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (mangoAccountAddress) {
|
||||
const set = mangoStore.getState().set
|
||||
set((s) => {
|
||||
s.mangoAccount.performance.initialLoad = false
|
||||
})
|
||||
setOneDayPerformanceData([])
|
||||
actions.fetchAccountPerformance(mangoAccountAddress, 1)
|
||||
actions.fetchAccountInterestTotals(mangoAccountAddress)
|
||||
}
|
||||
}, [actions, mangoAccountAddress])
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccount && performanceData.length && !chartToShow) {
|
||||
if (
|
||||
performanceData.length &&
|
||||
performanceInitialLoad &&
|
||||
!oneDayPerformanceData.length
|
||||
) {
|
||||
setOneDayPerformanceData(performanceData)
|
||||
}
|
||||
}, [mangoAccount, performanceData, chartToShow])
|
||||
}, [performanceInitialLoad, oneDayPerformanceData, performanceData])
|
||||
|
||||
const onHoverMenu = (open: boolean, action: string) => {
|
||||
if (
|
||||
|
@ -115,11 +112,19 @@ const AccountPage = () => {
|
|||
const handleHideChart = () => {
|
||||
const set = mangoStore.getState().set
|
||||
set((s) => {
|
||||
s.mangoAccount.stats.performance.data = oneDayPerformanceData
|
||||
s.mangoAccount.performance.data = oneDayPerformanceData
|
||||
})
|
||||
setChartToShow('')
|
||||
}
|
||||
|
||||
const handleCloseDailyPnlModal = () => {
|
||||
const set = mangoStore.getState().set
|
||||
set((s) => {
|
||||
s.mangoAccount.performance.data = oneDayPerformanceData
|
||||
})
|
||||
setShowPnlHistory(false)
|
||||
}
|
||||
|
||||
const accountValue = useMemo(() => {
|
||||
if (!group || !mangoAccount) return 0.0
|
||||
return toUiDecimalsForQuote(mangoAccount.getEquity(group).toNumber())
|
||||
|
@ -138,23 +143,24 @@ const AccountPage = () => {
|
|||
}
|
||||
}, [mangoAccount, group, accountValue])
|
||||
|
||||
const { accountPnl, accountValueChange } = useMemo(() => {
|
||||
if (accountValue && performanceData.length) {
|
||||
return {
|
||||
accountPnl: performanceData[performanceData.length - 1].pnl,
|
||||
accountValueChange: accountValue - performanceData[0].account_equity,
|
||||
}
|
||||
}
|
||||
return { accountPnl: 0, accountValueChange: 0 }
|
||||
}, [accountValue, performanceData])
|
||||
|
||||
const oneDayPnlChange = useMemo(() => {
|
||||
if (accountPnl && oneDayPerformanceData.length) {
|
||||
const [accountPnl, accountValueChange, oneDayPnlChange] = useMemo(() => {
|
||||
if (
|
||||
accountValue &&
|
||||
oneDayPerformanceData.length &&
|
||||
performanceData.length
|
||||
) {
|
||||
const accountPnl = performanceData[performanceData.length - 1].pnl
|
||||
const accountValueChange =
|
||||
accountValue - oneDayPerformanceData[0].account_equity
|
||||
const startDayPnl = oneDayPerformanceData[0].pnl
|
||||
return accountPnl - startDayPnl
|
||||
const endDayPnl =
|
||||
oneDayPerformanceData[oneDayPerformanceData.length - 1].pnl
|
||||
const oneDayPnlChange = endDayPnl - startDayPnl
|
||||
|
||||
return [accountPnl, accountValueChange, oneDayPnlChange]
|
||||
}
|
||||
return 0.0
|
||||
}, [accountPnl, oneDayPerformanceData])
|
||||
return [0, 0, 0]
|
||||
}, [accountValue, oneDayPerformanceData, performanceData])
|
||||
|
||||
const interestTotalValue = useMemo(() => {
|
||||
if (totalInterestData.length) {
|
||||
|
@ -167,7 +173,7 @@ const AccountPage = () => {
|
|||
}, [totalInterestData])
|
||||
|
||||
const oneDayInterestChange = useMemo(() => {
|
||||
if (oneDayPerformanceData.length && mangoAccount) {
|
||||
if (oneDayPerformanceData.length) {
|
||||
const startDayInterest =
|
||||
oneDayPerformanceData[0].borrow_interest_cumulative_usd +
|
||||
oneDayPerformanceData[0].deposit_interest_cumulative_usd
|
||||
|
@ -181,7 +187,7 @@ const AccountPage = () => {
|
|||
return endDayInterest - startDayInterest
|
||||
}
|
||||
return 0.0
|
||||
}, [oneDayPerformanceData, mangoAccount])
|
||||
}, [oneDayPerformanceData])
|
||||
|
||||
const maintHealth = useMemo(() => {
|
||||
return group && mangoAccount
|
||||
|
@ -262,12 +268,15 @@ const AccountPage = () => {
|
|||
)}
|
||||
</div>
|
||||
<div className="flex items-center space-x-1.5">
|
||||
<Change change={accountValueChange} prefix="$" />
|
||||
<Change
|
||||
change={Number(formatDecimal(accountValueChange, 2))}
|
||||
prefix="$"
|
||||
/>
|
||||
<p className="text-th-fgd-4">{t('today')}</p>
|
||||
</div>
|
||||
</div>
|
||||
{!loadPerformanceData ? (
|
||||
mangoAccount && performanceData.length ? (
|
||||
{performanceInitialLoad ? (
|
||||
oneDayPerformanceData.length ? (
|
||||
<div
|
||||
className="relative mt-4 flex h-44 items-end md:mt-0 md:h-24 md:w-48"
|
||||
onMouseEnter={() =>
|
||||
|
@ -283,7 +292,7 @@ const AccountPage = () => {
|
|||
? COLORS.UP[theme]
|
||||
: COLORS.DOWN[theme]
|
||||
}
|
||||
data={performanceData.concat(latestAccountData)}
|
||||
data={oneDayPerformanceData.concat(latestAccountData)}
|
||||
name="accountValue"
|
||||
xKey="time"
|
||||
yKey="account_equity"
|
||||
|
@ -309,11 +318,11 @@ const AccountPage = () => {
|
|||
</Transition>
|
||||
</div>
|
||||
) : null
|
||||
) : (
|
||||
) : mangoAccountAddress ? (
|
||||
<SheenLoader className="mt-4 flex flex-1 md:mt-0">
|
||||
<div className="h-40 w-full rounded-md bg-th-bkg-2 md:h-24 md:w-48" />
|
||||
</SheenLoader>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-6 mb-1 lg:mt-0 lg:mb-0">
|
||||
<AccountActions />
|
||||
|
@ -369,7 +378,7 @@ const AccountPage = () => {
|
|||
<div className="col-span-5 flex border-t border-th-bkg-3 py-3 pl-6 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div id="account-step-five">
|
||||
<Tooltip
|
||||
content="The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw."
|
||||
content={t('account:tooltip-free-collateral')}
|
||||
maxWidth="20rem"
|
||||
placement="bottom"
|
||||
delay={250}
|
||||
|
@ -391,12 +400,12 @@ const AccountPage = () => {
|
|||
</p>
|
||||
<span className="text-xs font-normal text-th-fgd-4">
|
||||
<Tooltip
|
||||
content="Total value of collateral for trading and borrowing (including unsettled PnL)."
|
||||
content={t('account:tooltip-total-collateral')}
|
||||
maxWidth="20rem"
|
||||
placement="bottom"
|
||||
delay={250}
|
||||
>
|
||||
<span className="tooltip-underline">Total</span>:
|
||||
<span className="tooltip-underline">{t('total')}</span>:
|
||||
<span className="ml-1 font-mono text-th-fgd-2">
|
||||
{group && mangoAccount
|
||||
? formatFixedDecimals(
|
||||
|
@ -417,7 +426,7 @@ const AccountPage = () => {
|
|||
<div className="col-span-5 flex border-t border-th-bkg-3 py-3 pl-6 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div id="account-step-six">
|
||||
<Tooltip
|
||||
content="Total assets value divided by account equity value."
|
||||
content={t('account:tooltip-leverage')}
|
||||
maxWidth="20rem"
|
||||
placement="bottom"
|
||||
delay={250}
|
||||
|
@ -431,99 +440,130 @@ const AccountPage = () => {
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className={`col-span-5 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 pr-4 lg:col-span-1 lg:border-l lg:border-t-0 ${
|
||||
performanceData.length > 4
|
||||
? 'default-transition cursor-pointer md:hover:bg-th-bkg-2'
|
||||
: 'cursor-default'
|
||||
}`}
|
||||
onClick={() => handleChartToShow('pnl')}
|
||||
>
|
||||
<div className="col-span-5 border-t border-th-bkg-3 py-3 pl-6 pr-4 lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div id="account-step-seven" className="flex flex-col items-start">
|
||||
<Tooltip
|
||||
content="The amount your account has made or lost."
|
||||
placement="bottom"
|
||||
delay={250}
|
||||
>
|
||||
<p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('pnl')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-pnl')}
|
||||
placement="bottom"
|
||||
delay={250}
|
||||
>
|
||||
<p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('pnl')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
{mangoAccountAddress ? (
|
||||
<div className="flex items-center space-x-3">
|
||||
{performanceData.length > 4 ? (
|
||||
<Tooltip content={t('account:pnl-chart')} delay={250}>
|
||||
<IconButton
|
||||
className="text-th-fgd-3"
|
||||
hideBg
|
||||
onClick={() => handleChartToShow('pnl')}
|
||||
>
|
||||
<ChartBarIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
<Tooltip content={t('account:pnl-history')} delay={250}>
|
||||
<IconButton
|
||||
className="text-th-fgd-3"
|
||||
hideBg
|
||||
onClick={() => setShowPnlHistory(true)}
|
||||
>
|
||||
<ClockIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="mt-1 mb-0.5 text-left text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
|
||||
{formatFixedDecimals(accountPnl, true, true)}
|
||||
</p>
|
||||
<div className="flex space-x-1">
|
||||
<Change change={oneDayPnlChange} prefix="$" size="small" />
|
||||
<Change
|
||||
change={Number(formatDecimal(oneDayPnlChange, 2))}
|
||||
prefix="$"
|
||||
size="small"
|
||||
/>
|
||||
<p className="text-xs text-th-fgd-4">{t('today')}</p>
|
||||
</div>
|
||||
</div>
|
||||
{performanceData.length > 4 ? (
|
||||
<ChevronRightIcon className="h-6 w-6" />
|
||||
) : null}
|
||||
</button>
|
||||
<button
|
||||
className={`col-span-5 flex items-center justify-between border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left lg:col-span-1 lg:border-l lg:border-t-0 ${
|
||||
interestTotalValue > 1 || interestTotalValue < -1
|
||||
? 'default-transition cursor-pointer md:hover:bg-th-bkg-2'
|
||||
: 'cursor-default'
|
||||
}`}
|
||||
onClick={() => handleChartToShow('cumulative-interest-value')}
|
||||
>
|
||||
</div>
|
||||
<div className="col-span-5 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left lg:col-span-1 lg:border-l lg:border-t-0">
|
||||
<div id="account-step-eight">
|
||||
<Tooltip
|
||||
content="The value of interest earned (deposits) minus interest paid (borrows)."
|
||||
maxWidth="20rem"
|
||||
placement="bottom-end"
|
||||
delay={250}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('total-interest-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-total-interest')}
|
||||
maxWidth="20rem"
|
||||
placement="bottom-end"
|
||||
delay={250}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('total-interest-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
{interestTotalValue > 1 || interestTotalValue < -1 ? (
|
||||
<Tooltip content="Cumulative Interest Chart" delay={250}>
|
||||
<IconButton
|
||||
className="text-th-fgd-3"
|
||||
hideBg
|
||||
onClick={() =>
|
||||
handleChartToShow('cumulative-interest-value')
|
||||
}
|
||||
>
|
||||
<ChartBarIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
|
||||
{formatFixedDecimals(interestTotalValue, true, true)}
|
||||
</p>
|
||||
<div className="flex space-x-1">
|
||||
<Change change={oneDayInterestChange} prefix="$" size="small" />
|
||||
<Change
|
||||
change={Number(formatDecimal(oneDayInterestChange, 2))}
|
||||
prefix="$"
|
||||
size="small"
|
||||
/>
|
||||
<p className="text-xs text-th-fgd-4">{t('today')}</p>
|
||||
</div>
|
||||
</div>
|
||||
{interestTotalValue > 1 || interestTotalValue < -1 ? (
|
||||
<ChevronRightIcon className="-mt-0.5 h-6 w-6" />
|
||||
) : null}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<AccountTabs />
|
||||
{/* {!tourSettings?.account_tour_seen && isOnBoarded && connected ? (
|
||||
<AccountOnboardingTour />
|
||||
) : null} */}
|
||||
{showPnlHistory ? (
|
||||
<PnlHistoryModal
|
||||
pnlChangeToday={oneDayPnlChange}
|
||||
isOpen={showPnlHistory}
|
||||
onClose={handleCloseDailyPnlModal}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div className="p-6 pb-0">
|
||||
{chartToShow === 'account-value' ? (
|
||||
<AccountChart
|
||||
chartToShow="account-value"
|
||||
data={performanceData}
|
||||
hideChart={handleHideChart}
|
||||
mangoAccountAddress={mangoAccountAddress}
|
||||
yKey="account_equity"
|
||||
/>
|
||||
) : chartToShow === 'pnl' ? (
|
||||
<AccountChart
|
||||
chartToShow="pnl"
|
||||
data={performanceData}
|
||||
hideChart={handleHideChart}
|
||||
mangoAccountAddress={mangoAccountAddress}
|
||||
yKey="pnl"
|
||||
/>
|
||||
) : (
|
||||
<AccountChart
|
||||
chartToShow="cumulative-interest-value"
|
||||
data={performanceData.map((d) => ({
|
||||
interest_value:
|
||||
d.borrow_interest_cumulative_usd +
|
||||
d.deposit_interest_cumulative_usd,
|
||||
time: d.time,
|
||||
}))}
|
||||
hideChart={handleHideChart}
|
||||
mangoAccountAddress={mangoAccountAddress}
|
||||
yKey="interest_value"
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useUnsettledSpotBalances } from 'hooks/useUnsettledSpotBalances'
|
|||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
|
||||
import TradeHistory from '@components/trade/TradeHistory'
|
||||
|
||||
const AccountTabs = () => {
|
||||
const [activeTab, setActiveTab] = useState('balances')
|
||||
|
@ -23,15 +24,16 @@ const AccountTabs = () => {
|
|||
|
||||
return [
|
||||
['balances', 0],
|
||||
['trade:unsettled', unsettledTradeCount],
|
||||
['activity:activity', 0],
|
||||
['swap:swap-history', 0],
|
||||
['trade:unsettled', unsettledTradeCount],
|
||||
['trade-history', 0],
|
||||
]
|
||||
}, [unsettledPerpPositions, unsettledSpotBalances])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
<div className="hide-scroll overflow-x-auto border-b border-th-bkg-3">
|
||||
<TabButtons
|
||||
activeValue={activeTab}
|
||||
onChange={(v) => setActiveTab(v)}
|
||||
|
@ -51,10 +53,6 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
|
|||
switch (activeTab) {
|
||||
case 'balances':
|
||||
return <TokenList />
|
||||
case 'activity:activity':
|
||||
return <ActivityFeed />
|
||||
case 'swap:swap-history':
|
||||
return <SwapHistoryTable />
|
||||
case 'trade:unsettled':
|
||||
return (
|
||||
<UnsettledTrades
|
||||
|
@ -62,6 +60,12 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
|
|||
unsettledPerpPositions={unsettledPerpPositions}
|
||||
/>
|
||||
)
|
||||
case 'activity:activity':
|
||||
return <ActivityFeed />
|
||||
case 'swap:swap-history':
|
||||
return <SwapHistoryTable />
|
||||
case 'trade-history':
|
||||
return <TradeHistory />
|
||||
default:
|
||||
return <TokenList />
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { useViewport } from 'hooks/useViewport'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import Image from 'next/legacy/image'
|
||||
import { Fragment, useCallback, useState } from 'react'
|
||||
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
||||
import { PAGINATION_PAGE_LENGTH, PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
||||
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
|
||||
|
@ -55,8 +55,12 @@ const ActivityFeedTable = ({
|
|||
s.activityFeed.loading = true
|
||||
})
|
||||
if (!mangoAccountAddress) return
|
||||
setOffset(offset + 25)
|
||||
actions.fetchActivityFeed(mangoAccountAddress, offset + 25, params)
|
||||
setOffset(offset + PAGINATION_PAGE_LENGTH)
|
||||
actions.fetchActivityFeed(
|
||||
mangoAccountAddress,
|
||||
offset + PAGINATION_PAGE_LENGTH,
|
||||
params
|
||||
)
|
||||
}, [actions, offset, params, mangoAccountAddress])
|
||||
|
||||
const getCreditAndDebit = (activity: any) => {
|
||||
|
@ -316,7 +320,8 @@ const ActivityFeedTable = ({
|
|||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{activityFeed.length && activityFeed.length % 25 === 0 ? (
|
||||
{activityFeed.length &&
|
||||
activityFeed.length % PAGINATION_PAGE_LENGTH === 0 ? (
|
||||
<div className="flex justify-center py-6">
|
||||
<LinkButton onClick={handleShowMore}>Show More</LinkButton>
|
||||
</div>
|
||||
|
|
|
@ -4,25 +4,53 @@ import mangoStore from '@store/mangoStore'
|
|||
import { notify } from '../../utils/notifications'
|
||||
import Button from '../shared/Button'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import BounceLoader from '../shared/BounceLoader'
|
||||
import { MangoAccount } from '@blockworks-foundation/mango-v4'
|
||||
import { TrashIcon } from '@heroicons/react/20/solid'
|
||||
import {
|
||||
MangoAccount,
|
||||
TokenPosition,
|
||||
toUiDecimalsForQuote,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ExclamationCircleIcon,
|
||||
TrashIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
|
||||
import { getMultipleAccounts } from '@project-serum/anchor/dist/cjs/utils/rpc'
|
||||
import { formatFixedDecimals } from 'utils/numbers'
|
||||
|
||||
const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { t } = useTranslation(['close-account'])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const set = mangoStore((s) => s.set)
|
||||
const openOrders = Object.values(mangoStore((s) => s.mangoAccount.openOrders))
|
||||
const connection = mangoStore.getState().connection
|
||||
const hasOpenOrders =
|
||||
openOrders.length && openOrders.filter((x) => x.length).length > 0
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount)
|
||||
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
|
||||
const openPerpPositions = Object.values(perpPositions).filter((p) =>
|
||||
p.basePositionLots.toNumber()
|
||||
)
|
||||
const group = mangoStore.getState().group
|
||||
const unsettledBalances = Object.values(mangoAccount.spotBalances).filter(
|
||||
(x) => x.unsettled && x.unsettled > 0
|
||||
)
|
||||
const unsettledPerpPositions = useUnsettledPerpPositions()
|
||||
const [hasBorrows, setHasBorrows] = useState(false)
|
||||
const [hasOpenPositions, setHasOpenPositions] = useState(false)
|
||||
const [totalAccountSOL, setTotalAccountSOL] = useState(0)
|
||||
|
||||
const handleCloseMangoAccount = async () => {
|
||||
const client = mangoStore.getState().client
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const mangoAccounts = mangoStore.getState().mangoAccounts
|
||||
const group = mangoStore.getState().group
|
||||
|
||||
if (!mangoAccount || !group) return
|
||||
setLoading(true)
|
||||
try {
|
||||
const tx = await client.closeMangoAccount(group, mangoAccount)
|
||||
const tx = await client.emptyAndCloseMangoAccount(group, mangoAccount)
|
||||
if (tx) {
|
||||
const newMangoAccounts = mangoAccounts.filter(
|
||||
(ma) => !ma.publicKey.equals(mangoAccount.publicKey)
|
||||
|
@ -50,19 +78,126 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
}
|
||||
}
|
||||
|
||||
const fetchTotalAccountSOL = useCallback(async () => {
|
||||
if (!mangoAccount) {
|
||||
return
|
||||
}
|
||||
const accountKeys = [
|
||||
mangoAccount.current!.publicKey,
|
||||
...mangoAccount.openOrderAccounts.map((x) => x.address),
|
||||
]
|
||||
const accounts = await getMultipleAccounts(connection, accountKeys)
|
||||
const lamports =
|
||||
accounts.reduce((total, account) => {
|
||||
return total + (account?.account.lamports || 0)
|
||||
}, 0) * 0.000000001
|
||||
|
||||
setTotalAccountSOL(lamports)
|
||||
}, [mangoAccount])
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccount && group) {
|
||||
if (
|
||||
mangoAccount.current
|
||||
?.tokensActive()
|
||||
.filter((token: TokenPosition) =>
|
||||
token
|
||||
.balance(group.getFirstBankByTokenIndex(token.tokenIndex))
|
||||
.isNeg()
|
||||
).length
|
||||
) {
|
||||
setHasBorrows(true)
|
||||
}
|
||||
if (openPerpPositions.length || unsettledPerpPositions.length) {
|
||||
setHasOpenPositions(true)
|
||||
}
|
||||
fetchTotalAccountSOL()
|
||||
}
|
||||
}, [mangoAccount, group])
|
||||
|
||||
const isDisabled =
|
||||
hasOpenOrders ||
|
||||
hasBorrows ||
|
||||
hasOpenPositions ||
|
||||
!!unsettledBalances.length
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<div className="h-56">
|
||||
<div className="h-[550px]">
|
||||
{loading ? (
|
||||
<BounceLoader loadingMessage={t('closing-account')} />
|
||||
) : (
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<div className="pb-6">
|
||||
<div className="space-y-4">
|
||||
<h2 className="mb-1">{t('close-account')}</h2>
|
||||
<p>{t('close-account-desc')}</p>
|
||||
<p>{t('description')}</p>
|
||||
<p>{t('you-must')}:</p>
|
||||
<div className="overflow-none space-y-2 rounded-md bg-th-bkg-4 p-2 sm:p-4">
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
{hasBorrows ? (
|
||||
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
||||
) : (
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
)}
|
||||
{t('close-all-borrows')}
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
{hasOpenPositions ? (
|
||||
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
||||
) : (
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
)}
|
||||
{t('close-perp-positions')}
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
{hasOpenOrders ? (
|
||||
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
||||
) : (
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
)}
|
||||
{t('close-open-orders')}
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
{unsettledBalances.length ? (
|
||||
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
||||
) : (
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
)}
|
||||
{t('settle-balances')}
|
||||
</div>
|
||||
</div>
|
||||
<p>By closing your account you will:</p>
|
||||
<div className="overflow-none space-y-2 rounded-md bg-th-bkg-4 p-2 sm:p-4">
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
{t('delete-your-account')}
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
{t('withdraw-assets-worth', {
|
||||
value:
|
||||
mangoAccount && group
|
||||
? formatFixedDecimals(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount!.current!.getEquity(group).toNumber()
|
||||
),
|
||||
false,
|
||||
true
|
||||
)
|
||||
: 0,
|
||||
})}
|
||||
</div>
|
||||
<div className="flex items-center text-th-fgd-2">
|
||||
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
||||
{t('recover-x-sol', {
|
||||
amount: totalAccountSOL.toFixed(3),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="w-full"
|
||||
disabled={isDisabled}
|
||||
onClick={handleCloseMangoAccount}
|
||||
size="large"
|
||||
>
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import { ModalProps } from '../../types/modal'
|
||||
import Modal from '../shared/Modal'
|
||||
import mangoStore, { PerformanceDataItem } from '@store/mangoStore'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import dayjs from 'dayjs'
|
||||
import Change from '@components/shared/Change'
|
||||
import SheenLoader from '@components/shared/SheenLoader'
|
||||
import { NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
import { formatDecimal } from 'utils/numbers'
|
||||
|
||||
interface PnlChange {
|
||||
time: string
|
||||
pnlChange: number
|
||||
}
|
||||
|
||||
interface PnlHistoryModalProps {
|
||||
pnlChangeToday: number
|
||||
}
|
||||
|
||||
type ModalCombinedProps = PnlHistoryModalProps & ModalProps
|
||||
|
||||
const PnlHistoryModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
pnlChangeToday,
|
||||
}: ModalCombinedProps) => {
|
||||
const { t } = useTranslation('account')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const actions = mangoStore.getState().actions
|
||||
const loading = mangoStore((s) => s.mangoAccount.performance.loading)
|
||||
const performanceData = mangoStore((s) => s.mangoAccount.performance.data)
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccountAddress) {
|
||||
actions.fetchAccountPerformance(mangoAccountAddress, 30)
|
||||
}
|
||||
}, [actions, mangoAccountAddress])
|
||||
|
||||
const dailyValues: PnlChange[] = useMemo(() => {
|
||||
if (!performanceData.length) return []
|
||||
|
||||
const dailyPnl = performanceData.filter((d: PerformanceDataItem) => {
|
||||
const date = new Date(d.time)
|
||||
return date.getHours() === 0
|
||||
})
|
||||
|
||||
return dailyPnl.length
|
||||
? dailyPnl
|
||||
.map((d: PerformanceDataItem, index: number) => {
|
||||
if (index < dailyPnl.length - 1) {
|
||||
const change = dailyPnl[index + 1].pnl - d.pnl
|
||||
return {
|
||||
time: d.time,
|
||||
pnlChange: change,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
time: performanceData[performanceData.length - 1].time,
|
||||
pnlChange: pnlChangeToday,
|
||||
}
|
||||
}
|
||||
})
|
||||
.reverse()
|
||||
: []
|
||||
}, [performanceData])
|
||||
|
||||
const pnlThisWeek = useMemo(() => {
|
||||
if (dailyValues.length) {
|
||||
const saturdayIndex = dailyValues.findIndex((d) => {
|
||||
const day = new Date(d.time).getDay()
|
||||
return day === 6
|
||||
})
|
||||
if (saturdayIndex !== -1) {
|
||||
return dailyValues
|
||||
.slice(0, saturdayIndex)
|
||||
.reduce((a, c) => a + c.pnlChange, 0)
|
||||
} else {
|
||||
return dailyValues.reduce((a, c) => a + c.pnlChange, 0)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}, [dailyValues])
|
||||
|
||||
const getLastSunday = (d: Date) => {
|
||||
return d.setDate(d.getDate() - d.getDay())
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<div className="h-96">
|
||||
<div className="flex h-full flex-col">
|
||||
<h2 className="mb-4">{t('pnl-history')}</h2>
|
||||
{loading ? (
|
||||
<div className="space-y-1.5">
|
||||
{[...Array(4)].map((x, i) => (
|
||||
<SheenLoader className="flex flex-1" key={i}>
|
||||
<div className="h-12 w-full bg-th-bkg-2" />
|
||||
</SheenLoader>
|
||||
))}
|
||||
</div>
|
||||
) : dailyValues?.length ? (
|
||||
<>
|
||||
<div className="thin-scroll overflow-auto pr-1">
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{dailyValues.map((v: any) => (
|
||||
<div
|
||||
className="flex items-center justify-between border-t border-th-bkg-3 p-3"
|
||||
key={v.time + v.pnlChange}
|
||||
>
|
||||
<p>{dayjs(v.time).format('YYYY-MM-DD')}</p>
|
||||
<Change
|
||||
change={Number(formatDecimal(v.pnlChange, 2))}
|
||||
prefix="$"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex justify-between rounded-md bg-th-bkg-2 p-3">
|
||||
<p>
|
||||
{t('week-starting', {
|
||||
week: dayjs(getLastSunday(new Date())).format('MM-DD'),
|
||||
})}
|
||||
</p>
|
||||
<Change change={pnlThisWeek} prefix="$" />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex h-full flex-col items-center justify-center pb-12">
|
||||
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-3" />
|
||||
<p>{t('no-pnl-history')}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default PnlHistoryModal
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'next-i18next'
|
|||
import Button, { LinkButton } from '../shared/Button'
|
||||
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { MANGO_DATA_API_URL } from 'utils/constants'
|
||||
|
||||
const ImgWithLoader = (props: any) => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
@ -67,7 +68,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
|
|||
}),
|
||||
}
|
||||
const response = await fetch(
|
||||
'https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details',
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details`,
|
||||
requestOptions
|
||||
)
|
||||
if (response.status === 200) {
|
||||
|
@ -113,7 +114,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
|
|||
}),
|
||||
}
|
||||
const response = await fetch(
|
||||
'https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details',
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details`,
|
||||
requestOptions
|
||||
)
|
||||
if (response.status === 200) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import mangoStore from '@store/mangoStore'
|
|||
import startCase from 'lodash/startCase'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { ChangeEvent, useState } from 'react'
|
||||
import { MANGO_DATA_API_URL } from 'utils/constants'
|
||||
import { notify } from 'utils/notifications'
|
||||
import ProfileImage from './ProfileImage'
|
||||
|
||||
|
@ -42,7 +43,7 @@ const EditProfileForm = ({
|
|||
try {
|
||||
setLoadUniquenessCheck(true)
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/user-data/check-profile-name-unique?profile-name=${name}`
|
||||
`${MANGO_DATA_API_URL}/user-data/check-profile-name-unique?profile-name=${name}`
|
||||
)
|
||||
const uniquenessCheck = await response.json()
|
||||
|
||||
|
@ -103,7 +104,7 @@ const EditProfileForm = ({
|
|||
}),
|
||||
}
|
||||
const response = await fetch(
|
||||
'https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details',
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details`,
|
||||
requestOptions
|
||||
)
|
||||
if (response.status === 200) {
|
||||
|
|
|
@ -3,7 +3,7 @@ export const UpTriangle = ({ size }: { size?: 'small' }) => (
|
|||
className={`h-0 w-0 ${
|
||||
size === 'small'
|
||||
? 'border-l-[4px] border-r-[4px] border-b-[6.92px]'
|
||||
: 'border-l-[6px] border-r-[6px] border-b-[10.39px]'
|
||||
: 'border-l-[5px] border-r-[5px] border-b-[8.65px]'
|
||||
} border-b-th-up border-l-transparent border-r-transparent`}
|
||||
/>
|
||||
)
|
||||
|
@ -13,7 +13,7 @@ export const DownTriangle = ({ size }: { size?: 'small' }) => (
|
|||
className={`h-0 w-0 ${
|
||||
size === 'small'
|
||||
? 'border-l-[4px] border-r-[4px] border-t-[6.92px]'
|
||||
: 'border-l-[6px] border-r-[6px] border-t-[10.39px]'
|
||||
: 'border-l-[5px] border-r-[5px] border-t-[8.65px]'
|
||||
} border-l-transparent border-r-transparent border-t-th-down`}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -7,7 +7,11 @@ export const Table = ({
|
|||
}: {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}) => <table className={`m-0 min-w-full p-0 ${className}`}>{children}</table>
|
||||
}) => (
|
||||
<div className="thin-scroll overflow-x-auto">
|
||||
<table className={`m-0 min-w-full p-0 ${className}`}>{children}</table>
|
||||
</div>
|
||||
)
|
||||
|
||||
export const TrHead = ({
|
||||
children,
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Transition } from '@headlessui/react'
|
|||
import Button, { IconButton } from '../shared/Button'
|
||||
import Loading from '../shared/Loading'
|
||||
import { EnterBottomExitBottom } from '../shared/Transitions'
|
||||
import useJupiterRoutes from './useJupiterRoutes'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
import SheenLoader from '../shared/SheenLoader'
|
||||
import { HealthType } from '@blockworks-foundation/mango-v4'
|
||||
import {
|
||||
|
@ -80,7 +80,7 @@ const SwapForm = () => {
|
|||
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
|
||||
const [debouncedAmountOut] = useDebounce(amountOutFormValue, 300)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { connected, publicKey } = useWallet()
|
||||
|
||||
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountIn)
|
||||
|
@ -94,12 +94,13 @@ const SwapForm = () => {
|
|||
: new Decimal(0)
|
||||
}, [debouncedAmountOut])
|
||||
|
||||
const { bestRoute, routes } = useJupiterRoutes({
|
||||
const { bestRoute, routes } = useQuoteRoutes({
|
||||
inputMint: inputBank?.mint.toString() || USDC_MINT,
|
||||
outputMint: outputBank?.mint.toString() || MANGO_MINT,
|
||||
amount: swapMode === 'ExactIn' ? debouncedAmountIn : debouncedAmountOut,
|
||||
slippage,
|
||||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
})
|
||||
|
||||
const setAmountInFormValue = useCallback(
|
||||
|
|
|
@ -31,10 +31,8 @@ import { useWallet } from '@solana/wallet-adapter-react'
|
|||
|
||||
const SwapHistoryTable = () => {
|
||||
const { t } = useTranslation(['common', 'settings', 'swap'])
|
||||
const swapHistory = mangoStore((s) => s.mangoAccount.stats.swapHistory.data)
|
||||
const initialLoad = mangoStore(
|
||||
(s) => s.mangoAccount.stats.swapHistory.initialLoad
|
||||
)
|
||||
const swapHistory = mangoStore((s) => s.mangoAccount.swapHistory.data)
|
||||
const initialLoad = mangoStore((s) => s.mangoAccount.swapHistory.initialLoad)
|
||||
const { connected } = useWallet()
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
const [showSwapDetails, setSwapDetails] = useState('')
|
||||
|
@ -63,7 +61,8 @@ const SwapHistoryTable = () => {
|
|||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">{t('date')}</Th>
|
||||
<Th className="w-1/3 text-left">{t('swap')}</Th>
|
||||
<Th className="text-left">{t('swap:paid')}</Th>
|
||||
<Th className="text-left">{t('swap:received')}</Th>
|
||||
<Th className="text-right">{t('value')}</Th>
|
||||
<Th className="text-right">{t('borrow')}</Th>
|
||||
<Th className="text-right">{t('borrow-fee')}</Th>
|
||||
|
@ -105,14 +104,14 @@ const SwapHistoryTable = () => {
|
|||
|
||||
if (mangoTokens.length) {
|
||||
baseLogoURI = mangoTokens.find(
|
||||
(t) => t.symbol === inSymbol
|
||||
(t) => t.symbol.toUpperCase() === inSymbol.toUpperCase()
|
||||
)?.logoURI
|
||||
quoteLogoURI = mangoTokens.find(
|
||||
(t) => t.symbol === outSymbol
|
||||
(t) => t.symbol.toUpperCase() === outSymbol.toUpperCase()
|
||||
)?.logoURI
|
||||
}
|
||||
|
||||
const inDecimals = countLeadingZeros(swap_in_amount)
|
||||
const inDecimals = countLeadingZeros(swap_in_amount) + 2
|
||||
const outDecimals = countLeadingZeros(swap_out_amount) + 2
|
||||
return (
|
||||
<TrBody key={signature}>
|
||||
|
@ -124,54 +123,53 @@ const SwapHistoryTable = () => {
|
|||
{dayjs(block_datetime).format('h:mma')}
|
||||
</p>
|
||||
</Td>
|
||||
<Td className="w-1/3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex w-1/2 items-center">
|
||||
<div className="mr-2 flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={baseLogoURI || ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap">
|
||||
{`${formatDecimal(swap_in_amount, inDecimals)}`}
|
||||
<span className="ml-1 font-body text-th-fgd-3">
|
||||
{inSymbol}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('price')}:
|
||||
</span>{' '}
|
||||
{formatFixedDecimals(swap_in_price_usd, true)}
|
||||
</p>
|
||||
</div>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={baseLogoURI || ''}
|
||||
/>
|
||||
</div>
|
||||
<ArrowRightIcon className="mx-4 h-4 w-4 flex-shrink-0 text-th-fgd-4" />
|
||||
<div className="flex w-1/2 items-center">
|
||||
<div className="mr-2 flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={quoteLogoURI || ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap">
|
||||
{`${formatDecimal(swap_out_amount, outDecimals)}`}
|
||||
<span className="ml-1 font-body text-th-fgd-3">
|
||||
{outSymbol}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
<span className="font-body">{t('price')}:</span>{' '}
|
||||
{formatFixedDecimals(swap_out_price_usd, true)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap">
|
||||
{`${formatDecimal(swap_in_amount, inDecimals)}`}
|
||||
<span className="ml-1 font-body text-th-fgd-3">
|
||||
{inSymbol}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('price')}:
|
||||
</span>{' '}
|
||||
{formatFixedDecimals(swap_in_price_usd, true)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={quoteLogoURI || ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap">
|
||||
{`${formatDecimal(swap_out_amount, outDecimals)}`}
|
||||
<span className="ml-1 font-body text-th-fgd-3">
|
||||
{outSymbol}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-4">
|
||||
<span className="font-body">{t('price')}:</span>{' '}
|
||||
{formatFixedDecimals(swap_out_price_usd, true)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
|
@ -268,13 +266,16 @@ const SwapHistoryTable = () => {
|
|||
|
||||
if (mangoTokens.length) {
|
||||
baseLogoURI = mangoTokens.find(
|
||||
(t) => t.symbol === inSymbol
|
||||
(t) => t.symbol.toUpperCase() === inSymbol.toUpperCase()
|
||||
)?.logoURI
|
||||
quoteLogoURI = mangoTokens.find(
|
||||
(t) => t.symbol === outSymbol
|
||||
(t) => t.symbol.toUpperCase() === outSymbol.toUpperCase()
|
||||
)?.logoURI
|
||||
}
|
||||
|
||||
const inDecimals = countLeadingZeros(swap_in_amount) + 2
|
||||
const outDecimals = countLeadingZeros(swap_out_amount) + 2
|
||||
|
||||
return (
|
||||
<div key={signature} className="border-b border-th-bkg-3 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
@ -290,7 +291,7 @@ const SwapHistoryTable = () => {
|
|||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap font-mono text-th-fgd-1">
|
||||
{swap_in_amount.toFixed(2)}{' '}
|
||||
{formatDecimal(swap_in_amount, inDecimals)}{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{inSymbol}
|
||||
</span>
|
||||
|
@ -315,7 +316,7 @@ const SwapHistoryTable = () => {
|
|||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap font-mono text-th-fgd-1">
|
||||
{swap_out_amount.toFixed(2)}{' '}
|
||||
{formatDecimal(swap_out_amount, outDecimals)}{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{outSymbol}
|
||||
</span>
|
||||
|
@ -329,7 +330,10 @@ const SwapHistoryTable = () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<IconButton onClick={() => handleShowSwapDetails(signature)}>
|
||||
<IconButton
|
||||
onClick={() => handleShowSwapDetails(signature)}
|
||||
size="medium"
|
||||
>
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
showSwapDetails === signature
|
||||
|
|
|
@ -42,6 +42,7 @@ import Tooltip from '@components/shared/Tooltip'
|
|||
import { Disclosure } from '@headlessui/react'
|
||||
import RoutesModal from './RoutesModal'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { createAssociatedTokenAccountIdempotentInstruction } from '@blockworks-foundation/mango-v4'
|
||||
|
||||
type JupiterRouteInfoProps = {
|
||||
amountIn: Decimal
|
||||
|
@ -78,6 +79,40 @@ const deserializeJupiterIxAndAlt = async (
|
|||
return [decompiledMessage.instructions, addressLookupTables]
|
||||
}
|
||||
|
||||
const prepareMangoRouterInstructions = async (
|
||||
selectedRoute: RouteInfo,
|
||||
inputMint: PublicKey,
|
||||
outputMint: PublicKey,
|
||||
userPublicKey: PublicKey
|
||||
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
|
||||
if (!selectedRoute || !selectedRoute.mints || !selectedRoute.instructions) {
|
||||
return [[], []]
|
||||
}
|
||||
const mintsToFilterOut = [inputMint, outputMint]
|
||||
const filteredOutMints = [
|
||||
...selectedRoute.mints.filter(
|
||||
(routeMint) =>
|
||||
!mintsToFilterOut.find((filterOutMint) =>
|
||||
filterOutMint.equals(routeMint)
|
||||
)
|
||||
),
|
||||
]
|
||||
const additionalInstructions = []
|
||||
for (const mint of filteredOutMints) {
|
||||
const ix = await createAssociatedTokenAccountIdempotentInstruction(
|
||||
userPublicKey,
|
||||
userPublicKey,
|
||||
mint
|
||||
)
|
||||
additionalInstructions.push(ix)
|
||||
}
|
||||
const instructions = [
|
||||
...additionalInstructions,
|
||||
...selectedRoute.instructions,
|
||||
]
|
||||
return [instructions, []]
|
||||
}
|
||||
|
||||
const fetchJupiterTransaction = async (
|
||||
connection: Connection,
|
||||
selectedRoute: RouteInfo,
|
||||
|
@ -217,14 +252,22 @@ const SwapReviewRouteInfo = ({
|
|||
|
||||
if (!mangoAccount || !group || !inputBank || !outputBank) return
|
||||
setSubmitting(true)
|
||||
const [ixs, alts] = await fetchJupiterTransaction(
|
||||
connection,
|
||||
selectedRoute,
|
||||
mangoAccount.owner,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint
|
||||
)
|
||||
const [ixs, alts] =
|
||||
selectedRoute.routerName === 'Mango'
|
||||
? await prepareMangoRouterInstructions(
|
||||
selectedRoute,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
mangoAccount.owner
|
||||
)
|
||||
: await fetchJupiterTransaction(
|
||||
connection,
|
||||
selectedRoute,
|
||||
mangoAccount.owner,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint
|
||||
)
|
||||
|
||||
try {
|
||||
const tx = await client.marginTrade({
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
import { useQuery } from '@tanstack/react-query'
|
||||
import Decimal from 'decimal.js'
|
||||
import { RouteInfo } from 'types/jupiter'
|
||||
import useJupiterSwapData from './useJupiterSwapData'
|
||||
|
||||
type useJupiterPropTypes = {
|
||||
inputMint: string
|
||||
outputMint: string
|
||||
amount: string
|
||||
slippage: number
|
||||
swapMode: string
|
||||
}
|
||||
|
||||
const fetchJupiterRoutes = async (
|
||||
inputMint = 'So11111111111111111111111111111111111111112',
|
||||
outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
amount = 0,
|
||||
slippage = 50,
|
||||
swapMode = 'ExactIn',
|
||||
feeBps = 0
|
||||
) => {
|
||||
{
|
||||
const paramsString = new URLSearchParams({
|
||||
inputMint: inputMint.toString(),
|
||||
outputMint: outputMint.toString(),
|
||||
amount: amount.toString(),
|
||||
slippageBps: Math.ceil(slippage * 100).toString(),
|
||||
feeBps: feeBps.toString(),
|
||||
swapMode,
|
||||
}).toString()
|
||||
|
||||
const response = await fetch(
|
||||
`https://quote-api.jup.ag/v4/quote?${paramsString}`
|
||||
)
|
||||
|
||||
const res = await response.json()
|
||||
const data = res.data
|
||||
|
||||
return {
|
||||
routes: res.data,
|
||||
bestRoute: data[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const useJupiterRoutes = ({
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
}: useJupiterPropTypes) => {
|
||||
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
|
||||
|
||||
const decimals =
|
||||
swapMode === 'ExactIn'
|
||||
? inputTokenInfo?.decimals || 6
|
||||
: outputTokenInfo?.decimals || 6
|
||||
|
||||
const nativeAmount =
|
||||
amount && !Number.isNaN(+amount)
|
||||
? new Decimal(amount).mul(10 ** decimals)
|
||||
: new Decimal(0)
|
||||
|
||||
const res = useQuery<{ routes: RouteInfo[]; bestRoute: RouteInfo }, Error>(
|
||||
['swap-routes', inputMint, outputMint, amount, slippage, swapMode],
|
||||
async () =>
|
||||
fetchJupiterRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
nativeAmount.toNumber(),
|
||||
slippage,
|
||||
swapMode
|
||||
),
|
||||
{
|
||||
enabled: amount ? true : false,
|
||||
}
|
||||
)
|
||||
|
||||
return amount
|
||||
? {
|
||||
...(res.data ?? {
|
||||
routes: [],
|
||||
bestRoute: undefined,
|
||||
}),
|
||||
isLoading: res.isLoading,
|
||||
}
|
||||
: {
|
||||
routes: [],
|
||||
bestRoute: undefined,
|
||||
isLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
export default useJupiterRoutes
|
|
@ -0,0 +1,207 @@
|
|||
import { PublicKey } from '@solana/web3.js'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import Decimal from 'decimal.js'
|
||||
import { RouteInfo } from 'types/jupiter'
|
||||
import { MANGO_ROUTER_API_URL } from 'utils/constants'
|
||||
import useJupiterSwapData from './useJupiterSwapData'
|
||||
|
||||
type useQuoteRoutesPropTypes = {
|
||||
inputMint: string
|
||||
outputMint: string
|
||||
amount: string
|
||||
slippage: number
|
||||
swapMode: string
|
||||
wallet: string | undefined | null
|
||||
}
|
||||
|
||||
const fetchJupiterRoutes = async (
|
||||
inputMint = 'So11111111111111111111111111111111111111112',
|
||||
outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
amount = 0,
|
||||
slippage = 50,
|
||||
swapMode = 'ExactIn',
|
||||
feeBps = 0
|
||||
) => {
|
||||
{
|
||||
const paramsString = new URLSearchParams({
|
||||
inputMint: inputMint.toString(),
|
||||
outputMint: outputMint.toString(),
|
||||
amount: amount.toString(),
|
||||
slippageBps: Math.ceil(slippage * 100).toString(),
|
||||
feeBps: feeBps.toString(),
|
||||
swapMode,
|
||||
}).toString()
|
||||
|
||||
const response = await fetch(
|
||||
`https://quote-api.jup.ag/v4/quote?${paramsString}`
|
||||
)
|
||||
|
||||
const res = await response.json()
|
||||
const data = res.data
|
||||
|
||||
return {
|
||||
routes: res.data as RouteInfo[],
|
||||
bestRoute: (data.length ? data[0] : null) as RouteInfo | null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fetchMangoRoutes = async (
|
||||
inputMint = 'So11111111111111111111111111111111111111112',
|
||||
outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
amount = 0,
|
||||
slippage = 50,
|
||||
swapMode = 'ExactIn',
|
||||
feeBps = 0,
|
||||
wallet = PublicKey.default.toBase58()
|
||||
) => {
|
||||
{
|
||||
const defaultOtherAmount =
|
||||
swapMode === 'ExactIn' ? 0 : Number.MAX_SAFE_INTEGER
|
||||
|
||||
const paramsString = new URLSearchParams({
|
||||
inputMint: inputMint.toString(),
|
||||
outputMint: outputMint.toString(),
|
||||
amount: amount.toString(),
|
||||
slippage: ((slippage * 1) / 100).toString(),
|
||||
feeBps: feeBps.toString(),
|
||||
mode: swapMode,
|
||||
wallet: wallet,
|
||||
otherAmountThreshold: defaultOtherAmount.toString(),
|
||||
}).toString()
|
||||
|
||||
const response = await fetch(`${MANGO_ROUTER_API_URL}/swap?${paramsString}`)
|
||||
|
||||
const res = await response.json()
|
||||
const data: RouteInfo[] = res.map((route: any) => ({
|
||||
...route,
|
||||
priceImpactPct: route.priceImpact,
|
||||
slippageBps: slippage,
|
||||
marketInfos: route.marketInfos.map((mInfo: any) => ({
|
||||
...mInfo,
|
||||
lpFee: {
|
||||
...mInfo.fee,
|
||||
pct: mInfo.fee.rate,
|
||||
},
|
||||
})),
|
||||
mints: route.mints.map((x: string) => new PublicKey(x)),
|
||||
instructions: route.instructions.map((ix: any) => ({
|
||||
...ix,
|
||||
programId: new PublicKey(ix.programId),
|
||||
data: Buffer.from(ix.data, 'base64'),
|
||||
keys: ix.keys.map((key: any) => ({
|
||||
...key,
|
||||
pubkey: new PublicKey(key.pubkey),
|
||||
})),
|
||||
})),
|
||||
routerName: 'Mango',
|
||||
}))
|
||||
return {
|
||||
routes: data,
|
||||
bestRoute: (data.length ? data[0] : null) as RouteInfo | null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleGetRoutes = async (
|
||||
inputMint = 'So11111111111111111111111111111111111111112',
|
||||
outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
amount = 0,
|
||||
slippage = 50,
|
||||
swapMode = 'ExactIn',
|
||||
feeBps = 0,
|
||||
wallet: string | undefined | null
|
||||
) => {
|
||||
wallet ||= PublicKey.default.toBase58()
|
||||
|
||||
const results = await Promise.allSettled([
|
||||
fetchMangoRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
feeBps,
|
||||
wallet
|
||||
),
|
||||
fetchJupiterRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
feeBps
|
||||
),
|
||||
])
|
||||
const responses = results
|
||||
.filter((x) => x.status === 'fulfilled' && x.value.bestRoute !== null)
|
||||
.map((x) => (x as any).value)
|
||||
|
||||
const sortedByBiggestOutAmount = (
|
||||
responses as {
|
||||
routes: RouteInfo[]
|
||||
bestRoute: RouteInfo
|
||||
}[]
|
||||
).sort(
|
||||
(a, b) => Number(b.bestRoute.outAmount) - Number(a.bestRoute.outAmount)
|
||||
)
|
||||
|
||||
return {
|
||||
routes: sortedByBiggestOutAmount[0].routes,
|
||||
bestRoute: sortedByBiggestOutAmount[0].bestRoute,
|
||||
}
|
||||
}
|
||||
|
||||
const useQuoteRoutes = ({
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
wallet,
|
||||
}: useQuoteRoutesPropTypes) => {
|
||||
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
|
||||
|
||||
const decimals =
|
||||
swapMode === 'ExactIn'
|
||||
? inputTokenInfo?.decimals || 6
|
||||
: outputTokenInfo?.decimals || 6
|
||||
|
||||
const nativeAmount =
|
||||
amount && !Number.isNaN(+amount)
|
||||
? new Decimal(amount).mul(10 ** decimals)
|
||||
: new Decimal(0)
|
||||
|
||||
const res = useQuery<{ routes: RouteInfo[]; bestRoute: RouteInfo }, Error>(
|
||||
['swap-routes', inputMint, outputMint, amount, slippage, swapMode, wallet],
|
||||
async () =>
|
||||
handleGetRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
nativeAmount.toNumber(),
|
||||
slippage,
|
||||
swapMode,
|
||||
0,
|
||||
wallet
|
||||
),
|
||||
{
|
||||
enabled: amount ? true : false,
|
||||
}
|
||||
)
|
||||
|
||||
return amount
|
||||
? {
|
||||
...(res.data ?? {
|
||||
routes: [],
|
||||
bestRoute: undefined,
|
||||
}),
|
||||
isLoading: res.isLoading,
|
||||
}
|
||||
: {
|
||||
routes: [],
|
||||
bestRoute: undefined,
|
||||
isLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
export default useQuoteRoutes
|
|
@ -3,6 +3,7 @@ import { XMarkIcon } from '@heroicons/react/20/solid'
|
|||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useState } from 'react'
|
||||
import { MANGO_DATA_API_URL } from 'utils/constants'
|
||||
import { WalktourLogic } from 'walktour'
|
||||
|
||||
const CustomTooltip = ({
|
||||
|
@ -37,7 +38,7 @@ const CustomTooltip = ({
|
|||
body: message,
|
||||
}
|
||||
const response = await fetch(
|
||||
'https://mango-transaction-log.herokuapp.com/v4/user-data/settings-unsigned',
|
||||
`${MANGO_DATA_API_URL}/user-data/settings-unsigned`,
|
||||
requestOptions
|
||||
)
|
||||
if (response.status === 200) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import { IconButton } from '@components/shared/Button'
|
||||
import Change from '@components/shared/Change'
|
||||
import { ChartBarIcon } from '@heroicons/react/20/solid'
|
||||
import { useCoingecko } from 'hooks/useCoingecko'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -8,7 +10,13 @@ import { getDecimalCount } from 'utils/numbers'
|
|||
import MarketSelectDropdown from './MarketSelectDropdown'
|
||||
import PerpFundingRate from './PerpFundingRate'
|
||||
|
||||
const AdvancedMarketHeader = () => {
|
||||
const AdvancedMarketHeader = ({
|
||||
showChart,
|
||||
setShowChart,
|
||||
}: {
|
||||
showChart?: boolean
|
||||
setShowChart?: (x: boolean) => void
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { serumOrPerpMarket, baseSymbol, price } = useSelectedMarket()
|
||||
const { data: tokenPrices } = useCoingecko()
|
||||
|
@ -35,31 +43,46 @@ const AdvancedMarketHeader = () => {
|
|||
<div className=" w-full px-5 md:w-auto md:py-0 md:pr-6 lg:pb-0">
|
||||
<MarketSelectDropdown />
|
||||
</div>
|
||||
<div className="flex items-center border-t border-th-bkg-3 py-2 px-5 md:border-t-0 md:py-0 md:px-0">
|
||||
<div id="trade-step-two" className="flex-col md:ml-6">
|
||||
<div className="text-xs text-th-fgd-4">{t('trade:oracle-price')}</div>
|
||||
<div className="font-mono text-xs text-th-fgd-2">
|
||||
{price ? (
|
||||
`$${price.toFixed(
|
||||
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
|
||||
)}`
|
||||
) : (
|
||||
<span className="text-th-fgd-4">–</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-6 flex-col">
|
||||
<div className="text-xs text-th-fgd-4">{t('rolling-change')}</div>
|
||||
<Change change={change} size="small" suffix="%" />
|
||||
</div>
|
||||
{serumOrPerpMarket instanceof PerpMarket ? (
|
||||
<div className="ml-6 flex-col">
|
||||
<div className="text-xs text-th-fgd-4">
|
||||
{t('trade:funding-rate')}
|
||||
<div className="border-t border-th-bkg-3 py-2 px-5 md:border-t-0 md:py-0 md:px-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div id="trade-step-two" className="flex-col md:ml-6">
|
||||
<div className="text-xs text-th-fgd-4">
|
||||
{t('trade:oracle-price')}
|
||||
</div>
|
||||
<div className="font-mono text-xs text-th-fgd-2">
|
||||
{price ? (
|
||||
`$${price.toFixed(
|
||||
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
|
||||
)}`
|
||||
) : (
|
||||
<span className="text-th-fgd-4">–</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<PerpFundingRate />
|
||||
<div className="ml-6 flex-col">
|
||||
<div className="text-xs text-th-fgd-4">{t('rolling-change')}</div>
|
||||
<Change change={change} size="small" suffix="%" />
|
||||
</div>
|
||||
{serumOrPerpMarket instanceof PerpMarket ? (
|
||||
<div className="ml-6 flex-col">
|
||||
<div className="text-xs text-th-fgd-4">
|
||||
{t('trade:funding-rate')}
|
||||
</div>
|
||||
<PerpFundingRate />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{setShowChart ? (
|
||||
<IconButton
|
||||
className={showChart ? 'text-th-active' : 'text-th-fgd-2'}
|
||||
onClick={() => setShowChart(!showChart)}
|
||||
hideBg
|
||||
>
|
||||
<ChartBarIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -33,8 +33,12 @@ const MarketLogos = ({
|
|||
)
|
||||
} else {
|
||||
jupiterBaseToken =
|
||||
mangoTokens.find((t) => t.symbol === market.name.split('-')[0]) ||
|
||||
mangoTokens.find((t) => t.symbol.includes(market.name.split('-')[0]))
|
||||
mangoTokens.find(
|
||||
(t) => t.symbol.toUpperCase() === market.name.split('-')[0]
|
||||
) ||
|
||||
mangoTokens.find((t) =>
|
||||
t.symbol.toUpperCase()?.includes(market.name.split('-')[0])
|
||||
)
|
||||
}
|
||||
const baseLogoURI = jupiterBaseToken ? jupiterBaseToken.logoURI : ''
|
||||
const quoteLogoURI = jupiterQuoteToken ? jupiterQuoteToken.logoURI : ''
|
||||
|
|
|
@ -6,17 +6,24 @@ import TradeInfoTabs from './TradeInfoTabs'
|
|||
import TabButtons from '@components/shared/TabButtons'
|
||||
import { TABS } from './OrderbookAndTrades'
|
||||
import RecentTrades from './RecentTrades'
|
||||
import TradingChartContainer from './TradingChartContainer'
|
||||
|
||||
const MobileTradeAdvancedPage = () => {
|
||||
const [activeTab, setActiveTab] = useState('trade:book')
|
||||
const [showChart, setShowChart] = useState(false)
|
||||
return (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3">
|
||||
<div className="col-span-2 border-b border-th-bkg-3 sm:col-span-3">
|
||||
<AdvancedMarketHeader />
|
||||
<AdvancedMarketHeader
|
||||
showChart={showChart}
|
||||
setShowChart={setShowChart}
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="col-span-2 h-72 overflow-auto border-b border-th-bkg-3 sm:col-span-3 sm:h-96">
|
||||
<TradingViewChart />
|
||||
</div> */}
|
||||
{showChart ? (
|
||||
<div className="col-span-2 h-80 border-b border-th-bkg-3 sm:col-span-3">
|
||||
<TradingChartContainer />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="col-span-1 pb-6 sm:col-span-2">
|
||||
<AdvancedTradeForm />
|
||||
</div>
|
||||
|
|
|
@ -5,10 +5,11 @@ import { useQuery } from '@tanstack/react-query'
|
|||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useMemo } from 'react'
|
||||
import { MANGO_DATA_API_URL } from 'utils/constants'
|
||||
|
||||
const fetchFundingRate = async (groupPk: string | undefined) => {
|
||||
const res = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/one-hour-funding-rate?mango-group=${groupPk}`
|
||||
`${MANGO_DATA_API_URL}/one-hour-funding-rate?mango-group=${groupPk}`
|
||||
)
|
||||
return await res.json()
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { I80F48, PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import { LinkButton } from '@components/shared/Button'
|
||||
import SheenLoader from '@components/shared/SheenLoader'
|
||||
import SideBadge from '@components/shared/SideBadge'
|
||||
import {
|
||||
Table,
|
||||
|
@ -14,7 +16,9 @@ import mangoStore from '@store/mangoStore'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { PAGINATION_PAGE_LENGTH } from 'utils/constants'
|
||||
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import TableMarketName from './TableMarketName'
|
||||
|
@ -86,11 +90,17 @@ const formatTradeHistory = (
|
|||
}
|
||||
|
||||
const TradeHistory = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const group = mangoStore.getState().group
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const actions = mangoStore((s) => s.actions)
|
||||
const fills = mangoStore((s) => s.selectedMarket.fills)
|
||||
const tradeHistory = mangoStore((s) => s.mangoAccount.tradeHistory)
|
||||
const tradeHistory = mangoStore((s) => s.mangoAccount.tradeHistory.data)
|
||||
const loadingTradeHistory = mangoStore(
|
||||
(s) => s.mangoAccount.tradeHistory.loading
|
||||
)
|
||||
const [offset, setOffset] = useState(0)
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
||||
|
@ -151,27 +161,35 @@ const TradeHistory = () => {
|
|||
return [...newFills, ...tradeHistory]
|
||||
}, [eventQueueFillsForAccount, tradeHistory])
|
||||
|
||||
console.log('trade history', tradeHistory)
|
||||
const handleShowMore = useCallback(() => {
|
||||
const set = mangoStore.getState().set
|
||||
set((s) => {
|
||||
s.mangoAccount.tradeHistory.loading = true
|
||||
})
|
||||
setOffset(offset + PAGINATION_PAGE_LENGTH)
|
||||
actions.fetchTradeHistory(offset + PAGINATION_PAGE_LENGTH)
|
||||
}, [actions, offset])
|
||||
|
||||
if (!selectedMarket || !group) return null
|
||||
|
||||
return mangoAccount && combinedTradeHistory.length ? (
|
||||
showTableView ? (
|
||||
<div>
|
||||
return mangoAccount &&
|
||||
(combinedTradeHistory.length || loadingTradeHistory) ? (
|
||||
<>
|
||||
{showTableView ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">Market</Th>
|
||||
<Th className="text-right">Side</Th>
|
||||
<Th className="text-right">Size</Th>
|
||||
<Th className="text-right">Price</Th>
|
||||
<Th className="text-right">Value</Th>
|
||||
<Th className="text-right">Fee</Th>
|
||||
<Th className="text-right">Time</Th>
|
||||
<Th className="text-left">{t('market')}</Th>
|
||||
<Th className="text-right">{t('trade:side')}</Th>
|
||||
<Th className="text-right">{t('trade:size')}</Th>
|
||||
<Th className="text-right">{t('price')}</Th>
|
||||
<Th className="text-right">{t('value')}</Th>
|
||||
<Th className="text-right">{t('fee')}</Th>
|
||||
<Th className="text-right">{t('date')}</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{combinedTradeHistory.map((trade: any) => {
|
||||
{combinedTradeHistory.map((trade: any, index: number) => {
|
||||
let market
|
||||
if ('market' in trade) {
|
||||
market = group.getSerum3MarketByExternalMarket(
|
||||
|
@ -189,6 +207,17 @@ const TradeHistory = () => {
|
|||
makerTaker = 'Taker'
|
||||
}
|
||||
}
|
||||
let side = trade.side
|
||||
if ('perp_market' in trade) {
|
||||
const sideObj: any = {}
|
||||
if (makerTaker == 'Taker') {
|
||||
sideObj[trade.taker_side] = 1
|
||||
} else {
|
||||
sideObj[trade.taker_side == 'bid' ? 'ask' : 'bid'] = 1
|
||||
}
|
||||
side = sideObj
|
||||
}
|
||||
|
||||
const size = trade.size || trade.quantity
|
||||
let fee
|
||||
if (trade.fee_cost || trade.feeCost) {
|
||||
|
@ -202,21 +231,21 @@ const TradeHistory = () => {
|
|||
|
||||
return (
|
||||
<TrBody
|
||||
key={`${trade.signature || trade.marketIndex}${size}`}
|
||||
key={`${trade.signature || trade.marketIndex}${index}`}
|
||||
className="my-1 p-2"
|
||||
>
|
||||
<Td className="">
|
||||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<SideBadge side={trade.side} />
|
||||
<SideBadge side={side} />
|
||||
</Td>
|
||||
<Td className="text-right font-mono">{size}</Td>
|
||||
<Td className="text-right font-mono">
|
||||
{formatDecimal(trade.price)}
|
||||
</Td>
|
||||
<Td className="text-right font-mono">
|
||||
{formatFixedDecimals(trade.price * size)}
|
||||
${formatFixedDecimals(trade.price * size, true)}
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<span className="font-mono">{formatDecimal(fee)}</span>
|
||||
|
@ -240,36 +269,64 @@ const TradeHistory = () => {
|
|||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{eventQueueFillsForAccount.map((trade: any) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={`${trade.marketIndex}`}
|
||||
>
|
||||
<div>
|
||||
<TableMarketName market={selectedMarket} />
|
||||
<div className="mt-1 flex items-center space-x-1">
|
||||
<SideBadge side={trade.side} />
|
||||
<p className="text-th-fgd-4">
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
{trade.size}
|
||||
</span>
|
||||
{' for '}
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
{formatDecimal(trade.price)}
|
||||
</span>
|
||||
) : (
|
||||
<div>
|
||||
{combinedTradeHistory.map((trade: any, index: number) => {
|
||||
const size = trade.size || trade.quantity
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={`${trade.marketIndex}${index}`}
|
||||
>
|
||||
<div>
|
||||
<TableMarketName market={selectedMarket} />
|
||||
<div className="mt-1 flex items-center space-x-1">
|
||||
<SideBadge side={trade.side} />
|
||||
<p className="text-th-fgd-4">
|
||||
<span className="font-mono text-th-fgd-2">{size}</span>
|
||||
{' for '}
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
{formatDecimal(trade.price)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="mb-0.5 flex items-center space-x-1.5">
|
||||
{trade.block_datetime ? (
|
||||
<TableDateDisplay
|
||||
date={trade.block_datetime}
|
||||
showSeconds
|
||||
/>
|
||||
) : (
|
||||
'Recent'
|
||||
)}
|
||||
</span>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{formatFixedDecimals(trade.price * size, true, true)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="font-mono">${trade.value.toFixed(2)}</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{loadingTradeHistory ? (
|
||||
<div className="mt-4 space-y-1.5">
|
||||
{[...Array(4)].map((x, i) => (
|
||||
<SheenLoader className="mx-4 flex flex-1 md:mx-6" key={i}>
|
||||
<div className="h-16 w-full bg-th-bkg-2" />
|
||||
</SheenLoader>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{combinedTradeHistory.length &&
|
||||
combinedTradeHistory.length % PAGINATION_PAGE_LENGTH === 0 ? (
|
||||
<div className="flex justify-center py-6">
|
||||
<LinkButton onClick={handleShowMore}>Show More</LinkButton>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center p-8">
|
||||
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
|
|
|
@ -17,9 +17,9 @@ const TradingChartContainer = () => {
|
|||
const [tradingChart] = useLocalStorageState(TRADE_CHART_UI_KEY)
|
||||
const isTradingChart = tradingChart === 'custom'
|
||||
return !isTradingChart ? (
|
||||
<TradingViewChart></TradingViewChart>
|
||||
<TradingViewChart />
|
||||
) : (
|
||||
<TradingViewChartKlineContainer></TradingViewChartKlineContainer>
|
||||
<TradingViewChartKlineContainer />
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -405,9 +405,9 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
|
|||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'fixed h-full w-full',
|
||||
'h-full w-full',
|
||||
isFullView
|
||||
? 'left-0 top-0 right-0 bottom-0 z-40 bg-th-bkg-1 text-th-fgd-1'
|
||||
? 'fixed left-0 top-0 right-0 bottom-0 z-40 bg-th-bkg-1 text-th-fgd-1'
|
||||
: ''
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -29,24 +29,28 @@ export const ConnectWalletButton: React.FC = () => {
|
|||
>
|
||||
<div className="relative flex h-16 w-44 bg-th-bkg-2 py-2 before:absolute before:inset-0 before:bg-gradient-to-r before:from-transparent before:via-th-bkg-4 before:to-transparent before:opacity-0 hover:overflow-hidden hover:before:-translate-x-full hover:before:animate-[shimmer_0.75s_normal] hover:before:opacity-100">
|
||||
<div className="default-transition relative z-10 flex h-full items-center justify-center space-x-3 px-4">
|
||||
<div
|
||||
className={`flex h-[28px] w-[28px] items-center justify-center rounded-full ${
|
||||
wallet?.adapter.name === 'Solflare' ? 'bg-black' : ''
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={wallet?.adapter.icon || selectedWallet?.adapter.icon}
|
||||
className={
|
||||
wallet?.adapter.name === 'Solflare'
|
||||
? 'h-auto w-[20px]'
|
||||
: 'h-auto w-[28px]'
|
||||
}
|
||||
alt={`${wallet?.adapter.name} icon`}
|
||||
/>
|
||||
</div>
|
||||
{connecting ? (
|
||||
<Loading className="h-[28px] w-[28px]" />
|
||||
) : (
|
||||
<div
|
||||
className={`flex h-[28px] w-[28px] items-center justify-center rounded-full ${
|
||||
wallet?.adapter.name === 'Solflare' ? 'bg-black' : ''
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={wallet?.adapter.icon || selectedWallet?.adapter.icon}
|
||||
className={
|
||||
wallet?.adapter.name === 'Solflare'
|
||||
? 'h-auto w-[20px]'
|
||||
: 'h-auto w-[28px]'
|
||||
}
|
||||
alt={`${wallet?.adapter.name} icon`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-left">
|
||||
<div className="mb-1.5 flex justify-center font-display text-base leading-none text-th-fgd-1">
|
||||
{connecting ? <Loading className="h-4 w-4" /> : t('connect')}
|
||||
<div className="mb-1.5 flex font-display text-base leading-none text-th-fgd-1">
|
||||
{t('connect')}
|
||||
</div>
|
||||
|
||||
<div className="text-xxs font-normal leading-3 text-th-fgd-3">
|
||||
|
|
|
@ -46,8 +46,12 @@ const ConnectedMenu = () => {
|
|||
state.mangoAccount.current = undefined
|
||||
state.mangoAccounts = []
|
||||
state.mangoAccount.openOrders = {}
|
||||
state.mangoAccount.stats.interestTotals = { data: [], loading: false }
|
||||
state.mangoAccount.stats.performance = { data: [], loading: false }
|
||||
state.mangoAccount.interestTotals = { data: [], loading: false }
|
||||
state.mangoAccount.performance = {
|
||||
data: [],
|
||||
loading: false,
|
||||
initialLoad: false,
|
||||
}
|
||||
})
|
||||
disconnect()
|
||||
wallet?.adapter.disconnect()
|
||||
|
|
|
@ -40,8 +40,8 @@ export default function useSelectedMarket() {
|
|||
const baseLogoURI = useMemo(() => {
|
||||
if (!baseSymbol || !mangoTokens.length) return ''
|
||||
const token =
|
||||
mangoTokens.find((t) => t.symbol === baseSymbol) ||
|
||||
mangoTokens.find((t) => t.symbol?.includes(baseSymbol))
|
||||
mangoTokens.find((t) => t.symbol.toUpperCase() === baseSymbol) ||
|
||||
mangoTokens.find((t) => t.symbol.toUpperCase()?.includes(baseSymbol))
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
|
@ -64,7 +64,9 @@ export default function useSelectedMarket() {
|
|||
|
||||
const quoteLogoURI = useMemo(() => {
|
||||
if (!quoteSymbol || !mangoTokens.length) return ''
|
||||
const token = mangoTokens.find((t) => t.symbol === quoteSymbol)
|
||||
const token = mangoTokens.find(
|
||||
(t) => t.symbol.toUpperCase() === quoteSymbol
|
||||
)
|
||||
if (token) {
|
||||
return token.logoURI
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, [
|
||||
'account',
|
||||
'activity',
|
||||
'common',
|
||||
'onboarding',
|
||||
|
@ -16,6 +17,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
'swap',
|
||||
'token',
|
||||
'trade',
|
||||
'close-account'
|
||||
])),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
'swap',
|
||||
'settings',
|
||||
'trade',
|
||||
'close-account'
|
||||
])),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
'search',
|
||||
'settings',
|
||||
'trade',
|
||||
'close-account',
|
||||
])),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"no-pnl-history": "No PnL History",
|
||||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"tooltip-free-collateral": "The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw",
|
||||
"tooltip-leverage": "Total assets value divided by account equity value",
|
||||
"tooltip-pnl": "The amount your account has profited or lost",
|
||||
"tooltip-total-collateral": "Total value of collateral for trading and borrowing (including unsettled PnL)",
|
||||
"tooltip-total-interest": "The value of interest earned (deposits) minus interest paid (borrows)",
|
||||
"week-starting": "Week starting {{week}}"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "You can close your Mango account and recover the small amount amount of SOL used to cover rent exemption.",
|
||||
"you-must": "To close account you must",
|
||||
"are-you-sure": "Are you sure?",
|
||||
"before-you-continue": "Before you can continue",
|
||||
"close-all-borrows": "Close all borrows",
|
||||
"close-open-orders": "Close all open orders",
|
||||
"close-perp-positions": "Close and settle all futures positons",
|
||||
"closing-account-will": "Closing your Mango Account will:",
|
||||
"delete-your-account": "Delete your Mango Account",
|
||||
"error-deleting-account": "Error deleting your Mango Account",
|
||||
"goodbye": "Until next time 👋",
|
||||
"recover-x-sol": "Recover {{amount}} SOL (rent for your account)",
|
||||
"settle-balances": "Settle all balances",
|
||||
"transaction-confirmed": "Transaction Confirmed",
|
||||
"withdraw-assets-worth": "Withdraw assets worth {{value}}",
|
||||
"close-account": "Close Account",
|
||||
"closing-account": "Closing your account..."
|
||||
}
|
|
@ -121,6 +121,7 @@
|
|||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total": "Total",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -11,11 +11,13 @@
|
|||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"paid": "Paid",
|
||||
"pay": "You Pay",
|
||||
"preset": "Preset",
|
||||
"price-impact": "Price Impact",
|
||||
"rate": "Rate",
|
||||
"receive": "You Receive",
|
||||
"received": "Received",
|
||||
"review-swap": "Review Swap",
|
||||
"show-fees": "Show Fees",
|
||||
"slippage": "Slippage",
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"no-pnl-history": "No PnL History",
|
||||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"tooltip-free-collateral": "The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw",
|
||||
"tooltip-leverage": "Total assets value divided by account equity value",
|
||||
"tooltip-pnl": "The amount your account has profited or lost",
|
||||
"tooltip-total-collateral": "Total value of collateral for trading and borrowing (including unsettled PnL)",
|
||||
"tooltip-total-interest": "The value of interest earned (deposits) minus interest paid (borrows)",
|
||||
"week-starting": "Week starting {{week}}"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "You can close your Mango account and recover the small amount amount of SOL used to cover rent exemption.",
|
||||
"you-must": "To close account you must",
|
||||
"are-you-sure": "Are you sure?",
|
||||
"before-you-continue": "Before you can continue",
|
||||
"close-all-borrows": "Close all borrows",
|
||||
"close-open-orders": "Close all open orders",
|
||||
"close-perp-positions": "Close and settle all futures positons",
|
||||
"closing-account-will": "Closing your Mango Account will:",
|
||||
"delete-your-account": "Delete your Mango Account",
|
||||
"error-deleting-account": "Error deleting your Mango Account",
|
||||
"goodbye": "Until next time 👋",
|
||||
"recover-x-sol": "Recover {{amount}} SOL (rent for your account)",
|
||||
"settle-balances": "Settle all balances",
|
||||
"transaction-confirmed": "Transaction Confirmed",
|
||||
"withdraw-assets-worth": "Withdraw assets worth {{value}}",
|
||||
"close-account": "Close Account",
|
||||
"closing-account": "Closing your account..."
|
||||
}
|
|
@ -121,6 +121,7 @@
|
|||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total": "Total",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -11,18 +11,20 @@
|
|||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"paid": "Paid",
|
||||
"pay": "You Pay",
|
||||
"preset": "Preset",
|
||||
"price-impact": "Price Impact",
|
||||
"rate": "Rate",
|
||||
"receive": "You Receive",
|
||||
"received": "Received",
|
||||
"review-swap": "Review Swap",
|
||||
"show-fees": "Show Fees",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"no-pnl-history": "No PnL History",
|
||||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"tooltip-free-collateral": "The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw",
|
||||
"tooltip-leverage": "Total assets value divided by account equity value",
|
||||
"tooltip-pnl": "The amount your account has profited or lost",
|
||||
"tooltip-total-collateral": "Total value of collateral for trading and borrowing (including unsettled PnL)",
|
||||
"tooltip-total-interest": "The value of interest earned (deposits) minus interest paid (borrows)",
|
||||
"week-starting": "Week starting {{week}}"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "You can close your Mango account and recover the small amount amount of SOL used to cover rent exemption.",
|
||||
"you-must": "To close account you must",
|
||||
"are-you-sure": "Are you sure?",
|
||||
"before-you-continue": "Before you can continue",
|
||||
"close-all-borrows": "Close all borrows",
|
||||
"close-open-orders": "Close all open orders",
|
||||
"close-perp-positions": "Close and settle all futures positons",
|
||||
"closing-account-will": "Closing your Mango Account will:",
|
||||
"delete-your-account": "Delete your Mango Account",
|
||||
"error-deleting-account": "Error deleting your Mango Account",
|
||||
"goodbye": "Until next time 👋",
|
||||
"recover-x-sol": "Recover {{amount}} SOL (rent for your account)",
|
||||
"settle-balances": "Settle all balances",
|
||||
"transaction-confirmed": "Transaction Confirmed",
|
||||
"withdraw-assets-worth": "Withdraw assets worth {{value}}",
|
||||
"close-account": "Close Account",
|
||||
"closing-account": "Closing your account..."
|
||||
}
|
|
@ -121,6 +121,7 @@
|
|||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total": "Total",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -11,18 +11,20 @@
|
|||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"paid": "Paid",
|
||||
"pay": "You Pay",
|
||||
"preset": "Preset",
|
||||
"price-impact": "Price Impact",
|
||||
"rate": "Rate",
|
||||
"receive": "You Receive",
|
||||
"received": "Received",
|
||||
"review-swap": "Review Swap",
|
||||
"show-fees": "Show Fees",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"no-pnl-history": "No PnL History",
|
||||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"tooltip-free-collateral": "The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw",
|
||||
"tooltip-leverage": "Total assets value divided by account equity value",
|
||||
"tooltip-pnl": "The amount your account has profited or lost",
|
||||
"tooltip-total-collateral": "Total value of collateral for trading and borrowing (including unsettled PnL)",
|
||||
"tooltip-total-interest": "The value of interest earned (deposits) minus interest paid (borrows)",
|
||||
"week-starting": "Week starting {{week}}"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "You can close your Mango account and recover the small amount amount of SOL used to cover rent exemption.",
|
||||
"you-must": "To close account you must",
|
||||
"are-you-sure": "Are you sure?",
|
||||
"before-you-continue": "Before you can continue",
|
||||
"close-all-borrows": "Close all borrows",
|
||||
"close-open-orders": "Close all open orders",
|
||||
"close-perp-positions": "Close and settle all futures positons",
|
||||
"closing-account-will": "Closing your Mango Account will:",
|
||||
"delete-your-account": "Delete your Mango Account",
|
||||
"error-deleting-account": "Error deleting your Mango Account",
|
||||
"goodbye": "Until next time 👋",
|
||||
"recover-x-sol": "Recover {{amount}} SOL (rent for your account)",
|
||||
"settle-balances": "Settle all balances",
|
||||
"transaction-confirmed": "Transaction Confirmed",
|
||||
"withdraw-assets-worth": "Withdraw assets worth {{value}}",
|
||||
"close-account": "Close Account",
|
||||
"closing-account": "Closing your account..."
|
||||
}
|
|
@ -121,6 +121,7 @@
|
|||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total": "Total",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -11,18 +11,20 @@
|
|||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"paid": "Paid",
|
||||
"pay": "You Pay",
|
||||
"preset": "Preset",
|
||||
"price-impact": "Price Impact",
|
||||
"rate": "Rate",
|
||||
"receive": "You Receive",
|
||||
"received": "Received",
|
||||
"review-swap": "Review Swap",
|
||||
"show-fees": "Show Fees",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"no-pnl-history": "No PnL History",
|
||||
"pnl-chart": "PnL Chart",
|
||||
"pnl-history": "PnL History",
|
||||
"tooltip-free-collateral": "The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw",
|
||||
"tooltip-leverage": "Total assets value divided by account equity value",
|
||||
"tooltip-pnl": "The amount your account has profited or lost",
|
||||
"tooltip-total-collateral": "Total value of collateral for trading and borrowing (including unsettled PnL)",
|
||||
"tooltip-total-interest": "The value of interest earned (deposits) minus interest paid (borrows)",
|
||||
"week-starting": "Week starting {{week}}"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "You can close your Mango account and recover the small amount amount of SOL used to cover rent exemption.",
|
||||
"you-must": "To close account you must",
|
||||
"are-you-sure": "Are you sure?",
|
||||
"before-you-continue": "Before you can continue",
|
||||
"close-all-borrows": "Close all borrows",
|
||||
"close-open-orders": "Close all open orders",
|
||||
"close-perp-positions": "Close and settle all futures positons",
|
||||
"closing-account-will": "Closing your Mango Account will:",
|
||||
"delete-your-account": "Delete your Mango Account",
|
||||
"error-deleting-account": "Error deleting your Mango Account",
|
||||
"goodbye": "Until next time 👋",
|
||||
"recover-x-sol": "Recover {{amount}} SOL (rent for your account)",
|
||||
"settle-balances": "Settle all balances",
|
||||
"transaction-confirmed": "Transaction Confirmed",
|
||||
"withdraw-assets-worth": "Withdraw assets worth {{value}}",
|
||||
"close-account": "Close Account",
|
||||
"closing-account": "Closing your account..."
|
||||
}
|
|
@ -121,6 +121,7 @@
|
|||
"token-collateral-multiplier": "{{token}} Collateral Multiplier",
|
||||
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
|
||||
"tooltip-collateral-value": "The USD amount you can trade or borrow against",
|
||||
"total": "Total",
|
||||
"total-borrows": "Total Borrows",
|
||||
"total-borrow-value": "Total Borrow Value",
|
||||
"total-collateral": "Total Collateral",
|
||||
|
|
|
@ -11,18 +11,20 @@
|
|||
"maximum-cost": "Maximum Cost",
|
||||
"minimum-received": "Minimum Received",
|
||||
"no-history": "No swap history",
|
||||
"paid": "Paid",
|
||||
"pay": "You Pay",
|
||||
"preset": "Preset",
|
||||
"price-impact": "Price Impact",
|
||||
"rate": "Rate",
|
||||
"receive": "You Receive",
|
||||
"received": "Received",
|
||||
"review-swap": "Review Swap",
|
||||
"show-fees": "Show Fees",
|
||||
"slippage": "Slippage",
|
||||
"swap-history": "Swap History",
|
||||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin"
|
||||
}
|
|
@ -33,7 +33,9 @@ import {
|
|||
DEFAULT_MARKET_NAME,
|
||||
INPUT_TOKEN_DEFAULT,
|
||||
LAST_ACCOUNT_KEY,
|
||||
MANGO_DATA_API_URL,
|
||||
OUTPUT_TOKEN_DEFAULT,
|
||||
PAGINATION_PAGE_LENGTH,
|
||||
RPC_PROVIDER_KEY,
|
||||
} from '../utils/constants'
|
||||
import { OrderbookL2, SpotBalances, SpotTradeHistory } from 'types'
|
||||
|
@ -240,15 +242,17 @@ export type MangoStore = {
|
|||
openOrders: Record<string, Order[] | PerpOrder[]>
|
||||
perpPositions: PerpPosition[]
|
||||
spotBalances: SpotBalances
|
||||
stats: {
|
||||
interestTotals: { data: TotalInterestDataItem[]; loading: boolean }
|
||||
performance: { data: PerformanceDataItem[]; loading: boolean }
|
||||
swapHistory: {
|
||||
data: SwapHistoryItem[]
|
||||
initialLoad: boolean
|
||||
}
|
||||
interestTotals: { data: TotalInterestDataItem[]; loading: boolean }
|
||||
performance: {
|
||||
data: PerformanceDataItem[]
|
||||
loading: boolean
|
||||
initialLoad: boolean
|
||||
}
|
||||
tradeHistory: SpotTradeHistory[]
|
||||
swapHistory: {
|
||||
data: SwapHistoryItem[]
|
||||
initialLoad: boolean
|
||||
}
|
||||
tradeHistory: { data: SpotTradeHistory[]; loading: boolean }
|
||||
}
|
||||
mangoAccounts: MangoAccount[]
|
||||
markets: Serum3Market[] | undefined
|
||||
|
@ -332,7 +336,7 @@ export type MangoStore = {
|
|||
) => Promise<void>
|
||||
fetchTokenStats: () => void
|
||||
fetchTourSettings: (walletPk: string) => void
|
||||
fetchTradeHistory: () => Promise<void>
|
||||
fetchTradeHistory: (offset?: number) => Promise<void>
|
||||
fetchWalletTokens: (walletPk: PublicKey) => Promise<void>
|
||||
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
|
||||
loadMarketFills: () => Promise<void>
|
||||
|
@ -380,12 +384,10 @@ const mangoStore = create<MangoStore>()(
|
|||
openOrders: {},
|
||||
perpPositions: [],
|
||||
spotBalances: {},
|
||||
stats: {
|
||||
interestTotals: { data: [], loading: false },
|
||||
performance: { data: [], loading: false },
|
||||
swapHistory: { data: [], initialLoad: false },
|
||||
},
|
||||
tradeHistory: [],
|
||||
interestTotals: { data: [], loading: false },
|
||||
performance: { data: [], loading: false, initialLoad: false },
|
||||
swapHistory: { data: [], initialLoad: false },
|
||||
tradeHistory: { data: [], loading: true },
|
||||
},
|
||||
mangoAccounts: [],
|
||||
markets: undefined,
|
||||
|
@ -457,11 +459,11 @@ const mangoStore = create<MangoStore>()(
|
|||
fetchAccountInterestTotals: async (mangoAccountPk: string) => {
|
||||
const set = get().set
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.interestTotals.loading = true
|
||||
state.mangoAccount.interestTotals.loading = true
|
||||
})
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/stats/interest-account-total?mango-account=${mangoAccountPk}`
|
||||
`${MANGO_DATA_API_URL}/stats/interest-account-total?mango-account=${mangoAccountPk}`
|
||||
)
|
||||
const parsedResponse = await response.json()
|
||||
const entries: any = Object.entries(parsedResponse).sort((a, b) =>
|
||||
|
@ -475,12 +477,12 @@ const mangoStore = create<MangoStore>()(
|
|||
.filter((x: string) => x)
|
||||
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.interestTotals.data = stats
|
||||
state.mangoAccount.stats.interestTotals.loading = false
|
||||
state.mangoAccount.interestTotals.data = stats
|
||||
state.mangoAccount.interestTotals.loading = false
|
||||
})
|
||||
} catch {
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.interestTotals.loading = false
|
||||
state.mangoAccount.interestTotals.loading = false
|
||||
})
|
||||
console.error({
|
||||
title: 'Failed to load account interest totals',
|
||||
|
@ -494,11 +496,11 @@ const mangoStore = create<MangoStore>()(
|
|||
) => {
|
||||
const set = get().set
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.performance.loading = true
|
||||
state.mangoAccount.performance.loading = true
|
||||
})
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/stats/performance_account?mango-account=${mangoAccountPk}&start-date=${dayjs()
|
||||
`${MANGO_DATA_API_URL}/stats/performance_account?mango-account=${mangoAccountPk}&start-date=${dayjs()
|
||||
.subtract(range, 'day')
|
||||
.format('YYYY-MM-DD')}`
|
||||
)
|
||||
|
@ -514,18 +516,19 @@ const mangoStore = create<MangoStore>()(
|
|||
.filter((x: string) => x)
|
||||
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.performance.data = stats.reverse()
|
||||
state.mangoAccount.stats.performance.loading = false
|
||||
state.mangoAccount.performance.data = stats.reverse()
|
||||
})
|
||||
} catch (e) {
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.performance.loading = false
|
||||
})
|
||||
console.error('Failed to load account performance data', e)
|
||||
// notify({
|
||||
// title: 'Failed to load account performance data',
|
||||
// type: 'error',
|
||||
// })
|
||||
} finally {
|
||||
const hasLoaded =
|
||||
mangoStore.getState().mangoAccount.performance.initialLoad
|
||||
set((state) => {
|
||||
state.mangoAccount.performance.loading = false
|
||||
if (!hasLoaded) {
|
||||
state.mangoAccount.performance.initialLoad = true
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fetchActivityFeed: async (
|
||||
|
@ -541,7 +544,7 @@ const mangoStore = create<MangoStore>()(
|
|||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/stats/activity-feed?mango-account=${mangoAccountPk}&offset=${offset}&limit=25${
|
||||
`${MANGO_DATA_API_URL}/stats/activity-feed?mango-account=${mangoAccountPk}&offset=${offset}&limit=${PAGINATION_PAGE_LENGTH}${
|
||||
params ? params : ''
|
||||
}`
|
||||
)
|
||||
|
@ -817,7 +820,7 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/perp-historical-stats?mango-group=${group?.publicKey.toString()}`
|
||||
`${MANGO_DATA_API_URL}/perp-historical-stats?mango-group=${group?.publicKey.toString()}`
|
||||
)
|
||||
const data = await response.json()
|
||||
|
||||
|
@ -840,7 +843,7 @@ const mangoStore = create<MangoStore>()(
|
|||
setTimeout(async () => {
|
||||
try {
|
||||
const history = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/stats/swap-history?mango-account=${mangoAccountPk}`
|
||||
`${MANGO_DATA_API_URL}/stats/swap-history?mango-account=${mangoAccountPk}`
|
||||
)
|
||||
const parsedHistory = await history.json()
|
||||
const sortedHistory =
|
||||
|
@ -853,12 +856,12 @@ const mangoStore = create<MangoStore>()(
|
|||
: []
|
||||
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.swapHistory.data = sortedHistory
|
||||
state.mangoAccount.stats.swapHistory.initialLoad = true
|
||||
state.mangoAccount.swapHistory.data = sortedHistory
|
||||
state.mangoAccount.swapHistory.initialLoad = true
|
||||
})
|
||||
} catch {
|
||||
set((state) => {
|
||||
state.mangoAccount.stats.swapHistory.initialLoad = true
|
||||
state.mangoAccount.swapHistory.initialLoad = true
|
||||
})
|
||||
notify({
|
||||
title: 'Failed to load account swap history data',
|
||||
|
@ -876,7 +879,7 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/token-historical-stats?mango-group=${group?.publicKey.toString()}`
|
||||
`${MANGO_DATA_API_URL}/token-historical-stats?mango-group=${group?.publicKey.toString()}`
|
||||
)
|
||||
const data = await response.json()
|
||||
|
||||
|
@ -945,7 +948,7 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details?wallet-pk=${walletPk}`
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details?wallet-pk=${walletPk}`
|
||||
)
|
||||
const data = await response.json()
|
||||
set((state) => {
|
||||
|
@ -967,7 +970,7 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/user-data/settings-unsigned?wallet-pk=${walletPk}`
|
||||
`${MANGO_DATA_API_URL}/user-data/settings-unsigned?wallet-pk=${walletPk}`
|
||||
)
|
||||
const data = await response.json()
|
||||
set((state) => {
|
||||
|
@ -1015,36 +1018,33 @@ const mangoStore = create<MangoStore>()(
|
|||
console.log('Error fetching fills:', err)
|
||||
}
|
||||
},
|
||||
async fetchTradeHistory() {
|
||||
async fetchTradeHistory(offset = 0) {
|
||||
const set = get().set
|
||||
const mangoAccount = get().mangoAccount.current
|
||||
const mangoAccountPk =
|
||||
get().mangoAccount?.current?.publicKey.toString()
|
||||
const loadedHistory =
|
||||
mangoStore.getState().mangoAccount.tradeHistory.data
|
||||
try {
|
||||
const [spotRes, perpRes] = await Promise.all([
|
||||
fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/stats/openbook-trades?address=${mangoAccount?.publicKey.toString()}&address-type=mango-account`
|
||||
),
|
||||
fetch(
|
||||
`https://mango-transaction-log.herokuapp.com/v4/stats/perp-trade-history?mango-account=${mangoAccount?.publicKey.toString()}&limit=1000`
|
||||
),
|
||||
])
|
||||
const spotHistory = await spotRes.json()
|
||||
const perpHistory = await perpRes.json()
|
||||
console.log('th', spotHistory, perpHistory)
|
||||
let tradeHistory: any[] = []
|
||||
if (spotHistory?.length) {
|
||||
tradeHistory = tradeHistory.concat(spotHistory)
|
||||
}
|
||||
if (perpHistory?.length) {
|
||||
tradeHistory = tradeHistory.concat(perpHistory)
|
||||
}
|
||||
const response = await fetch(
|
||||
`${MANGO_DATA_API_URL}/stats/trade-history?mango-account=${mangoAccountPk}&limit=${PAGINATION_PAGE_LENGTH}&offset=${offset}`
|
||||
)
|
||||
const parsedHistory = await response.json()
|
||||
const newHistory = parsedHistory.map((h: any) => h.activity_details)
|
||||
|
||||
const history =
|
||||
offset !== 0 ? loadedHistory.concat(newHistory) : newHistory
|
||||
|
||||
set((s) => {
|
||||
s.mangoAccount.tradeHistory = tradeHistory.sort(
|
||||
s.mangoAccount.tradeHistory.data = history?.sort(
|
||||
(x: any) => x.block_datetime
|
||||
)
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('Unable to fetch trade history', e)
|
||||
} finally {
|
||||
set((s) => {
|
||||
s.mangoAccount.tradeHistory.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
updateConnection(endpointUrl) {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { AccountMeta } from '@solana/web3.js'
|
||||
import { AccountInfo } from '@solana/web3.js'
|
||||
import { AccountInfo, PublicKey, TransactionInstruction } from '@solana/web3.js'
|
||||
import Decimal from 'decimal.js'
|
||||
|
||||
export declare type SideType = typeof Side.Ask | typeof Side.Bid
|
||||
export declare const Side: {
|
||||
Bid: {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
bid: {}
|
||||
}
|
||||
Ask: {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
ask: {}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ export interface ExactOutSwapParams extends SwapParams {
|
|||
}
|
||||
export declare type AccountInfoMap = Map<string, AccountInfo<Buffer> | null>
|
||||
|
||||
declare type AmmLabel =
|
||||
export declare type AmmLabel =
|
||||
| 'Aldrin'
|
||||
| 'Crema'
|
||||
| 'Cropper'
|
||||
|
@ -127,6 +128,9 @@ export interface RouteInfo {
|
|||
priceImpactPct: number
|
||||
slippageBps: number
|
||||
swapMode: SwapMode
|
||||
instructions?: TransactionInstruction[]
|
||||
mints?: PublicKey[]
|
||||
routerName?: 'Mango'
|
||||
}
|
||||
|
||||
export type Routes = {
|
||||
|
|
|
@ -58,6 +58,10 @@ export const PROFILE_CATEGORIES = [
|
|||
|
||||
export const CHART_DATA_FEED = `https://dry-ravine-67635.herokuapp.com/tv`
|
||||
|
||||
export const MANGO_ROUTER_API_URL = 'https://api.mngo.cloud/router/v1'
|
||||
|
||||
export const MANGO_DATA_API_URL = 'https://api.mngo.cloud/data/v4'
|
||||
|
||||
export const DEFAULT_MARKET_NAME = 'SOL/USDC'
|
||||
|
||||
export const MIN_SOL_BALANCE = 0.001
|
||||
|
@ -67,3 +71,5 @@ export const ACCOUNT_ACTION_MODAL_HEIGHT = '506px'
|
|||
export const ACCOUNT_ACTION_MODAL_INNER_HEIGHT = '444px'
|
||||
|
||||
export const TRADE_VOLUME_ALERT_KEY = 'tradeVolumeAlert-0.1'
|
||||
|
||||
export const PAGINATION_PAGE_LENGTH = 25
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
"@blockworks-foundation/mango-v4@https://github.com/blockworks-foundation/mango-v4.git#ts-client":
|
||||
version "0.0.1-beta.6"
|
||||
resolved "https://github.com/blockworks-foundation/mango-v4.git#adc9140ad2a3c2e9c3b425b8804d9415f2a05067"
|
||||
resolved "https://github.com/blockworks-foundation/mango-v4.git#1ca560c007081127ac31486a96a3729da22f99fb"
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.25.0"
|
||||
"@project-serum/serum" "^0.13.65"
|
||||
|
|
Loading…
Reference in New Issue