import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4' import { Transition } from '@headlessui/react' import { ArrowDownTrayIcon, CheckCircleIcon, ExclamationCircleIcon, FireIcon, PencilIcon, PlusCircleIcon, XMarkIcon, } from '@heroicons/react/20/solid' import { Wallet } from '@project-serum/anchor' import { useWallet } from '@solana/wallet-adapter-react' import mangoStore from '@store/mangoStore' import Decimal from 'decimal.js' import useMangoAccount from 'hooks/useMangoAccount' import useMangoGroup from 'hooks/useMangoGroup' import useSolBalance from 'hooks/useSolBalance' import { useTranslation } from 'next-i18next' import Image from 'next/image' import { ChangeEvent, ReactNode, useCallback, useEffect, useMemo, useState, } from 'react' import { ALPHA_DEPOSIT_LIMIT } from 'utils/constants' import { notify } from 'utils/notifications' import { floorToDecimal, formatFixedDecimals } from 'utils/numbers' import ActionTokenList from './account/ActionTokenList' import ButtonGroup from './forms/ButtonGroup' import Input from './forms/Input' import Label from './forms/Label' import WalletIcon from './icons/WalletIcon' import { walletBalanceForToken } from './modals/DepositModal' import ParticlesBackground from './ParticlesBackground' import EditNftProfilePic from './profile/EditNftProfilePic' import EditProfileForm from './profile/EditProfileForm' import Button, { IconButton, LinkButton } from './shared/Button' import InlineNotification from './shared/InlineNotification' import Loading from './shared/Loading' import MaxAmountButton from './shared/MaxAmountButton' import SolBalanceWarnings from './shared/SolBalanceWarnings' import { useEnhancedWallet } from './wallet/EnhancedWalletProvider' const UserSetup = ({ onClose }: { onClose: () => void }) => { const { t } = useTranslation(['common', 'onboarding', 'swap']) const { group } = useMangoGroup() const { connected, select, wallet, wallets } = useWallet() const { mangoAccount } = useMangoAccount() const mangoAccountLoading = mangoStore((s) => s.mangoAccount.initialLoad) const [accountName, setAccountName] = useState('') const [loadingAccount, setLoadingAccount] = useState(false) const [showSetupStep, setShowSetupStep] = useState(0) const [depositToken, setDepositToken] = useState('USDC') const [depositAmount, setDepositAmount] = useState('') const [submitDeposit, setSubmitDeposit] = useState(false) const [sizePercentage, setSizePercentage] = useState('') const [showEditProfilePic, setShowEditProfilePic] = useState(false) const walletTokens = mangoStore((s) => s.wallet.tokens) const { handleConnect } = useEnhancedWallet() const { maxSolDeposit } = useSolBalance() const exceedsAlphaMax = useMemo(() => { const mangoAccount = mangoStore.getState().mangoAccount.current if (!group || !mangoAccount) return if ( mangoAccount.owner.toString() === '8SSLjXBEVk9nesbhi9UMCA32uijbVBUqWoKPPQPTekzt' ) return false const accountValue = toUiDecimalsForQuote( mangoAccount.getEquity(group)!.toNumber() ) return ( parseFloat(depositAmount) > ALPHA_DEPOSIT_LIMIT || accountValue > ALPHA_DEPOSIT_LIMIT ) }, [depositAmount]) useEffect(() => { if (connected) { setShowSetupStep(2) } }, [connected]) const handleCreateAccount = useCallback(async () => { const client = mangoStore.getState().client const group = mangoStore.getState().group const actions = mangoStore.getState().actions if (!group || !wallet) return setLoadingAccount(true) try { const tx = await client.createMangoAccount( group, 0, accountName || 'Account 1', undefined, // tokenCount undefined, // serum3Count 8, // perpCount 8 // perpOoCount ) actions.fetchMangoAccounts(wallet!.adapter as unknown as Wallet) if (tx) { actions.fetchWalletTokens(wallet!.adapter as unknown as Wallet) // need to update sol balance after account rent setShowSetupStep(3) notify({ title: t('new-account-success'), type: 'success', txid: tx, }) } } catch (e: any) { notify({ title: t('new-account-failed'), txid: e?.signature, type: 'error', }) console.error(e) } finally { setLoadingAccount(false) } }, [accountName, wallet, t]) const handleDeposit = useCallback(async () => { const client = mangoStore.getState().client const group = mangoStore.getState().group const actions = mangoStore.getState().actions const mangoAccount = mangoStore.getState().mangoAccount.current if (!mangoAccount || !group) return const bank = group.banksMapByName.get(depositToken)![0] try { setSubmitDeposit(true) const tx = await client.tokenDeposit( group, mangoAccount, bank.mint, parseFloat(depositAmount) ) notify({ title: 'Transaction confirmed', type: 'success', txid: tx, }) await actions.reloadMangoAccount() setShowSetupStep(4) setSubmitDeposit(false) } catch (e: any) { notify({ title: 'Transaction failed', description: e.message, txid: e?.txid, type: 'error', }) setSubmitDeposit(false) console.error(e) } }, [depositAmount, depositToken, onClose]) useEffect(() => { if (mangoAccount && showSetupStep === 2) { onClose() } }, [mangoAccount, showSetupStep, onClose]) const banks = useMemo(() => { const banks = group?.banksMapByName ? Array.from(group?.banksMapByName, ([key, value]) => { const walletBalance = walletBalanceForToken(walletTokens, key) return { key, value, tokenDecimals: walletBalance.maxDecimals, walletBalance: floorToDecimal( walletBalance.maxAmount, walletBalance.maxDecimals ).toNumber(), walletBalanceValue: walletBalance.maxAmount * value?.[0].uiPrice, } }) : [] return banks }, [group?.banksMapByName, walletTokens]) const depositBank = useMemo(() => { return banks.find((b) => b.key === depositToken) }, [depositToken]) const tokenMax = useMemo(() => { const bank = banks.find((bank) => bank.key === depositToken) if (bank) { return { amount: bank.walletBalance, decimals: bank.tokenDecimals } } return { amount: 0, decimals: 0 } }, [banks, depositToken]) const showInsufficientBalance = tokenMax.amount < Number(depositAmount) const handleSizePercentage = useCallback( (percentage: string) => { setSizePercentage(percentage) let amount = new Decimal(tokenMax.amount).mul(percentage).div(100) if (percentage !== '100') { amount = floorToDecimal(amount, tokenMax.decimals) } setDepositAmount(amount.toString()) }, [tokenMax] ) const handleNextStep = () => { setShowSetupStep(showSetupStep + 1) } return (
next next
onClose()}>

