diff --git a/packages/app/src/components/SocketBridge/SocketBridge.tsx b/packages/app/src/components/SocketBridge/SocketBridge.tsx index cab0e2c5b4..3fcfc3f2d9 100644 --- a/packages/app/src/components/SocketBridge/SocketBridge.tsx +++ b/packages/app/src/components/SocketBridge/SocketBridge.tsx @@ -2,12 +2,10 @@ import { Bridge } from '@socket.tech/plugin' import { useCallback } from 'react' import styled, { useTheme } from 'styled-components' -import ArrowIcon from 'assets/svg/app/arrow-down.svg' import Connector from 'containers/Connector' import { chain } from 'containers/Connector/config' import { fetchBalances } from 'state/balances/actions' -import { selectFuturesType } from 'state/futures/selectors' -import { useAppDispatch, useAppSelector } from 'state/hooks' +import { useAppDispatch } from 'state/hooks' import { customizeSocket, socketDefaultChains, @@ -19,7 +17,6 @@ const SocketBridge = () => { const { activeChain, signer } = Connector.useContainer() const dispatch = useAppDispatch() const customize = customizeSocket(useTheme()) - const accountType = useAppSelector(selectFuturesType) const onBridgeSuccess = useCallback(() => { dispatch(fetchBalances()) }, [dispatch]) @@ -42,11 +39,6 @@ const SocketBridge = () => { enableSameChainSwaps={true} onBridgeSuccess={onBridgeSuccess} /> - {accountType === 'isolated_margin' && ( - - - - )} ) } diff --git a/packages/app/src/pages/market.tsx b/packages/app/src/pages/market.tsx index eb5db71291..899cce6da4 100644 --- a/packages/app/src/pages/market.tsx +++ b/packages/app/src/pages/market.tsx @@ -22,9 +22,9 @@ import FuturesUnsupportedNetwork from 'sections/futures/Trade/FuturesUnsupported import SwitchToSmartMargin from 'sections/futures/Trade/SwitchToSmartMargin' import TradeIsolatedMargin from 'sections/futures/Trade/TradePanel' import TransferIsolatedMarginModal from 'sections/futures/Trade/TransferIsolatedMarginModal' +import TransferSmartMarginModal from 'sections/futures/Trade/TransferSmartMarginModal' import DelayedOrderConfirmationModal from 'sections/futures/TradeConfirmation/DelayedOrderConfirmationModal' import TradeConfirmationModalCrossMargin from 'sections/futures/TradeConfirmation/TradeConfirmationModalCrossMargin' -import WithdrawSmartMargin from 'sections/futures/TradeCrossMargin/WithdrawSmartMargin' import AppLayout from 'sections/shared/Layout/AppLayout' import { setOpenModal } from 'state/app/reducer' import { selectShowModal, selectShowPositionModal } from 'state/app/selectors' @@ -128,7 +128,10 @@ const Market: MarketComponent = () => { /> )} {openModal === 'futures_cross_withdraw' && ( - dispatch(setOpenModal(null))} /> + dispatch(setOpenModal(null))} + /> )} {openModal === 'futures_confirm_smart_margin_trade' && } @@ -153,7 +156,12 @@ function TradePanelDesktop() { [accountType, isolatedPositionsCount] ) - if (walletAddress && !isL2 && openModal !== 'futures_smart_margin_socket') { + if ( + walletAddress && + !isL2 && + openModal !== 'futures_smart_margin_socket' && + openModal !== 'futures_cross_withdraw' + ) { return } diff --git a/packages/app/src/sections/futures/Trade/TradeBalance.tsx b/packages/app/src/sections/futures/Trade/TradeBalance.tsx index 754c813880..e5430033d0 100644 --- a/packages/app/src/sections/futures/Trade/TradeBalance.tsx +++ b/packages/app/src/sections/futures/Trade/TradeBalance.tsx @@ -49,8 +49,8 @@ const TradeBalance: React.FC = memo(({ isMobile = false }) => const [expanded, setExpanded] = useState(false) const isDepositRequired = useMemo(() => { - return walletBal.lt(MIN_MARGIN_AMOUNT) && withdrawable.eq(0) - }, [walletBal, withdrawable]) + return walletBal.lt(MIN_MARGIN_AMOUNT) && withdrawable.eq(0) && lockedMargin.eq(0) + }, [lockedMargin, walletBal, withdrawable]) const onClickContainer = () => { if (accountType === 'isolated_margin') return diff --git a/packages/app/src/sections/futures/Trade/TransferSmartMarginModal.tsx b/packages/app/src/sections/futures/Trade/TransferSmartMarginModal.tsx new file mode 100644 index 0000000000..2a1dcd4a53 --- /dev/null +++ b/packages/app/src/sections/futures/Trade/TransferSmartMarginModal.tsx @@ -0,0 +1,206 @@ +import { formatDollars } from '@kwenta/sdk/utils' +import { wei } from '@synthetixio/wei' +import dynamic from 'next/dynamic' +import React, { useCallback, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import CaretUpIcon from 'assets/svg/app/caret-up-slim.svg' +import BaseModal from 'components/BaseModal' +import Button from 'components/Button' +import { CardHeader } from 'components/Card' +import Error from 'components/ErrorView' +import NumericInput from 'components/Input/NumericInput' +import { FlexDivRowCentered } from 'components/layout/flex' +import SegmentedControl from 'components/SegmentedControl' +import Spacer from 'components/Spacer' +import { selectTransaction } from 'state/app/selectors' +import { selectSusdBalance } from 'state/balances/selectors' +import { withdrawCrossMargin } from 'state/futures/actions' +import { selectIsSubmittingCrossTransfer, selectWithdrawableMargin } from 'state/futures/selectors' +import { useAppDispatch, useAppSelector } from 'state/hooks' + +type Props = { + onDismiss(): void + defaultTab: 'deposit' | 'withdraw' +} + +const SocketBridge = dynamic(() => import('../../../components/SocketBridge'), { + ssr: false, +}) + +const PLACEHOLDER = '$0.00' + +const TransferSmartMarginModal: React.FC = ({ onDismiss, defaultTab }) => { + const { t } = useTranslation() + const dispatch = useAppDispatch() + + const submitting = useAppSelector(selectIsSubmittingCrossTransfer) + const totalWithdrawable = useAppSelector(selectWithdrawableMargin) + const transactionState = useAppSelector(selectTransaction) + const susdBalance = useAppSelector(selectSusdBalance) + + const [amount, setAmount] = useState('') + const [transferType, setTransferType] = useState(defaultTab === 'deposit' ? 0 : 1) + + const susdBal = transferType === 0 ? susdBalance : totalWithdrawable + + const isDisabled = useMemo(() => { + const amtWei = wei(amount || 0) + return submitting || amtWei.eq(0) || amtWei.gt(totalWithdrawable) + }, [amount, submitting, totalWithdrawable]) + + const handleSetMax = useCallback(() => { + if (transferType === 0) { + setAmount(susdBal.toString()) + } else { + setAmount(totalWithdrawable.toString()) + } + }, [transferType, susdBal, totalWithdrawable]) + + const onChangeTab = (selection: number) => { + setTransferType(selection) + setAmount('') + } + + const onWithdraw = () => { + dispatch(withdrawCrossMargin(wei(amount))) + } + + return ( + + + {transferType === 0 && ( + <> + + {t('futures.market.trade.margin.modal.bridge.title')} + + + + + + )} + + {t('futures.market.trade.margin.modal.balance')}: + + {formatDollars(susdBal)} sUSD + + + {transferType === 0 ? ( + <> + + + {t('futures.market.trade.margin.modal.deposit.disclaimer')} + + + ) : ( + <> + setAmount(v)} + right={ + + {t('futures.market.trade.margin.modal.max')} + + } + /> + + + + )} + + {transactionState?.error && ( + + )} + + ) +} + +export const StyledBaseModal = styled(BaseModal)` + [data-reach-dialog-content] { + width: 400px; + margin-top: 5vh; + } + + .card-header { + padding: 10px 20px 0px; + } +` + +export const BalanceContainer = styled(FlexDivRowCentered)` + margin-bottom: 8px; + p { + margin: 0; + } +` + +export const BalanceText = styled.p<{ $gold?: boolean }>` + color: ${(props) => + props.$gold ? props.theme.colors.selectedTheme.yellow : props.theme.colors.selectedTheme.gray}; + span { + color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; + } +` + +export const MaxButton = styled.button` + height: 22px; + padding: 4px 10px; + background: ${(props) => props.theme.colors.selectedTheme.button.background}; + border-radius: 11px; + font-family: ${(props) => props.theme.fonts.mono}; + font-size: 13px; + line-height: 13px; + border: ${(props) => props.theme.colors.selectedTheme.border}; + color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; + cursor: pointer; +` + +const MinimumAmountDisclaimer = styled.div` + font-size: 12px; + color: ${(props) => props.theme.colors.selectedTheme.button.text.primary}; + text-align: center; +` + +const StyledSegmentedControl = styled(SegmentedControl)` + margin: 16px 0; +` + +const StyledCardHeader = styled(CardHeader)<{ noBorder: boolean }>` + display: flex; + justify-content: space-between; + height: 30px; + font-size: 13px; + font-family: ${(props) => props.theme.fonts.regular}; + border-bottom: ${(props) => (props.noBorder ? 'none' : props.theme.colors.selectedTheme.border)}; + margin-left: 0px; + padding: 0px; + cursor: pointer; +` + +export default TransferSmartMarginModal