merge main

This commit is contained in:
saml33 2023-01-19 15:33:51 +11:00
commit 9f3d10d4ae
34 changed files with 658 additions and 292 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
yarn typecheck && yarn lint yarn typecheck && yarn lint --quiet

View File

@ -46,7 +46,7 @@ const TokenList = () => {
const { group } = useMangoGroup() const { group } = useMangoGroup()
const { mangoTokens } = useJupiterMints() const { mangoTokens } = useJupiterMints()
const totalInterestData = mangoStore( const totalInterestData = mangoStore(
(s) => s.mangoAccount.stats.interestTotals.data (s) => s.mangoAccount.interestTotals.data
) )
const { width } = useViewport() const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false const showTableView = width ? width > breakpoints.md : false
@ -304,7 +304,7 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances) const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
const { mangoAccount } = useMangoAccount() const { mangoAccount } = useMangoAccount()
const totalInterestData = mangoStore( const totalInterestData = mangoStore(
(s) => s.mangoAccount.stats.interestTotals.data (s) => s.mangoAccount.interestTotals.data
) )
const symbol = bank.name const symbol = bank.name
const oraclePrice = bank.uiPrice const oraclePrice = bank.uiPrice

View File

@ -1,6 +1,6 @@
import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4' import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import mangoStore from '@store/mangoStore' import mangoStore from '@store/mangoStore'
import dynamic from 'next/dynamic' import dynamic from 'next/dynamic'
import { numberCompacter } from 'utils/numbers' import { numberCompacter } from 'utils/numbers'
@ -11,19 +11,38 @@ const DetailedAreaChart = dynamic(
const AccountChart = ({ const AccountChart = ({
chartToShow, chartToShow,
data,
hideChart, hideChart,
mangoAccountAddress,
yKey, yKey,
}: { }: {
chartToShow: string chartToShow: string
data: Array<any>
hideChart: () => void hideChart: () => void
mangoAccountAddress: string
yKey: string yKey: string
}) => { }) => {
const { t } = useTranslation('common') const { t } = useTranslation('common')
const actions = mangoStore.getState().actions const actions = mangoStore.getState().actions
const [daysToShow, setDaysToShow] = useState<string>('1') 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 handleDaysToShow = async (days: string) => {
const mangoAccount = mangoStore.getState().mangoAccount.current const mangoAccount = mangoStore.getState().mangoAccount.current

View File

@ -3,11 +3,10 @@ import {
toUiDecimalsForQuote, toUiDecimalsForQuote,
} from '@blockworks-foundation/mango-v4' } from '@blockworks-foundation/mango-v4'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import AccountActions from './AccountActions' import AccountActions from './AccountActions'
import mangoStore, { PerformanceDataItem } from '@store/mangoStore' import mangoStore, { PerformanceDataItem } from '@store/mangoStore'
import { formatFixedDecimals } from '../../utils/numbers' import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
import FlipNumbers from 'react-flip-numbers' import FlipNumbers from 'react-flip-numbers'
import dynamic from 'next/dynamic' import dynamic from 'next/dynamic'
const SimpleAreaChart = dynamic( const SimpleAreaChart = dynamic(
@ -19,7 +18,8 @@ import { useTheme } from 'next-themes'
import { IconButton } from '../shared/Button' import { IconButton } from '../shared/Button'
import { import {
ArrowsPointingOutIcon, ArrowsPointingOutIcon,
ChevronRightIcon, ChartBarIcon,
ClockIcon,
} from '@heroicons/react/20/solid' } from '@heroicons/react/20/solid'
import { Transition } from '@headlessui/react' import { Transition } from '@headlessui/react'
import AccountTabs from './AccountTabs' import AccountTabs from './AccountTabs'
@ -40,33 +40,20 @@ import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettin
import { useViewport } from 'hooks/useViewport' import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme' import { breakpoints } from 'utils/theme'
import useMangoGroup from 'hooks/useMangoGroup' import useMangoGroup from 'hooks/useMangoGroup'
import PnlHistoryModal from '@components/modals/PnlHistoryModal'
export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'common',
'close-account',
'trade',
])),
},
}
}
const AccountPage = () => { const AccountPage = () => {
const { t } = useTranslation('common') const { t } = useTranslation(['common', 'account'])
// const { connected } = useWallet() // const { connected } = useWallet()
const { group } = useMangoGroup() const { group } = useMangoGroup()
const { mangoAccount, mangoAccountAddress } = useMangoAccount() const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const actions = mangoStore.getState().actions const actions = mangoStore.getState().actions
const loadPerformanceData = mangoStore( const performanceInitialLoad = mangoStore(
(s) => s.mangoAccount.stats.performance.loading (s) => s.mangoAccount.performance.initialLoad
)
const performanceData = mangoStore(
(s) => s.mangoAccount.stats.performance.data
) )
const performanceData = mangoStore((s) => s.mangoAccount.performance.data)
const totalInterestData = mangoStore( const totalInterestData = mangoStore(
(s) => s.mangoAccount.stats.interestTotals.data (s) => s.mangoAccount.interestTotals.data
) )
const [chartToShow, setChartToShow] = useState< const [chartToShow, setChartToShow] = useState<
'account-value' | 'cumulative-interest-value' | 'pnl' | '' 'account-value' | 'cumulative-interest-value' | 'pnl' | ''
@ -75,6 +62,7 @@ const AccountPage = () => {
PerformanceDataItem[] PerformanceDataItem[]
>([]) >([])
const [showExpandChart, setShowExpandChart] = useState<boolean>(false) const [showExpandChart, setShowExpandChart] = useState<boolean>(false)
const [showPnlHistory, setShowPnlHistory] = useState<boolean>(false)
const { theme } = useTheme() const { theme } = useTheme()
const { width } = useViewport() const { width } = useViewport()
const isMobile = width ? width < breakpoints.md : false const isMobile = width ? width < breakpoints.md : false
@ -87,16 +75,25 @@ const AccountPage = () => {
useEffect(() => { useEffect(() => {
if (mangoAccountAddress) { if (mangoAccountAddress) {
const set = mangoStore.getState().set
set((s) => {
s.mangoAccount.performance.initialLoad = false
})
setOneDayPerformanceData([])
actions.fetchAccountPerformance(mangoAccountAddress, 1) actions.fetchAccountPerformance(mangoAccountAddress, 1)
actions.fetchAccountInterestTotals(mangoAccountAddress) actions.fetchAccountInterestTotals(mangoAccountAddress)
} }
}, [actions, mangoAccountAddress]) }, [actions, mangoAccountAddress])
useEffect(() => { useEffect(() => {
if (mangoAccount && performanceData.length && !chartToShow) { if (
performanceData.length &&
performanceInitialLoad &&
!oneDayPerformanceData.length
) {
setOneDayPerformanceData(performanceData) setOneDayPerformanceData(performanceData)
} }
}, [mangoAccount, performanceData, chartToShow]) }, [performanceInitialLoad, oneDayPerformanceData, performanceData])
const onHoverMenu = (open: boolean, action: string) => { const onHoverMenu = (open: boolean, action: string) => {
if ( if (
@ -115,11 +112,19 @@ const AccountPage = () => {
const handleHideChart = () => { const handleHideChart = () => {
const set = mangoStore.getState().set const set = mangoStore.getState().set
set((s) => { set((s) => {
s.mangoAccount.stats.performance.data = oneDayPerformanceData s.mangoAccount.performance.data = oneDayPerformanceData
}) })
setChartToShow('') setChartToShow('')
} }
const handleCloseDailyPnlModal = () => {
const set = mangoStore.getState().set
set((s) => {
s.mangoAccount.performance.data = oneDayPerformanceData
})
setShowPnlHistory(false)
}
const accountValue = useMemo(() => { const accountValue = useMemo(() => {
if (!group || !mangoAccount) return 0.0 if (!group || !mangoAccount) return 0.0
return toUiDecimalsForQuote(mangoAccount.getEquity(group).toNumber()) return toUiDecimalsForQuote(mangoAccount.getEquity(group).toNumber())
@ -138,23 +143,24 @@ const AccountPage = () => {
} }
}, [mangoAccount, group, accountValue]) }, [mangoAccount, group, accountValue])
const { accountPnl, accountValueChange } = useMemo(() => { const [accountPnl, accountValueChange, oneDayPnlChange] = useMemo(() => {
if (accountValue && performanceData.length) { if (
return { accountValue &&
accountPnl: performanceData[performanceData.length - 1].pnl, oneDayPerformanceData.length &&
accountValueChange: accountValue - performanceData[0].account_equity, performanceData.length
} ) {
} const accountPnl = performanceData[performanceData.length - 1].pnl
return { accountPnl: 0, accountValueChange: 0 } const accountValueChange =
}, [accountValue, performanceData]) accountValue - oneDayPerformanceData[0].account_equity
const oneDayPnlChange = useMemo(() => {
if (accountPnl && oneDayPerformanceData.length) {
const startDayPnl = oneDayPerformanceData[0].pnl 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 return [0, 0, 0]
}, [accountPnl, oneDayPerformanceData]) }, [accountValue, oneDayPerformanceData, performanceData])
const interestTotalValue = useMemo(() => { const interestTotalValue = useMemo(() => {
if (totalInterestData.length) { if (totalInterestData.length) {
@ -167,7 +173,7 @@ const AccountPage = () => {
}, [totalInterestData]) }, [totalInterestData])
const oneDayInterestChange = useMemo(() => { const oneDayInterestChange = useMemo(() => {
if (oneDayPerformanceData.length && mangoAccount) { if (oneDayPerformanceData.length) {
const startDayInterest = const startDayInterest =
oneDayPerformanceData[0].borrow_interest_cumulative_usd + oneDayPerformanceData[0].borrow_interest_cumulative_usd +
oneDayPerformanceData[0].deposit_interest_cumulative_usd oneDayPerformanceData[0].deposit_interest_cumulative_usd
@ -181,7 +187,7 @@ const AccountPage = () => {
return endDayInterest - startDayInterest return endDayInterest - startDayInterest
} }
return 0.0 return 0.0
}, [oneDayPerformanceData, mangoAccount]) }, [oneDayPerformanceData])
const maintHealth = useMemo(() => { const maintHealth = useMemo(() => {
return group && mangoAccount return group && mangoAccount
@ -262,12 +268,15 @@ const AccountPage = () => {
)} )}
</div> </div>
<div className="flex items-center space-x-1.5"> <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> <p className="text-th-fgd-4">{t('today')}</p>
</div> </div>
</div> </div>
{!loadPerformanceData ? ( {performanceInitialLoad ? (
mangoAccount && performanceData.length ? ( oneDayPerformanceData.length ? (
<div <div
className="relative mt-4 flex h-44 items-end md:mt-0 md:h-24 md:w-48" className="relative mt-4 flex h-44 items-end md:mt-0 md:h-24 md:w-48"
onMouseEnter={() => onMouseEnter={() =>
@ -283,7 +292,7 @@ const AccountPage = () => {
? COLORS.UP[theme] ? COLORS.UP[theme]
: COLORS.DOWN[theme] : COLORS.DOWN[theme]
} }
data={performanceData.concat(latestAccountData)} data={oneDayPerformanceData.concat(latestAccountData)}
name="accountValue" name="accountValue"
xKey="time" xKey="time"
yKey="account_equity" yKey="account_equity"
@ -309,11 +318,11 @@ const AccountPage = () => {
</Transition> </Transition>
</div> </div>
) : null ) : null
) : ( ) : mangoAccountAddress ? (
<SheenLoader className="mt-4 flex flex-1 md:mt-0"> <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" /> <div className="h-40 w-full rounded-md bg-th-bkg-2 md:h-24 md:w-48" />
</SheenLoader> </SheenLoader>
)} ) : null}
</div> </div>
<div className="mt-6 mb-1 lg:mt-0 lg:mb-0"> <div className="mt-6 mb-1 lg:mt-0 lg:mb-0">
<AccountActions /> <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 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"> <div id="account-step-five">
<Tooltip <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" maxWidth="20rem"
placement="bottom" placement="bottom"
delay={250} delay={250}
@ -391,12 +400,12 @@ const AccountPage = () => {
</p> </p>
<span className="text-xs font-normal text-th-fgd-4"> <span className="text-xs font-normal text-th-fgd-4">
<Tooltip <Tooltip
content="Total value of collateral for trading and borrowing (including unsettled PnL)." content={t('account:tooltip-total-collateral')}
maxWidth="20rem" maxWidth="20rem"
placement="bottom" placement="bottom"
delay={250} 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"> <span className="ml-1 font-mono text-th-fgd-2">
{group && mangoAccount {group && mangoAccount
? formatFixedDecimals( ? 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 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"> <div id="account-step-six">
<Tooltip <Tooltip
content="Total assets value divided by account equity value." content={t('account:tooltip-leverage')}
maxWidth="20rem" maxWidth="20rem"
placement="bottom" placement="bottom"
delay={250} delay={250}
@ -431,99 +440,130 @@ const AccountPage = () => {
</p> </p>
</div> </div>
</div> </div>
<button <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">
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 id="account-step-seven" className="flex flex-col items-start"> <div id="account-step-seven" className="flex flex-col items-start">
<Tooltip <div className="flex w-full items-center justify-between">
content="The amount your account has made or lost." <Tooltip
placement="bottom" content={t('account:tooltip-pnl')}
delay={250} placement="bottom"
> delay={250}
<p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base"> >
{t('pnl')} <p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base">
</p> {t('pnl')}
</Tooltip> </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"> <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)} {formatFixedDecimals(accountPnl, true, true)}
</p> </p>
<div className="flex space-x-1"> <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> <p className="text-xs text-th-fgd-4">{t('today')}</p>
</div> </div>
</div> </div>
{performanceData.length > 4 ? ( </div>
<ChevronRightIcon className="h-6 w-6" /> <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">
) : 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 id="account-step-eight"> <div id="account-step-eight">
<Tooltip <div className="flex w-full items-center justify-between">
content="The value of interest earned (deposits) minus interest paid (borrows)." <Tooltip
maxWidth="20rem" content={t('account:tooltip-total-interest')}
placement="bottom-end" maxWidth="20rem"
delay={250} placement="bottom-end"
> delay={250}
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base"> >
{t('total-interest-earned')} <p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
</p> {t('total-interest-earned')}
</Tooltip> </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"> <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)} {formatFixedDecimals(interestTotalValue, true, true)}
</p> </p>
<div className="flex space-x-1"> <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> <p className="text-xs text-th-fgd-4">{t('today')}</p>
</div> </div>
</div> </div>
{interestTotalValue > 1 || interestTotalValue < -1 ? ( </div>
<ChevronRightIcon className="-mt-0.5 h-6 w-6" />
) : null}
</button>
</div> </div>
<AccountTabs /> <AccountTabs />
{/* {!tourSettings?.account_tour_seen && isOnBoarded && connected ? ( {/* {!tourSettings?.account_tour_seen && isOnBoarded && connected ? (
<AccountOnboardingTour /> <AccountOnboardingTour />
) : null} */} ) : null} */}
{showPnlHistory ? (
<PnlHistoryModal
pnlChangeToday={oneDayPnlChange}
isOpen={showPnlHistory}
onClose={handleCloseDailyPnlModal}
/>
) : null}
</> </>
) : ( ) : (
<div className="p-6 pb-0"> <div className="p-6 pb-0">
{chartToShow === 'account-value' ? ( {chartToShow === 'account-value' ? (
<AccountChart <AccountChart
chartToShow="account-value" chartToShow="account-value"
data={performanceData}
hideChart={handleHideChart} hideChart={handleHideChart}
mangoAccountAddress={mangoAccountAddress}
yKey="account_equity" yKey="account_equity"
/> />
) : chartToShow === 'pnl' ? ( ) : chartToShow === 'pnl' ? (
<AccountChart <AccountChart
chartToShow="pnl" chartToShow="pnl"
data={performanceData}
hideChart={handleHideChart} hideChart={handleHideChart}
mangoAccountAddress={mangoAccountAddress}
yKey="pnl" yKey="pnl"
/> />
) : ( ) : (
<AccountChart <AccountChart
chartToShow="cumulative-interest-value" 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} hideChart={handleHideChart}
mangoAccountAddress={mangoAccountAddress}
yKey="interest_value" yKey="interest_value"
/> />
)} )}

View File

@ -8,6 +8,7 @@ import { useUnsettledSpotBalances } from 'hooks/useUnsettledSpotBalances'
import { useViewport } from 'hooks/useViewport' import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme' import { breakpoints } from 'utils/theme'
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions' import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
import TradeHistory from '@components/trade/TradeHistory'
const AccountTabs = () => { const AccountTabs = () => {
const [activeTab, setActiveTab] = useState('balances') const [activeTab, setActiveTab] = useState('balances')
@ -23,15 +24,16 @@ const AccountTabs = () => {
return [ return [
['balances', 0], ['balances', 0],
['trade:unsettled', unsettledTradeCount],
['activity:activity', 0], ['activity:activity', 0],
['swap:swap-history', 0], ['swap:swap-history', 0],
['trade:unsettled', unsettledTradeCount], ['trade-history', 0],
] ]
}, [unsettledPerpPositions, unsettledSpotBalances]) }, [unsettledPerpPositions, unsettledSpotBalances])
return ( return (
<> <>
<div className="border-b border-th-bkg-3"> <div className="hide-scroll overflow-x-auto border-b border-th-bkg-3">
<TabButtons <TabButtons
activeValue={activeTab} activeValue={activeTab}
onChange={(v) => setActiveTab(v)} onChange={(v) => setActiveTab(v)}
@ -51,10 +53,6 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
switch (activeTab) { switch (activeTab) {
case 'balances': case 'balances':
return <TokenList /> return <TokenList />
case 'activity:activity':
return <ActivityFeed />
case 'swap:swap-history':
return <SwapHistoryTable />
case 'trade:unsettled': case 'trade:unsettled':
return ( return (
<UnsettledTrades <UnsettledTrades
@ -62,6 +60,12 @@ const TabContent = ({ activeTab }: { activeTab: string }) => {
unsettledPerpPositions={unsettledPerpPositions} unsettledPerpPositions={unsettledPerpPositions}
/> />
) )
case 'activity:activity':
return <ActivityFeed />
case 'swap:swap-history':
return <SwapHistoryTable />
case 'trade-history':
return <TradeHistory />
default: default:
return <TokenList /> return <TokenList />
} }

View File

@ -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

View File

@ -6,6 +6,7 @@ import { useTranslation } from 'next-i18next'
import Button, { LinkButton } from '../shared/Button' import Button, { LinkButton } from '../shared/Button'
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes' import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'
import { notify } from 'utils/notifications' import { notify } from 'utils/notifications'
import { MANGO_DATA_API_URL } from 'utils/constants'
const ImgWithLoader = (props: any) => { const ImgWithLoader = (props: any) => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
@ -67,7 +68,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
}), }),
} }
const response = await fetch( const response = await fetch(
'https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details', `${MANGO_DATA_API_URL}/user-data/profile-details`,
requestOptions requestOptions
) )
if (response.status === 200) { if (response.status === 200) {
@ -113,7 +114,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
}), }),
} }
const response = await fetch( const response = await fetch(
'https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details', `${MANGO_DATA_API_URL}/user-data/profile-details`,
requestOptions requestOptions
) )
if (response.status === 200) { if (response.status === 200) {

View File

@ -14,6 +14,7 @@ import mangoStore from '@store/mangoStore'
import startCase from 'lodash/startCase' import startCase from 'lodash/startCase'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { ChangeEvent, useState } from 'react' import { ChangeEvent, useState } from 'react'
import { MANGO_DATA_API_URL } from 'utils/constants'
import { notify } from 'utils/notifications' import { notify } from 'utils/notifications'
import ProfileImage from './ProfileImage' import ProfileImage from './ProfileImage'
@ -42,7 +43,7 @@ const EditProfileForm = ({
try { try {
setLoadUniquenessCheck(true) setLoadUniquenessCheck(true)
const response = await fetch( 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() const uniquenessCheck = await response.json()
@ -103,7 +104,7 @@ const EditProfileForm = ({
}), }),
} }
const response = await fetch( const response = await fetch(
'https://mango-transaction-log.herokuapp.com/v4/user-data/profile-details', `${MANGO_DATA_API_URL}/user-data/profile-details`,
requestOptions requestOptions
) )
if (response.status === 200) { if (response.status === 200) {

View File

@ -3,7 +3,7 @@ export const UpTriangle = ({ size }: { size?: 'small' }) => (
className={`h-0 w-0 ${ className={`h-0 w-0 ${
size === 'small' size === 'small'
? 'border-l-[4px] border-r-[4px] border-b-[6.92px]' ? '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`} } 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 ${ className={`h-0 w-0 ${
size === 'small' size === 'small'
? 'border-l-[4px] border-r-[4px] border-t-[6.92px]' ? '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`} } border-l-transparent border-r-transparent border-t-th-down`}
/> />
) )

View File

@ -7,7 +7,11 @@ export const Table = ({
}: { }: {
children: ReactNode children: ReactNode
className?: string 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 = ({ export const TrHead = ({
children, children,

View File

@ -30,10 +30,8 @@ import useMangoAccount from 'hooks/useMangoAccount'
const SwapHistoryTable = () => { const SwapHistoryTable = () => {
const { t } = useTranslation(['common', 'settings', 'swap']) const { t } = useTranslation(['common', 'settings', 'swap'])
const swapHistory = mangoStore((s) => s.mangoAccount.stats.swapHistory.data) const swapHistory = mangoStore((s) => s.mangoAccount.swapHistory.data)
const loadSwapHistory = mangoStore( const loadSwapHistory = mangoStore((s) => s.mangoAccount.swapHistory.loading)
(s) => s.mangoAccount.stats.swapHistory.loading
)
const { mangoTokens } = useJupiterMints() const { mangoTokens } = useJupiterMints()
const [showSwapDetails, setSwapDetails] = useState('') const [showSwapDetails, setSwapDetails] = useState('')
const [offset, setOffset] = useState(0) const [offset, setOffset] = useState(0)
@ -56,7 +54,7 @@ const SwapHistoryTable = () => {
const handleShowMore = useCallback(() => { const handleShowMore = useCallback(() => {
const set = mangoStore.getState().set const set = mangoStore.getState().set
set((s) => { set((s) => {
s.mangoAccount.stats.swapHistory.loading = true s.mangoAccount.swapHistory.loading = true
}) })
if (!mangoAccountAddress) return if (!mangoAccountAddress) return
setOffset(offset + PAGINATION_PAGE_LENGTH) setOffset(offset + PAGINATION_PAGE_LENGTH)

View File

@ -3,6 +3,7 @@ import { XMarkIcon } from '@heroicons/react/20/solid'
import { useWallet } from '@solana/wallet-adapter-react' import { useWallet } from '@solana/wallet-adapter-react'
import mangoStore from '@store/mangoStore' import mangoStore from '@store/mangoStore'
import { useState } from 'react' import { useState } from 'react'
import { MANGO_DATA_API_URL } from 'utils/constants'
import { WalktourLogic } from 'walktour' import { WalktourLogic } from 'walktour'
const CustomTooltip = ({ const CustomTooltip = ({
@ -37,7 +38,7 @@ const CustomTooltip = ({
body: message, body: message,
} }
const response = await fetch( const response = await fetch(
'https://mango-transaction-log.herokuapp.com/v4/user-data/settings-unsigned', `${MANGO_DATA_API_URL}/user-data/settings-unsigned`,
requestOptions requestOptions
) )
if (response.status === 200) { if (response.status === 200) {

View File

@ -1,5 +1,7 @@
import { PerpMarket } from '@blockworks-foundation/mango-v4' import { PerpMarket } from '@blockworks-foundation/mango-v4'
import { IconButton } from '@components/shared/Button'
import Change from '@components/shared/Change' import Change from '@components/shared/Change'
import { ChartBarIcon } from '@heroicons/react/20/solid'
import { useCoingecko } from 'hooks/useCoingecko' import { useCoingecko } from 'hooks/useCoingecko'
import useSelectedMarket from 'hooks/useSelectedMarket' import useSelectedMarket from 'hooks/useSelectedMarket'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
@ -8,7 +10,13 @@ import { getDecimalCount } from 'utils/numbers'
import MarketSelectDropdown from './MarketSelectDropdown' import MarketSelectDropdown from './MarketSelectDropdown'
import PerpFundingRate from './PerpFundingRate' import PerpFundingRate from './PerpFundingRate'
const AdvancedMarketHeader = () => { const AdvancedMarketHeader = ({
showChart,
setShowChart,
}: {
showChart?: boolean
setShowChart?: (x: boolean) => void
}) => {
const { t } = useTranslation(['common', 'trade']) const { t } = useTranslation(['common', 'trade'])
const { serumOrPerpMarket, baseSymbol, price } = useSelectedMarket() const { serumOrPerpMarket, baseSymbol, price } = useSelectedMarket()
const { data: tokenPrices } = useCoingecko() 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"> <div className=" w-full px-5 md:w-auto md:py-0 md:pr-6 lg:pb-0">
<MarketSelectDropdown /> <MarketSelectDropdown />
</div> </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 className="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="flex items-center justify-between">
<div className="text-xs text-th-fgd-4">{t('trade:oracle-price')}</div> <div className="flex items-center">
<div className="font-mono text-xs text-th-fgd-2"> <div id="trade-step-two" className="flex-col md:ml-6">
{price ? ( <div className="text-xs text-th-fgd-4">
`$${price.toFixed( {t('trade:oracle-price')}
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01) </div>
)}` <div className="font-mono text-xs text-th-fgd-2">
) : ( {price ? (
<span className="text-th-fgd-4"></span> `$${price.toFixed(
)} getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
</div> )}`
</div> ) : (
<div className="ml-6 flex-col"> <span className="text-th-fgd-4"></span>
<div className="text-xs text-th-fgd-4">{t('rolling-change')}</div> )}
<Change change={change} size="small" suffix="%" /> </div>
</div>
{serumOrPerpMarket instanceof PerpMarket ? (
<div className="ml-6 flex-col">
<div className="text-xs text-th-fgd-4">
{t('trade:funding-rate')}
</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> </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>
</div> </div>
) )

View File

@ -6,17 +6,24 @@ import TradeInfoTabs from './TradeInfoTabs'
import TabButtons from '@components/shared/TabButtons' import TabButtons from '@components/shared/TabButtons'
import { TABS } from './OrderbookAndTrades' import { TABS } from './OrderbookAndTrades'
import RecentTrades from './RecentTrades' import RecentTrades from './RecentTrades'
import TradingChartContainer from './TradingChartContainer'
const MobileTradeAdvancedPage = () => { const MobileTradeAdvancedPage = () => {
const [activeTab, setActiveTab] = useState('trade:book') const [activeTab, setActiveTab] = useState('trade:book')
const [showChart, setShowChart] = useState(false)
return ( return (
<div className="grid grid-cols-2 sm:grid-cols-3"> <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"> <div className="col-span-2 border-b border-th-bkg-3 sm:col-span-3">
<AdvancedMarketHeader /> <AdvancedMarketHeader
showChart={showChart}
setShowChart={setShowChart}
/>
</div> </div>
{/* <div className="col-span-2 h-72 overflow-auto border-b border-th-bkg-3 sm:col-span-3 sm:h-96"> {showChart ? (
<TradingViewChart /> <div className="col-span-2 h-80 border-b border-th-bkg-3 sm:col-span-3">
</div> */} <TradingChartContainer />
</div>
) : null}
<div className="col-span-1 pb-6 sm:col-span-2"> <div className="col-span-1 pb-6 sm:col-span-2">
<AdvancedTradeForm /> <AdvancedTradeForm />
</div> </div>

View File

@ -5,10 +5,11 @@ import { useQuery } from '@tanstack/react-query'
import useMangoGroup from 'hooks/useMangoGroup' import useMangoGroup from 'hooks/useMangoGroup'
import useSelectedMarket from 'hooks/useSelectedMarket' import useSelectedMarket from 'hooks/useSelectedMarket'
import { useMemo } from 'react' import { useMemo } from 'react'
import { MANGO_DATA_API_URL } from 'utils/constants'
const fetchFundingRate = async (groupPk: string | undefined) => { const fetchFundingRate = async (groupPk: string | undefined) => {
const res = await fetch( 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() return await res.json()
} }

View File

@ -1,4 +1,6 @@
import { I80F48, PerpMarket } from '@blockworks-foundation/mango-v4' 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 SideBadge from '@components/shared/SideBadge'
import { import {
Table, Table,
@ -14,7 +16,9 @@ import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount' import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket' import useSelectedMarket from 'hooks/useSelectedMarket'
import { useViewport } from 'hooks/useViewport' 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 { formatDecimal, formatFixedDecimals } from 'utils/numbers'
import { breakpoints } from 'utils/theme' import { breakpoints } from 'utils/theme'
import TableMarketName from './TableMarketName' import TableMarketName from './TableMarketName'
@ -86,11 +90,17 @@ const formatTradeHistory = (
} }
const TradeHistory = () => { const TradeHistory = () => {
const { t } = useTranslation(['common', 'trade'])
const group = mangoStore.getState().group const group = mangoStore.getState().group
const { selectedMarket } = useSelectedMarket() const { selectedMarket } = useSelectedMarket()
const { mangoAccount, mangoAccountAddress } = useMangoAccount() const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const actions = mangoStore((s) => s.actions)
const fills = mangoStore((s) => s.selectedMarket.fills) 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 { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false const showTableView = width ? width > breakpoints.md : false
@ -151,27 +161,35 @@ const TradeHistory = () => {
return [...newFills, ...tradeHistory] return [...newFills, ...tradeHistory]
}, [eventQueueFillsForAccount, 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 if (!selectedMarket || !group) return null
return mangoAccount && combinedTradeHistory.length ? ( return mangoAccount &&
showTableView ? ( (combinedTradeHistory.length || loadingTradeHistory) ? (
<div> <>
{showTableView ? (
<Table> <Table>
<thead> <thead>
<TrHead> <TrHead>
<Th className="text-left">Market</Th> <Th className="text-left">{t('market')}</Th>
<Th className="text-right">Side</Th> <Th className="text-right">{t('trade:side')}</Th>
<Th className="text-right">Size</Th> <Th className="text-right">{t('trade:size')}</Th>
<Th className="text-right">Price</Th> <Th className="text-right">{t('price')}</Th>
<Th className="text-right">Value</Th> <Th className="text-right">{t('value')}</Th>
<Th className="text-right">Fee</Th> <Th className="text-right">{t('fee')}</Th>
<Th className="text-right">Time</Th> <Th className="text-right">{t('date')}</Th>
</TrHead> </TrHead>
</thead> </thead>
<tbody> <tbody>
{combinedTradeHistory.map((trade: any) => { {combinedTradeHistory.map((trade: any, index: number) => {
let market let market
if ('market' in trade) { if ('market' in trade) {
market = group.getSerum3MarketByExternalMarket( market = group.getSerum3MarketByExternalMarket(
@ -189,6 +207,17 @@ const TradeHistory = () => {
makerTaker = 'Taker' 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 const size = trade.size || trade.quantity
let fee let fee
if (trade.fee_cost || trade.feeCost) { if (trade.fee_cost || trade.feeCost) {
@ -202,21 +231,21 @@ const TradeHistory = () => {
return ( return (
<TrBody <TrBody
key={`${trade.signature || trade.marketIndex}${size}`} key={`${trade.signature || trade.marketIndex}${index}`}
className="my-1 p-2" className="my-1 p-2"
> >
<Td className=""> <Td className="">
<TableMarketName market={market} /> <TableMarketName market={market} />
</Td> </Td>
<Td className="text-right"> <Td className="text-right">
<SideBadge side={trade.side} /> <SideBadge side={side} />
</Td> </Td>
<Td className="text-right font-mono">{size}</Td> <Td className="text-right font-mono">{size}</Td>
<Td className="text-right font-mono"> <Td className="text-right font-mono">
{formatDecimal(trade.price)} {formatDecimal(trade.price)}
</Td> </Td>
<Td className="text-right font-mono"> <Td className="text-right font-mono">
{formatFixedDecimals(trade.price * size)} ${formatFixedDecimals(trade.price * size, true)}
</Td> </Td>
<Td className="text-right"> <Td className="text-right">
<span className="font-mono">{formatDecimal(fee)}</span> <span className="font-mono">{formatDecimal(fee)}</span>
@ -240,36 +269,64 @@ const TradeHistory = () => {
})} })}
</tbody> </tbody>
</Table> </Table>
</div> ) : (
) : ( <div>
<div> {combinedTradeHistory.map((trade: any, index: number) => {
{eventQueueFillsForAccount.map((trade: any) => { const size = trade.size || trade.quantity
return ( return (
<div <div
className="flex items-center justify-between border-b border-th-bkg-3 p-4" className="flex items-center justify-between border-b border-th-bkg-3 p-4"
key={`${trade.marketIndex}`} key={`${trade.marketIndex}${index}`}
> >
<div> <div>
<TableMarketName market={selectedMarket} /> <TableMarketName market={selectedMarket} />
<div className="mt-1 flex items-center space-x-1"> <div className="mt-1 flex items-center space-x-1">
<SideBadge side={trade.side} /> <SideBadge side={trade.side} />
<p className="text-th-fgd-4"> <p className="text-th-fgd-4">
<span className="font-mono text-th-fgd-3"> <span className="font-mono text-th-fgd-2">{size}</span>
{trade.size} {' for '}
</span> <span className="font-mono text-th-fgd-2">
{' for '} {formatDecimal(trade.price)}
<span className="font-mono text-th-fgd-3"> </span>
{formatDecimal(trade.price)} </p>
</span> </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> </p>
</div> </div>
</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"> <div className="flex flex-col items-center p-8">
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" /> <NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" />

View File

@ -17,9 +17,9 @@ const TradingChartContainer = () => {
const [tradingChart] = useLocalStorageState(TRADE_CHART_UI_KEY) const [tradingChart] = useLocalStorageState(TRADE_CHART_UI_KEY)
const isTradingChart = tradingChart === 'custom' const isTradingChart = tradingChart === 'custom'
return !isTradingChart ? ( return !isTradingChart ? (
<TradingViewChart></TradingViewChart> <TradingViewChart />
) : ( ) : (
<TradingViewChartKlineContainer></TradingViewChartKlineContainer> <TradingViewChartKlineContainer />
) )
} }

View File

@ -405,9 +405,9 @@ const TradingViewChartKline = ({ setIsFullView, isFullView }: Props) => {
return ( return (
<div <div
className={clsx( className={clsx(
'fixed h-full w-full', 'h-full w-full',
isFullView 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'
: '' : ''
)} )}
> >

View File

@ -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="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="default-transition relative z-10 flex h-full items-center justify-center space-x-3 px-4">
<div {connecting ? (
className={`flex h-[28px] w-[28px] items-center justify-center rounded-full ${ <Loading className="h-[28px] w-[28px]" />
wallet?.adapter.name === 'Solflare' ? 'bg-black' : '' ) : (
}`} <div
> className={`flex h-[28px] w-[28px] items-center justify-center rounded-full ${
<img wallet?.adapter.name === 'Solflare' ? 'bg-black' : ''
src={wallet?.adapter.icon || selectedWallet?.adapter.icon} }`}
className={ >
wallet?.adapter.name === 'Solflare' <img
? 'h-auto w-[20px]' src={wallet?.adapter.icon || selectedWallet?.adapter.icon}
: 'h-auto w-[28px]' className={
} wallet?.adapter.name === 'Solflare'
alt={`${wallet?.adapter.name} icon`} ? 'h-auto w-[20px]'
/> : 'h-auto w-[28px]'
</div> }
alt={`${wallet?.adapter.name} icon`}
/>
</div>
)}
<div className="text-left"> <div className="text-left">
<div className="mb-1.5 flex justify-center font-display text-base leading-none text-th-fgd-1"> <div className="mb-1.5 flex font-display text-base leading-none text-th-fgd-1">
{connecting ? <Loading className="h-4 w-4" /> : t('connect')} {t('connect')}
</div> </div>
<div className="text-xxs font-normal leading-3 text-th-fgd-3"> <div className="text-xxs font-normal leading-3 text-th-fgd-3">

View File

@ -45,8 +45,12 @@ const ConnectedMenu = () => {
state.mangoAccount.current = undefined state.mangoAccount.current = undefined
state.mangoAccounts = [] state.mangoAccounts = []
state.mangoAccount.openOrders = {} state.mangoAccount.openOrders = {}
state.mangoAccount.stats.interestTotals = { data: [], loading: false } state.mangoAccount.interestTotals = { data: [], loading: false }
state.mangoAccount.stats.performance = { data: [], loading: false } state.mangoAccount.performance = {
data: [],
loading: false,
initialLoad: false,
}
}) })
disconnect() disconnect()
wallet?.adapter.disconnect() wallet?.adapter.disconnect()

View File

@ -6,6 +6,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
return { return {
props: { props: {
...(await serverSideTranslations(locale, [ ...(await serverSideTranslations(locale, [
'account',
'activity', 'activity',
'common', 'common',
'onboarding', 'onboarding',

View File

@ -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}}"
}

View File

@ -121,6 +121,7 @@
"token-collateral-multiplier": "{{token}} Collateral Multiplier", "token-collateral-multiplier": "{{token}} Collateral Multiplier",
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance", "tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
"tooltip-collateral-value": "The USD amount you can trade or borrow against", "tooltip-collateral-value": "The USD amount you can trade or borrow against",
"total": "Total",
"total-borrows": "Total Borrows", "total-borrows": "Total Borrows",
"total-borrow-value": "Total Borrow Value", "total-borrow-value": "Total Borrow Value",
"total-collateral": "Total Collateral", "total-collateral": "Total Collateral",

View File

@ -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}}"
}

View File

@ -121,6 +121,7 @@
"token-collateral-multiplier": "{{token}} Collateral Multiplier", "token-collateral-multiplier": "{{token}} Collateral Multiplier",
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance", "tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
"tooltip-collateral-value": "The USD amount you can trade or borrow against", "tooltip-collateral-value": "The USD amount you can trade or borrow against",
"total": "Total",
"total-borrows": "Total Borrows", "total-borrows": "Total Borrows",
"total-borrow-value": "Total Borrow Value", "total-borrow-value": "Total Borrow Value",
"total-collateral": "Total Collateral", "total-collateral": "Total Collateral",

View File

@ -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}}"
}

View File

@ -121,6 +121,7 @@
"token-collateral-multiplier": "{{token}} Collateral Multiplier", "token-collateral-multiplier": "{{token}} Collateral Multiplier",
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance", "tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
"tooltip-collateral-value": "The USD amount you can trade or borrow against", "tooltip-collateral-value": "The USD amount you can trade or borrow against",
"total": "Total",
"total-borrows": "Total Borrows", "total-borrows": "Total Borrows",
"total-borrow-value": "Total Borrow Value", "total-borrow-value": "Total Borrow Value",
"total-collateral": "Total Collateral", "total-collateral": "Total Collateral",

View File

@ -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}}"
}

View File

@ -121,6 +121,7 @@
"token-collateral-multiplier": "{{token}} Collateral Multiplier", "token-collateral-multiplier": "{{token}} Collateral Multiplier",
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance", "tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
"tooltip-collateral-value": "The USD amount you can trade or borrow against", "tooltip-collateral-value": "The USD amount you can trade or borrow against",
"total": "Total",
"total-borrows": "Total Borrows", "total-borrows": "Total Borrows",
"total-borrow-value": "Total Borrow Value", "total-borrow-value": "Total Borrow Value",
"total-collateral": "Total Collateral", "total-collateral": "Total Collateral",

View File

@ -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}}"
}

View File

@ -121,6 +121,7 @@
"token-collateral-multiplier": "{{token}} Collateral Multiplier", "token-collateral-multiplier": "{{token}} Collateral Multiplier",
"tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance", "tooltip-borrow-rate": "The variable interest rate you'll pay on your borrowed balance",
"tooltip-collateral-value": "The USD amount you can trade or borrow against", "tooltip-collateral-value": "The USD amount you can trade or borrow against",
"total": "Total",
"total-borrows": "Total Borrows", "total-borrows": "Total Borrows",
"total-borrow-value": "Total Borrow Value", "total-borrow-value": "Total Borrow Value",
"total-collateral": "Total Collateral", "total-collateral": "Total Collateral",

View File

@ -33,6 +33,7 @@ import {
DEFAULT_MARKET_NAME, DEFAULT_MARKET_NAME,
INPUT_TOKEN_DEFAULT, INPUT_TOKEN_DEFAULT,
LAST_ACCOUNT_KEY, LAST_ACCOUNT_KEY,
MANGO_DATA_API_URL,
OUTPUT_TOKEN_DEFAULT, OUTPUT_TOKEN_DEFAULT,
PAGINATION_PAGE_LENGTH, PAGINATION_PAGE_LENGTH,
RPC_PROVIDER_KEY, RPC_PROVIDER_KEY,
@ -240,15 +241,17 @@ export type MangoStore = {
openOrders: Record<string, Order[] | PerpOrder[]> openOrders: Record<string, Order[] | PerpOrder[]>
perpPositions: PerpPosition[] perpPositions: PerpPosition[]
spotBalances: SpotBalances spotBalances: SpotBalances
stats: { interestTotals: { data: TotalInterestDataItem[]; loading: boolean }
interestTotals: { data: TotalInterestDataItem[]; loading: boolean } performance: {
performance: { data: PerformanceDataItem[]; loading: boolean } data: PerformanceDataItem[]
swapHistory: { loading: boolean
data: SwapHistoryItem[] initialLoad: boolean
loading: boolean
}
} }
tradeHistory: SpotTradeHistory[] swapHistory: {
data: SwapHistoryItem[]
loading: boolean
}
tradeHistory: { data: SpotTradeHistory[]; loading: boolean }
} }
mangoAccounts: MangoAccount[] mangoAccounts: MangoAccount[]
markets: Serum3Market[] | undefined markets: Serum3Market[] | undefined
@ -333,7 +336,7 @@ export type MangoStore = {
) => Promise<void> ) => Promise<void>
fetchTokenStats: () => void fetchTokenStats: () => void
fetchTourSettings: (walletPk: string) => void fetchTourSettings: (walletPk: string) => void
fetchTradeHistory: () => Promise<void> fetchTradeHistory: (offset?: number) => Promise<void>
fetchWalletTokens: (walletPk: PublicKey) => Promise<void> fetchWalletTokens: (walletPk: PublicKey) => Promise<void>
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void> connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
loadMarketFills: () => Promise<void> loadMarketFills: () => Promise<void>
@ -380,12 +383,10 @@ const mangoStore = create<MangoStore>()(
openOrders: {}, openOrders: {},
perpPositions: [], perpPositions: [],
spotBalances: {}, spotBalances: {},
stats: { interestTotals: { data: [], loading: false },
interestTotals: { data: [], loading: false }, performance: { data: [], loading: false, initialLoad: false },
performance: { data: [], loading: false }, swapHistory: { data: [], loading: true },
swapHistory: { data: [], loading: true }, tradeHistory: { data: [], loading: true },
},
tradeHistory: [],
}, },
mangoAccounts: [], mangoAccounts: [],
markets: undefined, markets: undefined,
@ -457,11 +458,11 @@ const mangoStore = create<MangoStore>()(
fetchAccountInterestTotals: async (mangoAccountPk: string) => { fetchAccountInterestTotals: async (mangoAccountPk: string) => {
const set = get().set const set = get().set
set((state) => { set((state) => {
state.mangoAccount.stats.interestTotals.loading = true state.mangoAccount.interestTotals.loading = true
}) })
try { try {
const response = await fetch( 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 parsedResponse = await response.json()
const entries: any = Object.entries(parsedResponse).sort((a, b) => const entries: any = Object.entries(parsedResponse).sort((a, b) =>
@ -475,12 +476,12 @@ const mangoStore = create<MangoStore>()(
.filter((x: string) => x) .filter((x: string) => x)
set((state) => { set((state) => {
state.mangoAccount.stats.interestTotals.data = stats state.mangoAccount.interestTotals.data = stats
state.mangoAccount.stats.interestTotals.loading = false state.mangoAccount.interestTotals.loading = false
}) })
} catch { } catch {
set((state) => { set((state) => {
state.mangoAccount.stats.interestTotals.loading = false state.mangoAccount.interestTotals.loading = false
}) })
console.error({ console.error({
title: 'Failed to load account interest totals', title: 'Failed to load account interest totals',
@ -494,11 +495,11 @@ const mangoStore = create<MangoStore>()(
) => { ) => {
const set = get().set const set = get().set
set((state) => { set((state) => {
state.mangoAccount.stats.performance.loading = true state.mangoAccount.performance.loading = true
}) })
try { try {
const response = await fetch( 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') .subtract(range, 'day')
.format('YYYY-MM-DD')}` .format('YYYY-MM-DD')}`
) )
@ -514,18 +515,19 @@ const mangoStore = create<MangoStore>()(
.filter((x: string) => x) .filter((x: string) => x)
set((state) => { set((state) => {
state.mangoAccount.stats.performance.data = stats.reverse() state.mangoAccount.performance.data = stats.reverse()
state.mangoAccount.stats.performance.loading = false
}) })
} catch (e) { } catch (e) {
set((state) => {
state.mangoAccount.stats.performance.loading = false
})
console.error('Failed to load account performance data', e) console.error('Failed to load account performance data', e)
// notify({ } finally {
// title: 'Failed to load account performance data', const hasLoaded =
// type: 'error', mangoStore.getState().mangoAccount.performance.initialLoad
// }) set((state) => {
state.mangoAccount.performance.loading = false
if (!hasLoaded) {
state.mangoAccount.performance.initialLoad = true
}
})
} }
}, },
fetchActivityFeed: async ( fetchActivityFeed: async (
@ -541,7 +543,7 @@ const mangoStore = create<MangoStore>()(
try { try {
const response = await fetch( 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 : '' params ? params : ''
}` }`
) )
@ -811,7 +813,7 @@ const mangoStore = create<MangoStore>()(
}) })
try { try {
const response = await fetch( 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() const data = await response.json()
@ -836,15 +838,12 @@ const mangoStore = create<MangoStore>()(
) => { ) => {
const set = get().set const set = get().set
const loadedSwapHistory = const loadedSwapHistory =
mangoStore.getState().mangoAccount.stats.swapHistory.data mangoStore.getState().mangoAccount.swapHistory.data
const connectedMangoAccountPk = mangoStore
.getState()
.mangoAccount.current?.publicKey.toString()
setTimeout(async () => { setTimeout(async () => {
try { try {
const history = await fetch( const history = await fetch(
`https://mango-transaction-log.herokuapp.com/v4/stats/swap-history?mango-account=${mangoAccountPk}&offset=${offset}&limit=${PAGINATION_PAGE_LENGTH}` `${MANGO_DATA_API_URL}/stats/swap-history?mango-account=${mangoAccountPk}&offset=${offset}&limit=${PAGINATION_PAGE_LENGTH}`
) )
const parsedHistory = await history.json() const parsedHistory = await history.json()
const sortedHistory = const sortedHistory =
@ -862,13 +861,13 @@ const mangoStore = create<MangoStore>()(
: sortedHistory : sortedHistory
set((state) => { set((state) => {
state.mangoAccount.stats.swapHistory.data = combinedHistory state.mangoAccount.swapHistory.data = combinedHistory
}) })
} catch (e) { } catch (e) {
console.error('Unable to fetch swap history', e) console.error('Unable to fetch swap history', e)
} finally { } finally {
set((state) => { set((state) => {
state.mangoAccount.stats.swapHistory.loading = false state.mangoAccount.swapHistory.loading = false
}) })
} }
}, timeout) }, timeout)
@ -882,7 +881,7 @@ const mangoStore = create<MangoStore>()(
}) })
try { try {
const response = await fetch( 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() const data = await response.json()
@ -951,7 +950,7 @@ const mangoStore = create<MangoStore>()(
}) })
try { try {
const response = await fetch( 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() const data = await response.json()
set((state) => { set((state) => {
@ -973,7 +972,7 @@ const mangoStore = create<MangoStore>()(
}) })
try { try {
const response = await fetch( 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() const data = await response.json()
set((state) => { set((state) => {
@ -1021,36 +1020,33 @@ const mangoStore = create<MangoStore>()(
console.log('Error fetching fills:', err) console.log('Error fetching fills:', err)
} }
}, },
async fetchTradeHistory() { async fetchTradeHistory(offset = 0) {
const set = get().set const set = get().set
const mangoAccount = get().mangoAccount.current const mangoAccountPk =
get().mangoAccount?.current?.publicKey.toString()
const loadedHistory =
mangoStore.getState().mangoAccount.tradeHistory.data
try { try {
const [spotRes, perpRes] = await Promise.all([ const response = await fetch(
fetch( `${MANGO_DATA_API_URL}/stats/trade-history?mango-account=${mangoAccountPk}&limit=${PAGINATION_PAGE_LENGTH}&offset=${offset}`
`https://mango-transaction-log.herokuapp.com/v4/stats/openbook-trades?address=${mangoAccount?.publicKey.toString()}&address-type=mango-account` )
), const parsedHistory = await response.json()
fetch( const newHistory = parsedHistory.map((h: any) => h.activity_details)
`https://mango-transaction-log.herokuapp.com/v4/stats/perp-trade-history?mango-account=${mangoAccount?.publicKey.toString()}&limit=1000`
), const history =
]) offset !== 0 ? loadedHistory.concat(newHistory) : newHistory
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)
}
set((s) => { set((s) => {
s.mangoAccount.tradeHistory = tradeHistory.sort( s.mangoAccount.tradeHistory.data = history?.sort(
(x: any) => x.block_datetime (x: any) => x.block_datetime
) )
}) })
} catch (e) { } catch (e) {
console.error('Unable to fetch trade history', e) console.error('Unable to fetch trade history', e)
} finally {
set((s) => {
s.mangoAccount.tradeHistory.loading = false
})
} }
}, },
updateConnection(endpointUrl) { updateConnection(endpointUrl) {

View File

@ -60,6 +60,8 @@ 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_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 DEFAULT_MARKET_NAME = 'SOL/USDC'
export const MIN_SOL_BALANCE = 0.001 export const MIN_SOL_BALANCE = 0.001

View File

@ -52,7 +52,7 @@
"@blockworks-foundation/mango-v4@https://github.com/blockworks-foundation/mango-v4.git#ts-client": "@blockworks-foundation/mango-v4@https://github.com/blockworks-foundation/mango-v4.git#ts-client":
version "0.0.1-beta.6" 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: dependencies:
"@project-serum/anchor" "^0.25.0" "@project-serum/anchor" "^0.25.0"
"@project-serum/serum" "^0.13.65" "@project-serum/serum" "^0.13.65"