Skip to content

Commit 21b737b

Browse files
avclarkeplatschi
andauthored
fix refactor bugs(Kwenta#1749)
* Fix edit leverage modal and dynamic fee updates * Set leverage per asset * Improve error messaging * Clear candle polling interval Co-authored-by: platschi <[email protected]>
1 parent 684debe commit 21b737b

File tree

11 files changed

+79
-30
lines changed

11 files changed

+79
-30
lines changed

components/TVChart/TVChart.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ export function TVChart({
292292
}, [marketAsset]);
293293

294294
const onSubscribe = useCallback((newIntervalId: number) => {
295+
if (_intervalId.current) {
296+
clearInterval(_intervalId.current);
297+
}
295298
_intervalId.current = newIntervalId;
296299
}, []);
297300

hooks/useFuturesData.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ const useFuturesData = () => {
313313
// eslint-disable-next-line react-hooks/exhaustive-deps
314314
}, [dispatch, router.query.accountType]);
315315

316+
const priceString = marketAssetRate.toString();
317+
316318
useEffect(() => {
317319
const getDynamicFee = async () => {
318320
if (!synthetixjs) return;
@@ -324,7 +326,7 @@ const useFuturesData = () => {
324326
dispatch(setDynamicFeeRateRedux(wei(dynamicFeeRate.feeRate).toString()));
325327
};
326328
getDynamicFee();
327-
}, [marketAsset, synthetixjs, dispatch]);
329+
}, [marketAsset, priceString, synthetixjs, dispatch]);
328330

