From 24a274f8ae7d533a0b34ea9e096f2ee087c850c2 Mon Sep 17 00:00:00 2001 From: naturexie <786281870@qq.com> Date: Tue, 14 Jan 2025 20:48:57 +0800 Subject: [PATCH 1/4] feat: decode transactionHashes --- screens/Trading/components/Table.tsx | 1 - screens/Trading/index.tsx | 1 - services/transaction.ts | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/screens/Trading/components/Table.tsx b/screens/Trading/components/Table.tsx index 38b06ed6..24acb47a 100644 --- a/screens/Trading/components/Table.tsx +++ b/screens/Trading/components/Table.tsx @@ -122,7 +122,6 @@ const TradingTable = ({ setStateSelectedTab("positions"); } }, [storeSelectedTab]); - // TODOXX useEffect(() => { if (query?.transactionHashes && accountId) { handleTransactionHash(query?.transactionHashes); diff --git a/screens/Trading/index.tsx b/screens/Trading/index.tsx index 81a07762..5a94ffc3 100644 --- a/screens/Trading/index.tsx +++ b/screens/Trading/index.tsx @@ -141,7 +141,6 @@ const Trading = () => { }; useEffect(() => { - // TODOXX if (query?.transactionHashes && accountId) { handleTransactionHash(query?.transactionHashes); } diff --git a/services/transaction.ts b/services/transaction.ts index d5a8756b..ad22d8b6 100644 --- a/services/transaction.ts +++ b/services/transaction.ts @@ -24,7 +24,7 @@ export const handleTransactionHash = async ( // Parsing transactions const txhash = Array.isArray(transactionHashes) ? transactionHashes - : transactionHashes.split(","); + : decodeURIComponent(decodeURIComponent(transactionHashes)).split(","); const results = await Promise.all( txhash.map(async (txHash: string): Promise => { const result: any = await getTransactionResult(txHash); From 1a737ca3ffd65192a70c7114b9c2a1362122e352 Mon Sep 17 00:00:00 2001 From: naturexie <786281870@qq.com> Date: Tue, 14 Jan 2025 22:15:02 +0800 Subject: [PATCH 2/4] feat: add tx error popup --- .../components/ClosePositionMobile.tsx | 10 ++- screens/Trading/components/ConfirmMobile.tsx | 41 ++------- services/transaction.ts | 18 ++-- utils/transactionUtils.tsx | 90 +++++++++++++++++++ 4 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 utils/transactionUtils.tsx diff --git a/screens/Trading/components/ClosePositionMobile.tsx b/screens/Trading/components/ClosePositionMobile.tsx index 6903ab5c..0ac9ecf7 100644 --- a/screens/Trading/components/ClosePositionMobile.tsx +++ b/screens/Trading/components/ClosePositionMobile.tsx @@ -6,7 +6,7 @@ import { useAppSelector } from "../../../redux/hooks"; import { Wrapper } from "../../../components/Modal/style"; import { DEFAULT_POSITION } from "../../../utils/config"; import { CloseIcon } from "../../../components/Modal/svg"; -import { RefLogoIcon, RightArrow, MaxPositionIcon } from "./TradingIcon"; +import { RightArrow, MaxPositionIcon } from "./TradingIcon"; import { toInternationalCurrencySystem_number, toDecimal } from "../../../utils/uiNumber"; import { closePosition } from "../../../store/marginActions/closePosition"; import { useEstimateSwap } from "../../../hooks/useEstimateSwap"; @@ -22,7 +22,6 @@ import { YellowSolidSubmitButton as YellowSolidButton, RedSolidSubmitButton as RedSolidButton, } from "../../../components/Modal/button"; -import { showPositionClose } from "../../../components/HashResultModal"; import { beautifyPrice } from "../../../utils/beautyNumber"; import { findPathReserve } from "../../../api/get-swap-path"; import { getAssets, getAssetsMEME } from "../../../redux/assetsSelectors"; @@ -30,7 +29,7 @@ import { useMarginAccount } from "../../../hooks/useMarginAccount"; import { IClosePositionMobileProps } from "../comInterface"; import { getMarginConfig, getMarginConfigMEME } from "../../../redux/marginConfigSelectors"; import { handleTransactionHash } from "../../../services/transaction"; -import DataSource from "../../../data/datasource"; +import { showPositionFailure } from "../../../components/HashResultModal"; import { useRegisterTokenType } from "../../../hooks/useRegisterTokenType"; export const ModalContext = createContext(null) as any; @@ -204,7 +203,10 @@ const ClosePositionMobile: React.FC = ({ await handleTransactionHash(transactionHashes); } } catch (error) { - console.error("Failed to close position:", error); + showPositionFailure({ + title: "Transactions error", + errorMessage: error instanceof Error ? error.message : JSON.stringify(error), + }); } finally { setIsDisabled(false); } diff --git a/screens/Trading/components/ConfirmMobile.tsx b/screens/Trading/components/ConfirmMobile.tsx index 9e1fe5c7..d5a9e5ad 100644 --- a/screens/Trading/components/ConfirmMobile.tsx +++ b/screens/Trading/components/ConfirmMobile.tsx @@ -16,10 +16,8 @@ import { } from "../../../components/Modal/button"; import { shrinkToken, expandToken } from "../../../store"; import { beautifyPrice } from "../../../utils/beautyNumber"; -import { getAccountId } from "../../../redux/accountSelectors"; import { handleTransactionHash } from "../../../services/transaction"; import { showPositionFailure } from "../../../components/HashResultModal"; -import { getBurrow } from "../../../utils"; import { getSymbolById } from "../../../transformers/nearSymbolTrans"; import { IConfirmMobileProps } from "../comInterface"; import { useRegisterTokenType } from "../../../hooks/useRegisterTokenType"; @@ -31,20 +29,6 @@ const ConfirmMobile: React.FC = ({ action, confirmInfo, }) => { - const [burrowData, setBurrowData] = useState<{ - selector?: { - wallet: () => Promise<{ id: string }>; - }; - } | null>(null); - - useEffect(() => { - const initBurrow = async () => { - const data: any = await getBurrow(); - setBurrowData(data); - }; - initBurrow(); - }, []); - const accountId = useAppSelector(getAccountId); const theme = useTheme(); const [selectedCollateralType, setSelectedCollateralType] = useState(DEFAULT_POSITION); const { ReduxcategoryAssets1 } = useAppSelector((state) => state.category); @@ -193,26 +177,17 @@ const ConfirmMobile: React.FC = ({ : minAmountOutForPopUp / confirmInfo.tokenInAmount, }), ); - - const wallet = await burrowData?.selector?.wallet(); - if (wallet?.id && ["my-near-wallet", "mintbase-wallet", "bitte-wallet"].includes(wallet.id)) { - await openPosition(openPositionParams); - return; - } - const res: any = await openPosition(openPositionParams); - if (!res || !Array.isArray(res)) { - throw new Error("Invalid response from openPosition"); + if (res) { + const transactionHashes = res.map((item) => { + if (!item?.transaction?.hash) { + throw new Error("Invalid transaction hash"); + } + return item.transaction.hash; + }); + await handleTransactionHash(transactionHashes); } - const transactionHashes = res.map((item) => { - if (!item?.transaction?.hash) { - throw new Error("Invalid transaction hash"); - } - return item.transaction.hash; - }); - await handleTransactionHash(transactionHashes); } catch (error) { - console.error("Open position error:", error); showPositionFailure({ title: "Transactions error", errorMessage: error instanceof Error ? error.message : JSON.stringify(error), diff --git a/services/transaction.ts b/services/transaction.ts index ad22d8b6..1203b38d 100644 --- a/services/transaction.ts +++ b/services/transaction.ts @@ -3,18 +3,20 @@ import { showPositionResult, showPositionClose, showChangeCollateralPosition, + showPositionFailure, } from "../components/HashResultModal"; import { store } from "../redux/store"; import DataSource from "../data/datasource"; +import { getErrorMessage } from "../utils/transactionUtils"; interface ITransactionResult { txHash: string; - result: any; isOpenPosition?: boolean; isCloseMTPosition?: boolean; isDecreaseCollateral?: boolean; isIncreaseCollateral?: boolean; action?: Record; + error?: string | null | undefined; } export const handleTransactionHash = async ( transactionHashes: string | string[] | undefined, @@ -48,22 +50,22 @@ export const handleTransactionHash = async ( : JSON.parse(parsed_Args.msg)?.MarginExecute?.actions?.[0]; return { txHash, - result, isOpenPosition: Reflect.has(action, "OpenPosition"), isCloseMTPosition: Reflect.has(action, "CloseMTPosition"), isDecreaseCollateral: Reflect.has(action, "DecreaseCollateral"), isIncreaseCollateral: Reflect.has(action, "IncreaseCollateral"), action, + error: getErrorMessage(result?.receipts_outcome), }; } return { txHash, - result, isOpenPosition: false, isCloseMTPosition: false, isDecreaseCollateral: false, isIncreaseCollateral: false, action: undefined, + error: undefined, }; }), ); @@ -73,8 +75,9 @@ export const handleTransactionHash = async ( const targetCollateralTx = results.find( (item) => item.isDecreaseCollateral || item.isIncreaseCollateral, ); + const hasErrorTx = results.find((item) => item.error); // Reporting transactions - if (targetPositionTx) { + if (targetPositionTx && !hasErrorTx) { const currentState = store.getState(); await DataSource.shared.postMarginTradingPosition({ addr: currentState?.account?.accountId, @@ -83,7 +86,12 @@ export const handleTransactionHash = async ( }); } // Show transactions pop - if (targetPositionTx?.isCloseMTPosition) { + if (hasErrorTx) { + showPositionFailure({ + title: "Transactions error", + errorMessage: hasErrorTx.error || "Unknown error", + }); + } else if (targetPositionTx?.isCloseMTPosition) { // close position const marginPopType = localStorage.getItem("marginPopType") as "Long" | "Short" | undefined; showPositionClose({ type: marginPopType || "Long" }); diff --git a/utils/transactionUtils.tsx b/utils/transactionUtils.tsx new file mode 100644 index 00000000..548865e1 --- /dev/null +++ b/utils/transactionUtils.tsx @@ -0,0 +1,90 @@ +export const ERROR_PATTERN = { + slippageErrorPattern: /ERR_MIN_AMOUNT|slippage error/i, + invaliParamsErrorPattern: /invalid params/i, + ratesExpiredErrorPattern: /Rates expired/i, + integerOverflowErrorPattern: /Integer overflow/i, + ShareSupplyOverflowErrorPattern: /shares_total_supply overflow/i, + tokenFrozenErrorPattern: /token frozen/i, + poolBalanceLessPattern: /pool reserved token balance less than MIN_RESERVE/i, + nethErrorPattern: /Smart contract panicked: explicit guest panic/i, +}; +export enum TRANSACTION_ERROR_TYPE { + SLIPPAGE_VIOLATION = "Slippage Violation", + INVALID_PARAMS = "Invalid Params", + RATES_EXPIRED = "Rates Expired", + INTEGEROVERFLOW = "Integer Overflow", + SHARESUPPLYOVERFLOW = "Share Supply Overflow", + TOKEN_FROZEN = "Token Frozen", + POOL_BALANCE_LESS = "Pool Balance Less Than MIN_RESERVE", + NETH_ERROR = "Smart contract panicked", +} + +export const getErrorMessage = (receipts_outcome: any) => { + const isSlippageError = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.slippageErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isInvalidAmountError = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.invaliParamsErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isRatesExpiredError = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.ratesExpiredErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isIntegerOverFlowError = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.integerOverflowErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isShareSupplyOerflowError = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.ShareSupplyOverflowErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isTokenFrozen = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.tokenFrozenErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isPoolBalanceLess = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.poolBalanceLessPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + const isNETHErrpr = receipts_outcome.some((outcome: any) => { + return ERROR_PATTERN.nethErrorPattern.test( + outcome?.outcome?.status?.Failure?.ActionError?.kind?.FunctionCallError?.ExecutionError, + ); + }); + + if (isSlippageError) { + return TRANSACTION_ERROR_TYPE.SLIPPAGE_VIOLATION; + } else if (isInvalidAmountError) { + return TRANSACTION_ERROR_TYPE.INVALID_PARAMS; + } else if (isRatesExpiredError) { + return TRANSACTION_ERROR_TYPE.RATES_EXPIRED; + } else if (isIntegerOverFlowError) { + return TRANSACTION_ERROR_TYPE.INTEGEROVERFLOW; + } else if (isShareSupplyOerflowError) { + return TRANSACTION_ERROR_TYPE.SHARESUPPLYOVERFLOW; + } else if (isTokenFrozen) { + return TRANSACTION_ERROR_TYPE.TOKEN_FROZEN; + } else if (isPoolBalanceLess) { + return TRANSACTION_ERROR_TYPE.POOL_BALANCE_LESS; + } else if (isNETHErrpr) { + return TRANSACTION_ERROR_TYPE.NETH_ERROR; + } else { + return null; + } +}; From 51f2775ca74da57eb635f71fc99eef2136f89b07 Mon Sep 17 00:00:00 2001 From: lq0-github <1441665200@qq.com> Date: Wed, 15 Jan 2025 11:14:36 +0800 Subject: [PATCH 3/4] Fix : margin trading table title --- .../Trading/components/Table/PositionRow.tsx | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/screens/Trading/components/Table/PositionRow.tsx b/screens/Trading/components/Table/PositionRow.tsx index a242930f..47c63ca5 100644 --- a/screens/Trading/components/Table/PositionRow.tsx +++ b/screens/Trading/components/Table/PositionRow.tsx @@ -163,22 +163,15 @@ const PositionRow = ({ - + {entryPrice !== null && entryPrice !== undefined ? ( ${beautifyPrice(entryPrice)} ) : ( - )} - - ${beautifyPrice(indexPrice)} - - - ${beautifyPrice(LiqPrice)} - + ${beautifyPrice(indexPrice)} + ${beautifyPrice(LiqPrice)}

