Skip to content

Commit

Permalink
add edit budget on disposable / recurring edit price
Browse files Browse the repository at this point in the history
  • Loading branch information
GrandSchtroumpf committed Jun 24, 2024
1 parent d151fbd commit 3228ff7
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 117 deletions.
3 changes: 3 additions & 0 deletions src/components/strategies/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ export interface OverlappingOrder extends BaseOrder {
export interface OrderBlock extends BaseOrder {
settings: StrategySettings;
}
export interface EditOrderBlock extends OrderBlock {
action: 'deposit' | 'withdraw';
}
47 changes: 24 additions & 23 deletions src/components/strategies/edit/EditOverlappingPrice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ export const EditOverlappingPrice: FC<Props> = (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');
Expand All @@ -154,16 +164,6 @@ export const EditOverlappingPrice: FC<Props> = (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);
Expand Down Expand Up @@ -303,19 +303,20 @@ export const EditOverlappingPrice: FC<Props> = (props) => {
disableSell={belowMarket}
/>
{anchor && (
<OverlappingAction
base={base}
quote={quote}
anchor={anchor}
action={action}
setAction={setAction}
budget={budget ?? ''}
setBudget={setBudget}
buyBudget={initialBuyBudget}
sellBudget={initialSellBudget}
error={budgetError}
warning={budgetWarning}
/>
<article className="rounded-10 bg-background-900 flex w-full flex-col gap-16 p-20">
<OverlappingAction
base={base}
quote={quote}
anchor={anchor}
action={action}
setAction={setAction}
budget={budget ?? ''}
setBudget={setBudget}
buyBudget={initialBuyBudget}
sellBudget={initialSellBudget}
warning={budgetWarning}
/>
</article>
)}
{anchor && (
<article
Expand Down
43 changes: 38 additions & 5 deletions src/components/strategies/edit/EditPriceFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ import { FullOutcome } from 'components/strategies/FullOutcome';
import { OrderHeader } from 'components/strategies/common/OrderHeader';
import { InputRange } from 'components/strategies/common/InputRange';
import { InputLimit } from 'components/strategies/common/InputLimit';
import { OrderBlock } from 'components/strategies/common/types';
import { EditOrderBlock } from 'components/strategies/common/types';
import { useEditStrategyCtx } from './EditStrategyContext';
import { BudgetDistribution } from '../common/BudgetDistribution';
import {
BudgetDescription,
BudgetDistribution,
} from '../common/BudgetDistribution';
import { getDeposit, getWithdraw } from './utils';
import { useGetTokenBalance } from 'libs/queries';
import { StrategySettings } from 'libs/routing';
import { isLimitOrder, resetPrice } from '../common/utils';
import { OverlappingAction } from '../overlapping/OverlappingAction';

interface Props {
order: OrderBlock;
order: EditOrderBlock;
buy?: boolean;
initialBudget: string;
setOrder: (order: Partial<OrderBlock>) => void;
budget: string;
action?: 'deposit' | 'withdraw';
setOrder: (order: Partial<EditOrderBlock>) => void;
settings?: ReactNode;
warnings?: (string | undefined)[];
error?: string;
Expand All @@ -26,6 +32,7 @@ interface Props {
export const EditStrategyPriceField: FC<Props> = ({
order,
initialBudget,
budget,
setOrder,
buy = false,
settings,
Expand All @@ -44,7 +51,7 @@ export const EditStrategyPriceField: FC<Props> = ({

const inputTitle = (
<>
<span className="flex size-16 items-center justify-center rounded-full bg-white/10 text-[10px] text-white/60">
<span className="flex h-16 w-16 items-center justify-center rounded-full bg-black text-[10px] text-white/60">
1
</span>
<Tooltip
Expand All @@ -67,6 +74,14 @@ export const EditStrategyPriceField: FC<Props> = ({
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';
Expand Down Expand Up @@ -127,6 +142,17 @@ export const EditStrategyPriceField: FC<Props> = ({
/>
)}
</fieldset>
<OverlappingAction
base={base}
quote={quote}
anchor={buy ? 'buy' : 'sell'}
action={order.action}
setAction={setAction}
budget={budget}
setBudget={setBudget}
buyBudget={strategy.order0.balance}
sellBudget={strategy.order1.balance}
/>
<BudgetDistribution
token={token}
initialBudget={initialBudget}
Expand All @@ -135,6 +161,13 @@ export const EditStrategyPriceField: FC<Props> = ({
balance={balance.data ?? '0'}
buy={buy}
/>
<BudgetDescription
token={token}
initialBudget={initialBudget}
withdraw={getWithdraw(initialBudget, order.budget)}
deposit={getDeposit(initialBudget, order.budget)}
balance={balance.data ?? '0'}
/>
<FullOutcome
min={order.min}
max={order.max}
Expand Down
152 changes: 81 additions & 71 deletions src/components/strategies/overlapping/OverlappingAction.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { FC, SyntheticEvent, useRef } from 'react';
import { FC, SyntheticEvent, useId, useRef } from 'react';
import { Token } from 'libs/tokens';
import { ReactComponent as IconDeposit } from 'assets/icons/deposit.svg';
import { ReactComponent as IconWithdraw } from 'assets/icons/withdraw.svg';
import { ReactComponent as IconChevron } from 'assets/icons/chevron.svg';
import { useGetTokenBalance } from 'libs/queries';
import { cn } from 'utils/helpers';
import { InputBudget, BudgetAction } from '../common/InputBudget';
import { m } from 'libs/motion';
import { items } from '../common/variants';
import { SafeDecimal } from 'libs/safedecimal';
import style from './OverlappingBudget.module.css';

interface Props {
Expand All @@ -20,7 +19,7 @@ interface Props {
anchor: 'buy' | 'sell';
action?: BudgetAction;
setAction: (action: BudgetAction) => void;
error: string;
error?: string;
warning?: string;
}

Expand All @@ -38,6 +37,9 @@ export const OverlappingAction: FC<Props> = (props) => {
error,
warning,
} = props;
const actionName = useId();
const depositId = useId();
const withdrawId = useId();
const opened = useRef(!!anchor && !!budget);

const token = anchor === 'buy' ? quote : base;
Expand All @@ -50,77 +52,85 @@ export const OverlappingAction: FC<Props> = (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 (
<m.article
variants={items}
className="rounded-10 bg-background-900 flex w-full flex-col gap-16 p-20"
>
<details open={opened.current} onToggle={onToggle}>
<summary
className="flex cursor-pointer items-center gap-8"
data-testid="budget-summary"
<details open={opened.current} onToggle={onToggle}>
<summary
className="flex cursor-pointer items-center gap-8"
data-testid="budget-summary"
>
<span className="flex h-16 w-16 items-center justify-center rounded-full bg-black text-[10px] text-white/60">
2
</span>
<h3 className="text-16 font-weight-500">Edit Budget</h3>
<span className="text-12 text-white/60">(Optional)</span>
<IconChevron className="toggle h-14 w-14" />
</summary>
<div className="flex flex-col gap-16">
<p className="text-14 text-white/80">
Please select the action and amount of tokens
</p>
<div
role="radiogroup"
className="border-background-700 flex gap-2 self-start rounded-full border-2 p-4"
>
<span className="flex h-16 w-16 items-center justify-center rounded-full bg-black text-[10px] text-white/60">
2
</span>
<h3 className="text-16 font-weight-500">Edit Budget</h3>
<span className="text-12 text-white/60">(Optional)</span>
<IconChevron className="toggle h-14 w-14" />
</summary>
<div className="flex flex-col gap-16">
<p className="text-14 text-white/80">
Please select the action and amount of tokens
</p>
<div
role="radiogroup"
className="border-background-700 flex gap-2 self-start rounded-full border-2 p-4"
<input
className={cn('absolute opacity-0', style.budgetMode)}
type="radio"
name={actionName}
id={depositId}
checked={action === 'deposit'}
onChange={(e) => e.target.checked && setAction('deposit')}
/>
<label
htmlFor={depositId}
className="text-14 flex cursor-pointer items-center justify-center gap-8 rounded-full px-16 py-4"
data-testid="action-deposit"
>
<input
className={cn('absolute opacity-0', style.budgetMode)}
type="radio"
name="action"
id="action-deposit"
checked={action === 'deposit'}
onChange={(e) => e.target.checked && setAction('deposit')}
/>
<label
htmlFor="action-deposit"
className="text-14 flex cursor-pointer items-center justify-center gap-8 rounded-full px-16 py-4"
data-testid="action-deposit"
>
<IconDeposit className="h-14 w-14" />
Deposit
</label>
<input
className={cn('absolute opacity-0', style.budgetMode)}
type="radio"
name="action"
id="action-withdraw"
checked={action === 'withdraw'}
onChange={(e) => e.target.checked && setAction('withdraw')}
/>
<label
htmlFor="action-withdraw"
className="text-14 flex cursor-pointer items-center justify-center gap-8 rounded-full px-16 py-4"
data-testid="action-withdraw"
>
<IconWithdraw className="h-14 w-14" />
Withdraw
</label>
</div>
<InputBudget
editType={action}
token={anchor === 'buy' ? quote : base}
value={budget}
onChange={setBudget}
max={max}
maxIsLoading={maxIsLoading}
error={error}
warning={warning}
data-testid="input-budget"
<IconDeposit className="h-14 w-14" />
Deposit
</label>
<input
className={cn('absolute opacity-0', style.budgetMode)}
type="radio"
name={actionName}
id={withdrawId}
checked={action === 'withdraw'}
onChange={(e) => e.target.checked && setAction('withdraw')}
/>
<label
htmlFor={withdrawId}
className="text-14 flex cursor-pointer items-center justify-center gap-8 rounded-full px-16 py-4"
data-testid="action-withdraw"
>
<IconWithdraw className="h-14 w-14" />
Withdraw
</label>
</div>
</details>
</m.article>
<InputBudget
editType={action}
token={anchor === 'buy' ? quote : base}
value={budget}
onChange={setBudget}
max={max}
maxIsLoading={maxIsLoading}
error={budgetError || error}
warning={warning}
data-testid="input-budget"
/>
</div>
</details>
);
};
6 changes: 6 additions & 0 deletions src/libs/routing/routes/strategyEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']),
}),
});

Expand All @@ -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']),
}),
});

Expand Down
Loading

0 comments on commit 3228ff7

Please sign in to comment.