merge main

This commit is contained in:
saml33 2023-06-16 22:30:06 +10:00
commit f65305de7f
23 changed files with 247 additions and 45 deletions

View File

@ -62,7 +62,7 @@ const TokenList = () => {
return (
<ContentBox hideBorder hidePadding>
{mangoAccountAddress ? (
<div className="flex w-full items-center justify-end border-b border-th-bkg-3 py-3 px-6 lg:-mt-[36px] lg:mb-4 lg:w-auto lg:border-0 lg:py-0">
<div className="flex w-full items-center justify-end border-b border-th-bkg-3 py-3 px-6 lg:-mt-[36px] lg:mr-12 lg:mb-4 lg:w-auto lg:border-0 lg:py-0">
<Switch
checked={showZeroBalances}
disabled={!mangoAccount}

View File

@ -11,6 +11,7 @@ import PerpPositions from '@components/trade/PerpPositions'
import useOpenPerpPositions from 'hooks/useOpenPerpPositions'
import OpenOrders from '@components/trade/OpenOrders'
import HistoryTabs from './HistoryTabs'
import ManualRefresh from '@components/shared/ManualRefresh'
const AccountTabs = () => {
const [activeTab, setActiveTab] = useState('balances')
@ -42,7 +43,7 @@ const AccountTabs = () => {
return (
<>
<div className="hide-scroll overflow-x-auto border-b border-th-bkg-3">
<div className="hide-scroll flex items-center overflow-x-auto border-b border-th-bkg-3">
<TabButtons
activeValue={activeTab}
onChange={(v) => setActiveTab(v)}
@ -50,6 +51,11 @@ const AccountTabs = () => {
showBorders
fillWidth={isMobile}
/>
<ManualRefresh
classNames="fixed bottom-16 right-4 lg:relative lg:bottom-0 md:bottom-6 md:right-6 z-10 shadow-lg lg:shadow-none bg-th-bkg-3 lg:bg-transparent"
hideBg={isMobile}
size={isMobile ? 'large' : 'small'}
/>
</div>
<TabContent activeTab={activeTab} />
</>

View File

@ -60,7 +60,7 @@ const defaultFormValues: ListMarketForm = {
const ListMarket = ({ goBack }: { goBack: () => void }) => {
const wallet = useWallet()
const { handleConnect } = useEnhancedWallet()
const { t } = useTranslation(['governance'])
const { t } = useTranslation(['governance', 'trade'])
const { group } = useMangoGroup()
const connection = mangoStore((s) => s.connection)
const client = mangoStore((s) => s.client)
@ -375,27 +375,65 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
) : null}
</>
) : (
<div className="rounded-md bg-th-bkg-2 p-4">
<h3 className="mb-2">{t('market-details')}</h3>
<div className="flex items-center justify-between">
<p>{t('market-name')}</p>
<p className="text-th-fgd-2">{`${baseToken}/${quoteToken}`}</p>
</div>
{tradingParams.minOrderSize ? (
<div className="mt-2 flex items-center justify-between">
<p>{t('min-order')}</p>
<p className="text-th-fgd-2">{tradingParams.minOrderSize}</p>
</div>
) : null}
{tradingParams.minOrderSize ? (
<div className="mt-2 flex items-center justify-between">
<p>{t('price-tick')}</p>
<p className="text-th-fgd-2">
{tradingParams.priceIncrement}
<>
<div className="mb-4 rounded-md bg-th-bkg-2 p-4">
<h3 className="mb-1">{t('market-name')}</h3>
<div className="mb-2 flex items-center">
<ExclamationTriangleIcon className="mr-1.5 h-5 w-5 text-th-warning" />
<p className="text-base text-th-fgd-2">
{t('market-name-desc')}
</p>
</div>
) : null}
</div>
<ul className="ml-4 mb-4 list-outside list-decimal space-y-1">
<li>{t('market-name-convention-1')}</li>
<li>{t('market-name-convention-2')}</li>
<li>{t('market-name-convention-3')}</li>
<li>{t('market-name-convention-4')}</li>
<li>{t('market-name-convention-5')}</li>
<li>
{t('market-name-convention-6')}
<a
href="https://discord.gg/2uwjsBc5yw"
target="_blank"
rel="noopener noreferrer"
>
Discord
</a>
</li>
</ul>
<Label className="mb-2" text={t('market-name')} />
<Input
className="max-w-[320px]"
hasError={formErrors.marketName !== undefined}
type="text"
value={advForm.marketName.toString()}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
handleSetAdvForm('marketName', e.target.value)
}
/>
</div>
<div className="rounded-md bg-th-bkg-2 p-4">
<h3 className="mb-2">
{t('trade:market-details', { market: '' })}
</h3>
{tradingParams.minOrderSize ? (
<div className="mt-2 flex items-center justify-between">
<p>{t('min-order')}</p>
<p className="text-th-fgd-2">
{tradingParams.minOrderSize}
</p>
</div>
) : null}
{tradingParams.minOrderSize ? (
<div className="mt-2 flex items-center justify-between">
<p>{t('price-tick')}</p>
<p className="text-th-fgd-2">
{tradingParams.priceIncrement}
</p>
</div>
) : null}
</div>
</>
)}
{marketPk ? (
<Disclosure>
@ -424,15 +462,6 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
<Disclosure.Panel>
<div className="space-y-4 rounded-md rounded-t-none bg-th-bkg-2 p-4">
<div>
<Label text={t('market-name')} />
<Input
hasError={formErrors.marketName !== undefined}
type="text"
value={advForm.marketName.toString()}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
handleSetAdvForm('marketName', e.target.value)
}
/>
{formErrors.marketName && (
<div className="mt-1.5 flex items-center space-x-1">
<ExclamationCircleIcon className="h-4 w-4 text-th-down" />

View File

@ -0,0 +1,61 @@
import { useEffect, useState } from 'react'
import { useTranslation } from 'next-i18next'
import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import Tooltip from './Tooltip'
import { IconButton } from './Button'
import { ArrowPathIcon } from '@heroicons/react/20/solid'
const ManualRefresh = ({
classNames,
hideBg = false,
size,
}: {
classNames?: string
hideBg?: boolean
size?: 'small' | 'medium' | 'large'
}) => {
const { t } = useTranslation('common')
const [spin, setSpin] = useState(false)
const actions = mangoStore((s) => s.actions)
const { mangoAccountAddress } = useMangoAccount()
const handleRefreshData = async () => {
setSpin(true)
await actions.fetchGroup()
if (mangoAccountAddress) {
await actions.reloadMangoAccount()
actions.fetchOpenOrders()
}
}
useEffect(() => {
let timer: NodeJS.Timeout
if (spin) {
timer = setTimeout(() => setSpin(false), 5000)
}
return () => {
clearTimeout(timer)
}
}, [spin])
return (
<div className={`${classNames} rounded-full`}>
<Tooltip content={t('refresh-data')} className="py-1 text-xs">
<IconButton
hideBg={hideBg}
onClick={handleRefreshData}
disabled={spin}
size={size}
>
<ArrowPathIcon
className={`h-5 w-5 ${spin ? 'animate-spin' : null}`}
/>
</IconButton>
</Tooltip>
</div>
)
}
export default ManualRefresh

View File

@ -108,7 +108,17 @@ const SpotMarketsTable = () => {
<div className="flex flex-col text-right">
<p>
{price ? (
<FormatNumericValue value={price} isUsd />
<>
<FormatNumericValue
value={price}
isUsd={quoteBank?.name === 'USDC'}
/>{' '}
{quoteBank?.name !== 'USDC' ? (
<span className="font-body text-th-fgd-4">
{quoteBank?.name}
</span>
) : null}
</>
) : (
''
)}
@ -300,7 +310,21 @@ const MobileSpotMarketItem = ({
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('price')}</p>
<p className="font-mono text-th-fgd-2">
{price ? <FormatNumericValue value={price} isUsd /> : '-'}
{price ? (
<>
<FormatNumericValue
value={price}
isUsd={quoteBank?.name === 'USDC'}
/>{' '}
{quoteBank?.name !== 'USDC' ? (
<span className="font-body text-th-fgd-4">
{quoteBank?.name}
</span>
) : null}
</>
) : (
'-'
)}
</p>
</div>
<div className="col-span-1">

View File

@ -4,11 +4,16 @@ import SwapTradeBalances from '../shared/BalancesTable'
import mangoStore from '@store/mangoStore'
import SwapHistoryTable from './SwapHistoryTable'
import useMangoAccount from 'hooks/useMangoAccount'
import ManualRefresh from '@components/shared/ManualRefresh'
import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme'
const SwapInfoTabs = () => {
const [selectedTab, setSelectedTab] = useState('balances')
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
const { mangoAccount } = useMangoAccount()
const { width } = useViewport()
const isMobile = width ? width < breakpoints.lg : false
const tabsWithCount: [string, number][] = useMemo(() => {
return [
@ -19,13 +24,18 @@ const SwapInfoTabs = () => {
return (
<div className="hide-scroll h-full overflow-y-scroll">
<div className="sticky top-0 z-10 border-b border-th-bkg-3">
<div className="flex items-center border-b border-th-bkg-3">
<TabButtons
activeValue={selectedTab}
onChange={(tab: string) => setSelectedTab(tab)}
values={tabsWithCount}
showBorders
/>
<ManualRefresh
classNames="fixed bottom-16 right-4 lg:relative lg:bottom-0 md:bottom-6 md:right-6 z-10 shadow-lg lg:shadow-none bg-th-bkg-3 lg:bg-transparent"
hideBg={isMobile}
size={isMobile ? 'large' : 'small'}
/>
</div>
{selectedTab === 'balances' ? <SwapTradeBalances /> : null}
{selectedTab === 'swap:swap-history' ? <SwapHistoryTable /> : null}

View File

@ -20,6 +20,9 @@ import SpotMarketDetailsModal from '@components/modals/SpotMarketDetailsModal'
import { useQuery } from '@tanstack/react-query'
import { MANGO_DATA_OPENBOOK_URL } from 'utils/constants'
import { TickerData } from 'types'
import ManualRefresh from '@components/shared/ManualRefresh'
import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme'
export const fetchSpotVolume = async () => {
try {
@ -53,6 +56,8 @@ const AdvancedMarketHeader = ({
const previousMarketName = usePrevious(selectedMarketName)
const [showMarketDetails, setShowMarketDetails] = useState(false)
const { group } = useMangoGroup()
const { width } = useViewport()
const isMobile = width ? width < breakpoints.md : false
const {
data: spotVolumeData,
@ -227,6 +232,10 @@ const AdvancedMarketHeader = ({
)}
</div>
<div className="ml-6 flex items-center space-x-4">
<ManualRefresh
hideBg={isMobile}
size={isMobile ? undefined : 'small'}
/>
<LinkButton
className="flex items-center whitespace-nowrap text-th-fgd-3"
onClick={() => setShowMarketDetails(true)}

View File

@ -1,4 +1,4 @@
import { U64_MAX_BN } from '@blockworks-foundation/mango-v4'
import { Bank, U64_MAX_BN } from '@blockworks-foundation/mango-v4'
import {
PerpMarket,
PerpOrder,
@ -269,6 +269,7 @@ const OpenOrders = () => {
let tickSize: number
let minOrderSize: number
let expiryTimestamp: number | undefined
let value: number
if (o instanceof PerpOrder) {
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
tickSize = market.tickSize
@ -277,6 +278,7 @@ const OpenOrders = () => {
o.expiryTimestamp === U64_MAX_BN
? 0
: o.expiryTimestamp.toNumber()
value = o.size * o.price
} else {
market = group.getSerum3MarketByExternalMarket(
new PublicKey(marketPk)
@ -284,8 +286,12 @@ const OpenOrders = () => {
const serumMarket = group.getSerum3ExternalMarket(
market.serumMarketExternal
)
const quoteBank = group.getFirstBankByTokenIndex(
market.quoteTokenIndex
)
tickSize = serumMarket.tickSize
minOrderSize = serumMarket.minOrderSize
value = o.size * o.price * quoteBank.uiPrice
}
return (
<TrBody
@ -345,7 +351,7 @@ const OpenOrders = () => {
</>
)}
<Td className="w-[16.67%] text-right font-mono">
<FormatNumericValue value={o.size * o.price} isUsd />
<FormatNumericValue value={value} isUsd />
{expiryTimestamp ? (
<div className="h-min text-xxs leading-tight text-th-fgd-4">{`Expires ${new Date(
expiryTimestamp * 1000
@ -419,6 +425,7 @@ const OpenOrders = () => {
let market: PerpMarket | Serum3Market
let tickSize: number
let minOrderSize: number
let quoteBank: Bank | undefined
if (o instanceof PerpOrder) {
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
tickSize = market.tickSize
@ -430,6 +437,8 @@ const OpenOrders = () => {
const serumMarket = group.getSerum3ExternalMarket(
market.serumMarketExternal
)
quoteBank = group.getFirstBankByTokenIndex(market.quoteTokenIndex)
tickSize = serumMarket.tickSize
minOrderSize = serumMarket.minOrderSize
}
@ -479,7 +488,13 @@ const OpenOrders = () => {
<FormatNumericValue
value={o.price}
decimals={getDecimalCount(tickSize)}
/>
isUsd={quoteBank?.name === 'USDC'}
/>{' '}
{quoteBank && quoteBank.name !== 'USDC' ? (
<span className="font-body text-th-fgd-3">
{quoteBank.name}
</span>
) : null}
</span>
</p>
</div>

View File

@ -8,7 +8,7 @@ import { PerpMarket, Bank } from '@blockworks-foundation/mango-v4'
import { BorshAccountsCoder } from '@coral-xyz/anchor'
import {
floorToDecimal,
formatCurrencyValue,
formatNumericValue,
getDecimalCount,
} from 'utils/numbers'
import dayjs from 'dayjs'
@ -127,6 +127,8 @@ const OraclePrice = ({
stalePrice,
])
const oracleDecimals = getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
return (
<>
<div id="trade-step-two" className="flex-col whitespace-nowrap md:ml-6">
@ -180,10 +182,15 @@ const OraclePrice = ({
</Tooltip>
<div className="font-mono text-xs text-th-fgd-2">
{price ? (
`${formatCurrencyValue(
price,
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
)}`
<>
{quoteBank?.name === 'USDC' ? '$' : ''}
{formatNumericValue(price, oracleDecimals)}{' '}
{quoteBank?.name !== 'USDC' ? (
<span className="font-body text-th-fgd-3">
{quoteBank?.name}
</span>
) : null}
</>
) : (
<span className="text-th-fgd-4"></span>
)}

View File

@ -109,8 +109,8 @@ const TradeSummary = ({
!quoteBank ||
!tradeForm.price ||
!tradeForm.baseSize ||
Number.isNaN(tradeForm.price) ||
Number.isNaN(tradeForm.baseSize)
isNaN(parseFloat(tradeForm.price)) ||
isNaN(parseFloat(tradeForm.baseSize))
)
return 0
const basePriceDecimal = new Decimal(tradeForm.price)

View File

@ -98,7 +98,7 @@ function MyApp({ Component, pageProps }: AppProps) {
/>
<meta
name="twitter:image"
content="https://app.mango.markets/images/1200x600-share.png?34567878"
content="https://app.mango.markets/images/1200x600-share.png?34567879"
/>
<meta name="google" content="notranslate" />
<link rel="manifest" href="/manifest.json"></link>

View File

@ -16,6 +16,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'onboarding',
'profile',
'search',
'trade',
])),
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -118,6 +118,7 @@
"quantity": "Quantity",
"rate": "Rate (APR)",
"rates": "Rates (APR)",
"refresh-data": "Manually refresh data",
"remove": "Remove",
"remove-delegate": "Remove Delegate",
"repay": "Repay",

View File

@ -39,6 +39,13 @@
"market-created-successful": "Market created successfully",
"market-index": "Market Index",
"market-name": "Market Name",
"market-name-convention-1": "The base symbol for each token should be uppercase",
"market-name-convention-2": "If either token is bridged via Portal append 'po' to the base symbol e.g. ETHpo",
"market-name-convention-3": "If either token is a liquid staking derivative prepend the characters to the base symbol in lowercase e.g. mSOL",
"market-name-convention-4": "If either token is wrapped prepend a lowercase 'w'",
"market-name-convention-5": "Tokens are separated by '/' with no spaces",
"market-name-convention-6": "If it isn't clear what the market name should be reach out to us on ",
"market-name-desc": "Follow these conventions when creating the market name:",
"market-pair": "Market Pair",
"min-order": "Min Order Size",
"mint": "Mint",

View File

@ -118,6 +118,7 @@
"quantity": "Quantity",
"rate": "Rate (APR)",
"rates": "Rates (APR)",
"refresh-data": "Manually refresh data",
"remove": "Remove",
"remove-delegate": "Remove Delegate",
"repay": "Repay",

View File

@ -39,6 +39,13 @@
"market-created-successful": "Market created successfully",
"market-index": "Market Index",
"market-name": "Market Name",
"market-name-convention-1": "The base symbol for each token should be uppercase",
"market-name-convention-2": "If either token is bridged via Portal append 'po' to the base symbol e.g. ETHpo",
"market-name-convention-3": "If either token is a liquid staking derivative prepend the characters to the base symbol in lowercase e.g. mSOL",
"market-name-convention-4": "If either token is wrapped prepend a lowercase 'w'",
"market-name-convention-5": "Tokens are separated by '/' with no spaces",
"market-name-convention-6": "If it isn't clear what the market name should be reach out to us on ",
"market-name-desc": "Follow these conventions when creating the market name:",
"market-pair": "Market Pair",
"min-order": "Min Order Size",
"mint": "Mint",

View File

@ -118,6 +118,7 @@
"quantity": "Quantity",
"rate": "Rate (APR)",
"rates": "Rates (APR)",
"refresh-data": "Manually refresh data",
"remove": "Remove",
"remove-delegate": "Remove Delegate",
"repay": "Repay",

View File

@ -39,6 +39,13 @@
"market-created-successful": "Market created successfully",
"market-index": "Market Index",
"market-name": "Market Name",
"market-name-convention-1": "The base symbol for each token should be uppercase",
"market-name-convention-2": "If either token is bridged via Portal append 'po' to the base symbol e.g. ETHpo",
"market-name-convention-3": "If either token is a liquid staking derivative prepend the characters to the base symbol in lowercase e.g. mSOL",
"market-name-convention-4": "If either token is wrapped prepend a lowercase 'w'",
"market-name-convention-5": "Tokens are separated by '/' with no spaces",
"market-name-convention-6": "If it isn't clear what the market name should be reach out to us on ",
"market-name-desc": "Follow these conventions when creating the market name:",
"market-pair": "Market Pair",
"min-order": "Min Order Size",
"mint": "Mint",

View File

@ -118,6 +118,7 @@
"quantity": "数量",
"rate": "利率(APR)",
"rates": "利率(APR)",
"refresh-data": "Manually refresh data",
"remove": "删除",
"remove-delegate": "铲除委托",
"repay": "归还",

View File

@ -39,6 +39,13 @@
"market-created-successful": "Market created successfully",
"market-index": "Market Index",
"market-name": "Market Name",
"market-name-convention-1": "The base symbol for each token should be uppercase",
"market-name-convention-2": "If either token is bridged via Portal append 'po' to the base symbol e.g. ETHpo",
"market-name-convention-3": "If either token is a liquid staking derivative prepend the characters to the base symbol in lowercase e.g. mSOL",
"market-name-convention-4": "If either token is wrapped prepend a lowercase 'w'",
"market-name-convention-5": "Tokens are separated by '/' with no spaces",
"market-name-convention-6": "If it isn't clear what the market name should be reach out to us on ",
"market-name-desc": "Follow these conventions when creating the market name:",
"market-pair": "Market Pair",
"min-order": "Min Order Size",
"mint": "Mint",

View File

@ -118,6 +118,7 @@
"quantity": "數量",
"rate": "利率(APR)",
"rates": "利率(APR)",
"refresh-data": "Manually refresh data",
"remove": "刪除",
"remove-delegate": "剷除委託",
"repay": "歸還",

View File

@ -39,6 +39,13 @@
"market-created-successful": "Market created successfully",
"market-index": "Market Index",
"market-name": "Market Name",
"market-name-convention-1": "The base symbol for each token should be uppercase",
"market-name-convention-2": "If either token is bridged via Portal append 'po' to the base symbol e.g. ETHpo",
"market-name-convention-3": "If either token is a liquid staking derivative prepend the characters to the base symbol in lowercase e.g. mSOL",
"market-name-convention-4": "If either token is wrapped prepend a lowercase 'w'",
"market-name-convention-5": "Tokens are separated by '/' with no spaces",
"market-name-convention-6": "If it isn't clear what the market name should be reach out to us on ",
"market-name-desc": "Follow these conventions when creating the market name:",
"market-pair": "Market Pair",
"min-order": "Min Order Size",
"mint": "Mint",