diff --git a/apis/coingecko.ts b/apis/coingecko.ts index f1a59fd3..428e9be0 100644 --- a/apis/coingecko.ts +++ b/apis/coingecko.ts @@ -1,7 +1,7 @@ export const fetchChartData = async ( baseTokenId: string | undefined, quoteTokenId: string | undefined, - daysToShow: number + daysToShow: string ) => { console.log('fetching chart:', baseTokenId, quoteTokenId) diff --git a/components/account/AccountChart.tsx b/components/account/AccountChart.tsx index 06b47b84..90434550 100644 --- a/components/account/AccountChart.tsx +++ b/components/account/AccountChart.tsx @@ -26,13 +26,13 @@ const AccountChart = ({ }) => { const { t } = useTranslation('common') const actions = mangoStore((s) => s.actions) - const [daysToShow, setDaysToShow] = useState(1) + const [daysToShow, setDaysToShow] = useState('1') const loading = mangoStore((s) => s.mangoAccount.stats.performance.loading) - const handleDaysToShow = async (days: number) => { + const handleDaysToShow = async (days: string) => { await actions.fetchAccountPerformance( mangoAccount.publicKey.toString(), - days + parseInt(days) ) setDaysToShow(days) } diff --git a/components/account/ActivityFeed.tsx b/components/account/ActivityFeed.tsx index 0c9998ea..1f792e95 100644 --- a/components/account/ActivityFeed.tsx +++ b/components/account/ActivityFeed.tsx @@ -4,11 +4,11 @@ import Input from '@components/forms/Input' import Label from '@components/forms/Label' import MultiSelectDropdown from '@components/forms/MultiSelectDropdown' import { EXPLORERS } from '@components/settings/PreferredExplorerSettings' -import Button, { IconButton, LinkButton } from '@components/shared/Button' -import Modal from '@components/shared/Modal' +import Button, { IconButton } from '@components/shared/Button' import Tooltip from '@components/shared/Tooltip' import { Disclosure, Transition } from '@headlessui/react' import { + AdjustmentsVerticalIcon, ArrowLeftIcon, ArrowPathIcon, ChevronDownIcon, @@ -18,18 +18,17 @@ import dayjs from 'dayjs' import useLocalStorageState from 'hooks/useLocalStorageState' import useMangoAccount from 'hooks/useMangoAccount' import useMangoGroup from 'hooks/useMangoGroup' -import { useViewport } from 'hooks/useViewport' import { useTranslation } from 'next-i18next' import Image from 'next/legacy/image' import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react' import { PREFERRED_EXPLORER_KEY } from 'utils/constants' import { formatDecimal, formatFixedDecimals } from 'utils/numbers' -import { breakpoints } from 'utils/theme' import ActivityFeedTable from './ActivityFeedTable' interface Filters { deposit: boolean liquidate_token_with_token: boolean + perp_trade: boolean swap: boolean withdraw: boolean } @@ -45,6 +44,7 @@ interface AdvancedFilters { const DEFAULT_FILTERS = { deposit: true, liquidate_token_with_token: true, + perp_trade: true, swap: true, withdraw: true, } @@ -61,6 +61,7 @@ const DEFAULT_PARAMS = [ 'deposit', 'liquidate_token_with_token', 'swap', + 'perp_trade', 'withdraw', ] @@ -111,7 +112,7 @@ const ActivityFeed = () => { }, [advancedFilters]) const queryParams = useMemo(() => { - return params.length === 3 + return params.length === 5 ? advancedParamsString : `&activity-type=${params.toString()}${advancedParamsString}` }, [advancedParamsString, params]) @@ -123,6 +124,7 @@ const ActivityFeed = () => { setFilters={setFilters} updateFilters={updateFilters} params={queryParams} + setParams={setParams} advancedFilters={advancedFilters} setAdvancedFilters={setAdvancedFilters} /> @@ -147,6 +149,7 @@ const ActivityFilters = ({ setFilters, updateFilters, params, + setParams, advancedFilters, setAdvancedFilters, }: { @@ -154,6 +157,7 @@ const ActivityFilters = ({ setFilters: (x: Filters) => void updateFilters: (e: ChangeEvent, filter: string) => void params: string + setParams: (x: string[]) => void advancedFilters: AdvancedFilters setAdvancedFilters: (x: AdvancedFilters) => void }) => { @@ -161,10 +165,6 @@ const ActivityFilters = ({ const actions = mangoStore((s) => s.actions) const loadActivityFeed = mangoStore((s) => s.activityFeed.loading) const { mangoAccount } = useMangoAccount() - const [showAdvancedFiltersModal, setShowAdvancedFiltersModal] = - useState(false) - const { width } = useViewport() - const isMobile = width ? width < breakpoints.lg : false const [showMobileFilters, setShowMobileFilters] = useState(false) const [hasFilters, setHasFilters] = useState(false) @@ -197,148 +197,85 @@ const ActivityFilters = ({ await actions.fetchActivityFeed(mangoAccount.publicKey.toString()) setAdvancedFilters(DEFAULT_ADVANCED_FILTERS) setFilters(DEFAULT_FILTERS) + setParams(DEFAULT_PARAMS) } }, [actions]) - const handleUpdateModalResults = () => { - handleUpdateResults() - setShowAdvancedFiltersModal(false) - } - const handleUpdateMobileResults = () => { handleUpdateResults() setShowMobileFilters(false) } return mangoAccount ? ( - !isMobile ? ( - <> -
-

- {t('activity:filter-results')} -

- -
- setShowAdvancedFiltersModal(true)} - > - {t('activity:advanced-filters')} - - {hasFilters ? ( - - handleResetFilters()} - size="small" - > - - - - ) : null} + +
+ {hasFilters ? ( +
+ + handleResetFilters()} + size="small" + > + + +
- -
- {showAdvancedFiltersModal ? ( - setShowAdvancedFiltersModal(false)} - > -

- {t('activity:advanced-filters')} -

- - -
) : null} - - ) : ( - -
- {hasFilters ? ( -
- - handleResetFilters()} - size="small" - > - - - -
- ) : null} -
setShowMobileFilters(!showMobileFilters)} - role="button" - className={`default-transition w-full border-b border-th-bkg-3 bg-th-bkg-2 p-4 hover:bg-th-bkg-3`} +
setShowMobileFilters(!showMobileFilters)} + role="button" + className={`default-transition w-full border-b border-th-bkg-3 bg-th-bkg-2 p-4 hover:bg-th-bkg-3 md:px-6`} + > + - +
+ {t('activity:filter-results')} - - - -
-
- - -
-
- - -
-
- - ) + +
+
+ + +
+
+ + +
+
+
) : null } @@ -351,7 +288,7 @@ const ActivityTypeFiltersForm = ({ }) => { const { t } = useTranslation('activity') return ( -
+
{t('swaps')}
+
+ updateFilters(e, 'perp_trade')} + > + {t('perps')} + +
+ ) : isPerp ? ( + <> + + {activity.activity_details.taker_side === 'bid' + ? 'BUY' + : 'SELL'} + + + {activity.activity_details.quantity} + + + {activity.activity_details.perp_market} + + ) : ( <> diff --git a/components/shared/ChartRangeButtons.tsx b/components/shared/ChartRangeButtons.tsx index 192162fd..c06a3228 100644 --- a/components/shared/ChartRangeButtons.tsx +++ b/components/shared/ChartRangeButtons.tsx @@ -1,10 +1,10 @@ import { FunctionComponent } from 'react' interface ChartRangeButtonsProps { - activeValue: number + activeValue: string className?: string - onChange: (x: number) => void - values: Array + onChange: (x: string) => void + values: Array names?: Array } @@ -20,7 +20,7 @@ const ChartRangeButtons: FunctionComponent = ({
{activeValue && values.includes(activeValue) ? (
v === activeValue) * 100 diff --git a/components/shared/DetailedAreaChart.tsx b/components/shared/DetailedAreaChart.tsx index ff56c8b2..5b658f9b 100644 --- a/components/shared/DetailedAreaChart.tsx +++ b/components/shared/DetailedAreaChart.tsx @@ -30,11 +30,11 @@ dayjs.extend(relativeTime) interface DetailedAreaChartProps { data: any[] - daysToShow: number + daysToShow: string hideChange?: boolean hideChart?: () => void loading?: boolean - setDaysToShow: (x: number) => void + setDaysToShow: (x: string) => void tickFormat?: (x: any) => string title?: string xKey: string @@ -51,7 +51,7 @@ export const formatDateAxis = (date: string, days: number) => { const DetailedAreaChart: FunctionComponent = ({ data, - daysToShow = 1, + daysToShow = '1', hideChange, hideChart, loading, @@ -183,7 +183,7 @@ const DetailedAreaChart: FunctionComponent = ({ setDaysToShow(v)} />
@@ -252,7 +252,9 @@ const DetailedAreaChart: FunctionComponent = ({ fontSize: 10, }} tickLine={false} - tickFormatter={(d) => formatDateAxis(d, daysToShow)} + tickFormatter={(d) => + formatDateAxis(d, parseInt(daysToShow)) + } /> { + const [favoriteMarkets, setFavoriteMarkets] = useLocalStorageState( + FAVORITE_MARKETS_KEY, + [] + ) + + const addToFavorites = (marketName: string) => { + const newFavorites: any = [...favoriteMarkets, marketName] + setFavoriteMarkets(newFavorites) + } + + const removeFromFavorites = (marketName: string) => { + setFavoriteMarkets(favoriteMarkets.filter((m: string) => m !== marketName)) + } + + return favoriteMarkets.find( + (marketName: string) => marketName === market.name + ) ? ( + + ) : ( + + ) +} + +export default FavoriteMarketButton diff --git a/components/swap/SwapTokenChart.tsx b/components/swap/SwapTokenChart.tsx index 21d4288d..b807d0c1 100644 --- a/components/swap/SwapTokenChart.tsx +++ b/components/swap/SwapTokenChart.tsx @@ -76,7 +76,7 @@ const SwapTokenChart = () => { const [baseTokenId, setBaseTokenId] = useState(inputCoingeckoId) const [quoteTokenId, setQuoteTokenId] = useState(outputCoingeckoId) const [mouseData, setMouseData] = useState(null) - const [daysToShow, setDaysToShow] = useState(1) + const [daysToShow, setDaysToShow] = useState('1') const { theme } = useTheme() const [animationSettings] = useLocalStorageState( ANIMATION_SETTINGS_KEY, @@ -245,7 +245,7 @@ const SwapTokenChart = () => { setDaysToShow(v)} />
diff --git a/components/trade/AdvancedMarketHeader.tsx b/components/trade/AdvancedMarketHeader.tsx index 7e77ac01..7b45e34c 100644 --- a/components/trade/AdvancedMarketHeader.tsx +++ b/components/trade/AdvancedMarketHeader.tsx @@ -1,115 +1,13 @@ +import { PerpMarket } from '@blockworks-foundation/mango-v4' import Change from '@components/shared/Change' -import TabUnderline from '@components/shared/TabUnderline' -import { Popover } from '@headlessui/react' -import { ChevronDownIcon } from '@heroicons/react/20/solid' -import mangoStore from '@store/mangoStore' import { useCoingecko } from 'hooks/useCoingecko' import useOraclePrice from 'hooks/useOraclePrice' import useSelectedMarket from 'hooks/useSelectedMarket' import { useTranslation } from 'next-i18next' -import Link from 'next/link' -import { useMemo, useState } from 'react' -import { DEFAULT_MARKET_NAME } from 'utils/constants' +import { useMemo } from 'react' import { formatFixedDecimals } from 'utils/numbers' -import MarketLogos from './MarketLogos' - -const MarketSelectDropdown = () => { - const { selectedMarket } = useSelectedMarket() - const serumMarkets = mangoStore((s) => s.serumMarkets) - const perpMarkets = mangoStore((s) => s.perpMarkets) - const [activeTab, setActiveTab] = useState('perp') - - return ( - - {({ open }) => ( -
- - <> - {selectedMarket ? : null} - -
- {selectedMarket?.name || DEFAULT_MARKET_NAME} -
- -
- - setActiveTab(v)} - small - values={['perp', 'spot']} - /> - {activeTab === 'spot' - ? serumMarkets?.length - ? serumMarkets.map((m) => { - return ( - -
- - - {m.name} - -
- - ) - }) - : null - : null} - {activeTab === 'perp' - ? perpMarkets?.length - ? perpMarkets.map((m) => { - return ( - -
- - - {m.name} - -
- - ) - }) - : null - : null} -
-
- )} -
- ) -} +import MarketSelectDropdown from './MarketSelectDropdown' +import PerpFundingRate from './PerpFundingRate' const AdvancedMarketHeader = () => { const { t } = useTranslation(['common', 'trade']) @@ -154,14 +52,13 @@ const AdvancedMarketHeader = () => {
{t('rolling-change')}
- {/*
- {isNaN(change) ? '0.00' : change.toFixed(2)}% -
*/}
+ {selectedMarket instanceof PerpMarket ? ( +
+
Funding Rate
+ +
+ ) : null}
) } diff --git a/components/trade/FavoriteMarketsBar.tsx b/components/trade/FavoriteMarketsBar.tsx new file mode 100644 index 00000000..c412bd5f --- /dev/null +++ b/components/trade/FavoriteMarketsBar.tsx @@ -0,0 +1,77 @@ +import { Transition } from '@headlessui/react' +import { StarIcon } from '@heroicons/react/20/solid' +import useLocalStorageState from 'hooks/useLocalStorageState' +import useMangoGroup from 'hooks/useMangoGroup' +import useSelectedMarket from 'hooks/useSelectedMarket' +import { useViewport } from 'hooks/useViewport' +import Link from 'next/link' +import { useRouter } from 'next/router' +import { FAVORITE_MARKETS_KEY } from 'utils/constants' +import { breakpoints } from 'utils/theme' +import MarketLogos from './MarketLogos' + +const FavoriteMarketsBar = () => { + const [favoriteMarkets] = useLocalStorageState(FAVORITE_MARKETS_KEY, []) + const { width } = useViewport() + const isMobile = width ? width < breakpoints.sm : false + const { asPath } = useRouter() + const { selectedMarket } = useSelectedMarket() + const { group } = useMangoGroup() + + return !isMobile ? ( + + + {favoriteMarkets.map((mkt: string) => { + // const change24h = marketsInfo?.find((m) => m.name === mkt)?.change24h + const isPerp = mkt.includes('PERP') + let market + if (isPerp) { + market = group?.getPerpMarketByName(mkt) + } else { + market = group?.getSerum3MarketByName(mkt) + } + return ( + +
+ {market ? : null} + {mkt} + {/* {change24h ? ( +
= 0 + ? 'text-th-green' + : 'text-th-red' + : 'text-th-fgd-4' + }`} + > + {`${(change24h * 100).toFixed(1)}%`} +
+ ) : null} */} +
+ + ) + })} +
+ ) : null +} + +export default FavoriteMarketsBar diff --git a/components/trade/MarketLogos.tsx b/components/trade/MarketLogos.tsx index e3db8248..acd1f6c6 100644 --- a/components/trade/MarketLogos.tsx +++ b/components/trade/MarketLogos.tsx @@ -5,7 +5,13 @@ import Image from 'next/legacy/image' import { useMemo } from 'react' import useMangoGroup from 'hooks/useMangoGroup' -const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => { +const MarketLogos = ({ + market, + small, +}: { + market: Serum3Market | PerpMarket + small?: boolean +}) => { const { group } = useMangoGroup() const { mangoTokens } = useJupiterMints() @@ -39,8 +45,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => { return (
@@ -48,12 +60,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => { ) : ( - + )}
@@ -61,12 +75,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => { ) : market instanceof PerpMarket ? null : ( - + )}
diff --git a/components/trade/MarketSelectDropdown.tsx b/components/trade/MarketSelectDropdown.tsx new file mode 100644 index 00000000..6a6839ac --- /dev/null +++ b/components/trade/MarketSelectDropdown.tsx @@ -0,0 +1,153 @@ +import ChartRangeButtons from '@components/shared/ChartRangeButtons' +import FavoriteMarketButton from '@components/shared/FavoriteMarketButton' +import TabUnderline from '@components/shared/TabUnderline' +import { Popover } from '@headlessui/react' +import { ChevronDownIcon } from '@heroicons/react/20/solid' +import mangoStore from '@store/mangoStore' +import useSelectedMarket from 'hooks/useSelectedMarket' +import Link from 'next/link' +import { useMemo, useState } from 'react' +import { DEFAULT_MARKET_NAME } from 'utils/constants' +import MarketLogos from './MarketLogos' + +const MarketSelectDropdown = () => { + const { selectedMarket } = useSelectedMarket() + const serumMarkets = mangoStore((s) => s.serumMarkets) + const perpMarkets = mangoStore((s) => s.perpMarkets) + const [activeTab, setActiveTab] = useState('perp') + const [spotBaseFilter, setSpotBaseFilter] = useState('All') + + const spotBaseTokens: string[] = useMemo(() => { + if (serumMarkets.length) { + const baseTokens: string[] = [] + serumMarkets.map((m) => { + const base = m.name.split('/')[1] + if (!baseTokens.includes(base)) { + baseTokens.push(base) + } + }) + return baseTokens + } + return [] + }, [serumMarkets]) + + return ( + + {({ open }) => ( +
+ + <> + {selectedMarket ? : null} + +
+ {selectedMarket?.name || DEFAULT_MARKET_NAME} +
+ +
+ + setActiveTab(v)} + small + values={['perp', 'spot']} + /> + {activeTab === 'spot' ? ( + serumMarkets?.length ? ( + <> +
+ setSpotBaseFilter(v)} + /> +
+ {serumMarkets + .filter((mkt) => { + if (spotBaseFilter === 'All') { + return mkt + } else { + return mkt.name.split('/')[1] === spotBaseFilter + } + }) + .map((m) => { + return ( +
+ +
+ + + {m.name} + +
+ + +
+ ) + })} + + ) : null + ) : null} + {activeTab === 'perp' + ? perpMarkets?.length + ? perpMarkets.map((m) => { + return ( +
+ +
+ + + {m.name} + +
+ + +
+ ) + }) + : null + : null} +
+
+ )} +
+ ) +} + +export default MarketSelectDropdown diff --git a/components/trade/Orderbook.tsx b/components/trade/Orderbook.tsx index e0de1855..f6aa54fa 100644 --- a/components/trade/Orderbook.tsx +++ b/components/trade/Orderbook.tsx @@ -25,33 +25,38 @@ import { import useSelectedMarket from 'hooks/useSelectedMarket' import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' -function decodeBookL2( +export const decodeBookL2 = (book: SpotOrderBook | BookSide): number[][] => { + const depth = 40 + if (book instanceof SpotOrderBook) { + book.getL2(depth).map(([price, size]) => [price, size]) + } else if (book instanceof BookSide) { + return book.getL2Ui(depth) + } + return [] +} + +function decodeBook( client: MangoClient, market: Market | PerpMarket, accInfo: AccountInfo, side: 'bids' | 'asks' -): number[][] { - if (market && accInfo?.data) { - const depth = 40 - if (market instanceof Market) { - const book = SpotOrderBook.decode(market, accInfo.data) - return book.getL2(depth).map(([price, size]) => [price, size]) - } else if (market instanceof PerpMarket) { - // FIXME: Review the null being passed here - const decodedAcc = client.program.coder.accounts.decode( - 'bookSide', - accInfo.data - ) - const book = BookSide.from( - client, - market, - side === 'bids' ? BookSideType.bids : BookSideType.asks, - decodedAcc - ) - return book.getL2Ui(depth) - } +): SpotOrderBook | BookSide { + if (market instanceof Market) { + const book = SpotOrderBook.decode(market, accInfo.data) + return book + } else { + const decodedAcc = client.program.coder.accounts.decode( + 'bookSide', + accInfo.data + ) + const book = BookSide.from( + client, + market, + side === 'bids' ? BookSideType.bids : BookSideType.asks, + decodedAcc + ) + return book } - return [] } // export function decodeBook( @@ -327,14 +332,10 @@ const Orderbook = () => { if (bidsPk) { connection.getAccountInfo(bidsPk).then((info) => { if (!info) return + const decodedBook = decodeBook(client, market, info, 'bids') set((state) => { - // state.accountInfos[bidsPk.toString()] = info - state.selectedMarket.orderbook.bids = decodeBookL2( - client, - market, - info, - 'bids' - ) + state.selectedMarket.bidsAccount = decodedBook + state.selectedMarket.orderbook.bids = decodeBookL2(decodedBook) }) }) console.log('bidsPk', bidsPk) @@ -347,15 +348,10 @@ const Orderbook = () => { previousBidInfo.lamports !== info.lamports ) { previousBidInfo = info - // info['parsed'] = decodeBook(serum3MarketExternal, info) + const decodedBook = decodeBook(client, market, info, 'bids') set((state) => { - // state.accountInfos[bidsPk.toString()] = info - state.selectedMarket.orderbook.bids = decodeBookL2( - client, - market, - info, - 'bids' - ) + state.selectedMarket.bidsAccount = decodedBook + state.selectedMarket.orderbook.bids = decodeBookL2(decodedBook) }) } } @@ -367,14 +363,10 @@ const Orderbook = () => { if (asksPk) { connection.getAccountInfo(asksPk).then((info) => { if (!info) return + const decodedBook = decodeBook(client, market, info, 'asks') set((state) => { - // state.accountInfos[bidsPk.toString()] = info - state.selectedMarket.orderbook.asks = decodeBookL2( - client, - market, - info, - 'bids' - ) + state.selectedMarket.asksAccount = decodedBook + state.selectedMarket.orderbook.asks = decodeBookL2(decodedBook) }) }) askSubscriptionId = connection.onAccountChange( @@ -386,15 +378,10 @@ const Orderbook = () => { previousAskInfo.lamports !== info.lamports ) { previousAskInfo = info - // info['parsed'] = decodeBook(serum3MarketExternal, info) + const decodedBook = decodeBook(client, market, info, 'asks') set((state) => { - // state.accountInfos[asksPk.toString()] = info - state.selectedMarket.orderbook.asks = decodeBookL2( - client, - market, - info, - 'asks' - ) + state.selectedMarket.asksAccount = decodedBook + state.selectedMarket.orderbook.asks = decodeBookL2(decodedBook) }) } } diff --git a/components/trade/PerpFundingRate.tsx b/components/trade/PerpFundingRate.tsx new file mode 100644 index 00000000..ea641615 --- /dev/null +++ b/components/trade/PerpFundingRate.tsx @@ -0,0 +1,21 @@ +import { BookSide, PerpMarket } from '@blockworks-foundation/mango-v4' +import mangoStore from '@store/mangoStore' +import useSelectedMarket from 'hooks/useSelectedMarket' + +const PerpFundingRate = () => { + const { selectedMarket } = useSelectedMarket() + const bids = mangoStore((s) => s.selectedMarket.bidsAccount) + const asks = mangoStore((s) => s.selectedMarket.asksAccount) + + return ( +
+ {selectedMarket instanceof PerpMarket && + bids instanceof BookSide && + asks instanceof BookSide + ? selectedMarket.getCurrentFundingRate(bids, asks) + : '-'} +
+ ) +} + +export default PerpFundingRate diff --git a/components/trade/TradeAdvancedPage.tsx b/components/trade/TradeAdvancedPage.tsx index 1013aeb3..1a8d4759 100644 --- a/components/trade/TradeAdvancedPage.tsx +++ b/components/trade/TradeAdvancedPage.tsx @@ -14,6 +14,7 @@ import MobileTradeAdvancedPage from './MobileTradeAdvancedPage' import OrderbookAndTrades from './OrderbookAndTrades' import { useWallet } from '@solana/wallet-adapter-react' import TradeOnboardingTour from '@components/tours/TradeOnboardingTour' +import FavoriteMarketsBar from './FavoriteMarketsBar' const TradingViewChart = dynamic(() => import('./TradingViewChart'), { ssr: false, @@ -149,6 +150,7 @@ const TradeAdvancedPage = () => { ) : ( <> + { const { isLoading: loadingPrices, data: coingeckoPrices } = useCoingecko() const [chartData, setChartData] = useState<{ prices: any[] } | null>(null) const [loadChartData, setLoadChartData] = useState(true) - const [daysToShow, setDaysToShow] = useState(1) + const [daysToShow, setDaysToShow] = useState('1') const [animationSettings] = useLocalStorageState( ANIMATION_SETTINGS_KEY, INITIAL_ANIMATION_SETTINGS @@ -177,11 +177,11 @@ const Token: NextPage = () => { } = coingeckoData ? coingeckoData.market_data : DEFAULT_COINGECKO_VALUES const loadingChart = useMemo(() => { - return daysToShow == 1 ? loadingPrices : loadChartData + return daysToShow == '1' ? loadingPrices : loadChartData }, [loadChartData, loadingPrices]) const coingeckoTokenPrices = useMemo(() => { - if (daysToShow === 1 && coingeckoPrices.length && bank) { + if (daysToShow === '1' && coingeckoPrices.length && bank) { const tokenPriceData = coingeckoPrices.find( (asset) => asset.symbol === bank.name ) @@ -197,8 +197,8 @@ const Token: NextPage = () => { return [] }, [coingeckoPrices, bank, daysToShow, chartData, loadingChart]) - const handleDaysToShow = async (days: number) => { - if (days !== 1) { + const handleDaysToShow = async (days: string) => { + if (days !== '1') { try { const response = await fetch( `https://api.coingecko.com/api/v3/coins/${coingeckoId}/market_chart?vs_currency=usd&days=${days}` @@ -406,12 +406,12 @@ const Token: NextPage = () => { handleDaysToShow(v)} />
diff --git a/public/locales/en/activity.json b/public/locales/en/activity.json index 672c5275..d2f490ae 100644 --- a/public/locales/en/activity.json +++ b/public/locales/en/activity.json @@ -13,6 +13,8 @@ "liquidation": "Liquidation", "liquidations": "Liquidations", "liquidation-details": "Liquidation Details", + "perps": "Perps", + "perp_trade": "Perp", "reset-filters": "Reset Filters", "select-tokens": "Select Tokens", "swap": "Swap", diff --git a/public/locales/es/activity.json b/public/locales/es/activity.json index 672c5275..d2f490ae 100644 --- a/public/locales/es/activity.json +++ b/public/locales/es/activity.json @@ -13,6 +13,8 @@ "liquidation": "Liquidation", "liquidations": "Liquidations", "liquidation-details": "Liquidation Details", + "perps": "Perps", + "perp_trade": "Perp", "reset-filters": "Reset Filters", "select-tokens": "Select Tokens", "swap": "Swap", diff --git a/public/locales/ru/activity.json b/public/locales/ru/activity.json index 672c5275..d2f490ae 100644 --- a/public/locales/ru/activity.json +++ b/public/locales/ru/activity.json @@ -13,6 +13,8 @@ "liquidation": "Liquidation", "liquidations": "Liquidations", "liquidation-details": "Liquidation Details", + "perps": "Perps", + "perp_trade": "Perp", "reset-filters": "Reset Filters", "select-tokens": "Select Tokens", "swap": "Swap", diff --git a/public/locales/zh/activity.json b/public/locales/zh/activity.json index 672c5275..d2f490ae 100644 --- a/public/locales/zh/activity.json +++ b/public/locales/zh/activity.json @@ -13,6 +13,8 @@ "liquidation": "Liquidation", "liquidations": "Liquidations", "liquidation-details": "Liquidation Details", + "perps": "Perps", + "perp_trade": "Perp", "reset-filters": "Reset Filters", "select-tokens": "Select Tokens", "swap": "Swap", diff --git a/public/locales/zh_tw/activity.json b/public/locales/zh_tw/activity.json index 672c5275..d2f490ae 100644 --- a/public/locales/zh_tw/activity.json +++ b/public/locales/zh_tw/activity.json @@ -13,6 +13,8 @@ "liquidation": "Liquidation", "liquidations": "Liquidations", "liquidation-details": "Liquidation Details", + "perps": "Perps", + "perp_trade": "Perp", "reset-filters": "Reset Filters", "select-tokens": "Select Tokens", "swap": "Swap", diff --git a/store/mangoStore.ts b/store/mangoStore.ts index e15a1351..662a3945 100644 --- a/store/mangoStore.ts +++ b/store/mangoStore.ts @@ -5,6 +5,7 @@ import { subscribeWithSelector } from 'zustand/middleware' import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor' import { Connection, Keypair, PublicKey } from '@solana/web3.js' import { OpenOrders, Order } from '@project-serum/serum/lib/market' +import { Orderbook as SpotOrderBook } from '@project-serum/serum' import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react' import { MangoClient, @@ -15,6 +16,7 @@ import { Bank, PerpOrder, PerpPosition, + BookSide, } from '@blockworks-foundation/mango-v4' import EmptyWallet from '../utils/wallet' @@ -194,6 +196,8 @@ export type MangoStore = { name: string current: Serum3Market | PerpMarket | undefined fills: any + bidsAccount: BookSide | SpotOrderBook | undefined + asksAccount: BookSide | SpotOrderBook | undefined orderbook: Orderbook } serumMarkets: Serum3Market[] @@ -295,6 +299,8 @@ const mangoStore = create()( name: DEFAULT_MARKET_NAME, current: undefined, fills: [], + bidsAccount: undefined, + asksAccount: undefined, orderbook: { bids: [], asks: [], diff --git a/utils/constants.ts b/utils/constants.ts index dfdd9ae6..95b721e4 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -48,3 +48,5 @@ export const GRID_LAYOUT_KEY = 'savedLayouts-0.1' export const NOTIFICATION_POSITION_KEY = 'notificationPosition' export const MIN_SOL_BALANCE = 0.04 + +export const FAVORITE_MARKETS_KEY = 'favoriteMarkets'