{t('onboarding:intro-heading')}

{t('onboarding:intro-desc')}

{t('onboarding:bullet-1')}

{/*

Deeply liquid markets

*/}

{t('onboarding:bullet-2')}

{t('onboarding:bullet-3')}

{t('onboarding:bullet-4')}

{showSetupStep === 1 ? (

{t('onboarding:connect-wallet')}

{t('onboarding:choose-wallet')}

{wallets?.map((w) => ( ))}
) : null}
{showSetupStep === 2 ? (

{t('onboarding:create-account')}

{t('onboarding:create-account-desc')}

{t('onboarding:skip')}
) : null}
{showSetupStep === 3 ? (

{t('onboarding:fund-account')}

0}>
) => setDepositAmount(e.target.value) } />
handleSizePercentage(p)} values={['10', '25', '50', '75', '100']} unit="%" />

{t('deposit-value')}

{depositBank ? formatFixedDecimals( depositBank.value[0].uiPrice * Number(depositAmount), true ) : ''}

setShowSetupStep(4)}> {t('onboarding:skip')}

{t('token')}

{t('deposit-rate')}

{t('wallet-balance')}

) : null}
{showSetupStep === 4 ? (

{t('onboarding:your-profile')}

{t('onboarding:profile-desc')}

{!showEditProfilePic ? (
setShowEditProfilePic(true)} onboarding /> {t('onboarding:skip-finish')}
) : null}
setShowEditProfilePic(false)} />
) : null}
) } export default UserSetup const UserSetupTransition = ({ show, children, delay = false, }: { show: boolean children: ReactNode delay?: boolean }) => { return ( {children} ) }