From f68cbbc4d213087db3af81d9a3152ef20fa14d7b Mon Sep 17 00:00:00 2001 From: tjs Date: Wed, 28 Sep 2022 12:45:38 -0400 Subject: [PATCH 01/10] add try catch for recent trades --- components/trade/RecentTrades.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/components/trade/RecentTrades.tsx b/components/trade/RecentTrades.tsx index e15189ed..9fa95b38 100644 --- a/components/trade/RecentTrades.tsx +++ b/components/trade/RecentTrades.tsx @@ -30,17 +30,21 @@ const RecentTrades = () => { const fetchTradesForChart = useCallback(async () => { if (!selectedMarketPk) return - const response = await fetch( - `https://event-history-api-candles.herokuapp.com/trades/address/${selectedMarketPk}` - ) - const parsedResp = await response.json() - const newTrades = parsedResp.data - if (!newTrades) return null + try { + const response = await fetch( + `https://event-history-api-candles.herokuapp.com/trades/address/${selectedMarketPk}` + ) + const parsedResp = await response.json() + const newTrades = parsedResp.data + if (!newTrades) return null - if (newTrades.length && trades.length === 0) { - setTrades(newTrades) - } else if (newTrades?.length && !isEqual(newTrades[0], trades[0])) { - setTrades(newTrades) + if (newTrades.length && trades.length === 0) { + setTrades(newTrades) + } else if (newTrades?.length && !isEqual(newTrades[0], trades[0])) { + setTrades(newTrades) + } + } catch (e) { + console.error('Unable to fetch recent trades', e) } }, [selectedMarketPk, trades]) From 255ace6c00775148d9e1f69f42609ff02f9002c7 Mon Sep 17 00:00:00 2001 From: tjs Date: Wed, 28 Sep 2022 12:45:53 -0400 Subject: [PATCH 02/10] log breakpoint changes --- components/trade/TradeAdvancedPage.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/trade/TradeAdvancedPage.tsx b/components/trade/TradeAdvancedPage.tsx index 35e577d2..98bb702b 100644 --- a/components/trade/TradeAdvancedPage.tsx +++ b/components/trade/TradeAdvancedPage.tsx @@ -156,6 +156,8 @@ const TradeAdvancedPage = () => { }, []) const onBreakpointChange = useCallback((newBreakpoint: string) => { + console.log('newBreakpoints', newBreakpoint) + setCurrentBreakpoint(newBreakpoint) }, []) From 6b737f03c7d5270ffd88bfe3618a23f5a961f720 Mon Sep 17 00:00:00 2001 From: tjs Date: Wed, 28 Sep 2022 16:02:12 -0400 Subject: [PATCH 03/10] support passing in a count to tabbuttons --- components/account/AccountTabs.tsx | 12 +- components/shared/TabButtons.tsx | 14 +- components/trade/AdvancedTradeForm.tsx | 7 +- components/trade/MobileTradeAdvancedPage.tsx | 14 +- ...alanceAndOpenOrders.tsx => OpenOrders.tsx} | 130 +----------------- components/trade/OrderbookAndTrades.tsx | 7 +- components/trade/TradeAdvancedPage.tsx | 8 +- components/trade/TradeBalances.tsx | 103 ++++++++++++++ components/trade/TradeInfoTabs.tsx | 33 +++++ components/trade/UnsettledTrades.tsx | 103 ++++++++++++++ 10 files changed, 275 insertions(+), 156 deletions(-) rename components/trade/{BalanceAndOpenOrders.tsx => OpenOrders.tsx} (57%) create mode 100644 components/trade/TradeBalances.tsx create mode 100644 components/trade/TradeInfoTabs.tsx create mode 100644 components/trade/UnsettledTrades.tsx diff --git a/components/account/AccountTabs.tsx b/components/account/AccountTabs.tsx index 99150387..73c85cac 100644 --- a/components/account/AccountTabs.tsx +++ b/components/account/AccountTabs.tsx @@ -1,9 +1,10 @@ -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import mangoStore from '@store/mangoStore' import TabButtons from '../shared/TabButtons' import TokenList from '../TokenList' import SwapHistoryTable from '../swap/SwapHistoryTable' -import { useRouter } from 'next/router' + +const TABS = ['balances', 'swap:swap-history'] const AccountTabs = () => { const [activeTab, setActiveTab] = useState('balances') @@ -11,7 +12,10 @@ const AccountTabs = () => { const mangoAccount = mangoStore((s) => s.mangoAccount.current) const tradeHistory = mangoStore((s) => s.mangoAccount.stats.tradeHistory.data) const loading = mangoStore((s) => s.mangoAccount.stats.tradeHistory.loading) - const { pathname } = useRouter() + + const tabsWithCount: [string, number][] = useMemo(() => { + return TABS.map((t) => [t, 0]) + }, []) useEffect(() => { if (mangoAccount) { @@ -24,7 +28,7 @@ const AccountTabs = () => { setActiveTab(v)} - values={['balances', 'swap:swap-history']} + values={tabsWithCount} showBorders /> {activeTab === 'balances' ? ( diff --git a/components/shared/TabButtons.tsx b/components/shared/TabButtons.tsx index 06d2da5e..53fbd0e1 100644 --- a/components/shared/TabButtons.tsx +++ b/components/shared/TabButtons.tsx @@ -4,7 +4,7 @@ import { FunctionComponent } from 'react' interface TabButtonsProps { activeValue: string onChange: (x: any) => void - values: Array + values: [string, number][] showBorders?: boolean rounded?: boolean fillWidth?: boolean @@ -26,20 +26,20 @@ const TabButtons: FunctionComponent = ({ showBorders ? 'border-b border-th-bkg-3' : '' }`} > - {values.map((v, i) => ( -
+ {values.map(([label, count], i) => ( +
))} diff --git a/components/trade/AdvancedTradeForm.tsx b/components/trade/AdvancedTradeForm.tsx index 230f77aa..83181644 100644 --- a/components/trade/AdvancedTradeForm.tsx +++ b/components/trade/AdvancedTradeForm.tsx @@ -19,6 +19,11 @@ import { notify } from 'utils/notifications' import SpotSlider from './SpotSlider' import { calculateMarketPrice } from 'utils/tradeForm' +const TABS: [string, number][] = [ + ['Limit', 0], + ['Market', 0], +] + const AdvancedTradeForm = () => { const { t } = useTranslation('common') const set = mangoStore.getState().set @@ -201,7 +206,7 @@ const AdvancedTradeForm = () => { setTradeType(tab)} - values={['Limit', 'Market']} + values={TABS} fillWidth />
diff --git a/components/trade/MobileTradeAdvancedPage.tsx b/components/trade/MobileTradeAdvancedPage.tsx index 943586ac..153b0154 100644 --- a/components/trade/MobileTradeAdvancedPage.tsx +++ b/components/trade/MobileTradeAdvancedPage.tsx @@ -1,18 +1,8 @@ import { useCallback, useEffect, useState } from 'react' -// import { useTranslation } from 'next-i18next' -import dynamic from 'next/dynamic' -import ReactGridLayout, { Responsive, WidthProvider } from 'react-grid-layout' - -import mangoStore from '@store/mangoStore' -import { GRID_LAYOUT_KEY } from 'utils/constants' -import useLocalStorageState from 'hooks/useLocalStorageState' -import { breakpoints } from 'utils/theme' -import { useViewport } from 'hooks/useViewport' import Orderbook from './Orderbook' import AdvancedMarketHeader from './AdvancedMarketHeader' import AdvancedTradeForm from './AdvancedTradeForm' -import BalanceAndOpenOrders from './BalanceAndOpenOrders' -import TradingViewChart from './TradingViewChart' +import TradeInfoTabs from './TradeInfoTabs' const MobileTradeAdvancedPage = () => { return ( @@ -30,7 +20,7 @@ const MobileTradeAdvancedPage = () => {
- +
) diff --git a/components/trade/BalanceAndOpenOrders.tsx b/components/trade/OpenOrders.tsx similarity index 57% rename from components/trade/BalanceAndOpenOrders.tsx rename to components/trade/OpenOrders.tsx index 6654eff7..1c12784f 100644 --- a/components/trade/BalanceAndOpenOrders.tsx +++ b/components/trade/OpenOrders.tsx @@ -1,140 +1,18 @@ import { Serum3Side } from '@blockworks-foundation/mango-v4' import { IconButton } from '@components/shared/Button' import SideBadge from '@components/shared/SideBadge' -import TabButtons from '@components/shared/TabButtons' import Tooltip from '@components/shared/Tooltip' -import { - LinkIcon, - QuestionMarkCircleIcon, - TrashIcon, -} from '@heroicons/react/20/solid' +import { LinkIcon, TrashIcon } from '@heroicons/react/20/solid' import { Order } from '@project-serum/serum/lib/market' import { useWallet } from '@solana/wallet-adapter-react' import { PublicKey } from '@solana/web3.js' import mangoStore from '@store/mangoStore' import { useTranslation } from 'next-i18next' -import Image from 'next/image' -import { useCallback, useMemo, useState } from 'react' +import { useCallback } from 'react' import { notify } from 'utils/notifications' -import { formatDecimal, formatFixedDecimals } from 'utils/numbers' +import { formatFixedDecimals } from 'utils/numbers' import MarketLogos from './MarketLogos' -const TABS = ['Balances', 'Orders'] - -const BalanceAndOpenOrders = () => { - const [selectedTab, setSelectedTab] = useState('Balances') - - return ( -
-
- setSelectedTab(tab)} - values={TABS} - showBorders - /> -
- {selectedTab === 'Balances' ? : null} - {selectedTab === 'Orders' ? : null} -
- ) -} - -const Balances = () => { - const { t } = useTranslation('common') - const mangoAccount = mangoStore((s) => s.mangoAccount.current) - const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances) - const group = mangoStore((s) => s.group) - const jupiterTokens = mangoStore((s) => s.jupiterTokens) - - const banks = useMemo(() => { - if (group) { - const rawBanks = Array.from(group?.banksMapByName, ([key, value]) => ({ - key, - value, - })) - const sortedBanks = mangoAccount - ? rawBanks.sort( - (a, b) => - Math.abs( - mangoAccount?.getTokenBalanceUi(b.value[0]) * - b.value[0].uiPrice! - ) - - Math.abs( - mangoAccount?.getTokenBalanceUi(a.value[0]) * - a.value[0].uiPrice! - ) - ) - : rawBanks - - return mangoAccount - ? sortedBanks.filter( - (b) => mangoAccount?.getTokenBalanceUi(b.value[0]) !== 0 - ) - : sortedBanks - } - return [] - }, [group, mangoAccount]) - - return ( - - - - - - - - - - - {banks.map(({ key, value }) => { - const bank = value[0] - - let logoURI - if (jupiterTokens.length) { - logoURI = jupiterTokens.find( - (t) => t.address === bank.mint.toString() - )!.logoURI - } - - return ( - - - - - - - ) - })} - -
{t('token')}{t('balance')}{t('in-orders')}{t('unsettled')}
-
-
- {logoURI ? ( - - ) : ( - - )} -
- {bank.name} -
-
-
- {mangoAccount - ? formatDecimal( - mangoAccount.getTokenBalanceUi(bank), - bank.mintDecimals - ) - : 0} -
-
- {spotBalances[bank.mint.toString()]?.inOrders || 0.0} - - {spotBalances[bank.mint.toString()]?.unsettled || 0.0} -
- ) -} - const OpenOrders = () => { const { t } = useTranslation('common') const { connected } = useWallet() @@ -271,4 +149,4 @@ const OpenOrders = () => { ) } -export default BalanceAndOpenOrders +export default OpenOrders diff --git a/components/trade/OrderbookAndTrades.tsx b/components/trade/OrderbookAndTrades.tsx index 9ec9615c..28a72661 100644 --- a/components/trade/OrderbookAndTrades.tsx +++ b/components/trade/OrderbookAndTrades.tsx @@ -3,6 +3,11 @@ import { useState } from 'react' import Orderbook from './Orderbook' import RecentTrades from './RecentTrades' +const TABS: [string, number][] = [ + ['book', 0], + ['trades', 0], +] + const OrderbookAndTrades = () => { const [activeTab, setActiveTab] = useState('book') return ( @@ -11,7 +16,7 @@ const OrderbookAndTrades = () => { setActiveTab(tab)} - values={['book', 'trades']} + values={TABS} fillWidth /> diff --git a/components/trade/TradeAdvancedPage.tsx b/components/trade/TradeAdvancedPage.tsx index 98bb702b..2601c80b 100644 --- a/components/trade/TradeAdvancedPage.tsx +++ b/components/trade/TradeAdvancedPage.tsx @@ -1,5 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' -// import { useTranslation } from 'next-i18next' +import { useCallback, useMemo, useState } from 'react' import dynamic from 'next/dynamic' import ReactGridLayout, { Responsive, WidthProvider } from 'react-grid-layout' @@ -8,10 +7,9 @@ import { GRID_LAYOUT_KEY } from 'utils/constants' import useLocalStorageState from 'hooks/useLocalStorageState' import { breakpoints } from 'utils/theme' import { useViewport } from 'hooks/useViewport' -import Orderbook from './Orderbook' import AdvancedMarketHeader from './AdvancedMarketHeader' import AdvancedTradeForm from './AdvancedTradeForm' -import BalanceAndOpenOrders from './BalanceAndOpenOrders' +import TradeInfoTabs from './TradeInfoTabs' import MobileTradeAdvancedPage from './MobileTradeAdvancedPage' import OrderbookAndTrades from './OrderbookAndTrades' @@ -195,7 +193,7 @@ const TradeAdvancedPage = () => {
- +
{ + const { t } = useTranslation('common') + const mangoAccount = mangoStore((s) => s.mangoAccount.current) + const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances) + const group = mangoStore((s) => s.group) + const jupiterTokens = mangoStore((s) => s.jupiterTokens) + + const banks = useMemo(() => { + if (group) { + const rawBanks = Array.from(group?.banksMapByName, ([key, value]) => ({ + key, + value, + })) + const sortedBanks = mangoAccount + ? rawBanks.sort( + (a, b) => + Math.abs( + mangoAccount?.getTokenBalanceUi(b.value[0]) * + b.value[0].uiPrice! + ) - + Math.abs( + mangoAccount?.getTokenBalanceUi(a.value[0]) * + a.value[0].uiPrice! + ) + ) + : rawBanks + + return mangoAccount + ? sortedBanks.filter( + (b) => mangoAccount?.getTokenBalanceUi(b.value[0]) !== 0 + ) + : sortedBanks + } + return [] + }, [group, mangoAccount]) + + return ( + + + + + + + + + + + {banks.map(({ key, value }) => { + const bank = value[0] + + let logoURI + if (jupiterTokens.length) { + logoURI = jupiterTokens.find( + (t) => t.address === bank.mint.toString() + )!.logoURI + } + + return ( + + + + + + + ) + })} + +
{t('token')}{t('balance')}{t('in-orders')}{t('unsettled')}
+
+
+ {logoURI ? ( + + ) : ( + + )} +
+ {bank.name} +
+
+
+ {mangoAccount + ? formatDecimal( + mangoAccount.getTokenBalanceUi(bank), + bank.mintDecimals + ) + : 0} +
+
+ {spotBalances[bank.mint.toString()]?.inOrders || 0.0} + + {spotBalances[bank.mint.toString()]?.unsettled || 0.0} +
+ ) +} + +export default Balances diff --git a/components/trade/TradeInfoTabs.tsx b/components/trade/TradeInfoTabs.tsx new file mode 100644 index 00000000..ce4ff0a2 --- /dev/null +++ b/components/trade/TradeInfoTabs.tsx @@ -0,0 +1,33 @@ +import { useMemo, useState } from 'react' +import TabButtons from '@components/shared/TabButtons' +import OpenOrders from './OpenOrders' +import Balances from './TradeBalances' +import UnsettledTrades from './UnsettledTrades' + +const TABS = ['Balances', 'Orders', 'Unsettled P&L'] + +const TradeInfoTabs = () => { + const [selectedTab, setSelectedTab] = useState('Balances') + + const tabsWithCount: [string, number][] = useMemo(() => { + return TABS.map((t) => [t, 0]) + }, []) + + return ( +
+
+ setSelectedTab(tab)} + values={tabsWithCount} + showBorders + /> +
+ {selectedTab === 'Balances' ? : null} + {selectedTab === 'Orders' ? : null} + {selectedTab === 'Unsettled' ? : null} +
+ ) +} + +export default TradeInfoTabs diff --git a/components/trade/UnsettledTrades.tsx b/components/trade/UnsettledTrades.tsx new file mode 100644 index 00000000..00804658 --- /dev/null +++ b/components/trade/UnsettledTrades.tsx @@ -0,0 +1,103 @@ +import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid' +import mangoStore from '@store/mangoStore' +import { useTranslation } from 'next-i18next' +import Image from 'next/image' +import { useMemo } from 'react' +import { formatDecimal } from 'utils/numbers' + +const UnsettledTrades = () => { + const { t } = useTranslation('common') + const mangoAccount = mangoStore((s) => s.mangoAccount.current) + const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances) + const group = mangoStore((s) => s.group) + const jupiterTokens = mangoStore((s) => s.jupiterTokens) + + const banks = useMemo(() => { + if (group) { + const rawBanks = Array.from(group?.banksMapByName, ([key, value]) => ({ + key, + value, + })) + const sortedBanks = mangoAccount + ? rawBanks.sort( + (a, b) => + Math.abs( + mangoAccount?.getTokenBalanceUi(b.value[0]) * + b.value[0].uiPrice! + ) - + Math.abs( + mangoAccount?.getTokenBalanceUi(a.value[0]) * + a.value[0].uiPrice! + ) + ) + : rawBanks + + return mangoAccount + ? sortedBanks.filter( + (b) => mangoAccount?.getTokenBalanceUi(b.value[0]) !== 0 + ) + : sortedBanks + } + return [] + }, [group, mangoAccount]) + + return ( + + + + + + + + + + + {banks.map(({ key, value }) => { + const bank = value[0] + + let logoURI + if (jupiterTokens.length) { + logoURI = jupiterTokens.find( + (t) => t.address === bank.mint.toString() + )!.logoURI + } + + return ( + + + + + + + ) + })} + +
{t('token')}{t('balance')}{t('in-orders')}{t('unsettled')}
+
+
+ {logoURI ? ( + + ) : ( + + )} +
+ {bank.name} +
+
+
+ {mangoAccount + ? formatDecimal( + mangoAccount.getTokenBalanceUi(bank), + bank.mintDecimals + ) + : 0} +
+
+ {spotBalances[bank.mint.toString()]?.inOrders || 0.0} + + {spotBalances[bank.mint.toString()]?.unsettled || 0.0} +
+ ) +} + +export default UnsettledTrades From 9afe04bde7658eb310a24123af7b3e15e361af39 Mon Sep 17 00:00:00 2001 From: tjs Date: Wed, 28 Sep 2022 16:44:49 -0400 Subject: [PATCH 04/10] add open orders count --- components/trade/TradeInfoTabs.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/trade/TradeInfoTabs.tsx b/components/trade/TradeInfoTabs.tsx index ce4ff0a2..6d8df63b 100644 --- a/components/trade/TradeInfoTabs.tsx +++ b/components/trade/TradeInfoTabs.tsx @@ -3,15 +3,19 @@ import TabButtons from '@components/shared/TabButtons' import OpenOrders from './OpenOrders' import Balances from './TradeBalances' import UnsettledTrades from './UnsettledTrades' - -const TABS = ['Balances', 'Orders', 'Unsettled P&L'] +import mangoStore from '@store/mangoStore' const TradeInfoTabs = () => { const [selectedTab, setSelectedTab] = useState('Balances') + const openOrders = mangoStore((s) => s.mangoAccount.openOrders) const tabsWithCount: [string, number][] = useMemo(() => { - return TABS.map((t) => [t, 0]) - }, []) + return [ + ['Balances', 0], + ['Orders', Object.values(openOrders).flat().length], + ['Unsettled P&L', 0], + ] + }, [openOrders]) return (
From 0b07ded8a5035bc27d7a46272c4957e04adb78f8 Mon Sep 17 00:00:00 2001 From: tjs Date: Wed, 28 Sep 2022 21:50:02 -0400 Subject: [PATCH 05/10] add settle funds --- components/trade/TradeInfoTabs.tsx | 2 +- components/trade/UnsettledTrades.tsx | 162 ++++++++++++++++----------- 2 files changed, 97 insertions(+), 67 deletions(-) diff --git a/components/trade/TradeInfoTabs.tsx b/components/trade/TradeInfoTabs.tsx index 6d8df63b..129ab9db 100644 --- a/components/trade/TradeInfoTabs.tsx +++ b/components/trade/TradeInfoTabs.tsx @@ -13,7 +13,7 @@ const TradeInfoTabs = () => { return [ ['Balances', 0], ['Orders', Object.values(openOrders).flat().length], - ['Unsettled P&L', 0], + ['Unsettled', 0], ] }, [openOrders]) diff --git a/components/trade/UnsettledTrades.tsx b/components/trade/UnsettledTrades.tsx index 00804658..365a2312 100644 --- a/components/trade/UnsettledTrades.tsx +++ b/components/trade/UnsettledTrades.tsx @@ -1,96 +1,126 @@ -import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid' import mangoStore from '@store/mangoStore' import { useTranslation } from 'next-i18next' -import Image from 'next/image' -import { useMemo } from 'react' -import { formatDecimal } from 'utils/numbers' +import { useCallback, useMemo } from 'react' +import { PublicKey } from '@solana/web3.js' +import { toUiDecimals } from '@blockworks-foundation/mango-v4' +import Button from '@components/shared/Button' +import { notify } from 'utils/notifications' const UnsettledTrades = () => { const { t } = useTranslation('common') const mangoAccount = mangoStore((s) => s.mangoAccount.current) - const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances) + const openOrdersAccounts = + mangoStore.getState().mangoAccount.openOrderAccounts const group = mangoStore((s) => s.group) - const jupiterTokens = mangoStore((s) => s.jupiterTokens) + // const jupiterTokens = mangoStore((s) => s.jupiterTokens) - const banks = useMemo(() => { - if (group) { - const rawBanks = Array.from(group?.banksMapByName, ([key, value]) => ({ - key, - value, - })) - const sortedBanks = mangoAccount - ? rawBanks.sort( - (a, b) => - Math.abs( - mangoAccount?.getTokenBalanceUi(b.value[0]) * - b.value[0].uiPrice! - ) - - Math.abs( - mangoAccount?.getTokenBalanceUi(a.value[0]) * - a.value[0].uiPrice! - ) - ) - : rawBanks + const unsettledSpotBalances = useMemo(() => { + if (!group || !mangoAccount) return {} + const unsettledBalances: Record = + {} + mangoAccount.serum3Active().forEach((serumMarket) => { + const market = group.getSerum3MarketByIndex(serumMarket.marketIndex)! + const openOrdersAccForMkt = openOrdersAccounts.find((oo) => + oo.market.equals(market.serumMarketExternal) + ) + const baseTokenUnsettled = toUiDecimals( + openOrdersAccForMkt!.baseTokenFree.toNumber(), + group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex).mintDecimals + ) + const quoteTokenUnsettled = toUiDecimals( + openOrdersAccForMkt!.quoteTokenFree + // @ts-ignore + .add(openOrdersAccForMkt['referrerRebatesAccrued']) + .toNumber(), + group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex).mintDecimals + ) + unsettledBalances[market.serumMarketExternal.toString()] = { + base: baseTokenUnsettled, + quote: quoteTokenUnsettled, + } + }) - return mangoAccount - ? sortedBanks.filter( - (b) => mangoAccount?.getTokenBalanceUi(b.value[0]) !== 0 - ) - : sortedBanks + const filtered = Object.entries(unsettledBalances).filter( + ([_mkt, balance]) => balance.base > 0 || balance.quote > 0 + ) + return Object.fromEntries(filtered)! + }, [mangoAccount, group, openOrdersAccounts]) + + const handleSettleFunds = useCallback(async (mktAddress: string) => { + const client = mangoStore.getState().client + const group = mangoStore.getState().group + const mangoAccount = mangoStore.getState().mangoAccount.current + const actions = mangoStore.getState().actions + + if (!group || !mangoAccount) return + + try { + const txid = await client.serum3SettleFunds( + group, + mangoAccount, + new PublicKey(mktAddress) + ) + actions.fetchSerumOpenOrders() + actions.reloadMangoAccount() + notify({ + type: 'success', + title: 'Successfully settled funds', + txid, + }) + } catch (e: any) { + notify({ + type: 'error', + title: 'Settle transaction failed', + description: e?.message, + txid: e?.txid, + }) + console.error('Settle funds error:', e) } - return [] - }, [group, mangoAccount]) + }, []) + + if (!group) return null + + // console.log('unsettledSpotBalances', unsettledSpotBalances) return ( - - - - + + + + - {banks.map(({ key, value }) => { - const bank = value[0] - - let logoURI - if (jupiterTokens.length) { - logoURI = jupiterTokens.find( - (t) => t.address === bank.mint.toString() - )!.logoURI - } + {Object.entries(unsettledSpotBalances).map(([mktAddress, balance]) => { + const market = group.getSerum3MarketByPk(new PublicKey(mktAddress)) + console.log('market', mktAddress) + const base = market?.name.split('/')[0] + const quote = market?.name.split('/')[1] return ( - + - + ) From 511d5c9dbdb85beec1abc4bc9fc5ea6eb31a237d Mon Sep 17 00:00:00 2001 From: tjs Date: Thu, 29 Sep 2022 16:00:36 -0400 Subject: [PATCH 06/10] add cummulative size percent --- components/trade/Orderbook.tsx | 71 ++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/components/trade/Orderbook.tsx b/components/trade/Orderbook.tsx index a17ec50b..ced7471b 100644 --- a/components/trade/Orderbook.tsx +++ b/components/trade/Orderbook.tsx @@ -72,6 +72,7 @@ type cumOrderbookSide = { cumulativeSize: number sizePercent: number maxSizePercent: number + cumulativeSizePercent: number } const getCumulativeOrderbookSide = ( @@ -79,7 +80,7 @@ const getCumulativeOrderbookSide = ( totalSize: number, maxSize: number, depth: number, - backwards = false + isBids = true ): cumOrderbookSide[] => { let cumulative = orders .slice(0, depth) @@ -90,13 +91,15 @@ const getCumulativeOrderbookSide = ( size, cumulativeSize, sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100), + cumulativeSizePercent: Math.round((size / (cumulativeSize || 1)) * 100), maxSizePercent: Math.round((size / (maxSize || 1)) * 100), }) return cumulative }, []) - if (backwards) { - cumulative = cumulative.reverse() + if (!isBids) { + console.log('cumulative', cumulative) } + return cumulative } @@ -245,30 +248,30 @@ const Orderbook = () => { const sum = (total: number, [, size]: number[], index: number) => index < depth ? total + size : total - const totalBidSize = bids.reduce(sum, 0) - const totalAskSize = asks.reduce(sum, 0) - const maxBidSize = Math.max( - ...bids.map((b: number[]) => { - return b[1] - }) - ) - const maxAskSize = Math.max( - ...asks.map((a: number[]) => { - return a[1] - }) - ) + const totalSize = bids.reduce(sum, 0) + asks.reduce(sum, 0) + + const maxSize = + Math.max( + ...bids.map((b: number[]) => { + return b[1] + }) + ) + + Math.max( + ...asks.map((a: number[]) => { + return a[1] + }) + ) const bidsToDisplay = getCumulativeOrderbookSide( bids, - totalBidSize, - maxBidSize, - depth, - false + totalSize, + maxSize, + depth ) const asksToDisplay = getCumulativeOrderbookSide( asks, - totalAskSize, - maxAskSize, + totalSize, + maxSize, depth, false ) @@ -444,6 +447,9 @@ const Orderbook = () => { size={orderbookData?.asks[index].size} side="sell" sizePercent={orderbookData?.asks[index].sizePercent} + cumulativeSizePercent={ + orderbookData?.asks[index].cumulativeSizePercent + } grouping={grouping} /> ) : null} @@ -480,6 +486,9 @@ const Orderbook = () => { size={orderbookData?.bids[index].size} side="buy" sizePercent={orderbookData?.bids[index].sizePercent} + cumulativeSizePercent={ + orderbookData?.bids[index].cumulativeSizePercent + } grouping={grouping} /> ) : null} @@ -499,6 +508,7 @@ const OrderbookRow = ({ // invert, // hasOpenOrder, minOrderSize, + cumulativeSizePercent, tickSize, grouping, }: { @@ -506,6 +516,7 @@ const OrderbookRow = ({ price: number size: number sizePercent: number + cumulativeSizePercent: number // hasOpenOrder: boolean // invert: boolean grouping: number @@ -573,11 +584,11 @@ const OrderbookRow = ({ onClick={handlePriceClick} > <> -
+
-
+
{formattedPrice.toFixed(groupingDecimalCount)}
+
From d3802e80aeca971e023b2acf2b212a2f62adc303 Mon Sep 17 00:00:00 2001 From: tjs Date: Thu, 29 Sep 2022 16:01:30 -0400 Subject: [PATCH 07/10] update theme colors for orderbook --- pages/settings.tsx | 2 +- tailwind.config.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/settings.tsx b/pages/settings.tsx index d31978c9..a170e3ec 100644 --- a/pages/settings.tsx +++ b/pages/settings.tsx @@ -62,7 +62,7 @@ const Settings: NextPage = () => { EXPLORERS[0] ) const themes = useMemo(() => { - return [t('settings:light'), t('settings:dark'), t('settings:mango')] + return [t('settings:light'), t('settings:mango'), t('settings:dark')] }, [t]) const handleLangChange = useCallback( diff --git a/tailwind.config.js b/tailwind.config.js index 8afb0ece..232e2a88 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -66,10 +66,10 @@ module.exports = { red: { DEFAULT: '#F84638', dark: '#C7251A', muted: '#6d2832' }, green: { DEFAULT: '#AFD803', dark: '#91B503', muted: '#49601b' }, orange: { DEFAULT: '#FF9C24' }, - 'bkg-1': '#141026', - 'bkg-2': '#1D1832', - 'bkg-3': '#2A2440', - 'bkg-4': '#37324D', + 'bkg-1': '#1C1924', // '#141026', + 'bkg-2': '#252232', // '#1D1832', + 'bkg-3': '#302B40', // '#2A2440', + 'bkg-4': '#383544', // '#37324D', 'fgd-1': '#E5E3EC', 'fgd-2': '#D2CEDE', 'fgd-3': '#C1BED3', From 5d07a1d9b4ab245896102f0fadf45181274bcc0c Mon Sep 17 00:00:00 2001 From: tjs Date: Thu, 29 Sep 2022 16:06:04 -0400 Subject: [PATCH 08/10] add minimum for cumulative size percent --- components/trade/Orderbook.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/trade/Orderbook.tsx b/components/trade/Orderbook.tsx index ced7471b..12ed0085 100644 --- a/components/trade/Orderbook.tsx +++ b/components/trade/Orderbook.tsx @@ -611,7 +611,9 @@ const OrderbookRow = ({ className={`absolute left-0 opacity-70 ${ side === 'buy' ? `bg-th-green` : `bg-th-red` }`} - data-width={(cumulativeSizePercent / 100) * sizePercent + '%'} + data-width={ + Math.max((cumulativeSizePercent / 100) * sizePercent, 0.1) + '%' + } />
From fc933758dc0ced8650247b140fdec9b86b4fe375 Mon Sep 17 00:00:00 2001 From: tjs Date: Thu, 29 Sep 2022 16:27:11 -0400 Subject: [PATCH 09/10] slightly more room for balances --- components/trade/TradeAdvancedPage.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/trade/TradeAdvancedPage.tsx b/components/trade/TradeAdvancedPage.tsx index 2601c80b..0861b505 100644 --- a/components/trade/TradeAdvancedPage.tsx +++ b/components/trade/TradeAdvancedPage.tsx @@ -58,13 +58,13 @@ const TradeAdvancedPage = () => { return { xxxl: [ { i: 'market-header', x: 0, y: 0, w: 16, h: marketHeaderHeight }, - { i: 'tv-chart', x: 0, y: 1, w: 16, h: 676 }, + { i: 'tv-chart', x: 0, y: 1, w: 16, h: 640 }, { i: 'balances', x: 0, y: 2, w: 16, - h: getHeight(innerHeight, 300, 676 + marketHeaderHeight), + h: getHeight(innerHeight, 300, 640 + marketHeaderHeight), }, { i: 'orderbook', @@ -77,13 +77,13 @@ const TradeAdvancedPage = () => { ], xxl: [ { i: 'market-header', x: 0, y: 0, w: 15, h: marketHeaderHeight }, - { i: 'tv-chart', x: 0, y: 1, w: 15, h: 576 }, + { i: 'tv-chart', x: 0, y: 1, w: 15, h: 536 }, { i: 'balances', x: 0, y: 2, w: 15, - h: getHeight(innerHeight, 300, 576 + marketHeaderHeight), + h: getHeight(innerHeight, 300, 536 + marketHeaderHeight), }, { i: 'orderbook', @@ -96,13 +96,13 @@ const TradeAdvancedPage = () => { ], xl: [ { i: 'market-header', x: 0, y: 0, w: 14, h: marketHeaderHeight }, - { i: 'tv-chart', x: 0, y: 1, w: 14, h: 520 }, + { i: 'tv-chart', x: 0, y: 1, w: 14, h: 488 }, { i: 'balances', x: 0, y: 2, w: 14, - h: getHeight(innerHeight, 300, 520 + marketHeaderHeight), + h: getHeight(innerHeight, 300, 488 + marketHeaderHeight), }, { i: 'orderbook', @@ -115,13 +115,13 @@ const TradeAdvancedPage = () => { ], lg: [ { i: 'market-header', x: 0, y: 0, w: 14, h: marketHeaderHeight }, - { i: 'tv-chart', x: 0, y: 1, w: 14, h: 520 }, + { i: 'tv-chart', x: 0, y: 1, w: 14, h: 488 }, { i: 'balances', x: 0, y: 2, w: 14, - h: getHeight(innerHeight, 300, 520 + marketHeaderHeight), + h: getHeight(innerHeight, 300, 488 + marketHeaderHeight), }, { i: 'orderbook', @@ -134,10 +134,10 @@ const TradeAdvancedPage = () => { ], md: [ { i: 'market-header', x: 0, y: 0, w: 18, h: marketHeaderHeight }, - { i: 'tv-chart', x: 0, y: 1, w: 18, h: 520 }, + { i: 'tv-chart', x: 0, y: 1, w: 18, h: 488 }, { i: 'balances', x: 0, y: 2, w: 18, h: 488 }, - { i: 'orderbook', x: 18, y: 2, w: 6, h: 489 }, - { i: 'trade-form', x: 18, y: 1, w: 6, h: 568 }, + { i: 'orderbook', x: 18, y: 2, w: 6, h: 488 }, + { i: 'trade-form', x: 18, y: 1, w: 6, h: 488 }, ], } }, [height]) From 32578711b28896acf117eeed11530417915159eb Mon Sep 17 00:00:00 2001 From: tjs Date: Fri, 30 Sep 2022 00:21:23 -0400 Subject: [PATCH 10/10] general refinements --- components/Layout.tsx | 4 +-- components/MangoAccountsList.tsx | 4 +-- components/OnboardingTour.tsx | 2 +- components/SideNav.tsx | 32 ++++++++--------------- components/mobile/BottomBar.tsx | 24 ++++++++++------- components/shared/IconDropMenu.tsx | 6 ++--- components/shared/Modal.tsx | 4 +-- components/shared/Notification.tsx | 8 ++++-- components/shared/TabButtons.tsx | 13 +++++++-- components/shared/Transitions.tsx | 4 +-- components/swap/SwapForm.tsx | 2 +- components/swap/SwapFormTokenList.tsx | 27 ++++++++++--------- components/swap/SwapSettings.tsx | 2 +- components/trade/AdvancedMarketHeader.tsx | 4 +-- components/trade/RecentTrades.tsx | 2 +- components/trade/TradingViewChart.tsx | 1 + styles/colors.ts | 2 +- styles/globals.css | 3 +-- tailwind.config.js | 2 +- utils/constants.ts | 2 +- 20 files changed, 79 insertions(+), 69 deletions(-) diff --git a/components/Layout.tsx b/components/Layout.tsx index cef9d66d..21653440 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -35,7 +35,7 @@ const Layout = ({ children }: { children: ReactNode }) => { }, [width]) useEffect(() => { - const animationFrames = 5 + const animationFrames = 15 for (let x = 1; x <= animationFrames; x++) { setTimeout(() => { @@ -55,7 +55,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
) : null} -
+
diff --git a/components/MangoAccountsList.tsx b/components/MangoAccountsList.tsx index de3b7b09..f7faee25 100644 --- a/components/MangoAccountsList.tsx +++ b/components/MangoAccountsList.tsx @@ -76,7 +76,7 @@ const MangoAccountsList = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > - + {loading ? ( ) : mangoAccounts.length ? ( @@ -84,7 +84,7 @@ const MangoAccountsList = ({

{title}

{description}

diff --git a/components/SideNav.tsx b/components/SideNav.tsx index 8f5e5ac5..b6ebc118 100644 --- a/components/SideNav.tsx +++ b/components/SideNav.tsx @@ -42,7 +42,7 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => { return (
@@ -50,7 +50,7 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
@@ -323,26 +323,16 @@ export const ExpandableMenuItem = ({ {icon}
- - - {children} - - + {children} +
) : ( diff --git a/components/mobile/BottomBar.tsx b/components/mobile/BottomBar.tsx index 930dc182..69fcb964 100644 --- a/components/mobile/BottomBar.tsx +++ b/components/mobile/BottomBar.tsx @@ -12,6 +12,7 @@ import { ChevronRightIcon, CurrencyDollarIcon as FeesIcon, LightBulbIcon, + ArrowsRightLeftIcon, } from '@heroicons/react/20/solid' const StyledBarItemLabel = ({ @@ -58,18 +59,18 @@ const BottomBar = () => { asPath === '/swap' ? 'text-th-primary' : 'text-th-fgd-3' } col-span-1 flex cursor-pointer flex-col items-center`} > - - {t('trade')} + + {t('swap')} - + - - {t('stats')} + + {t('trade')}
setShowPanel(false)} hideBg> - +
setShowPanel(false)} > + } + /> { const classNames = - 'default-transition flex w-full items-center justify-between border-t border-th-bkg-4 px-2 py-3 text-th-fgd-2 hover:text-th-fgd-1' + 'default-transition flex w-full items-center justify-between border-t border-th-bkg-4 px-2 py-4 text-th-fgd-2 hover:text-th-fgd-1' return isExternal ? ( - {open ? : icon} + {open ? : icon}
@@ -39,7 +39,7 @@ function Modal({ onClick={onClose} className={`absolute right-4 top-4 z-50 text-th-fgd-4 focus:outline-none md:right-2 md:top-2 md:hover:text-th-primary`} > - + ) : null} {title} diff --git a/components/shared/Notification.tsx b/components/shared/Notification.tsx index 2b23a1f5..e5833f4c 100644 --- a/components/shared/Notification.tsx +++ b/components/shared/Notification.tsx @@ -106,8 +106,10 @@ const Notification = ({ notification }: { notification: Notification }) => { hideNotification() } }, - parsedTitle || type === 'confirm' || type === 'error' + parsedTitle || type === 'confirm' ? CLIENT_TX_TIMEOUT + : type === 'error' + ? 30000 : 8000 ) @@ -151,7 +153,9 @@ const Notification = ({ notification }: { notification: Notification }) => { {parsedTitle || title}

{description ? ( -

+

{description}

) : null} diff --git a/components/shared/TabButtons.tsx b/components/shared/TabButtons.tsx index 53fbd0e1..8269995c 100644 --- a/components/shared/TabButtons.tsx +++ b/components/shared/TabButtons.tsx @@ -29,7 +29,7 @@ const TabButtons: FunctionComponent = ({ {values.map(([label, count], i) => (
))} diff --git a/components/shared/Transitions.tsx b/components/shared/Transitions.tsx index ffb3428c..5fc1940a 100644 --- a/components/shared/Transitions.tsx +++ b/components/shared/Transitions.tsx @@ -1,8 +1,8 @@ import { Transition } from '@headlessui/react' import { CSSProperties, ReactNode } from 'react' -const transitionEnterStyle = 'transition-all ease-in duration-300' -const transitionExitStyle = 'transition-all ease-out duration-300' +const transitionEnterStyle = 'transition-all ease-out duration-500' +const transitionExitStyle = 'transition-all ease-in duration-300' export const EnterRightExitLeft = ({ children, diff --git a/components/swap/SwapForm.tsx b/components/swap/SwapForm.tsx index 70607aff..b5ac8975 100644 --- a/components/swap/SwapForm.tsx +++ b/components/swap/SwapForm.tsx @@ -287,7 +287,7 @@ const SwapForm = () => { /> ) : null}
-
+
{t('token')}{t('balance')}{t('in-orders')}{t('unsettled')}MarketBaseQuote
-
- {logoURI ? ( - - ) : ( - - )} -
- {bank.name} -
-
-
- {mangoAccount - ? formatDecimal( - mangoAccount.getTokenBalanceUi(bank), - bank.mintDecimals - ) - : 0} + {market ? market.name : ''}
- {spotBalances[bank.mint.toString()]?.inOrders || 0.0} + {unsettledSpotBalances[mktAddress].base || 0.0} {base} - {spotBalances[bank.mint.toString()]?.unsettled || 0.0} + {unsettledSpotBalances[mktAddress].quote || 0.0} {quote} + +