Add warning bnner, add granular features for geoblock, allow geoblock reduceonly

This commit is contained in:
Riordan Panayides 2023-10-25 14:32:54 +01:00
parent 4e4fc5f455
commit 2aa2f0422c
9 changed files with 142 additions and 15 deletions

View File

@ -30,6 +30,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'
export const sideBarAnimationDuration = 300
const termsLastUpdated = 1679441610978
@ -127,6 +128,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
}`}
>
<TopBar />
<WarningBanner />
{asPath !== '/rewards' ? <PromoBanner /> : null}
{children}
<StatusBar collapsed={isCollapsed} />

View File

@ -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 border-b border-th-bkg-3 bg-th-bkg-2 px-10 py-3'
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 lg:text-base'
const TEXT_CLASSES = 'mr-2 text-center text-th-fgd-1 lg:text-base'
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>
)
}

View File

@ -88,7 +88,7 @@ const MarketSwapForm = ({
swapMode,
wallet: publicKey?.toBase58(),
})
const { ipAllowed, ipCountry } = useIpAddress()
const { ipAllowed, swapAllowed, ipCountry } = useIpAddress()
const amountInAsDecimal: Decimal | null = useMemo(() => {
return Number(debouncedAmountIn)
@ -274,7 +274,7 @@ const MarketSwapForm = ({
setShowTokenSelect={setShowTokenSelect}
handleRepay={handleRepay}
/>
{ipAllowed ? (
{ipAllowed || swapAllowed ? (
<SwapFormSubmitButton
loadingSwapDetails={loadingSwapDetails}
useMargin={useMargin}

View File

@ -151,7 +151,7 @@ export const fetchJupiterTransaction = async (
// This is the ATA account for the output token where the fee will be sent to. If you are swapping from SOL->USDC then this would be the USDC ATA you want to collect the fee.
// feeAccount: 'fee_account_public_key',
slippageBps: Math.ceil(slippage * 100),
maxAccounts: 50
maxAccounts: 50,
}),
})
).json()

View File

@ -112,7 +112,7 @@ const TriggerSwapForm = ({
}: TriggerSwapFormProps) => {
const { t } = useTranslation(['common', 'swap', 'trade'])
const { mangoAccountAddress } = useMangoAccount()
const { ipAllowed, ipCountry } = useIpAddress()
const { ipAllowed, swapAllowed, ipCountry } = useIpAddress()
const { setShowSettingsModal } = TopBarStore()
// const [triggerPrice, setTriggerPrice] = useState('')
const [orderType, setOrderType] = useState(ORDER_TYPES[0])
@ -854,7 +854,7 @@ const TriggerSwapForm = ({
<InlineNotification desc={orderDescription} type="info" />
</div>
) : null}
{ipAllowed ? (
{ipAllowed || swapAllowed ? (
<Button
disabled={borrowExceedsLimitInPeriod || tokenPositionsFull}
onClick={onClick}

View File

@ -72,7 +72,7 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
wallet: publicKey?.toBase58(),
mode: 'JUPITER',
})
const { ipAllowed, ipCountry } = useIpAddress()
const { ipAllowed, swapAllowed, ipCountry } = useIpAddress()
const walletTokens = mangoStore((s) => s.wallet.tokens)
@ -277,7 +277,7 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
loading={loadingSwapDetails}
setShowTokenSelect={setShowTokenSelect}
/>
{ipAllowed ? (
{ipAllowed || swapAllowed ? (
<SwapFormSubmitButton
loadingSwapDetails={loadingSwapDetails}
useMargin={useMargin}

View File

@ -30,7 +30,7 @@ const fetchJupiterRoute = async (
swapMode = 'ExactIn',
feeBps = 0,
onlyDirectRoutes = true,
maxAccounts = 50
maxAccounts = 50,
) => {
{
const paramsString = new URLSearchParams({

View File

@ -97,7 +97,7 @@ const AdvancedTradeForm = () => {
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
const [savedCheckboxSettings, setSavedCheckboxSettings] =
useLocalStorageState(TRADE_CHECKBOXES_KEY, DEFAULT_CHECKBOX_SETTINGS)
const { ipAllowed, ipCountry } = useIpAddress()
const { ipAllowed, perpAllowed, spotAllowed, ipCountry } = useIpAddress()
const [soundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS,
@ -336,6 +336,36 @@ const AdvancedTradeForm = () => {
}
}, [selectedMarket])
const isSanctioned = useMemo(() => {
return !(
ipAllowed ||
(selectedMarket instanceof PerpMarket && perpAllowed) ||
(selectedMarket instanceof Serum3Market && spotAllowed)
)
}, [selectedMarket, ipCountry])
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 tokenPosition != 0
}
}, [selectedMarket, ipCountry, mangoAccount])
const isForceReduceOnly = useMemo(() => {
if (!selectedMarket) return false
return selectedMarket.reduceOnly || (isSanctioned && hasPosition)
}, [selectedMarket])
/*
* Updates the limit price on page load
*/
@ -769,10 +799,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>
@ -782,7 +815,7 @@ const AdvancedTradeForm = () => {
)}
</div>
<div className="mb-4 mt-6 flex px-3 md:px-4">
{ipAllowed ? (
{!isSanctioned || isForceReduceOnly ? (
connected ? (
<Button
className={`flex w-full items-center justify-center ${

View File

@ -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,7 +36,11 @@ 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`)
@ -48,6 +53,10 @@ const fetchIpGeolocation = async () => {
export default function useIpAddress() {
const [ipAllowed, setIpAllowed] = useState(false)
const [spotAllowed, setSpotAllowed] = useState(false)
const [perpAllowed, setPerpAllowed] = useState(false)
const [swapAllowed, setSwapAllowed] = useState(false)
const [borrowLendAllowed, setBorrowLendAllowed] = useState(false)
const [showWarning, setShowWarning] = useState(false)
const [ipCountry, setIpCountry] = useState('')
const ipCountryCode = useQuery<string, Error>(
@ -66,12 +75,32 @@ export default function useIpAddress() {
setIpCountry(ipCountryCode.data)
setIpAllowed(!SANCTIONED_COUNTRY_CODES.includes(ipCountryCode.data))
setSpotAllowed(SPOT_ALLOWED.includes(ipCountryCode.data))
setPerpAllowed(PERP_ALLOWED.includes(ipCountryCode.data))
setSwapAllowed(SWAP_ALLOWED.includes(ipCountryCode.data))
setBorrowLendAllowed(BORROW_LEND_ALLOWED.includes(ipCountryCode.data))
setShowWarning(SHOW_WARNING.includes(ipCountryCode.data))
}
}, [ipCountryCode])
if (CLUSTER === 'mainnet-beta') {
return { ipAllowed, spotAllowed, ipCountry }
if (CLUSTER === 'mainnet-beta' && !process.env.NEXT_PUBLIC_DISABLE_GEOBLOCK) {
return {
ipAllowed,
spotAllowed,
perpAllowed,
swapAllowed,
borrowLendAllowed,
showWarning,
ipCountry,
}
} else {
return { ipAllowed: true, spotAllowed: true, ipCountry }
return {
ipAllowed: true,
spotAllowed: true,
perpAllowed: true,
swapAllowed: true,
borrowLendAllowed: true,
showWarning: true,
ipCountry,
}
}
}