From 3228ff7231b2030dd4e805c8e2fc96cc67399acf Mon Sep 17 00:00:00 2001 From: GrandSchtroumpf Date: Fri, 21 Jun 2024 17:05:23 +0200 Subject: [PATCH] add edit budget on disposable / recurring edit price --- src/components/strategies/common/types.ts | 3 + .../strategies/edit/EditOverlappingPrice.tsx | 47 +++--- .../strategies/edit/EditPriceFields.tsx | 43 ++++- .../overlapping/OverlappingAction.tsx | 152 ++++++++++-------- src/libs/routing/routes/strategyEdit.ts | 6 + .../strategies/edit/prices/disposable.tsx | 47 ++++-- .../strategies/edit/prices/recurring.tsx | 21 ++- 7 files changed, 202 insertions(+), 117 deletions(-) diff --git a/src/components/strategies/common/types.ts b/src/components/strategies/common/types.ts index fe394b763..2baa11bd7 100644 --- a/src/components/strategies/common/types.ts +++ b/src/components/strategies/common/types.ts @@ -14,3 +14,6 @@ export interface OverlappingOrder extends BaseOrder { export interface OrderBlock extends BaseOrder { settings: StrategySettings; } +export interface EditOrderBlock extends OrderBlock { + action: 'deposit' | 'withdraw'; +} diff --git a/src/components/strategies/edit/EditOverlappingPrice.tsx b/src/components/strategies/edit/EditOverlappingPrice.tsx index 70879e69f..c33093287 100644 --- a/src/components/strategies/edit/EditOverlappingPrice.tsx +++ b/src/components/strategies/edit/EditOverlappingPrice.tsx @@ -143,6 +143,16 @@ export const EditOverlappingPrice: FC = (props) => { return 'Notice: your strategy is “out of the money” and will be traded when the market price moves into your price range.'; })(); + const budgetWarning = (() => { + if (action !== 'deposit') return; + if (hasArbOpportunity(order0.marginalPrice, spread, marketPrice)) { + const buyBudgetChanged = strategy.order0.balance !== order0.budget; + const sellBudgetChanged = strategy.order1.balance !== order1.budget; + if (!buyBudgetChanged && !sellBudgetChanged) return; + return 'Please note that the deposit might create an arb opportunity.'; + } + })(); + useEffect(() => { if (anchor === 'buy' && aboveMarket) { set('anchor', 'sell'); @@ -154,16 +164,6 @@ export const EditOverlappingPrice: FC = (props) => { } }, [anchor, aboveMarket, belowMarket, set]); - const budgetWarning = (() => { - if (action !== 'deposit') return; - if (hasArbOpportunity(order0.marginalPrice, spread, marketPrice)) { - const buyBudgetChanged = strategy.order0.balance !== order0.budget; - const sellBudgetChanged = strategy.order1.balance !== order1.budget; - if (!buyBudgetChanged && !sellBudgetChanged) return; - return 'Please note that the deposit might create an arb opportunity.'; - } - })(); - const setMarketPrice = (price: string) => { setTouched(true); set('marketPrice', price); @@ -303,19 +303,20 @@ export const EditOverlappingPrice: FC = (props) => { disableSell={belowMarket} /> {anchor && ( - +
+ +
)} {anchor && (
) => void; + budget: string; + action?: 'deposit' | 'withdraw'; + setOrder: (order: Partial) => void; settings?: ReactNode; warnings?: (string | undefined)[]; error?: string; @@ -26,6 +32,7 @@ interface Props { export const EditStrategyPriceField: FC = ({ order, initialBudget, + budget, setOrder, buy = false, settings, @@ -44,7 +51,7 @@ export const EditStrategyPriceField: FC = ({ const inputTitle = ( <> - + 1 = ({ const setPrice = (price: string) => setOrder({ min: price, max: price }); const setMin = (min: string) => setOrder({ min }); const setMax = (max: string) => setOrder({ max }); + const setBudget = (budget: string) => { + console.log('set budget', { buy }); + setOrder({ budget }); + }; + const setAction = (action: 'deposit' | 'withdraw') => { + console.log('set action', { action, buy }); + setOrder({ action, budget: undefined }); + }; const setSettings = (settings: StrategySettings) => { const order = buy ? order0 : order1; const initialSettings = isLimitOrder(order) ? 'limit' : 'range'; @@ -127,6 +142,17 @@ export const EditStrategyPriceField: FC = ({ /> )} + = ({ balance={balance.data ?? '0'} buy={buy} /> + void; - error: string; + error?: string; warning?: string; } @@ -38,6 +37,9 @@ export const OverlappingAction: FC = (props) => { error, warning, } = props; + const actionName = useId(); + const depositId = useId(); + const withdrawId = useId(); const opened = useRef(!!anchor && !!budget); const token = anchor === 'buy' ? quote : base; @@ -50,77 +52,85 @@ export const OverlappingAction: FC = (props) => { if (e.nativeEvent.oldState === 'open') setBudget(''); }; + const budgetError = (() => { + const value = new SafeDecimal(budget); + if (action === 'deposit') { + if (balance.isLoading || !balance.data) return; + if (value.gt(balance.data)) return 'Insufficient balance'; + } else { + if (value.gt(allocatedBudget)) return 'Insufficient funds'; + } + return ''; + })(); + + console.log({ anchor, action }); + return ( - -
- + + + 2 + +

Edit Budget

+ (Optional) + +
+
+

+ Please select the action and amount of tokens +

+
- - 2 - -

Edit Budget

- (Optional) - -
-
-

- Please select the action and amount of tokens -

-
e.target.checked && setAction('deposit')} + /> +
- + Deposit + + e.target.checked && setAction('withdraw')} /> +
-
-
+ + + ); }; diff --git a/src/libs/routing/routes/strategyEdit.ts b/src/libs/routing/routes/strategyEdit.ts index 2a64fd689..e48e50e43 100644 --- a/src/libs/routing/routes/strategyEdit.ts +++ b/src/libs/routing/routes/strategyEdit.ts @@ -70,8 +70,10 @@ export const editPricesDisposable = new Route({ editType: validLiteral(['editPrices', 'renew']), min: validNumber, max: validNumber, + budget: validNumber, settings: validLiteral(['limit', 'range']), direction: validLiteral(['buy', 'sell']), + action: validLiteral(['deposit', 'withdraw']), }), }); @@ -98,10 +100,14 @@ export const editPricesRecurring = new Route({ editType: validLiteral(['editPrices', 'renew']), buyMin: validNumber, buyMax: validNumber, + buyBudget: validNumber, buySettings: validLiteral(['limit', 'range']), + buyAction: validLiteral(['deposit', 'withdraw']), sellMin: validNumber, sellMax: validNumber, + sellBudget: validNumber, sellSettings: validLiteral(['limit', 'range']), + sellAction: validLiteral(['deposit', 'withdraw']), }), }); diff --git a/src/pages/strategies/edit/prices/disposable.tsx b/src/pages/strategies/edit/prices/disposable.tsx index ff259a1f1..9997d66d3 100644 --- a/src/pages/strategies/edit/prices/disposable.tsx +++ b/src/pages/strategies/edit/prices/disposable.tsx @@ -3,7 +3,7 @@ import { useEditStrategyCtx } from 'components/strategies/edit/EditStrategyConte import { roundSearchParam } from 'utils/helpers'; import { EditStrategyPriceField } from 'components/strategies/edit/EditPriceFields'; import { StrategyDirection, StrategySettings } from 'libs/routing'; -import { OrderBlock } from 'components/strategies/common/types'; +import { EditOrderBlock } from 'components/strategies/common/types'; import { useMarketPrice } from 'hooks/useMarketPrice'; import { emptyOrder, @@ -19,7 +19,11 @@ import { BudgetDescription, BudgetDistribution, } from 'components/strategies/common/BudgetDistribution'; -import { getDeposit, getWithdraw } from 'components/strategies/edit/utils'; +import { + getDeposit, + getTotalBudget, + getWithdraw, +} from 'components/strategies/edit/utils'; import { useGetTokenBalance } from 'libs/queries'; export interface EditDisposableStrategySearch { @@ -28,6 +32,8 @@ export interface EditDisposableStrategySearch { max: string; settings: StrategySettings; direction: StrategyDirection; + action?: 'deposit' | 'withdraw'; + budget?: string; } const url = '/strategies/edit/$strategyId/prices/disposable'; @@ -38,23 +44,29 @@ export const EditStrategyDisposablePage = () => { const search = useSearch({ from: url }); const marketPrice = useMarketPrice({ base, quote }); - const buy = search.direction !== 'sell'; - const otherOrder = buy ? order1 : order0; + const isBuy = search.direction !== 'sell'; + const otherOrder = isBuy ? order1 : order0; const { setOrder, setDirection } = useSetDisposableOrder(url, otherOrder); const baseBalance = useGetTokenBalance(base); const quoteBalance = useGetTokenBalance(quote); - const initialBudget = buy ? order0.balance : order1.balance; - const order: OrderBlock = { + const initialBudget = isBuy ? order0.balance : order1.balance; + const totalBudget = getTotalBudget( + search.action ?? 'deposit', + initialBudget, + search.budget + ); + const order: EditOrderBlock = { min: search.min ?? '', max: search.max ?? '', - budget: initialBudget, + budget: totalBudget, settings: search.settings ?? 'limit', + action: search.action ?? 'deposit', }; const orders = { - buy: buy ? order : emptyOrder(), - sell: buy ? emptyOrder() : order, + buy: isBuy ? order : emptyOrder(), + sell: isBuy ? emptyOrder() : order, }; const hasChanged = (() => { @@ -72,12 +84,12 @@ export const EditStrategyDisposablePage = () => { marketPrice, min: search.min, max: search.max, - buy: search.direction !== 'sell', + buy: isBuy, }); const fromRecurring = !isZero(order0.startRate) && !isZero(order1.startRate); - const buyBudgetChanges = order0.balance !== orders.buy.budget; - const sellBudgetChanges = order1.balance !== orders.sell.budget; + const buyBudgetChanges = !isBuy && totalBudget !== order0.balance; + const sellBudgetChanges = isBuy && totalBudget !== order1.balance; return ( { > setDirection('buy')} - isActive={buy} + isActive={isBuy} data-testid="tab-buy" > Buy setDirection('sell')} - isActive={!buy} + isActive={!isBuy} data-testid="tab-sell" > Sell @@ -166,7 +179,9 @@ export const EditStrategyDisposablePage = () => {
)} {fromRecurring && ( - {buy ? 'Sell High' : 'Buy Low'} order will be removed + + {isBuy ? 'Sell High' : 'Buy Low'} order will be removed + )} ); diff --git a/src/pages/strategies/edit/prices/recurring.tsx b/src/pages/strategies/edit/prices/recurring.tsx index 93bcbffee..21215a016 100644 --- a/src/pages/strategies/edit/prices/recurring.tsx +++ b/src/pages/strategies/edit/prices/recurring.tsx @@ -11,15 +11,20 @@ import { checkIfOrdersOverlap, checkIfOrdersReversed, } from 'components/strategies/utils'; +import { getTotalBudget } from 'components/strategies/edit/utils'; export interface EditRecurringStrategySearch { editType: 'editPrices' | 'renew'; buyMin: string; buyMax: string; buySettings: StrategySettings; + buyBudget?: string; + buyAction?: 'deposit' | 'withdraw'; sellMin: string; sellMax: string; sellSettings: StrategySettings; + sellBudget?: string; + sellAction?: 'deposit' | 'withdraw'; } type Search = EditRecurringStrategySearch; @@ -55,14 +60,24 @@ export const EditStrategyRecurringPage = () => { buy: { min: search.buyMin, max: search.buyMax, - budget: order0.balance, settings: search.buySettings, + action: search.buyAction ?? 'deposit', + budget: getTotalBudget( + search.buyAction ?? 'deposit', + order0.balance, + search.buyBudget + ), }, sell: { min: search.sellMin, max: search.sellMax, - budget: order1.balance, settings: search.sellSettings, + action: search.sellAction ?? 'deposit', + budget: getTotalBudget( + search.sellAction ?? 'deposit', + order1.balance, + search.sellBudget + ), }, }; @@ -102,6 +117,7 @@ export const EditStrategyRecurringPage = () => { > { />