0 ? "text-green-150" : pnl < 0 ? "text-red-150" : "text-gray-400"}`}> {pnl === 0 ? "" : `${pnl > 0 ? `+$` : `-$`}`} @@ -280,7 +273,7 @@ const PositionRow = ({

Entry Price

-

+

{entryPrice !== null ? ( ${beautifyPrice(entryPrice)} ) : ( @@ -290,11 +283,11 @@ const PositionRow = ({

Index Price

-

${beautifyPrice(indexPrice)}

+

${beautifyPrice(indexPrice)}

Liq. Price

-

${beautifyPrice(LiqPrice)}

+

${beautifyPrice(LiqPrice)}

Opening time

From 8cf7a0068b919bce7429119a70f7507843c95600 Mon Sep 17 00:00:00 2001 From: naturexie <786281870@qq.com> Date: Wed, 15 Jan 2025 12:05:06 +0800 Subject: [PATCH 4/4] feat: add A few limit tip for open position --- interfaces/asset.ts | 1 + screens/Trading/components/RangeSlider.tsx | 48 +- screens/Trading/components/TradingIcon.tsx | 1 - screens/Trading/components/TradingOperate.tsx | 1028 +++++++++-------- 4 files changed, 562 insertions(+), 516 deletions(-) diff --git a/interfaces/asset.ts b/interfaces/asset.ts index 661e491a..d1f21031 100644 --- a/interfaces/asset.ts +++ b/interfaces/asset.ts @@ -13,6 +13,7 @@ export interface IAssetConfig { can_borrow: boolean; net_tvl_multiplier: number; holding_position_fee_rate: string; + min_borrowed_amount?: string; } export interface IAssetEntry { diff --git a/screens/Trading/components/RangeSlider.tsx b/screens/Trading/components/RangeSlider.tsx index 7fc1d666..9135686d 100644 --- a/screens/Trading/components/RangeSlider.tsx +++ b/screens/Trading/components/RangeSlider.tsx @@ -13,31 +13,18 @@ interface RangeSliderProps { } const RangeSlider: React.FC = ({ defaultValue, action, setRangeMount }) => { + const [value, setValue] = useState(defaultValue); + const [splitList, setSplitList] = useState([]); + const [matchValue, setMatchValue] = useState(value); + const router = useRouter(); const dispatch = useAppDispatch(); const { filteredTokenTypeMap } = useRegisterTokenType(); - const router = useRouter(); + const { marginConfigTokens, marginConfigTokensMEME } = useMarginConfigToken(); + const valueRef = useRef(null); const { id }: any = router.query; const isMainStream = filteredTokenTypeMap.mainStream.includes(id); - - const generateArithmeticSequence = (value: number) => { - const increment = (value - 1) / 4; - const sequence: number[] = []; - - for (let i = 0; i <= 4; i++) { - sequence.push(+(1 + i * increment).toFixed(2)); - } - - return sequence; - }; - const { marginConfigTokens, marginConfigTokensMEME } = useMarginConfigToken(); const marginConfigTokensCombined = isMainStream ? marginConfigTokens : marginConfigTokensMEME; - const [value, setValue] = useState(defaultValue); - const [splitList, setSplitList] = useState([]); - const [matchValue, setMatchValue] = useState(value); - const valueRef = useRef(null); - const [selectedItem, setSelectedItem] = useState(defaultValue); - useEffect(() => { const newAllowedValues = generateArithmeticSequence( marginConfigTokensCombined["max_leverage_rate"], @@ -46,10 +33,14 @@ const RangeSlider: React.FC = ({ defaultValue, action, setRang }, [marginConfigTokensCombined["max_leverage_rate"]]); // init value useEffect(() => { - if (splitList.length > 0 && !splitList.includes(value)) { - changeValue(splitList[0]); + if (splitList.length > 0) { + if (!splitList.includes(value)) { + changeValue(splitList[0]); + } else if (value !== defaultValue) { + changeValue(defaultValue); + } } - }, [value, splitList]); + }, [value, splitList, defaultValue]); useEffect(() => { if (valueRef.current && splitList.length > 0) { const nearestValue = splitList.reduce((prev, curr) => { @@ -68,10 +59,19 @@ const RangeSlider: React.FC = ({ defaultValue, action, setRang return Math.abs(curr - numValue) < Math.abs(prev - numValue) ? curr : prev; }); setValue(nearestValue); - setSelectedItem(nearestValue); setRangeMount(nearestValue); dispatch(setReduxRangeMount(nearestValue)); } + const generateArithmeticSequence = (value: number) => { + const increment = (value - 1) / 4; + const sequence: number[] = []; + + for (let i = 0; i <= 4; i++) { + sequence.push(+(1 + i * increment).toFixed(2)); + } + + return sequence; + }; const actionShowRedColor = action === "Long"; return (
@@ -100,7 +100,7 @@ const RangeSlider: React.FC = ({ defaultValue, action, setRang className={twMerge( `flex items-center justify-center text-xs text-gray-400 w-11 py-0.5 border border-transparent hover:border-v3LiquidityRemoveBarColor rounded-lg`, - p === selectedItem && "bg-black bg-opacity-20", + p === value && "bg-black bg-opacity-20", )} > {p}X diff --git a/screens/Trading/components/TradingIcon.tsx b/screens/Trading/components/TradingIcon.tsx index e4dffe88..117f1aa5 100644 --- a/screens/Trading/components/TradingIcon.tsx +++ b/screens/Trading/components/TradingIcon.tsx @@ -219,7 +219,6 @@ export const MaxPositionIcon = (props: any) => { viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" - className="mt-[2px]" > void; id: string; } +const tip_leverage = "Leverage must be greater than 1"; +const tip_min_usd = `Deposit at least $${MARGIN_MIN_COLLATERAL_USD}`; +const tip_max_positions = "Exceeded the maximum number of open positions."; +const tip_max_available_debt = "Current maximum available position:"; +const tip_min_available_debt = "Current minimum available position:"; +const insufficient_balance = "Insufficient Balance"; -// main components const TradingOperate: React.FC = ({ onMobileClose, id }) => { - const customInputRef = useRef(null); - const accountId = useAppSelector(getAccountId); - const { filteredTokenTypeMap } = useRegisterTokenType(); - const isMainStream = filteredTokenTypeMap.mainStream.includes(id); - const assets = useAppSelector(getAssets); - const assetsMEME = useAppSelector(getAssetsMEME); - const combinedAssetsData = isMainStream ? assets.data : assetsMEME.data; - const config = useAppSelector(getMarginConfigCategory(!isMainStream)); - const { - categoryAssets1, - categoryAssets1MEME, - categoryAssets2, - categoryAssets2MEME, - marginConfigTokens, - marginConfigTokensMEME, - } = useMarginConfigToken(); - const { marginAccountList, getAssetById, getAssetByIdMEME } = useMarginAccount(); - const { max_active_user_margin_position, min_safety_buffer } = isMainStream - ? marginConfigTokens - : marginConfigTokensMEME; const { ReduxcategoryAssets1, ReduxcategoryAssets2, @@ -63,108 +50,70 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => ReduxRangeMount, ReduxActiveTab, } = useAppSelector((state) => state.category); - // const [slippageTolerance, setSlippageTolerance] = useState(0.5); const [showFeeModal, setShowFeeModal] = useState(false); const [forceUpdateLoading, setForceUpdateLoading] = useState(false); const dispatch = useAppDispatch(); const [activeTab, setActiveTab] = useState(ReduxActiveTab || "long"); const [estimateLoading, setEstimateLoading] = useState(false); - - // const [selectedSetUpOption, setSelectedSetUpOption] = useState("custom"); const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false); const [rangeMount, setRangeMount] = useState(ReduxRangeMount || 1); const [isDisabled, setIsDisabled] = useState(false); - const [isMaxPosition, setIsMaxPosition] = useState(false); - - // const [longInput, setLongInput] = useState(""); const [shortInput, setShortInput] = useState(""); const [longOutput, setLongOutput] = useState(0); const [shortOutput, setShortOutput] = useState(0); - - // amount const [longInputUsd, setLongInputUsd] = useState(0); const [longOutputUsd, setLongOutputUsd] = useState(0); const [shortInputUsd, setShortInputUsd] = useState(0); const [shortOutputUsd, setShortOutputUsd] = useState(0); - - // pools + const [tokenInAmount, setTokenInAmount] = useState(0); + const [warnTip, setWarnTip] = useState(""); + const [LiqPrice, setLiqPrice] = useState(""); + const [forceUpdate, setForceUpdate] = useState(0); + const [lastTokenInAmount, setLastTokenInAmount] = useState(0); + const customInputRef = useRef(null); + const accountId = useAppSelector(getAccountId); + const { filteredTokenTypeMap } = useRegisterTokenType(); + const assets = useAppSelector(getAssets); + const assetsMEME = useAppSelector(getAssetsMEME); + const isMainStream = filteredTokenTypeMap.mainStream.includes(id); + const combinedAssetsData = isMainStream ? assets.data : assetsMEME.data; + const config = useAppSelector(getMarginConfigCategory(!isMainStream)); + const { + categoryAssets1, + categoryAssets1MEME, + categoryAssets2, + categoryAssets2MEME, + marginConfigTokens, + marginConfigTokensMEME, + } = useMarginConfigToken(); + const { marginAccountList: marginAccountListMain, marginAccountListMEME } = useMarginAccount(); + const { max_active_user_margin_position, min_safety_buffer } = isMainStream + ? marginConfigTokens + : marginConfigTokensMEME; + const marginAccountList = isMainStream ? marginAccountListMain : marginAccountListMEME; + // fetch all pools for script estimate TODOXX const { simplePools, stablePools, stablePoolsDetail } = usePoolsData(); - - const setOwnBanlance = (key: string) => { - if (activeTab === "long") { - setLongInput(key); - } else { - setShortInput(key); - } - }; - - // for tab change - const initCateState = (tabString: string) => { - setLiqPrice("0"); - if (tabString == "long") { - setShortInput(""); - setShortInputUsd(0); - setShortOutput(0); - setShortOutputUsd(0); - } else { - setLongInput(""); - setLongInputUsd(0); - setLongOutput(0); - setLongOutputUsd(0); - } - }; - // tab click event - const handleTabClick = (tabString: string) => { - dispatch(setReduxActiveTab(tabString)); - setActiveTab(tabString); - initCateState(tabString); - setForceUpdate((prev) => prev + 1); - }; - - const getTabClassName = (tabName: string) => { - return activeTab === tabName - ? "bg-primary text-dark-200 py-2.5 px-5 rounded-md h-11 w-[50%] overflow-hidden text-ellipsis whitespace-nowrap" - : "text-gray-300 py-2.5 px-5 h-11 w-[50%] overflow-hidden text-ellipsis whitespace-nowrap"; - }; - - const cateSymbol = getSymbolById( - ReduxcategoryAssets1?.token_id, - ReduxcategoryAssets1?.metadata?.symbol, - ); - + // estimate + const estimateData = useEstimateSwap({ + tokenIn_id: + activeTab === "long" ? ReduxcategoryAssets2?.token_id : ReduxcategoryAssets1?.token_id, + tokenOut_id: + activeTab === "long" ? ReduxcategoryAssets1?.token_id : ReduxcategoryAssets2?.token_id, + tokenIn_amount: toDecimal(tokenInAmount), + account_id: accountId, + simplePools, + stablePools, + stablePoolsDetail, + slippageTolerance: slippageTolerance / 100, + forceUpdate, + }); // slippageTolerance change ecent useEffect(() => { dispatch(setSlippageToleranceFromRedux(0.5)); }, []); - - const handleSetUpOptionClick = (option: string) => { - setSelectedSetUpOption(option); - if (option === "auto") { - setSlippageTolerance(0.5); - dispatch(setSlippageToleranceFromRedux(0.5)); - } - }; - // for mobile focus - const handleSetUpFocus = () => { - if (selectedSetUpOption === "custom" && customInputRef.current) { - customInputRef.current.focus(); - } - }; - - const slippageToleranceChange = (e: any) => { - setSlippageTolerance(e); - dispatch(setSlippageToleranceFromRedux(e)); - }; - - // open position btn click eve. - const handleConfirmButtonClick = () => { - if (isDisabled) return; - setIsConfirmModalOpen(true); - }; - // condition btn iswhether disabled useEffect(() => { const setDisableBasedOnInputs = () => { @@ -183,88 +132,6 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => }; setDisableBasedOnInputs(); }, [activeTab, ReduxcategoryCurrentBalance2, longInput, shortInput, longOutput, shortOutput]); - - useEffect(() => { - if (Object.values(marginAccountList).length >= max_active_user_margin_position) { - setIsMaxPosition(true); - } else { - setIsMaxPosition(false); - } - }, [marginAccountList, max_active_user_margin_position]); - - const isValidDecimalString = (str) => { - if (str <= 0) return false; - // const regex = /^(?![0]+$)\d+(\.\d+)?$/; - const regex = /^\d+(\.\d+)?$/; - return regex.test(str); - }; - // pools end - - // get cate1 amount start - const [tokenInAmount, setTokenInAmount] = useState(0); - const [LiqPrice, setLiqPrice] = useState(""); - const [forceUpdate, setForceUpdate] = useState(0); - const estimateData = useEstimateSwap({ - tokenIn_id: - activeTab === "long" ? ReduxcategoryAssets2?.token_id : ReduxcategoryAssets1?.token_id, - tokenOut_id: - activeTab === "long" ? ReduxcategoryAssets1?.token_id : ReduxcategoryAssets2?.token_id, - tokenIn_amount: toDecimal(tokenInAmount), - account_id: accountId, - simplePools, - stablePools, - stablePoolsDetail, - slippageTolerance: slippageTolerance / 100, - forceUpdate, - }); - - // long & short input change fn. - const inputPriceChange = _.debounce((newValue: string) => { - // eslint-disable-next-line no-unused-expressions - activeTab === "long" ? setLongInput(newValue) : setShortInput(newValue); - }, 10); - let lastValue = ""; - - // validate input - const isValidInput = (value: string): boolean => { - if (value === "") return true; - const regex = /^\d*\.?\d*$/; - if (!regex.test(value)) return false; - const num = parseFloat(value); - if (Number.isNaN(num)) return false; - return true; - }; - - // handle input change - const tokenChange = (e: React.ChangeEvent) => { - const { value } = e.target; - if (value === "") { - setLongInput(""); - setShortInput(""); - setLongOutput(0); - setShortOutput(0); - setLongInputUsd(0); - setShortInputUsd(0); - setLongOutputUsd(0); - setShortOutputUsd(0); - setTokenInAmount(0); - setLiqPrice(""); - return; - } - setEstimateLoading(true); - if (!isValidInput(value)) return; - if (value.includes(".") && !lastValue.includes(".")) { - inputPriceChange.cancel(); - setTimeout(() => { - inputPriceChange(value); - }, 10); - } else { - inputPriceChange(value); - } - - lastValue = value; - }; - /** * longInput shortInput deal start * */ @@ -293,12 +160,7 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => useDebounce( () => { if (ReduxcategoryAssets2 && ReduxcategoryAssets1 && estimateData) { - if ( - activeTab == "long" && - +(longInput || 0) > 0 && - // rangeMount > 1 && - (longOutput || 0) > 0 - ) { + if (activeTab == "long" && +(longInput || 0) > 0 && (longOutput || 0) > 0) { const safetyBufferFactor = 1 - min_safety_buffer / 10000; const assetPrice = getAssetPrice(ReduxcategoryAssets2) as any; const token_c_value = new Decimal(longInput).mul(assetPrice || 0); @@ -321,7 +183,6 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => () => { if (ReduxcategoryAssets2 && ReduxcategoryAssets1 && estimateData) { if ( - // rangeMount > 1 && activeTab === "short" && +(shortInput || 0) > 0 && +(estimateData.amount_out || 0) > 0 && @@ -344,10 +205,6 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => 200, [estimateData, activeTab, shortOutput, shortInput], ); - - // interval refresh price - const [lastTokenInAmount, setLastTokenInAmount] = useState(0); - // update price and make refresh icon spin useEffect(() => { const interval = setInterval(() => { @@ -362,7 +219,6 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => return () => clearInterval(interval); }, [tokenInAmount, lastTokenInAmount]); - // for same input, estimateLoading or forceUpdateLoading is true useEffect(() => { if (forceUpdateLoading) { @@ -373,7 +229,6 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => return () => clearTimeout(timer); } }, [forceUpdateLoading]); - useEffect(() => { if (estimateLoading) { const timer = setTimeout(() => { @@ -383,6 +238,11 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => return () => clearTimeout(timer); // Cleanup the timer on component unmount or when estimateLoading changes } }, [estimateLoading]); + useEffect(() => { + if (selectedSetUpOption === "custom" && customInputRef.current) { + customInputRef.current.focus(); + } + }, [selectedSetUpOption]); const Fee = useMemo(() => { return { openPFee: @@ -397,45 +257,223 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => price: getAssetPrice(ReduxcategoryAssets1), }; }, [longInput, shortInput, ReduxcategoryAssets1, estimateData, tokenInAmount]); + // check to show tip + useDebounce( + () => { + if (!accountId) { + setWarnTip(""); + } else if (Object.values(marginAccountList).length >= max_active_user_margin_position) { + setWarnTip(tip_max_positions); + } else if (activeTab == "long" && longOutput > 0) { + if (rangeMount <= 1) { + setWarnTip(tip_leverage); + } else if (longInputUsd < MARGIN_MIN_COLLATERAL_USD) { + setWarnTip(tip_min_usd); + } else if (new Decimal(longInput || 0).gt(ReduxcategoryCurrentBalance2 || 0)) { + setWarnTip(insufficient_balance); + } else { + const borrowLimit = getBorrowLimit(ReduxcategoryAssets2, tokenInAmount); + const price_1 = getAssetPrice(ReduxcategoryAssets1); + const price_2 = getAssetPrice(ReduxcategoryAssets2); + if (borrowLimit?.isExceed) { + if (Number(price_1 || 0) > 0) { + const max_position = new Decimal(borrowLimit.availableLiquidity || 0) + .mul(price_2 || 0) + .div(price_1!); + setWarnTip( + <> + {tip_max_available_debt} {beautifyPrice(Number(max_position.toFixed() || 0))}{" "} + {ReduxcategoryAssets1?.metadata?.symbol} + , + ); + } + } else if (borrowLimit?.lowLoanLimit) { + if (Number(price_1 || 0) > 0) { + const min_position = new Decimal(borrowLimit.lowAmount || 0) + .mul(price_2 || 0) + .div(price_1!); + setWarnTip( + <> + {tip_min_available_debt} {beautifyPrice(Number(min_position.toFixed() || 0))}{" "} + {ReduxcategoryAssets1?.metadata?.symbol} + , + ); + } + } else { + setWarnTip(""); + } + } + } else if (activeTab == "short" && shortOutput > 0) { + if (rangeMount <= 1) { + setWarnTip(tip_leverage); + } else if (shortInputUsd < MARGIN_MIN_COLLATERAL_USD) { + setWarnTip(tip_min_usd); + } else if (new Decimal(shortInput || 0).gt(ReduxcategoryCurrentBalance2 || 0)) { + setWarnTip(insufficient_balance); + } else { + const borrowLimit = getBorrowLimit(ReduxcategoryAssets1, tokenInAmount); + if (borrowLimit?.isExceed) { + setWarnTip( + <> + {tip_max_available_debt} {beautifyPrice(borrowLimit.availableLiquidity || 0)}{" "} + {ReduxcategoryAssets1?.metadata?.symbol} + , + ); + } else if (borrowLimit?.lowLoanLimit) { + setWarnTip( + <> + {tip_min_available_debt} {beautifyPrice(borrowLimit.lowAmount || 0)}{" "} + {ReduxcategoryAssets1?.metadata?.symbol} + , + ); + } else { + setWarnTip(""); + } + } + } else { + setWarnTip(""); + } + }, + 100, + [ + marginAccountList, + max_active_user_margin_position, + longInputUsd, + shortInputUsd, + rangeMount, + accountId, + shortOutput, + longOutput, + activeTab, + tokenInAmount, + ReduxcategoryAssets1, + ReduxcategoryAssets2, + ReduxcategoryCurrentBalance2, + longInput, + shortInput, + ], + ); + console.log("-----------ReduxcategoryAssets1", ReduxcategoryAssets1); - useEffect(() => { + const setOwnBanlance = (key: string) => { + if (activeTab === "long") { + setLongInput(key); + } else { + setShortInput(key); + } + }; + // for tab change + const initCateState = (tabString: string) => { + setLiqPrice("0"); + if (tabString == "long") { + setShortInput(""); + setShortInputUsd(0); + setShortOutput(0); + setShortOutputUsd(0); + } else { + setLongInput(""); + setLongInputUsd(0); + setLongOutput(0); + setLongOutputUsd(0); + } + }; + // tab click event + const handleTabClick = (tabString: string) => { + dispatch(setReduxActiveTab(tabString)); + setActiveTab(tabString); + initCateState(tabString); + setForceUpdate((prev) => prev + 1); + }; + const getTabClassName = (tabName: string) => { + return activeTab === tabName + ? "bg-primary text-dark-200 py-2.5 px-5 rounded-md h-11 w-[50%] overflow-hidden text-ellipsis whitespace-nowrap" + : "text-gray-300 py-2.5 px-5 h-11 w-[50%] overflow-hidden text-ellipsis whitespace-nowrap"; + }; + const cateSymbol = getSymbolById( + ReduxcategoryAssets1?.token_id, + ReduxcategoryAssets1?.metadata?.symbol, + ); + const handleSetUpOptionClick = (option: string) => { + setSelectedSetUpOption(option); + if (option === "auto") { + setSlippageTolerance(0.5); + dispatch(setSlippageToleranceFromRedux(0.5)); + } + }; + // for mobile focus + const handleSetUpFocus = () => { if (selectedSetUpOption === "custom" && customInputRef.current) { customInputRef.current.focus(); } - }, [selectedSetUpOption]); - // whether the collateral is too small - const [longInputTooSmall, shortInputTooSmall] = useMemo(() => { - return [longInputUsd < MARGIN_MIN_COLLATERAL_USD, shortInputUsd < MARGIN_MIN_COLLATERAL_USD]; - }, [longInputUsd, shortInputUsd]); - // long tip - const tip_leverage = "Leverage must be greater than 1"; - const tip_usd = `Deposit at least $${MARGIN_MIN_COLLATERAL_USD}`; - const longTip = useMemo(() => { - if (accountId && longOutput > 0) { - if (rangeMount <= 1) return tip_leverage; - else if (longInputTooSmall) return tip_usd; + }; + const slippageToleranceChange = (e: any) => { + setSlippageTolerance(e); + dispatch(setSlippageToleranceFromRedux(e)); + }; + // open position btn click eve. + const handleConfirmButtonClick = () => { + if (isDisabled) return; + setIsConfirmModalOpen(true); + }; + + const isValidDecimalString = (str) => { + if (str <= 0) return false; + // const regex = /^(?![0]+$)\d+(\.\d+)?$/; + const regex = /^\d+(\.\d+)?$/; + return regex.test(str); + }; + // long & short input change fn. + const inputPriceChange = _.debounce((newValue: string) => { + // eslint-disable-next-line no-unused-expressions + activeTab === "long" ? setLongInput(newValue) : setShortInput(newValue); + }, 10); + let lastValue = ""; + // validate input + const isValidInput = (value: string): boolean => { + if (value === "") return true; + const regex = /^\d*\.?\d*$/; + if (!regex.test(value)) return false; + const num = parseFloat(value); + if (Number.isNaN(num)) return false; + return true; + }; + // handle input change + const tokenChange = (e: React.ChangeEvent) => { + const { value } = e.target; + if (value === "") { + setLongInput(""); + setShortInput(""); + setLongOutput(0); + setShortOutput(0); + setLongInputUsd(0); + setShortInputUsd(0); + setLongOutputUsd(0); + setShortOutputUsd(0); + setTokenInAmount(0); + setLiqPrice(""); + return; } - return ""; - }, [rangeMount, accountId, longOutput, longInputTooSmall]); - // short tip - const shortTip = useMemo(() => { - if (accountId && shortOutput > 0) { - if (rangeMount <= 1) return tip_leverage; - else if (shortInputTooSmall) return tip_usd; + setEstimateLoading(true); + if (!isValidInput(value)) return; + if (value.includes(".") && !lastValue.includes(".")) { + inputPriceChange.cancel(); + setTimeout(() => { + inputPriceChange(value); + }, 10); + } else { + inputPriceChange(value); } - return ""; - }, [rangeMount, accountId, shortOutput, shortInputTooSmall]); + lastValue = value; + }; const handleMouseEnter = () => { if (selectedSetUpOption === "custom" && customInputRef.current) { customInputRef.current.focus(); } }; - function getAssetPrice(categoryId) { return categoryId ? combinedAssetsData[categoryId["token_id"]]?.price?.usd : 0; } - function updateOutput(tab: string, inputUsdCharcate: number) { /** * @param inputUsdCharcate category1 current price @@ -460,7 +498,6 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => outputUsdSetter(inputUsdCharcate * tokenInAmount); } } - function updateInputAmounts(tab, inputUsdCharcate2, inputUsdCharcate1) { /** * @param inputUsdCharcate2 category2 current price @@ -481,27 +518,50 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => setTokenInAmount(adjustedInputAmount / inputUsdCharcate1); } } - - // end - function formatNumber(value, len) { - let formattedValue = value.toFixed(len); // + let formattedValue = value.toFixed(len); if (formattedValue.endsWith(".00")) { - // formattedValue = formattedValue.substring(0, formattedValue.length - 3); } else if (formattedValue.endsWith("0")) { - // 0 formattedValue = formattedValue.substring(0, formattedValue.length - 1); } return formattedValue; } - const formatDecimal = (value: number) => { if (!value) return "0"; - // return value.toFixed(6).replace(/\.?0+$/, ""); }; - + function getBorrowLimit(assetDebt: Asset, debt_amount: number) { + const assetsMap = isMainStream ? assets.data : assetsMEME.data; + const asset = assetsMap[assetDebt.token_id]; + const temp1 = new Decimal(asset.supplied.balance) + .plus(new Decimal(asset.reserved)) + .plus(asset.prot_fee) + .minus(new Decimal(asset.borrowed.balance || 0)) + .minus(new Decimal(asset.margin_debt.balance || 0)) + .minus(new Decimal(asset.margin_pending_debt || 0)); + const temp2 = temp1.minus(temp1.mul(0.001)).toFixed(0); + const availableLiquidity = Number( + shrinkToken(temp2, asset.metadata.decimals + asset.config.extra_decimals), + ); + // asset.config.min_borrowed_amount; + if (new Decimal(debt_amount || 0).gte(availableLiquidity || 0)) { + return { + isExceed: true, + availableLiquidity, + }; + } + if (new Decimal(asset.config.min_borrowed_amount || 0).gt(0)) { + const asset_decimals = asset.metadata.decimals + asset.config.extra_decimals; + const debt_amount_decimals = expandToken(debt_amount, asset_decimals); + if (new Decimal(debt_amount_decimals).lte(asset.config.min_borrowed_amount || 0)) { + return { + lowLoanLimit: true, + lowAmount: shrinkToken(asset.config.min_borrowed_amount || 0, asset_decimals), + }; + } + } + } return (
@@ -595,287 +655,273 @@ const TradingOperate: React.FC = ({ onMobileClose, id }) => {/* slip end */}
- {activeTab === "long" && ( - <> -
- +
+ +
+ setForceUpdate((prev) => prev + 1)} /> -
- setForceUpdate((prev) => prev + 1)} - /> -
-

${longInputUsd.toFixed(2)}

-
- +

${longInputUsd.toFixed(2)}

+
+
+ +
+
+ {/* long out */} +
{longOutput && beautifyPrice(Number(longOutput))}
+ {/* */} +
+
-
- {/* long out */} -
{longOutput && beautifyPrice(Number(longOutput))}
- {/* */} -
- +

+ ${longOutputUsd && formatNumber(Number(longOutputUsd), 2)} +

+
+ +
+
+
Position Size
+
+ {beautifyPrice(longOutput)} {cateSymbol} + + (${beautifyPrice(longOutputUsd)}) +
-

- ${longOutputUsd && formatNumber(Number(longOutputUsd), 2)} -

- -
-
-
Position Size
-
- {beautifyPrice(longOutput)} {cateSymbol} - - (${beautifyPrice(longOutputUsd)}) - -
-
-
-
Minimum received
-
- {beautifyPrice(Number(longOutput) * (1 - slippageTolerance / 100))} {cateSymbol} -
+
+
Minimum received
+
+ {beautifyPrice(Number(longOutput) * (1 - slippageTolerance / 100))} {cateSymbol}
-
-
Liq. Price
-
- {estimateLoading ? ( - - ) : ( - ${beautifyPrice(LiqPrice)} - )} -
-
-
-
Fee
-
-

setShowFeeModal(true)} - onMouseLeave={() => setShowFeeModal(false)} - > - ${beautifyPrice(Number(formatDecimal(Fee.swapFee + Fee.openPFee)))} -

- - {showFeeModal && ( -
-

- Open Fee:$ - {beautifyPrice(Fee.openPFee)} -

-

- Trade Fee:$ - {beautifyPrice(Fee.swapFee)} -

-
- )} -
+
+
+
Liq. Price
+
+ {estimateLoading ? ( + + ) : ( + ${beautifyPrice(LiqPrice)} + )}
- - {isMaxPosition && accountId && ( -
- - Exceeded the maximum number of open positions. -
- )} - {longTip && ( - - {longTip} - - )} - {accountId ? ( - +
+
Fee
+
+

setShowFeeModal(true)} + onMouseLeave={() => setShowFeeModal(false)} > - Long {cateSymbol} {rangeMount}x - - ) : ( - - )} - {isConfirmModalOpen && ( - { - setIsConfirmModalOpen(false); - if (onMobileClose) onMobileClose(); - }} - action="Long" - confirmInfo={{ - longInput, - longInputUsd, - longOutput, - longOutputUsd, - rangeMount, - estimateData, - longInputName: ReduxcategoryAssets2, - longOutputName: ReduxcategoryAssets1, - assets: isMainStream ? assets : assetsMEME, - tokenInAmount, - LiqPrice, - Fee, - }} - /> - )} + ${beautifyPrice(Number(formatDecimal(Fee.swapFee + Fee.openPFee)))} +

+ + {showFeeModal && ( +
+

+ Open Fee:$ + {beautifyPrice(Fee.openPFee)} +

+

+ Trade Fee:$ + {beautifyPrice(Fee.swapFee)} +

+
+ )} +
- - )} - {activeTab === "short" && ( - <> -
- -
- setForceUpdate((prev) => prev + 1)} - /> + {warnTip ? ( +
+ + {warnTip}
-

${shortInputUsd.toFixed(2)}

+ ) : null} + + {accountId ? ( + + Long {cateSymbol} {rangeMount}x + + ) : ( + + )} + {isConfirmModalOpen && ( + { + setIsConfirmModalOpen(false); + if (onMobileClose) onMobileClose(); + }} + action="Long" + confirmInfo={{ + longInput, + longInputUsd, + longOutput, + longOutputUsd, + rangeMount, + estimateData, + longInputName: ReduxcategoryAssets2, + longOutputName: ReduxcategoryAssets1, + assets: isMainStream ? assets : assetsMEME, + tokenInAmount, + LiqPrice, + Fee, + }} + /> + )} +
+
+
+
+ +
+ setForceUpdate((prev) => prev + 1)} + />
-
- +

${shortInputUsd.toFixed(2)}

+
+
+ +
+
+ {/* short out */} +
{shortOutput && beautifyPrice(Number(shortOutput))}
+ {/* */} +
+
-
- {/* short out */} -
{shortOutput && beautifyPrice(Number(shortOutput))}
- {/* */} -
- +

+ ${shortOutputUsd && formatNumber(Number(shortOutputUsd), 2)} +

+
+ +
+
+
Position Size
+
+ {beautifyPrice(shortOutput)} {cateSymbol} + + (${beautifyPrice(shortOutputUsd)}) +
-

- ${shortOutputUsd && formatNumber(Number(shortOutputUsd), 2)} -

- -
-
-
Position Size
-
- {beautifyPrice(shortOutput)} {cateSymbol} - - (${beautifyPrice(shortOutputUsd)}) - -
+
+
Minimum received
+
+ {beautifyPrice(Number(shortOutput) * (1 - slippageTolerance / 100))} {cateSymbol}
-
-
Minimum received
-
- {beautifyPrice(Number(shortOutput) * (1 - slippageTolerance / 100))} {cateSymbol} -
-
-
-
Liq. Price
-
- {estimateLoading ? ( - - ) : ( - ${beautifyPrice(LiqPrice)} - )} -
-
-
-
Fee
-
-

setShowFeeModal(true)} - onMouseLeave={() => setShowFeeModal(false)} - > - ${beautifyPrice(Number(formatDecimal(Fee.swapFee + Fee.openPFee)))} -

- {showFeeModal && ( -
-

- Open Fee:$ - {beautifyPrice(Fee.openPFee)} -

-

- Trade Fee:$ - {beautifyPrice(Fee.swapFee)} -

-
- )} -
+
+
+
Liq. Price
+
+ {estimateLoading ? ( + + ) : ( + ${beautifyPrice(LiqPrice)} + )}
- {isMaxPosition && accountId && ( -
- - Exceeded the maximum number of open positions. -
- )} - {shortTip && ( - - {shortTip} - - )} - {accountId ? ( - +
+
Fee
+
+

setShowFeeModal(true)} + onMouseLeave={() => setShowFeeModal(false)} > - Short {cateSymbol} {rangeMount}x - - ) : ( - - )} - {isConfirmModalOpen && ( - { - setIsConfirmModalOpen(false); - if (onMobileClose) onMobileClose(); - }} - action="Short" - confirmInfo={{ - longInput: shortInput, - longInputUsd: shortInputUsd, - longOutput: shortOutput, - longOutputUsd: shortOutputUsd, - rangeMount, - estimateData, - longInputName: ReduxcategoryAssets2, - longOutputName: ReduxcategoryAssets1, - assets: isMainStream ? assets : assetsMEME, - tokenInAmount, - LiqPrice, - Fee, - }} - /> - )} + ${beautifyPrice(Number(formatDecimal(Fee.swapFee + Fee.openPFee)))} +

+ {showFeeModal && ( +
+

+ Open Fee:$ + {beautifyPrice(Fee.openPFee)} +

+

+ Trade Fee:$ + {beautifyPrice(Fee.swapFee)} +

+
+ )} +
- - )} + {warnTip ? ( +
+ + {warnTip} +
+ ) : null} + {accountId ? ( + + Short {cateSymbol} {rangeMount}x + + ) : ( + + )} + {isConfirmModalOpen && ( + { + setIsConfirmModalOpen(false); + if (onMobileClose) onMobileClose(); + }} + action="Short" + confirmInfo={{ + longInput: shortInput, + longInputUsd: shortInputUsd, + longOutput: shortOutput, + longOutputUsd: shortOutputUsd, + rangeMount, + estimateData, + longInputName: ReduxcategoryAssets2, + longOutputName: ReduxcategoryAssets1, + assets: isMainStream ? assets : assetsMEME, + tokenInAmount, + LiqPrice, + Fee, + }} + /> + )} +
+
);