329331
return {
330332
submitCrossMarginOrder,

queries/futures/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export const ORDER_PREVIEW_ERRORS = { insufficient_margin: 'Insufficient free ma
5454
export const ORDER_PREVIEW_ERRORS_I18N: Record<string, string> = {
5555
insufficient_margin: 'futures.market.trade.preview.insufficient-margin',
5656
insufficient_margin_edit_leverage: 'futures.market.trade.edit-leverage.insufficient-margin',
57+
insufficient_free_margin_edit_leverage:
58+
'futures.market.trade.edit-leverage.insufficient-free-margin',
5759
};
5860

5961
export const previewErrorI18n = (message: string) => {

sdk/types/futures.ts

+4
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export type FuturesPotentialTradeDetails<T = Wei> = {
212212

213213
// https://github.com/Synthetixio/synthetix/blob/4d2add4f74c68ac4f1106f6e7be4c31d4f1ccc76/contracts/interfaces/IFuturesMarketBaseTypes.sol#L6-L19
214214
export enum PotentialTradeStatus {
215+
// Contract status mapping
215216
OK = 0,
216217
INVALID_PRICE = 1,
217218
PRICE_OUT_OF_BOUNDS = 2,
@@ -224,6 +225,9 @@ export enum PotentialTradeStatus {
224225
NIL_ORDER = 9,
225226
NO_POSITION_OPEN = 10,
226227
PRICE_TOO_VOLATILE = 11,
228+
229+
// Our own local status
230+
INSUFFICIENT_FREE_MARGIN = 100,
227231
}
228232

229233
export type PostTradeDetailsResponse = {

sdk/utils/futures.ts

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ export const POTENTIAL_TRADE_STATUS_TO_MESSAGE: { [key: string]: string } = {
272272
NIL_ORDER: 'Cannot submit empty order',
273273
NO_POSITION_OPEN: 'No position open',
274274
PRICE_TOO_VOLATILE: 'Price too volatile',
275+
INSUFFICIENT_FREE_MARGIN: 'Insufficient free margin',
275276
};
276277

277278
export const calculateCrossMarginFee = (

sections/futures/TradeCrossMargin/EditCrossMarginLeverageModal.tsx

+19-9
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ import { useFuturesContext } from 'contexts/FuturesContext';
1616
import { useRefetchContext } from 'contexts/RefetchContext';
1717
import { monitorTransaction } from 'contexts/RelayerContext';
1818
import { ORDER_PREVIEW_ERRORS_I18N, previewErrorI18n } from 'queries/futures/constants';
19-
import { editExistingPositionLeverage, editCrossMarginSize } from 'state/futures/actions';
20-
import { setCrossMarginLeverage, setOrderType as setReduxOrderType } from 'state/futures/reducer';
19+
import {
20+
editExistingPositionLeverage,
21+
editCrossMarginSize,
22+
setCrossMarginLeverage,
23+
} from 'state/futures/actions';
24+
import { setOrderType as setReduxOrderType } from 'state/futures/reducer';
2125
import {
2226
selectCrossMarginBalanceInfo,
2327
selectCrossMarginSelectedLeverage,
@@ -260,13 +264,7 @@ export default function EditLeverageModal({ onDismiss, editMode }: DepositMargin
260264
<>
261265
<Spacer height={12} />
262266
<ErrorView
263-
message={t(
264-
errorMessage === 'insufficient_margin' || errorMessage === 'Insufficient margin'
265-
? ORDER_PREVIEW_ERRORS_I18N.insufficient_margin_edit_leverage
266-
: previewError
267-
? previewErrorI18n(errorMessage)
268-
: t('futures.market.trade.edit-leverage.failed')
269-
)}
267+
message={t(getStatusMessageI18n(errorMessage, previewError))}
270268
formatter="revert"
271269
/>
272270
</>
@@ -275,6 +273,18 @@ export default function EditLeverageModal({ onDismiss, editMode }: DepositMargin
275273
);
276274
}
277275

276+
// TODO: Clean up preview error messaging
277+
278+
const getStatusMessageI18n = (message: string, previewError: string | null | undefined) => {
279+
if (message === 'insufficient_margin' || message === 'Insufficient margin') {
280+
return ORDER_PREVIEW_ERRORS_I18N.insufficient_margin_edit_leverage;
281+
} else if (message === 'insufficient_free_margin' || message === 'Insufficient free margin') {
282+
return ORDER_PREVIEW_ERRORS_I18N.insufficient_free_margin_edit_leverage;
283+
}
284+
if (previewError) return previewErrorI18n(previewError);
285+
return 'futures.market.trade.edit-leverage.failed';
286+
};
287+
278288
const StyledBaseModal = styled(BaseModal)`
279289
[data-reach-dialog-content] {
280290
width: 400px;

state/futures/actions.ts

+28-8
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ import {
1616
FuturesPotentialTradeDetails,
1717
FuturesVolumes,
1818
PositionSide,
19+
PotentialTradeStatus,
1920
} from 'sdk/types/futures';
20-
import { calculateCrossMarginFee, serializePotentialTrade } from 'sdk/utils/futures';
21+
import {
22+
calculateCrossMarginFee,
23+
getTradeStatusMessage,
24+
serializePotentialTrade,
25+
} from 'sdk/utils/futures';
2126
import { unserializeGasPrice } from 'state/app/helpers';
2227
import { setOpenModal } from 'state/app/reducer';
2328
import { fetchBalances } from 'state/balances/actions';
@@ -46,7 +51,7 @@ import {
4651
handleIsolatedMarginPreviewError,
4752
handleTransactionError,
4853
setCrossMarginFees,
49-
setCrossMarginLeverage,
54+
setCrossMarginLeverageForAsset,
5055
setCrossMarginMarginDelta,
5156
setCrossMarginOrderPrice,
5257
setCrossMarginOrderPriceInvalidLabel,
@@ -66,6 +71,7 @@ import {
6671
} from './reducer';
6772
import {
6873
selectCrossMarginAccount,
74+
selectCrossMarginBalanceInfo,
6975
selectCrossMarginMarginDelta,
7076
selectCrossMarginOrderPrice,
7177
selectCrossMarginSelectedLeverage,
@@ -81,6 +87,7 @@ import {
8187
selectLeverageSide,
8288
selectMarketAssetRate,
8389
selectMarketInfo,
90+
selectMarketKey,
8491
selectOrderType,
8592
selectPosition,
8693
selectTradeSizeInputs,
@@ -304,6 +311,8 @@ export const fetchCrossMarginTradePreview = createAsyncThunk<
304311
async (inputs, { dispatch, getState, extra: { sdk } }) => {
305312
const marketInfo = selectMarketInfo(getState());
306313
const account = selectFuturesAccount(getState());
314+
const marginDelta = selectCrossMarginMarginDelta(getState());
315+
const { freeMargin } = selectCrossMarginBalanceInfo(getState());
307316
if (!account) throw new Error('No account to fetch orders');
308317
if (!marketInfo) throw new Error('No market info');
309318
const leverageSide = selectLeverageSide(getState());
@@ -314,6 +323,14 @@ export const fetchCrossMarginTradePreview = createAsyncThunk<
314323
marketInfo.market,
315324
{ ...inputs, leverageSide }
316325
);
326+
if (marginDelta.gt(freeMargin) && preview.status === 0) {
327+
// Show insufficient margin message
328+
preview.status = PotentialTradeStatus.INSUFFICIENT_FREE_MARGIN;
329+
preview.statusMessage = getTradeStatusMessage(
330+
PotentialTradeStatus.INSUFFICIENT_FREE_MARGIN
331+
);
332+
preview.showStatus = true;
333+
}
317334
return serializePotentialTrade(preview);
318335
} catch (err) {
319336
dispatch(handleCrossMarginPreviewError(err.message));
@@ -368,6 +385,7 @@ export const editCrossMarginSize = (size: string, currencyType: 'usd' | 'native'
368385
const stageCrossMarginSizeChange = createAsyncThunk<void, void, ThunkConfig>(
369386
'futures/stageCrossMarginSizeChange',
370387
async (_, { dispatch, getState }) => {
388+
dispatch(calculateCrossMarginFees());
371389
const tradeInputs = selectCrossMarginTradeInputs(getState());
372390
const fees = selectCrossMarginTradeFees(getState());
373391
const rate = selectMarketAssetRate(getState());
@@ -388,32 +406,29 @@ const stageCrossMarginSizeChange = createAsyncThunk<void, void, ThunkConfig>(
388406
)
389407
: wei(0);
390408
dispatch(setCrossMarginMarginDelta(marginDelta.toString()));
391-
dispatch(calculateCrossMarginFees());
392409
debouncedPrepareCrossMarginTradePreview(dispatch);
393410
}
394411
);
395412

396413
export const editExistingPositionLeverage = createAsyncThunk<void, string, ThunkConfig>(
397414
'futures/editExistingPositionLeverage',
398415
async (leverage, { dispatch, getState }) => {
399-
const tradeInputs = selectCrossMarginTradeInputs(getState());
416+
dispatch(calculateCrossMarginFees());
400417
const fees = selectCrossMarginTradeFees(getState());
401418
const position = selectPosition(getState());
402419
const rate = selectMarketAssetRate(getState());
403420
const price = selectCrossMarginOrderPrice(getState());
404-
dispatch(setCrossMarginLeverage(leverage));
405421
const marginDelta = await calculateMarginDelta(
406422
{
407-
susdSizeDelta: tradeInputs.susdSizeDelta,
408-
nativeSizeDelta: tradeInputs.nativeSizeDelta,
423+
susdSizeDelta: wei(0),
424+
nativeSizeDelta: wei(0),
409425
price: price ? wei(price) : rate,
410426
leverage: wei(leverage),
411427
},
412428
fees,
413429
position
414430
);
415431
dispatch(setCrossMarginMarginDelta(marginDelta.toString()));
416-
dispatch(calculateCrossMarginFees());
417432
debouncedPrepareCrossMarginTradePreview(dispatch);
418433
}
419434
);
@@ -471,6 +486,11 @@ export const changeLeverageSide = (side: PositionSide): AppThunk => (dispatch, g
471486
dispatch(editTradeSizeInput(nativeSizeString, 'native'));
472487
};
473488

489+
export const setCrossMarginLeverage = (leverage: string): AppThunk => (dispatch, getState) => {
490+
const marketKey = selectMarketKey(getState());
491+
dispatch(setCrossMarginLeverageForAsset({ marketKey: marketKey, leverage: leverage }));
492+
};
493+
474494
export const debouncedPrepareCrossMarginTradePreview = debounce((dispatch) => {
475495
dispatch(prepareCrossMarginTradePreview());
476496
}, 500);

state/futures/reducer.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
22

3-
import { DEFAULT_FUTURES_MARGIN_TYPE, DEFAULT_LEVERAGE } from 'constants/defaults';
3+
import { DEFAULT_FUTURES_MARGIN_TYPE } from 'constants/defaults';
44
import { ORDER_PREVIEW_ERRORS } from 'queries/futures/constants';
55
import { TransactionStatus } from 'sdk/types/common';
66
import { FuturesMarket, FuturesMarketKey, FuturesPotentialTradeDetails } from 'sdk/types/futures';
@@ -94,7 +94,7 @@ const initialState: FuturesState = {
9494
selectedMarketKey: FuturesMarketKey.sETH,
9595
leverageSide: PositionSide.LONG,
9696
orderType: 'market',
97-
selectedLeverage: DEFAULT_LEVERAGE,
97+
selectedLeverageByAsset: {},
9898
showCrossMarginOnboard: false,
9999
tradeInputs: ZERO_STATE_CM_TRADE_INPUTS,
100100
fees: ZERO_CM_FEES,
@@ -124,7 +124,6 @@ const initialState: FuturesState = {
124124
leverageSide: PositionSide.LONG,
125125
orderType: 'market',
126126
tradePreview: null,
127-
selectedLeverage: DEFAULT_LEVERAGE,
128127
tradeInputs: ZERO_STATE_TRADE_INPUTS,
129128
positions: {},
130129
openOrders: {},
@@ -155,8 +154,11 @@ const futuresSlice = createSlice({
155154
setLeverageSide: (state, action) => {
156155
state[accountType(state.selectedType)].leverageSide = action.payload;
157156
},
158-
setCrossMarginLeverage: (state, action: PayloadAction<string>) => {
159-
state.crossMargin.tradeInputs.leverage = action.payload;
157+
setCrossMarginLeverageForAsset: (
158+
state,
159+
action: PayloadAction<{ marketKey: FuturesMarketKey; leverage: string }>
160+
) => {
161+
state.crossMargin.selectedLeverageByAsset[action.payload.marketKey] = action.payload.leverage;
160162
},
161163
setCrossMarginMarginDelta: (state, action: PayloadAction<string>) => {
162164
state.crossMargin.marginDelta = action.payload;
@@ -459,6 +461,6 @@ export const {
459461
setIsolatedTradePreview,
460462
setIsolatedMarginFee,
461463
setCrossMarginTradePreview,
462-
setCrossMarginLeverage,
464+
setCrossMarginLeverageForAsset,
463465
setPreviewError,
464466
} = futuresSlice.actions;

state/futures/selectors.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ export const selectMarketsQueryStatus = (state: RootState) => state.futures.quer
3939
export const selectIsolatedLeverageInput = (state: RootState) =>
4040
state.futures.isolatedMargin.leverageInput;
4141

42-
export const selectCrossMarginSelectedLeverage = (state: RootState) =>
43-
wei(state.futures.crossMargin.tradeInputs.leverage || DEFAULT_LEVERAGE);
44-
4542
export const selectCrossMarginMarginDelta = (state: RootState) =>
4643
wei(state.futures.crossMargin.marginDelta || 0);
4744

@@ -349,6 +346,13 @@ export const selectIsolatedMarginTradeInputs = createSelector(
349346
};
350347
}
351348
);
349+
350+
export const selectCrossMarginSelectedLeverage = createSelector(
351+
selectMarketKey,
352+
(state: RootState) => state.futures.crossMargin.selectedLeverageByAsset,
353+
(key, selectedLeverageByAsset) => wei(selectedLeverageByAsset[key] || DEFAULT_LEVERAGE)
354+
);
355+
352356
export const selectDynamicFeeRate = (state: RootState) => wei(state.futures.dynamicFeeRate);
353357

354358
export const selectIsolatedMarginFee = (state: RootState) =>

state/futures/types.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export type CrossMarginState = {
134134
tradeInputs: CrossMarginTradeInputs<string>;
135135
marginDelta: string;
136136
orderType: CrossMarginOrderType;
137-
selectedLeverage: string;
137+
selectedLeverageByAsset: Partial<Record<FuturesMarketKey, string>>;
138138
leverageSide: PositionSide;
139139
selectedMarketKey: FuturesMarketKey;
140140
selectedMarketAsset: FuturesMarketAsset;
@@ -161,7 +161,6 @@ export type CrossMarginState = {
161161
export type IsolatedMarginState = {
162162
tradeInputs: IsolatedMarginTradeInputs<string>;
163163
orderType: IsolatedMarginOrderType;
164-
selectedLeverage: string;
165164
tradePreview: FuturesPotentialTradeDetails<string> | null;
166165
leverageSide: PositionSide;
167166
selectedMarketKey: FuturesMarketKey;

translations/en.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,9 @@
832832
},
833833
"edit-leverage": {
834834
"failed": "Transaction failed",
835-
"insufficient-margin": "Leverage cannot be modified, please deposit more margin or reduce open position sizes"
835+
"insufficient-margin": "Leverage cannot be increased, it exceeds the minimum margin requirement of $40",
836+
"insufficient-free-margin": "Leverage cannot be modified, please deposit more margin or reduce open position sizes"
837+
836838
}
837839
},
838840
"history": {

0 commit comments

Comments
 (0)