From ea2ef43b419ec774323f387483fb7ec80f7315ae Mon Sep 17 00:00:00 2001 From: saml33 Date: Mon, 26 Dec 2022 22:58:17 +1100 Subject: [PATCH] move leverage calculation to components --- components/trade/AdvancedTradeForm.tsx | 113 ++-------------------- components/trade/MaxSizeButton.tsx | 127 +++++++++++++++++++++++++ components/trade/PerpButtonGroup.tsx | 38 +++++++- components/trade/PerpSlider.tsx | 30 +++++- components/trade/SpotButtonGroup.tsx | 30 +++++- components/trade/SpotSlider.tsx | 35 ++++++- 6 files changed, 249 insertions(+), 124 deletions(-) create mode 100644 components/trade/MaxSizeButton.tsx diff --git a/components/trade/AdvancedTradeForm.tsx b/components/trade/AdvancedTradeForm.tsx index bff6c87b..6e227461 100644 --- a/components/trade/AdvancedTradeForm.tsx +++ b/components/trade/AdvancedTradeForm.tsx @@ -33,14 +33,13 @@ import PerpButtonGroup from './PerpButtonGroup' import SolBalanceWarnings from '@components/shared/SolBalanceWarnings' import useJupiterMints from 'hooks/useJupiterMints' import useSelectedMarket from 'hooks/useSelectedMarket' -import { getDecimalCount, trimDecimals } from 'utils/numbers' +import { getDecimalCount } from 'utils/numbers' import LogoWithFallback from '@components/shared/LogoWithFallback' import useIpAddress from 'hooks/useIpAddress' import ButtonGroup from '@components/forms/ButtonGroup' import TradeSummary from './TradeSummary' import useMangoAccount from 'hooks/useMangoAccount' -import MaxAmountButton from '@components/shared/MaxAmountButton' -import { FadeInFadeOut } from '@components/shared/Transitions' +import MaxSizeButton from './MaxSizeButton' const set = mangoStore.getState().set @@ -233,42 +232,6 @@ const AdvancedTradeForm = () => { } }, [oraclePrice, selectedMarket, tickDecimals, tradeForm]) - const leverageMax = useMemo(() => { - const group = mangoStore.getState().group - if (!mangoAccount || !group || !selectedMarket) return 100 - - try { - if (selectedMarket instanceof Serum3Market) { - if (tradeForm.side === 'buy') { - return mangoAccount.getMaxQuoteForSerum3BidUi( - group, - selectedMarket.serumMarketExternal - ) - } else { - return mangoAccount.getMaxBaseForSerum3AskUi( - group, - selectedMarket.serumMarketExternal - ) - } - } else { - if (tradeForm.side === 'buy') { - return mangoAccount.getMaxQuoteForPerpBidUi( - group, - selectedMarket.perpMarketIndex - ) - } else { - return mangoAccount.getMaxBaseForPerpAskUi( - group, - selectedMarket.perpMarketIndex - ) - } - } - } catch (e) { - console.error('Error calculating max leverage: spot btn group: ', e) - return 0 - } - }, [mangoAccount, tradeForm.side, selectedMarket]) - const handlePlaceOrder = useCallback(async () => { const client = mangoStore.getState().client const group = mangoStore.getState().group @@ -372,59 +335,6 @@ const AdvancedTradeForm = () => { return minOrderDecimals }, [selectedMarket]) - const handleMax = useCallback(() => { - const set = mangoStore.getState().set - set((state) => { - if (tradeForm.side === 'buy') { - state.tradeForm.quoteSize = trimDecimals( - leverageMax, - tickDecimals - ).toFixed(tickDecimals) - if (tradeForm.tradeType === 'Market' || !tradeForm.price) { - state.tradeForm.baseSize = trimDecimals( - leverageMax / oraclePrice, - minOrderDecimals - ).toFixed(minOrderDecimals) - } else { - state.tradeForm.baseSize = trimDecimals( - leverageMax / parseFloat(tradeForm.price), - minOrderDecimals - ).toFixed(minOrderDecimals) - } - } else { - state.tradeForm.baseSize = trimDecimals( - leverageMax, - tickDecimals - ).toFixed(tickDecimals) - if (tradeForm.tradeType === 'Market' || !tradeForm.price) { - state.tradeForm.quoteSize = trimDecimals( - leverageMax * oraclePrice, - minOrderDecimals - ).toFixed(minOrderDecimals) - } else { - state.tradeForm.quoteSize = trimDecimals( - leverageMax * parseFloat(tradeForm.price), - minOrderDecimals - ).toFixed(minOrderDecimals) - } - } - }) - }, [leverageMax, tradeForm]) - - const maxAmount = useMemo(() => { - if (!tradeForm.price) return '0' - if (tradeForm.side === 'buy') { - return trimDecimals( - leverageMax / parseFloat(tradeForm.price), - tickDecimals - ).toFixed(tickDecimals) - } else { - return trimDecimals(leverageMax, minOrderDecimals).toFixed( - minOrderDecimals - ) - } - }, [leverageMax, minOrderDecimals, tickDecimals, tradeForm]) - return (
@@ -481,17 +391,10 @@ const AdvancedTradeForm = () => {
) : null} -
-

{t('trade:size')}

- - - -
+
@@ -556,26 +459,22 @@ const AdvancedTradeForm = () => { {selectedMarket instanceof Serum3Market ? ( tradeFormSizeUi === 'slider' ? ( ) : ( ) ) : tradeFormSizeUi === 'slider' ? ( ) : ( diff --git a/components/trade/MaxSizeButton.tsx b/components/trade/MaxSizeButton.tsx new file mode 100644 index 00000000..ea5e6a8f --- /dev/null +++ b/components/trade/MaxSizeButton.tsx @@ -0,0 +1,127 @@ +import { Serum3Market } from '@blockworks-foundation/mango-v4' +import MaxAmountButton from '@components/shared/MaxAmountButton' +import { FadeInFadeOut } from '@components/shared/Transitions' +import mangoStore from '@store/mangoStore' +import useMangoAccount from 'hooks/useMangoAccount' +import useSelectedMarket from 'hooks/useSelectedMarket' +import { useTranslation } from 'next-i18next' +import { useCallback, useMemo } from 'react' +import { trimDecimals } from 'utils/numbers' + +const MaxSizeButton = ({ + minOrderDecimals, + tickDecimals, +}: { + minOrderDecimals: number + tickDecimals: number +}) => { + const { t } = useTranslation(['common', 'trade']) + const { mangoAccount } = useMangoAccount() + const { selectedMarket, price: oraclePrice } = useSelectedMarket() + const tradeForm = mangoStore((s) => s.tradeForm) + + const leverageMax = useMemo(() => { + const group = mangoStore.getState().group + if (!mangoAccount || !group || !selectedMarket) return 0 + + try { + if (selectedMarket instanceof Serum3Market) { + if (tradeForm.side === 'buy') { + return mangoAccount.getMaxQuoteForSerum3BidUi( + group, + selectedMarket.serumMarketExternal + ) + } else { + return mangoAccount.getMaxBaseForSerum3AskUi( + group, + selectedMarket.serumMarketExternal + ) + } + } else { + if (tradeForm.side === 'buy') { + return mangoAccount.getMaxQuoteForPerpBidUi( + group, + selectedMarket.perpMarketIndex + ) + } else { + return mangoAccount.getMaxBaseForPerpAskUi( + group, + selectedMarket.perpMarketIndex + ) + } + } + } catch (e) { + console.error('Error calculating max leverage: spot btn group: ', e) + return 0 + } + }, [mangoAccount, tradeForm.side, selectedMarket]) + + const handleMax = useCallback(() => { + const set = mangoStore.getState().set + set((state) => { + if (tradeForm.side === 'buy') { + state.tradeForm.quoteSize = trimDecimals( + leverageMax, + tickDecimals + ).toFixed(tickDecimals) + if (tradeForm.tradeType === 'Market' || !tradeForm.price) { + state.tradeForm.baseSize = trimDecimals( + leverageMax / oraclePrice, + minOrderDecimals + ).toFixed(minOrderDecimals) + } else { + state.tradeForm.baseSize = trimDecimals( + leverageMax / parseFloat(tradeForm.price), + minOrderDecimals + ).toFixed(minOrderDecimals) + } + } else { + state.tradeForm.baseSize = trimDecimals( + leverageMax, + tickDecimals + ).toFixed(tickDecimals) + if (tradeForm.tradeType === 'Market' || !tradeForm.price) { + state.tradeForm.quoteSize = trimDecimals( + leverageMax * oraclePrice, + minOrderDecimals + ).toFixed(minOrderDecimals) + } else { + state.tradeForm.quoteSize = trimDecimals( + leverageMax * parseFloat(tradeForm.price), + minOrderDecimals + ).toFixed(minOrderDecimals) + } + } + }) + }, [leverageMax, tradeForm]) + + const maxAmount = useMemo(() => { + if (!tradeForm.price) return '0' + if (tradeForm.side === 'buy') { + return trimDecimals( + leverageMax / parseFloat(tradeForm.price), + tickDecimals + ).toFixed(tickDecimals) + } else { + return trimDecimals(leverageMax, minOrderDecimals).toFixed( + minOrderDecimals + ) + } + }, [leverageMax, minOrderDecimals, tickDecimals, tradeForm]) + + return ( +
+

{t('trade:size')}

+ + + +
+ ) +} + +export default MaxSizeButton diff --git a/components/trade/PerpButtonGroup.tsx b/components/trade/PerpButtonGroup.tsx index 15420d7a..92de86d6 100644 --- a/components/trade/PerpButtonGroup.tsx +++ b/components/trade/PerpButtonGroup.tsx @@ -1,24 +1,52 @@ +import { PerpMarket } from '@blockworks-foundation/mango-v4' import ButtonGroup from '@components/forms/ButtonGroup' import mangoStore from '@store/mangoStore' -import { useCallback, useState } from 'react' +import useMangoAccount from 'hooks/useMangoAccount' +import useSelectedMarket from 'hooks/useSelectedMarket' +import { useCallback, useMemo, useState } from 'react' import { trimDecimals } from 'utils/numbers' const PerpButtonGroup = ({ - max, minOrderDecimals, tickDecimals, }: { - max: number minOrderDecimals: number tickDecimals: number }) => { + const side = mangoStore((s) => s.tradeForm.side) + const { selectedMarket } = useSelectedMarket() + const { mangoAccount } = useMangoAccount() const [sizePercentage, setSizePercentage] = useState('') + const tradeFormPrice = mangoStore((s) => s.tradeForm.price) + + const leverageMax = useMemo(() => { + const group = mangoStore.getState().group + if (!mangoAccount || !group || !selectedMarket) return 100 + if (!(selectedMarket instanceof PerpMarket)) return 100 + + try { + if (side === 'buy') { + return mangoAccount.getMaxQuoteForPerpBidUi( + group, + selectedMarket.perpMarketIndex + ) + } else { + return mangoAccount.getMaxBaseForPerpAskUi( + group, + selectedMarket.perpMarketIndex + ) + } + } catch (e) { + console.error('Error calculating max leverage perp btn grp: ', e) + return 0 + } + }, [side, selectedMarket, mangoAccount, tradeFormPrice]) const handleSizePercentage = useCallback( (percentage: string) => { const set = mangoStore.getState().set setSizePercentage(percentage) - const size = max * (Number(percentage) / 100) + const size = leverageMax * (Number(percentage) / 100) set((s) => { if (s.tradeForm.side === 'buy') { @@ -48,7 +76,7 @@ const PerpButtonGroup = ({ } }) }, - [max, minOrderDecimals, tickDecimals] + [leverageMax, minOrderDecimals, tickDecimals] ) return ( diff --git a/components/trade/PerpSlider.tsx b/components/trade/PerpSlider.tsx index e3fc731a..217b6e06 100644 --- a/components/trade/PerpSlider.tsx +++ b/components/trade/PerpSlider.tsx @@ -1,20 +1,21 @@ import { PerpMarket } from '@blockworks-foundation/mango-v4' import LeverageSlider from '@components/shared/LeverageSlider' import mangoStore from '@store/mangoStore' +import useMangoAccount from 'hooks/useMangoAccount' import useSelectedMarket from 'hooks/useSelectedMarket' import { useCallback, useMemo } from 'react' import { trimDecimals } from 'utils/numbers' const PerpSlider = ({ - max, minOrderDecimals, tickDecimals, }: { - max: number minOrderDecimals: number tickDecimals: number }) => { + const side = mangoStore((s) => s.tradeForm.side) const { selectedMarket, price: marketPrice } = useSelectedMarket() + const { mangoAccount } = useMangoAccount() const tradeForm = mangoStore((s) => s.tradeForm) const step = useMemo(() => { @@ -24,6 +25,29 @@ const PerpSlider = ({ return 0.01 }, [selectedMarket]) + const leverageMax = useMemo(() => { + const group = mangoStore.getState().group + if (!mangoAccount || !group || !selectedMarket) return 100 + if (!(selectedMarket instanceof PerpMarket)) return 100 + + try { + if (side === 'buy') { + return mangoAccount.getMaxQuoteForPerpBidUi( + group, + selectedMarket.perpMarketIndex + ) + } else { + return mangoAccount.getMaxBaseForPerpAskUi( + group, + selectedMarket.perpMarketIndex + ) + } + } catch (e) { + console.error('Error calculating max leverage for PerpSlider: ', e) + return 0 + } + }, [side, selectedMarket, mangoAccount]) + const handleSlide = useCallback( (val: string) => { const set = mangoStore.getState().set @@ -66,7 +90,7 @@ const PerpSlider = ({ ? parseFloat(tradeForm.quoteSize) : parseFloat(tradeForm.baseSize) } - leverageMax={max} + leverageMax={leverageMax} onChange={handleSlide} step={step} /> diff --git a/components/trade/SpotButtonGroup.tsx b/components/trade/SpotButtonGroup.tsx index 32780998..5b0ce147 100644 --- a/components/trade/SpotButtonGroup.tsx +++ b/components/trade/SpotButtonGroup.tsx @@ -1,16 +1,15 @@ +import { Serum3Market } from '@blockworks-foundation/mango-v4' import ButtonGroup from '@components/forms/ButtonGroup' import mangoStore from '@store/mangoStore' import useMangoAccount from 'hooks/useMangoAccount' import useSelectedMarket from 'hooks/useSelectedMarket' -import { useCallback, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { trimDecimals } from 'utils/numbers' const SpotButtonGroup = ({ - max, minOrderDecimals, tickDecimals, }: { - max: number minOrderDecimals: number tickDecimals: number }) => { @@ -19,11 +18,34 @@ const SpotButtonGroup = ({ const { mangoAccount } = useMangoAccount() const [sizePercentage, setSizePercentage] = useState('') + const leverageMax = useMemo(() => { + const group = mangoStore.getState().group + if (!mangoAccount || !group || !selectedMarket) return 100 + if (!(selectedMarket instanceof Serum3Market)) return 100 + + try { + if (side === 'buy') { + return mangoAccount.getMaxQuoteForSerum3BidUi( + group, + selectedMarket.serumMarketExternal + ) + } else { + return mangoAccount.getMaxBaseForSerum3AskUi( + group, + selectedMarket.serumMarketExternal + ) + } + } catch (e) { + console.error('Error calculating max leverage: spot btn group: ', e) + return 0 + } + }, [side, selectedMarket, mangoAccount]) + const handleSizePercentage = useCallback( (percentage: string) => { const set = mangoStore.getState().set setSizePercentage(percentage) - const size = max * (Number(percentage) / 100) + const size = leverageMax * (Number(percentage) / 100) set((s) => { if (s.tradeForm.side === 'buy') { diff --git a/components/trade/SpotSlider.tsx b/components/trade/SpotSlider.tsx index 3c30930e..e7ee3cdc 100644 --- a/components/trade/SpotSlider.tsx +++ b/components/trade/SpotSlider.tsx @@ -1,21 +1,46 @@ +import { Serum3Market } from '@blockworks-foundation/mango-v4' import LeverageSlider from '@components/shared/LeverageSlider' import mangoStore from '@store/mangoStore' +import useMangoAccount from 'hooks/useMangoAccount' import useSelectedMarket from 'hooks/useSelectedMarket' -import { useCallback } from 'react' +import { useCallback, useMemo } from 'react' import { trimDecimals } from 'utils/numbers' const SpotSlider = ({ - max, minOrderDecimals, tickDecimals, }: { - max: number minOrderDecimals: number tickDecimals: number }) => { - const { price: marketPrice } = useSelectedMarket() + const side = mangoStore((s) => s.tradeForm.side) + const { selectedMarket, price: marketPrice } = useSelectedMarket() + const { mangoAccount } = useMangoAccount() const tradeForm = mangoStore((s) => s.tradeForm) + const leverageMax = useMemo(() => { + const group = mangoStore.getState().group + if (!mangoAccount || !group || !selectedMarket) return 100 + if (!(selectedMarket instanceof Serum3Market)) return 100 + + try { + if (side === 'buy') { + return mangoAccount.getMaxQuoteForSerum3BidUi( + group, + selectedMarket.serumMarketExternal + ) + } else { + return mangoAccount.getMaxBaseForSerum3AskUi( + group, + selectedMarket.serumMarketExternal + ) + } + } catch (e) { + console.error('Error calculating max leverage for spot slider: ', e) + return 0 + } + }, [side, selectedMarket, mangoAccount]) + const handleSlide = useCallback( (val: string) => { const set = mangoStore.getState().set @@ -58,7 +83,7 @@ const SpotSlider = ({ ? parseFloat(tradeForm.quoteSize) : parseFloat(tradeForm.baseSize) } - leverageMax={max} + leverageMax={leverageMax} onChange={handleSlide} step={0.01} />