From a6b4dd175e8529f355b0e29ef442a119d2411c4a Mon Sep 17 00:00:00 2001 From: lq0-github <1441665200@qq.com> Date: Fri, 22 Nov 2024 18:19:53 +0800 Subject: [PATCH] Fix margin trading page --- .../components/MyTrading/MyTradingPage.tsx | 59 +++++-- screens/MarginTrading/index.tsx | 21 ++- .../components/ChangeCollateralMobile.tsx | 148 ++++++++++++++++-- 3 files changed, 193 insertions(+), 35 deletions(-) diff --git a/screens/MarginTrading/components/MyTrading/MyTradingPage.tsx b/screens/MarginTrading/components/MyTrading/MyTradingPage.tsx index 411e0929..6b021775 100644 --- a/screens/MarginTrading/components/MyTrading/MyTradingPage.tsx +++ b/screens/MarginTrading/components/MyTrading/MyTradingPage.tsx @@ -95,27 +95,52 @@ const MyMarginTradingPage = () => { onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > - {Object.values(marginAccountList).map((item, index) => { - const assetC = getAssetById(item.token_c_info.token_id); - const { - icon: iconC, - symbol: symbolC, - decimals: decimalsC, - price: priceC, - } = getAssetDetails(assetC); - const netValue = - parseTokenValue(item.token_c_info.balance, decimalsC) * (priceC || 0); + {(() => { + interface MergedCollateralData { + icon: string; + symbol: string; + totalValue: number; + } - return ( -
- -

{symbolC}

+ // 定义累加器对象的类型 + type CollateralAccumulator = { + [tokenId: string]: MergedCollateralData; + }; + + const mergedCollateral = Object.values( + marginAccountList, + ).reduce((acc, item) => { + const assetC = getAssetById(item.token_c_info.token_id); + const { decimals: decimalsC, price: priceC } = getAssetDetails(assetC); + const tokenId = item.token_c_info.token_id; + + const netValue = + parseTokenValue(item.token_c_info.balance, decimalsC) * (priceC || 0); + + if (!acc[tokenId]) { + const { icon: iconC, symbol: symbolC } = getAssetDetails(assetC); + acc[tokenId] = { + icon: iconC, + symbol: symbolC, + totalValue: netValue, + }; + } else { + acc[tokenId].totalValue += netValue; + } + + return acc; + }, {}); + + return Object.entries(mergedCollateral).map(([tokenId, data], index) => ( +
+ +

{data.symbol}

- ${toInternationalCurrencySystem_number(netValue)} + ${toInternationalCurrencySystem_number(data.totalValue)}
- ); - })} + )); + })()}
)} diff --git a/screens/MarginTrading/index.tsx b/screens/MarginTrading/index.tsx index 92a31580..8f1acd89 100644 --- a/screens/MarginTrading/index.tsx +++ b/screens/MarginTrading/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { useAppDispatch } from "../../redux/hooks"; import { LayoutBox } from "../../components/LayoutContainer/LayoutContainer"; import MyMarginTrading from "./components/MyTrading"; @@ -6,7 +6,16 @@ import MarketMarginTrading from "./components/MarketTrading"; const MarginTrading = () => { const dispatch = useAppDispatch(); - const [activeTab, setActiveTab] = useState("market"); + const [activeTab, setActiveTab] = useState(() => { + const savedTab = localStorage.getItem("marginTradingTab"); + return savedTab || "market"; + }); + + const handleTabChange = (tab: string) => { + setActiveTab(tab); + localStorage.setItem("marginTradingTab", tab); + }; + const getTabClassName = (tabName) => { const baseClass = "py-2.5 px-24 text-base"; const activeClass = "bg-primary rounded-md text-dark-200"; @@ -19,11 +28,15 @@ const MarginTrading = () => { - diff --git a/screens/Trading/components/ChangeCollateralMobile.tsx b/screens/Trading/components/ChangeCollateralMobile.tsx index 6560d64e..b270e8a2 100644 --- a/screens/Trading/components/ChangeCollateralMobile.tsx +++ b/screens/Trading/components/ChangeCollateralMobile.tsx @@ -73,15 +73,66 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) => setAddLeverage(0); } }; + const handleInputValidation = (value: string) => { + if (value.startsWith("-")) return false; + if (!/^\d*\.?\d*$/.test(value)) return false; + if (value.includes(".")) { + const decimals = value.split(".")[1]; + if (decimals.length > 8) return false; + } + return true; + }; const handleAddChange = (event) => { + const { value } = event.target; + if (value === "" || value === ".") { + setInputValue(NaN); + setAddedValue(0); + setAddLeverage(0); + return; + } + if (!handleInputValidation(value)) { + return; + } + const numValue = parseFloat(value); + const maxAmount = getMaxAvailableAmount(); + if (numValue > maxAmount) { + setInputValue(maxAmount); + const syntheticEvent = { + target: { value: String(maxAmount) }, + }; + handleCollateralChange(syntheticEvent, true); + return; + } + setInputValue(numValue); handleCollateralChange(event, true); }; - const handleDeleteChange = (event) => { + const { value } = event.target; + if (value === "" || value === ".") { + setInputValue(NaN); + setAddedValue(0); + setAddLeverage(0); + return; + } + if (!handleInputValidation(value)) { + return; + } + const numValue = parseFloat(value); + const tokenCInfoBalance = parseTokenValue(rowData.data.token_c_info.balance, decimalsC); + const maxRemovable = calculateMaxRemovable(); + const actualMaxRemovable = Math.min(tokenCInfoBalance, maxRemovable); + if (numValue > actualMaxRemovable) { + setInputValue(actualMaxRemovable); + const syntheticEvent = { + target: { value: String(actualMaxRemovable) }, + }; + handleCollateralChange(syntheticEvent, false); + return; + } + setInputValue(numValue); handleCollateralChange(event, false); }; - const assetD = getAssetById(rowData.data.token_d_info.token_id); const assetC = getAssetById(rowData.data.token_c_info.token_id); const assetP = getAssetById(rowData.data.token_p_id); @@ -137,6 +188,7 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) => const handleAddCollateralClick = async () => { try { await increaseCollateral({ pos_id, token_c_id, amount, assets }); + localStorage.setItem("marginTradingTab", "my"); } catch (error) { console.error("Error adding collateral:", error); } @@ -144,10 +196,66 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) => const handleDeleteCollateralClick = async () => { try { await decreaseCollateral({ pos_id, token_c_id, amount, assets }); + localStorage.setItem("marginTradingTab", "my"); } catch (error) { console.error("Error deleted collateral:", error); } }; + const getMaxAvailableAmount = () => { + const currentLeverage = leverage || 1; + const targetMinLeverage = 1; + const rawMaxAmount = Number(balance) / priceC; + const getNewLeverageAfterAdd = (addAmount: number) => { + const tokenCInfoBalance = parseTokenValue(rowData.data.token_c_info.balance, decimalsC); + const tokenDInfoBalance = parseTokenValue(rowData.data.token_d_info.balance, decimalsD); + const newCollateral = tokenCInfoBalance + addAmount; + return calculateLeverage(tokenDInfoBalance, priceD, newCollateral, priceC); + }; + let left = 0; + let right = rawMaxAmount; + let result = 0; + while (left <= right) { + const mid = (left + right) / 2; + const newLeverage = getNewLeverageAfterAdd(mid); + if (newLeverage >= targetMinLeverage) { + result = mid; + left = mid + 0.0001; // 增加精度 + } else { + right = mid - 0.0001; + } + } + return Math.min(rawMaxAmount, result); + }; + const calculateMaxRemovable = () => { + const tokenCInfoBalance = parseTokenValue(rowData.data.token_c_info.balance, decimalsC); + const tokenDInfoBalance = parseTokenValue(rowData.data.token_d_info.balance, decimalsD); + const maxLeverage = 10; + let left = 0; + let right = tokenCInfoBalance; + let result = 0; + while (left <= right) { + const mid = (left + right) / 2; + const remainingCollateral = tokenCInfoBalance - mid; + const newLeverage = calculateLeverage(tokenDInfoBalance, priceD, remainingCollateral, priceC); + if (newLeverage <= maxLeverage) { + result = mid; + left = mid + 0.0001; + } else { + right = mid - 0.0001; + } + } + return result; + }; + // const validateAmount = (amount: number) => { + // if (isNaN(amount) || amount <= 0) return false; + // const maxAmount = getMaxAvailableAmount(); + // if (amount > maxAmount) return false; + // const tokenCInfoBalance = parseTokenValue(rowData.data.token_c_info.balance, decimalsC); + // const tokenDInfoBalance = parseTokenValue(rowData.data.token_d_info.balance, decimalsD); + // const newCollateral = tokenCInfoBalance + amount; + // const newLeverage = calculateLeverage(tokenDInfoBalance, priceD, newCollateral, priceC); + // return newLeverage >= 1; + // }; const handleLeverAddClick = (value) => { if (selectedLever === value) { setSelectedLever(null); @@ -155,13 +263,13 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) => return; } setSelectedLever(value); + const maxAmount = getMaxAvailableAmount(); let newInputValue; if (value === "Max") { - const maxPercentage = Number(balance) / priceC; - newInputValue = Number(maxPercentage); + newInputValue = maxAmount; } else { const percentage = parseFloat(value); - newInputValue = (Number(balance) * percentage) / 100 / priceC; + newInputValue = (maxAmount * percentage) / 100; } setInputValue(newInputValue); handleCollateralChange({ target: { value: newInputValue } }, true); @@ -173,13 +281,15 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) => return; } setSelectedLever(value); + const maxRemovable = calculateMaxRemovable(); + const tokenCInfoBalance = parseTokenValue(rowData.data.token_c_info.balance, decimalsC); + const actualMaxRemovable = Math.min(tokenCInfoBalance, maxRemovable); let newInputValue; if (value === "Max") { - const maxPercentage = collateralTotal / priceC; - newInputValue = Number(maxPercentage); + newInputValue = actualMaxRemovable; } else { const percentage = parseFloat(value); - newInputValue = (collateralTotal * percentage) / 100 / priceC; + newInputValue = (actualMaxRemovable * percentage) / 100; } setInputValue(newInputValue); handleCollateralChange({ target: { value: newInputValue } }, false); @@ -248,11 +358,17 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) =>

Add: ${Number.isNaN(inputValue) ? 0 : inputValue * priceC} @@ -264,7 +380,11 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) =>

{symbolC}

- Max Available: ${balance} + Max Available:{" "} + + {" "} + ${toInternationalCurrencySystem_number(getMaxAvailableAmount() * priceC)} +

@@ -381,7 +501,7 @@ const ChangeCollateralMobile = ({ open, onClose, rowData, collateralTotal }) =>

Max Available:{" "} - ${toInternationalCurrencySystem_number(collateralTotal)} + ${toInternationalCurrencySystem_number(calculateMaxRemovable() * priceC)}