Merge pull request #300 from blockworks-foundation/pan/granular-geoblock
Granular Geoblocking
This commit is contained in:
commit
1b69697deb
|
@ -15,6 +15,7 @@ import TopBar from './TopBar'
|
|||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import {
|
||||
ACCEPT_TERMS_KEY,
|
||||
NON_RESTRICTED_JURISDICTION_KEY,
|
||||
SECONDS,
|
||||
SIDEBAR_COLLAPSE_KEY,
|
||||
SLOTS_WARNING_KEY,
|
||||
|
@ -31,6 +32,7 @@ import { useTheme } from 'next-themes'
|
|||
import PromoBanner from './rewards/PromoBanner'
|
||||
import { useRouter } from 'next/router'
|
||||
import StatusBar from './StatusBar'
|
||||
import WarningBanner from './shared/WarningBanner'
|
||||
import useMangoAccountAccounts from 'hooks/useMangoAccountAccounts'
|
||||
import TokenSlotsWarningModal, {
|
||||
WARNING_LEVEL,
|
||||
|
@ -38,6 +40,8 @@ import TokenSlotsWarningModal, {
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
import NewListingBanner from './NewListingBanner'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import RestrictedCountryModal from './modals/RestrictedCountryModal'
|
||||
|
||||
export const sideBarAnimationDuration = 300
|
||||
const termsLastUpdated = 1679441610978
|
||||
|
@ -167,11 +171,15 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
<TopBar />
|
||||
<NewListingBanner />
|
||||
{asPath !== '/rewards' ? <PromoBanner /> : null}
|
||||
{children}
|
||||
<div className="pb-12 md:pb-[27px]">
|
||||
{children}
|
||||
<WarningBanner />
|
||||
</div>
|
||||
<StatusBar collapsed={isCollapsed} />
|
||||
</div>
|
||||
<DeployRefreshManager />
|
||||
<TermsOfUse />
|
||||
<RestrictedCountryCheck />
|
||||
{showSlotsNearlyFullWarning ? (
|
||||
<TokenSlotsWarningModal
|
||||
isOpen={showSlotsNearlyFullWarning}
|
||||
|
@ -216,6 +224,26 @@ const TermsOfUse = () => {
|
|||
)
|
||||
}
|
||||
|
||||
// this will only show if the ip api doesn't return the country
|
||||
const RestrictedCountryCheck = () => {
|
||||
const { ipCountry, loadingIpCountry } = useIpAddress()
|
||||
const [confirmedCountry, setConfirmedCountry] = useLocalStorageState(
|
||||
NON_RESTRICTED_JURISDICTION_KEY,
|
||||
false,
|
||||
)
|
||||
|
||||
const showModal = useMemo(() => {
|
||||
return !confirmedCountry && !ipCountry && !loadingIpCountry
|
||||
}, [confirmedCountry, ipCountry, loadingIpCountry])
|
||||
|
||||
return showModal ? (
|
||||
<RestrictedCountryModal
|
||||
isOpen={showModal}
|
||||
onClose={() => setConfirmedCountry(true)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
function DeployRefreshManager(): JSX.Element | null {
|
||||
const { t } = useTranslation('common')
|
||||
const [newBuildAvailable, setNewBuildAvailable] = useState(false)
|
||||
|
|
|
@ -89,7 +89,7 @@ const BorrowPage = () => {
|
|||
}, [borrowValue, mangoAccount, group])
|
||||
|
||||
return (
|
||||
<div className="md:pb-[27px]">
|
||||
<div className="min-h-[calc(100vh-146px)]">
|
||||
<div className="flex flex-col border-b border-th-bkg-3 px-4 py-4 md:px-6 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="pb-4 md:pb-0 md:pr-6">
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { SANCTIONED_COUNTRIES } from 'hooks/useIpAddress'
|
||||
import { ModalProps } from '../../types/modal'
|
||||
import Modal from '../shared/Modal'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Checkbox from '@components/forms/Checkbox'
|
||||
import { useState } from 'react'
|
||||
import Button from '@components/shared/Button'
|
||||
|
||||
const RestrictedCountryModal = ({ isOpen, onClose }: ModalProps) => {
|
||||
const { t } = useTranslation('common')
|
||||
const [confirm, setConfirm] = useState(false)
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} hideClose disableOutsideClose>
|
||||
<h2 className="mb-2 text-center">{t('confirm-jurisdiction')}</h2>
|
||||
<p className="text-center">{t('confirm-jurisdiction-desc')}</p>
|
||||
<div className="thin-scroll my-4 max-h-[300px] overflow-auto rounded-lg bg-th-bkg-2 p-4">
|
||||
<ul>
|
||||
{SANCTIONED_COUNTRIES.map((country) => (
|
||||
<li key={country[0]}>{country[1]}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<Checkbox
|
||||
checked={confirm}
|
||||
onChange={(e) => setConfirm(e.target.checked)}
|
||||
>
|
||||
<p className="whitespace-normal">
|
||||
{t('confirm-non-restricted-jurisdiction')}
|
||||
</p>
|
||||
</Checkbox>
|
||||
<Button
|
||||
className="mt-6 w-full"
|
||||
disabled={!confirm}
|
||||
onClick={onClose}
|
||||
size="large"
|
||||
>
|
||||
{t('confirm')}
|
||||
</Button>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default RestrictedCountryModal
|
|
@ -0,0 +1,63 @@
|
|||
import { IconButton } from '@components/shared/Button'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import Link from 'next/link'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
|
||||
const BANNER_WRAPPER_CLASSES =
|
||||
'flex flex-wrap items-center justify-center bg-th-bkg-1 px-10 py-3 text-xs'
|
||||
|
||||
const LINK_TEXT_CLASSES =
|
||||
'bg-gradient-to-b from-mango-classic-theme-active to-mango-classic-theme-down bg-clip-text font-bold text-transparent'
|
||||
|
||||
const TEXT_CLASSES = 'mx-1 text-center text-th-fgd-1 text-xs'
|
||||
|
||||
const WarningBanner = () => {
|
||||
const { showWarning } = useIpAddress()
|
||||
|
||||
return showWarning ? (
|
||||
<BannerContent
|
||||
text={`Don't invest unless you're prepared to lose all the money you invest. This is a high-risk investment and you should not expect to be protected if something goes wrong.`}
|
||||
linkText="Learn More"
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default WarningBanner
|
||||
|
||||
const BannerContent = ({
|
||||
text,
|
||||
linkText,
|
||||
onClickLink,
|
||||
onClose,
|
||||
}: {
|
||||
text: string
|
||||
linkText: string
|
||||
onClickLink?: () => void
|
||||
onClose?: () => void
|
||||
}) => {
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className={BANNER_WRAPPER_CLASSES}>
|
||||
<p className={TEXT_CLASSES}>{text}</p>
|
||||
<Link
|
||||
className={LINK_TEXT_CLASSES}
|
||||
href="https://docs.mango.markets/mango-markets/risks"
|
||||
onClick={onClickLink}
|
||||
target="blank"
|
||||
>
|
||||
{linkText}
|
||||
</Link>
|
||||
</div>
|
||||
{onClose ? (
|
||||
<IconButton
|
||||
className="absolute right-0 top-1/2 -translate-y-1/2 sm:right-2"
|
||||
hideBg
|
||||
onClick={onClose}
|
||||
size="medium"
|
||||
>
|
||||
<XMarkIcon className="h-5 w-5 text-th-fgd-3" />
|
||||
</IconButton>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -34,7 +34,7 @@ const StatsPage = () => {
|
|||
return TABS.map((t) => [t, 0])
|
||||
}, [])
|
||||
return (
|
||||
<div className="pb-16 md:pb-[27px]">
|
||||
<>
|
||||
{market ? (
|
||||
<PerpStatsPage />
|
||||
) : token ? (
|
||||
|
@ -53,7 +53,7 @@ const StatsPage = () => {
|
|||
<TabContent activeTab={activeTab} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ const SwapFormSubmitButton = ({
|
|||
const { t } = useTranslation('common')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
const { ipAllowed, ipCountry, swapAllowed } = useIpAddress()
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
|
@ -61,7 +61,7 @@ const SwapFormSubmitButton = ({
|
|||
!selectedRoute ||
|
||||
!!selectedRoute.error
|
||||
|
||||
return ipAllowed ? (
|
||||
return ipAllowed || swapAllowed ? (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
|
|
|
@ -105,7 +105,7 @@ const TriggerSwapForm = ({
|
|||
}: TriggerSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
const { ipAllowed, ipCountry, swapAllowed } = useIpAddress()
|
||||
const [orderType, setOrderType] = useState(ORDER_TYPES[0])
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
|
@ -729,7 +729,7 @@ const TriggerSwapForm = ({
|
|||
<InlineNotification desc={orderDescription} type="info" />
|
||||
</div>
|
||||
) : null}
|
||||
{ipAllowed ? (
|
||||
{ipAllowed || swapAllowed ? (
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
|
|
|
@ -78,6 +78,7 @@ import AccountSlotsFullNotification from '@components/shared/AccountSlotsFullNot
|
|||
import DepositWithdrawModal from '@components/modals/DepositWithdrawModal'
|
||||
import CreateAccountModal from '@components/modals/CreateAccountModal'
|
||||
import TradeformSubmitButton from './TradeformSubmitButton'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -122,6 +123,7 @@ const AdvancedTradeForm = () => {
|
|||
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
const [savedCheckboxSettings, setSavedCheckboxSettings] =
|
||||
useLocalStorageState(TRADE_CHECKBOXES_KEY, DEFAULT_CHECKBOX_SETTINGS)
|
||||
const { ipAllowed, perpAllowed, spotAllowed, ipCountry } = useIpAddress()
|
||||
const [soundSettings] = useLocalStorageState(
|
||||
SOUND_SETTINGS_KEY,
|
||||
INITIAL_SOUND_SETTINGS,
|
||||
|
@ -405,6 +407,48 @@ const AdvancedTradeForm = () => {
|
|||
}
|
||||
}, [tradeForm.baseSize, marketAddress])
|
||||
|
||||
const isSanctioned = useMemo(() => {
|
||||
return (
|
||||
!ipAllowed ||
|
||||
(selectedMarket instanceof PerpMarket && !perpAllowed) ||
|
||||
(selectedMarket instanceof Serum3Market && !spotAllowed)
|
||||
)
|
||||
}, [selectedMarket, ipAllowed, perpAllowed, spotAllowed])
|
||||
|
||||
const hasPosition = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!mangoAccount || !selectedMarket || !group) return false
|
||||
if (selectedMarket instanceof PerpMarket) {
|
||||
const basePosition = mangoAccount
|
||||
.getPerpPosition(selectedMarket.perpMarketIndex)
|
||||
?.getBasePositionUi(selectedMarket)
|
||||
return basePosition !== undefined && basePosition !== 0
|
||||
} else if (selectedMarket instanceof Serum3Market) {
|
||||
const baseBank = group.getFirstBankByTokenIndex(
|
||||
selectedMarket.baseTokenIndex,
|
||||
)
|
||||
const tokenPosition = mangoAccount.getTokenBalanceUi(baseBank)
|
||||
return tradeForm.side === 'sell' && tokenPosition !== 0
|
||||
}
|
||||
}, [selectedMarket, ipCountry, mangoAccount, tradeForm])
|
||||
|
||||
const isForceReduceOnly = useMemo(() => {
|
||||
if (!selectedMarket) return false
|
||||
return selectedMarket.reduceOnly || !!(isSanctioned && hasPosition)
|
||||
}, [selectedMarket, isSanctioned, hasPosition])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSanctioned) {
|
||||
set((state) => {
|
||||
state.tradeForm.reduceOnly = true
|
||||
})
|
||||
setSavedCheckboxSettings({
|
||||
...savedCheckboxSettings,
|
||||
margin: false,
|
||||
})
|
||||
}
|
||||
}, [isSanctioned])
|
||||
|
||||
/*
|
||||
* Updates the limit price on page load
|
||||
*/
|
||||
|
@ -1083,6 +1127,7 @@ const AdvancedTradeForm = () => {
|
|||
>
|
||||
<Checkbox
|
||||
checked={savedCheckboxSettings.margin}
|
||||
disabled={isSanctioned}
|
||||
onChange={handleSetMargin}
|
||||
>
|
||||
{t('trade:margin')}
|
||||
|
@ -1101,10 +1146,13 @@ const AdvancedTradeForm = () => {
|
|||
>
|
||||
<div className="flex items-center text-xs text-th-fgd-3">
|
||||
<Checkbox
|
||||
checked={tradeForm.reduceOnly}
|
||||
checked={
|
||||
tradeForm.reduceOnly || isForceReduceOnly === true
|
||||
}
|
||||
onChange={(e) =>
|
||||
handleReduceOnlyChange(e.target.checked)
|
||||
}
|
||||
disabled={isForceReduceOnly}
|
||||
>
|
||||
{t('trade:reduce-only')}
|
||||
</Checkbox>
|
||||
|
@ -1116,6 +1164,8 @@ const AdvancedTradeForm = () => {
|
|||
<div className="mb-4 mt-6 flex px-3 md:px-4">
|
||||
<TradeformSubmitButton
|
||||
disabled={disabled}
|
||||
isForceReduceOnly={isForceReduceOnly}
|
||||
isSanctioned={isSanctioned}
|
||||
placingOrder={placingOrder}
|
||||
setShowCreateAccountModal={setShowCreateAccountModal}
|
||||
setShowDepositModal={setShowDepositModal}
|
||||
|
@ -1172,6 +1222,14 @@ const AdvancedTradeForm = () => {
|
|||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{isSanctioned && hasPosition ? (
|
||||
<div className="mb-4 px-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={t('trade:error-sanctioned-reduce-only')}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{isTriggerOrder ? (
|
||||
<div className="mb-4 px-4">
|
||||
<InlineNotification
|
||||
|
|
|
@ -288,7 +288,7 @@ const TradeAdvancedPage = () => {
|
|||
<MobileTradeAdvancedPage />
|
||||
) : (
|
||||
<TradeHotKeys>
|
||||
<div className="pb-[27px]">
|
||||
<div className="border-b border-th-bkg-3">
|
||||
<FavoriteMarketsBar />
|
||||
<ResponsiveGridLayout
|
||||
layouts={layouts}
|
||||
|
|
|
@ -13,6 +13,8 @@ const TradeformSubmitButton = ({
|
|||
setShowCreateAccountModal,
|
||||
sideNames,
|
||||
tooMuchSize,
|
||||
isForceReduceOnly,
|
||||
isSanctioned,
|
||||
}: {
|
||||
disabled: boolean
|
||||
placingOrder: boolean
|
||||
|
@ -21,6 +23,8 @@ const TradeformSubmitButton = ({
|
|||
sideNames: string[]
|
||||
tooMuchSize: boolean
|
||||
useMargin: boolean
|
||||
isForceReduceOnly: boolean
|
||||
isSanctioned: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
const side = mangoStore((s) => s.tradeForm.side)
|
||||
|
@ -28,9 +32,9 @@ const TradeformSubmitButton = ({
|
|||
const { connected } = useWallet()
|
||||
const { initialLoad: mangoAccountLoading, mangoAccountAddress } =
|
||||
useMangoAccount()
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
const { ipCountry } = useIpAddress()
|
||||
|
||||
return ipAllowed ? (
|
||||
return !isSanctioned || isForceReduceOnly ? (
|
||||
(connected && mangoAccountLoading) || mangoAccountAddress ? (
|
||||
<Button
|
||||
className={`flex w-full items-center justify-center ${
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CLUSTER } from '@store/mangoStore'
|
|||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const SANCTIONED_COUNTRIES = [
|
||||
export const SANCTIONED_COUNTRIES = [
|
||||
['AG', 'Antigua and Barbuda'],
|
||||
['DZ', 'Algeria'],
|
||||
['BD', 'Bangladesh'],
|
||||
|
@ -14,6 +14,7 @@ const SANCTIONED_COUNTRIES = [
|
|||
['CU', 'Cuba'],
|
||||
['CD', 'Democratic Republic of Congo'],
|
||||
['EC', 'Ecuador'],
|
||||
['GB', 'United Kingdom'],
|
||||
['IR', 'Iran'],
|
||||
['IQ', 'Iraq'],
|
||||
['LR', 'Liberia'],
|
||||
|
@ -35,43 +36,83 @@ const SANCTIONED_COUNTRY_CODES = SANCTIONED_COUNTRIES.map(
|
|||
(country) => country[0],
|
||||
)
|
||||
|
||||
const SPOT_ALLOWED = ['GB']
|
||||
const PERP_ALLOWED: string[] = []
|
||||
const SPOT_ALLOWED: string[] = []
|
||||
const SWAP_ALLOWED: string[] = []
|
||||
const BORROW_LEND_ALLOWED: string[] = []
|
||||
const SHOW_WARNING: string[] = ['GB']
|
||||
|
||||
const fetchIpGeolocation = async () => {
|
||||
const response = await fetch(`https://country-code.mangomarkets.workers.dev`)
|
||||
const parsedResponse = await response.json()
|
||||
const ipCountryCode = parsedResponse ? parsedResponse?.country : ''
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://country-code.mangomarkets.workers.dev`,
|
||||
)
|
||||
const parsedResponse = await response.json()
|
||||
const ipCountryCode = parsedResponse ? parsedResponse?.country : ''
|
||||
|
||||
return ipCountryCode
|
||||
return ipCountryCode
|
||||
} catch (e) {
|
||||
console.log('failed to fetch ip country', e)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export default function useIpAddress() {
|
||||
const [ipAllowed, setIpAllowed] = useState(true)
|
||||
const [spotAllowed, setSpotAllowed] = useState(true)
|
||||
const [perpAllowed, setPerpAllowed] = useState(true)
|
||||
const [swapAllowed, setSwapAllowed] = useState(true)
|
||||
const [borrowLendAllowed, setBorrowLendAllowed] = useState(true)
|
||||
const [showWarning, setShowWarning] = useState(false)
|
||||
const [ipCountry, setIpCountry] = useState('')
|
||||
|
||||
const ipCountryCode = useQuery<string, Error>(
|
||||
['ip-address'],
|
||||
() => fetchIpGeolocation(),
|
||||
{
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 2,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
)
|
||||
const {
|
||||
data: ipCountryCode,
|
||||
isInitialLoading,
|
||||
isFetching,
|
||||
isLoading,
|
||||
} = useQuery<string, Error>(['ip-address'], () => fetchIpGeolocation(), {
|
||||
cacheTime: 1000 * 60 * 2,
|
||||
staleTime: 1000 * 60 * 2,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: true,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (ipCountryCode.data) {
|
||||
setIpCountry(ipCountryCode.data)
|
||||
setIpAllowed(!SANCTIONED_COUNTRY_CODES.includes(ipCountryCode.data))
|
||||
setSpotAllowed(SPOT_ALLOWED.includes(ipCountryCode.data))
|
||||
if (ipCountryCode) {
|
||||
setIpCountry(ipCountryCode)
|
||||
setIpAllowed(!SANCTIONED_COUNTRY_CODES.includes(ipCountryCode))
|
||||
setSpotAllowed(SPOT_ALLOWED.includes(ipCountryCode))
|
||||
setPerpAllowed(PERP_ALLOWED.includes(ipCountryCode))
|
||||
setSwapAllowed(SWAP_ALLOWED.includes(ipCountryCode))
|
||||
setBorrowLendAllowed(BORROW_LEND_ALLOWED.includes(ipCountryCode))
|
||||
setShowWarning(SHOW_WARNING.includes(ipCountryCode))
|
||||
}
|
||||
}, [ipCountryCode])
|
||||
|
||||
if (CLUSTER === 'mainnet-beta') {
|
||||
return { ipAllowed, spotAllowed, ipCountry }
|
||||
const loadingIpCountry = isInitialLoading || isFetching || isLoading
|
||||
|
||||
if (CLUSTER === 'mainnet-beta' && !process.env.NEXT_PUBLIC_DISABLE_GEOBLOCK) {
|
||||
return {
|
||||
ipAllowed,
|
||||
spotAllowed,
|
||||
perpAllowed,
|
||||
swapAllowed,
|
||||
borrowLendAllowed,
|
||||
showWarning,
|
||||
ipCountry,
|
||||
loadingIpCountry,
|
||||
}
|
||||
} else {
|
||||
return { ipAllowed: true, spotAllowed: true, ipCountry }
|
||||
return {
|
||||
ipAllowed: true,
|
||||
spotAllowed: true,
|
||||
perpAllowed: true,
|
||||
swapAllowed: true,
|
||||
borrowLendAllowed: true,
|
||||
showWarning: true,
|
||||
ipCountry,
|
||||
loadingIpCountry,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ const Index: NextPage = () => {
|
|||
<meta name="twitter:title" content={metaTitle} />
|
||||
<meta name="twitter:description" content={metaDescription} />
|
||||
</Head>
|
||||
<div className="min-h-[calc(100vh-64px)] pb-32 md:pb-20 lg:pb-[27px]">
|
||||
<div className="min-h-[calc(100vh-64px)]">
|
||||
<AccountPage />
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -36,9 +36,7 @@ const Leaderboard: NextPage = () => {
|
|||
<meta name="twitter:title" content={metaTitle} />
|
||||
<meta name="twitter:description" content={metaDescription} />
|
||||
</Head>
|
||||
<div className="pb-16 md:pb-[27px]">
|
||||
<LeaderboardPage />
|
||||
</div>
|
||||
<LeaderboardPage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -38,9 +38,7 @@ const Swap: NextPage = () => {
|
|||
<meta name="twitter:title" content={metaTitle} />
|
||||
<meta name="twitter:description" content={metaDescription} />
|
||||
</Head>
|
||||
<div className="pb-32 md:pb-20 lg:pb-[27px]">
|
||||
<SwapPage />
|
||||
</div>
|
||||
<SwapPage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -105,9 +105,7 @@ const Trade: NextPage = () => {
|
|||
<meta name="twitter:title" content={metaTitle} />
|
||||
<meta name="twitter:description" content={metaDescription} />
|
||||
</Head>
|
||||
<div className="pb-32 md:pb-0">
|
||||
<TradeAdvancedPage />
|
||||
</div>
|
||||
<TradeAdvancedPage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
"close-borrow": "Close {{token}} Borrow",
|
||||
"collateral-value": "Collateral Value",
|
||||
"confirm": "Confirm",
|
||||
"confirm-jurisdiction": "Confirm Jurisdiction",
|
||||
"confirm-jurisdiction-desc": "It's against our Terms of Service to use Mango if you're a resident, citizen or are located in any of the following jurisdictions.",
|
||||
"confirm-non-restricted-jurisdiction": "I confirm I am not a resident, citizen or are located in a prohibited jurisdiction",
|
||||
"connect": "Connect",
|
||||
"connect-balances": "Connect to view your balances",
|
||||
"connect-helper": "Connect to get started",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"error-no-route": "No route found. Try adjusting your size",
|
||||
"error-no-short": "No borrow position to reduce",
|
||||
"error-perp-positions-full": "You've used all of your available perp market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-sanctioned-reduce-only": "Country not allowed. You can reduce your position only.",
|
||||
"error-serum-positions-full": "You've used all of your available spot market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-trigger-above": "Trigger price must be above oracle price",
|
||||
"error-trigger-below": "Trigger price must be below oracle price",
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
"close-borrow": "Close {{token}} Borrow",
|
||||
"collateral-value": "Collateral Value",
|
||||
"confirm": "Confirm",
|
||||
"confirm-jurisdiction": "Confirm Jurisdiction",
|
||||
"confirm-jurisdiction-desc": "It's against our Terms of Service to use Mango if you're a resident, citizen or are located in any of the following jurisdictions.",
|
||||
"confirm-non-restricted-jurisdiction": "I confirm I am not a resident, citizen or are located in a prohibited jurisdiction",
|
||||
"connect": "Connect",
|
||||
"connect-balances": "Connect to view your balances",
|
||||
"connect-helper": "Connect to get started",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"error-no-route": "No route found. Try adjusting your size",
|
||||
"error-no-short": "No borrow position to reduce",
|
||||
"error-perp-positions-full": "You've used all of your available perp market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-sanctioned-reduce-only": "Country not allowed. You can reduce your position only.",
|
||||
"error-serum-positions-full": "You've used all of your available spot market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-trigger-above": "Trigger price must be above oracle price",
|
||||
"error-trigger-below": "Trigger price must be below oracle price",
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
"close-borrow": "Close {{token}} Borrow",
|
||||
"collateral-value": "Collateral Value",
|
||||
"confirm": "Confirm",
|
||||
"confirm-jurisdiction": "Confirm Jurisdiction",
|
||||
"confirm-jurisdiction-desc": "It's against our Terms of Service to use Mango if you're a resident, citizen or are located in any of the following jurisdictions.",
|
||||
"confirm-non-restricted-jurisdiction": "I confirm I am not a resident, citizen or are located in a prohibited jurisdiction",
|
||||
"connect": "Connect",
|
||||
"connect-balances": "Connect to view your balances",
|
||||
"connect-helper": "Connect to get started",
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"error-no-short": "No borrow position to reduce",
|
||||
"error-perp-positions-full": "You've used all of your available perp market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-serum-positions-full": "You've used all of your available spot market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-sanctioned-reduce-only": "Country not allowed. You can reduce your position only.",
|
||||
"error-trigger-above": "Trigger price must be above oracle price",
|
||||
"error-trigger-below": "Trigger price must be below oracle price",
|
||||
"est-liq-price": "Est. Liq. Price",
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
"close-borrow": "结清{{token}}借贷",
|
||||
"collateral-value": "质押品价值",
|
||||
"confirm": "确认",
|
||||
"confirm-jurisdiction": "Confirm Jurisdiction",
|
||||
"confirm-jurisdiction-desc": "It's against our Terms of Service to use Mango if you're a resident, citizen or are located in any of the following jurisdictions.",
|
||||
"confirm-non-restricted-jurisdiction": "I confirm I am not a resident, citizen or are located in a prohibited jurisdiction",
|
||||
"connect": "连接",
|
||||
"connect-balances": "连接而查看资产余额",
|
||||
"connect-helper": "连接来开始",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"error-trigger-above": "触发价格必须高于预言机价格",
|
||||
"error-trigger-below": "触发价格必须低于预言机价格",
|
||||
"error-perp-positions-full": "You've used all of your available perp market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-sanctioned-reduce-only": "Country not allowed. You can reduce your position only.",
|
||||
"error-serum-positions-full": "You've used all of your available spot market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"est-liq-price": "预计清算价格",
|
||||
"est-slippage": "预计下滑",
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
"close-borrow": "結清{{token}}借貸",
|
||||
"collateral-value": "質押品價值",
|
||||
"confirm": "確認",
|
||||
"confirm-jurisdiction": "Confirm Jurisdiction",
|
||||
"confirm-jurisdiction-desc": "It's against our Terms of Service to use Mango if you're a resident, citizen or are located in any of the following jurisdictions.",
|
||||
"confirm-non-restricted-jurisdiction": "I confirm I am not a resident, citizen or are located in a prohibited jurisdiction",
|
||||
"connect": "連接",
|
||||
"connect-balances": "連接而查看資產餘額",
|
||||
"connect-helper": "連接來開始",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"error-trigger-above": "觸發價格必須高於預言機價格",
|
||||
"error-trigger-below": "觸發價格必須低於預言機價格",
|
||||
"error-perp-positions-full": "You've used all of your available perp market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"error-sanctioned-reduce-only": "Country not allowed. You can reduce your position only.",
|
||||
"error-serum-positions-full": "You've used all of your available spot market account slots. Close unused slots to trade this market or open a new account.",
|
||||
"est-liq-price": "預計清算價格",
|
||||
"est-slippage": "預計下滑",
|
||||
|
|
|
@ -83,6 +83,8 @@ export const SLOTS_WARNING_KEY = 'tokenSlotsWarning-0.1'
|
|||
|
||||
export const NEW_LISTING_BANNER_KEY = 'new-listing-banner-0.2'
|
||||
|
||||
export const NON_RESTRICTED_JURISDICTION_KEY = 'non-restricted-jurisdiction-0.1'
|
||||
|
||||
// Unused
|
||||
export const PROFILE_CATEGORIES = [
|
||||
'borrower',
|
||||
|
|
Loading…
Reference in New Issue